2
Copyright (C) 2002-2010 Jeroen Frijters
4
This software is provided 'as-is', without any express or implied
5
warranty. In no event will the authors be held liable for any damages
6
arising from the use of this software.
8
Permission is granted to anyone to use this software for any purpose,
9
including commercial applications, and to alter it and redistribute it
10
freely, subject to the following restrictions:
12
1. The origin of this software must not be misrepresented; you must not
13
claim that you wrote the original software. If you use this software
14
in a product, an acknowledgment in the product documentation would be
15
appreciated but is not required.
16
2. Altered source versions must be plainly marked as such, and must not be
17
misrepresented as being the original software.
18
3. This notice may not be removed or altered from any source distribution.
26
using System.Collections.Generic;
27
using System.Diagnostics;
29
using IKVM.Reflection.Emit;
30
using Type = IKVM.Reflection.Type;
32
using System.Reflection.Emit;
35
using InstructionFlags = IKVM.Internal.ClassFile.Method.InstructionFlags;
36
using ExceptionTableEntry = IKVM.Internal.ClassFile.Method.ExceptionTableEntry;
42
internal TypeWrapper type;
43
internal CodeEmitterLocal builder;
44
// used to emit debugging info, only available if ClassLoaderWrapper.EmitDebugInfo is true
46
internal int start_pc;
52
private readonly LocalVar[/*instructionIndex*/] localVars;
53
private readonly LocalVar[/*instructionIndex*/][/*localIndex*/] invokespecialLocalVars;
54
private readonly LocalVar[/*index*/] allLocalVars;
56
internal LocalVarInfo(CodeInfo ma, ClassFile classFile, ClassFile.Method method, UntangledExceptionTable exceptions, MethodWrapper mw, ClassLoaderWrapper classLoader)
58
Dictionary<int, string>[] localStoreReaders = FindLocalVariables(ma, mw, classFile, method);
60
// now that we've done the code flow analysis, we can do a liveness analysis on the local variables
61
ClassFile.Method.Instruction[] instructions = method.Instructions;
62
Dictionary<long, LocalVar> localByStoreSite = new Dictionary<long, LocalVar>();
63
List<LocalVar> locals = new List<LocalVar>();
64
for (int i = 0; i < localStoreReaders.Length; i++)
66
if (localStoreReaders[i] != null)
68
VisitLocalLoads(ma, method, locals, localByStoreSite, localStoreReaders[i], i, classLoader.EmitDebugInfo);
71
Dictionary<LocalVar, LocalVar> forwarders = new Dictionary<LocalVar, LocalVar>();
72
if (classLoader.EmitDebugInfo)
74
InstructionFlags[] flags = MethodAnalyzer.ComputePartialReachability(ma, method.Instructions, exceptions, 0, false);
75
// if we're emitting debug info, we need to keep dead stores as well...
76
for (int i = 0; i < instructions.Length; i++)
78
if ((flags[i] & InstructionFlags.Reachable) != 0
79
&& IsStoreLocal(instructions[i].NormalizedOpCode))
81
if (!localByStoreSite.ContainsKey(MakeKey(i, instructions[i].NormalizedArg1)))
83
LocalVar v = new LocalVar();
84
v.local = instructions[i].NormalizedArg1;
85
v.type = ma.GetStackTypeWrapper(i, 0);
86
FindLvtEntry(v, method, i);
88
localByStoreSite.Add(MakeKey(i, v.local), v);
92
// to make the debugging experience better, we have to trust the
93
// LocalVariableTable (unless it's clearly bogus) and merge locals
94
// together that are the same according to the LVT
95
for (int i = 0; i < locals.Count - 1; i++)
97
for (int j = i + 1; j < locals.Count; j++)
99
LocalVar v1 = (LocalVar)locals[i];
100
LocalVar v2 = (LocalVar)locals[j];
101
if (v1.name != null && v1.name == v2.name && v1.start_pc == v2.start_pc && v1.end_pc == v2.end_pc)
103
// we can only merge if the resulting type is valid (this protects against incorrect
104
// LVT data, but is also needed for constructors, where the uninitialized this is a different
105
// type from the initialized this)
106
TypeWrapper tw = InstructionState.FindCommonBaseType(v1.type, v2.type);
107
if (tw != VerifierTypeWrapper.Invalid)
109
v1.isArg |= v2.isArg;
111
forwarders.Add(v2, v1);
121
for (int i = 0; i < locals.Count - 1; i++)
123
for (int j = i + 1; j < locals.Count; j++)
125
LocalVar v1 = (LocalVar)locals[i];
126
LocalVar v2 = (LocalVar)locals[j];
127
// if the two locals are the same, we merge them, this is a small
128
// optimization, it should *not* be required for correctness.
129
if (v1.local == v2.local && v1.type == v2.type)
131
v1.isArg |= v2.isArg;
132
forwarders.Add(v2, v1);
139
invokespecialLocalVars = new LocalVar[instructions.Length][];
140
localVars = new LocalVar[instructions.Length];
141
for (int i = 0; i < localVars.Length; i++)
144
if (localStoreReaders[i] != null)
146
Debug.Assert(IsLoadLocal(instructions[i].NormalizedOpCode));
147
// lame way to look up the local variable for a load
148
// (by indirecting through a corresponding store)
149
foreach (int store in localStoreReaders[i].Keys)
151
v = localByStoreSite[MakeKey(store, instructions[i].NormalizedArg1)];
157
if (instructions[i].NormalizedOpCode == NormalizedByteCode.__invokespecial)
159
invokespecialLocalVars[i] = new LocalVar[method.MaxLocals];
160
for (int j = 0; j < invokespecialLocalVars[i].Length; j++)
162
localByStoreSite.TryGetValue(MakeKey(i, j), out invokespecialLocalVars[i][j]);
167
localByStoreSite.TryGetValue(MakeKey(i, instructions[i].NormalizedArg1), out v);
173
if (forwarders.TryGetValue(v, out fwd))
180
this.allLocalVars = locals.ToArray();
183
private static void FindLvtEntry(LocalVar lv, ClassFile.Method method, int instructionIndex)
185
ClassFile.Method.LocalVariableTableEntry[] lvt = method.LocalVariableTableAttribute;
188
int pc = method.Instructions[instructionIndex].PC;
189
int nextPC = method.Instructions[instructionIndex + 1].PC;
190
bool isStore = IsStoreLocal(method.Instructions[instructionIndex].NormalizedOpCode);
191
foreach (ClassFile.Method.LocalVariableTableEntry e in lvt)
193
// TODO validate the contents of the LVT entry
194
if (e.index == lv.local &&
195
(e.start_pc <= pc || (e.start_pc == nextPC && isStore)) &&
196
e.start_pc + e.length > pc)
199
lv.start_pc = e.start_pc;
200
lv.end_pc = e.start_pc + e.length;
207
// NOTE for dead stores, this returns null
208
internal LocalVar GetLocalVar(int instructionIndex)
210
return localVars[instructionIndex];
213
internal LocalVar[] GetLocalVarsForInvokeSpecial(int instructionIndex)
215
return invokespecialLocalVars[instructionIndex];
218
internal LocalVar[] GetAllLocalVars()
223
private static bool IsLoadLocal(NormalizedByteCode bc)
225
return bc == NormalizedByteCode.__aload ||
226
bc == NormalizedByteCode.__iload ||
227
bc == NormalizedByteCode.__lload ||
228
bc == NormalizedByteCode.__fload ||
229
bc == NormalizedByteCode.__dload ||
230
bc == NormalizedByteCode.__iinc ||
231
bc == NormalizedByteCode.__ret;
234
private static bool IsStoreLocal(NormalizedByteCode bc)
236
return bc == NormalizedByteCode.__astore ||
237
bc == NormalizedByteCode.__istore ||
238
bc == NormalizedByteCode.__lstore ||
239
bc == NormalizedByteCode.__fstore ||
240
bc == NormalizedByteCode.__dstore;
243
struct FindLocalVarState
245
internal bool changed;
246
internal FindLocalVarStoreSite[] sites;
248
internal void Store(int instructionIndex, int localIndex)
250
if (sites[localIndex].Count == 1 && sites[localIndex][0] == instructionIndex)
254
sites = (FindLocalVarStoreSite[])sites.Clone();
255
sites[localIndex] = new FindLocalVarStoreSite();
256
sites[localIndex].Add(instructionIndex);
259
internal void Merge(FindLocalVarState state)
269
for (int i = 0; i < sites.Length; i++)
271
for (int j = 0; j < state.sites[i].Count; j++)
273
if (!sites[i].Contains(state.sites[i][j]))
278
sites = (FindLocalVarStoreSite[])sites.Clone();
280
sites[i].Add(state.sites[i][j]);
288
internal FindLocalVarState Copy()
290
FindLocalVarState copy = new FindLocalVarState();
295
public override string ToString()
297
System.Text.StringBuilder sb = new System.Text.StringBuilder();
300
foreach (FindLocalVarStoreSite site in sites)
303
for (int i = 0; i < site.Count; i++)
305
sb.AppendFormat("{0}, ", site[i]);
310
return sb.ToString();
314
struct FindLocalVarStoreSite
318
internal bool Contains(int instructionIndex)
322
for (int i = 0; i < data.Length; i++)
324
if (data[i] == instructionIndex)
333
internal void Add(int instructionIndex)
337
data = new int[] { instructionIndex };
341
Array.Resize(ref data, data.Length + 1);
342
data[data.Length - 1] = instructionIndex;
346
internal int this[int index]
348
get { return data[index]; }
353
get { return data == null ? 0 : data.Length; }
357
private static Dictionary<int, string>[] FindLocalVariables(CodeInfo codeInfo, MethodWrapper mw, ClassFile classFile, ClassFile.Method method)
359
FindLocalVarState[] state = new FindLocalVarState[method.Instructions.Length];
360
state[0].changed = true;
361
state[0].sites = new FindLocalVarStoreSite[method.MaxLocals];
362
TypeWrapper[] parameters = mw.GetParameters();
366
state[0].sites[argpos++].Add(-1);
368
for (int i = 0; i < parameters.Length; i++)
370
state[0].sites[argpos++].Add(-1);
371
if (parameters[i].IsWidePrimitive)
376
return FindLocalVariablesImpl(codeInfo, classFile, method, state);
379
private static Dictionary<int, string>[] FindLocalVariablesImpl(CodeInfo codeInfo, ClassFile classFile, ClassFile.Method method, FindLocalVarState[] state)
381
ClassFile.Method.Instruction[] instructions = method.Instructions;
382
ExceptionTableEntry[] exceptions = method.ExceptionTable;
383
int maxLocals = method.MaxLocals;
384
Dictionary<int, string>[] localStoreReaders = new Dictionary<int, string>[instructions.Length];
390
for (int i = 0; i < instructions.Length; i++)
392
if (state[i].changed)
395
state[i].changed = false;
397
FindLocalVarState curr = state[i].Copy();
399
for (int j = 0; j < exceptions.Length; j++)
401
if (exceptions[j].startIndex <= i && i < exceptions[j].endIndex)
403
state[exceptions[j].handlerIndex].Merge(curr);
407
if (IsLoadLocal(instructions[i].NormalizedOpCode)
408
&& (instructions[i].NormalizedOpCode != NormalizedByteCode.__aload || !VerifierTypeWrapper.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(i + 1, 0))))
410
if (localStoreReaders[i] == null)
412
localStoreReaders[i] = new Dictionary<int, string>();
414
for (int j = 0; j < curr.sites[instructions[i].NormalizedArg1].Count; j++)
416
localStoreReaders[i][curr.sites[instructions[i].NormalizedArg1][j]] = "";
420
if (IsStoreLocal(instructions[i].NormalizedOpCode)
421
&& (instructions[i].NormalizedOpCode != NormalizedByteCode.__astore || !VerifierTypeWrapper.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(i, 0))))
423
curr.Store(i, instructions[i].NormalizedArg1);
424
// if this is a store at the end of an exception block,
425
// we need to propagate the new state to the exception handler
426
for (int j = 0; j < exceptions.Length; j++)
428
if (exceptions[j].endIndex == i + 1)
430
state[exceptions[j].handlerIndex].Merge(curr);
435
if (instructions[i].NormalizedOpCode == NormalizedByteCode.__invokespecial)
437
ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(instructions[i].Arg1);
438
if (ReferenceEquals(cpi.Name, StringConstants.INIT))
440
TypeWrapper type = codeInfo.GetRawStackTypeWrapper(i, cpi.GetArgTypes().Length);
441
// after we've invoked the constructor, the uninitialized references
442
// are now initialized
443
if (type == VerifierTypeWrapper.UninitializedThis
444
|| VerifierTypeWrapper.IsNew(type))
446
for (int j = 0; j < maxLocals; j++)
448
if (codeInfo.GetLocalTypeWrapper(i, j) == type)
456
else if (instructions[i].NormalizedOpCode == NormalizedByteCode.__goto_finally)
458
int handler = instructions[i].HandlerIndex;
460
// Normally a store at the end of a try block doesn't affect the handler block,
461
// but in the case of a finally handler it does, so we need to make sure that
462
// we merge here in case the try block ended with a store.
463
state[handler].Merge(curr);
465
// Now we recursively analyse the handler and afterwards merge the endfault locations back to us
466
FindLocalVarState[] handlerState = new FindLocalVarState[instructions.Length];
467
handlerState[handler].changed = true;
468
handlerState[handler].sites = new FindLocalVarStoreSite[maxLocals];
469
FindLocalVariablesImpl(codeInfo, classFile, method, handlerState);
471
// Merge back to the target of our __goto_finally
472
for (int j = 0; j < handlerState.Length; j++)
474
if (instructions[j].NormalizedOpCode == NormalizedByteCode.__athrow
475
&& codeInfo.HasState(j)
476
&& VerifierTypeWrapper.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(j, 0))
477
&& ((VerifierTypeWrapper)codeInfo.GetRawStackTypeWrapper(j, 0)).Index == handler)
479
state[instructions[i].Arg1].Merge(handlerState[j]);
484
switch (ByteCodeMetaData.GetFlowControl(instructions[i].NormalizedOpCode))
486
case ByteCodeFlowControl.Switch:
488
for (int j = 0; j < instructions[i].SwitchEntryCount; j++)
490
state[instructions[i].GetSwitchTargetIndex(j)].Merge(curr);
492
state[instructions[i].DefaultTarget].Merge(curr);
495
case ByteCodeFlowControl.Branch:
496
state[instructions[i].TargetIndex].Merge(curr);
498
case ByteCodeFlowControl.CondBranch:
499
state[instructions[i].TargetIndex].Merge(curr);
500
state[i + 1].Merge(curr);
502
case ByteCodeFlowControl.Return:
503
case ByteCodeFlowControl.Throw:
505
case ByteCodeFlowControl.Next:
506
state[i + 1].Merge(curr);
509
throw new InvalidOperationException();
514
return localStoreReaders;
517
private static void VisitLocalLoads(CodeInfo codeInfo, ClassFile.Method method, List<LocalVar> locals, Dictionary<long, LocalVar> localByStoreSite, Dictionary<int, string> storeSites, int instructionIndex, bool debug)
519
Debug.Assert(IsLoadLocal(method.Instructions[instructionIndex].NormalizedOpCode));
520
LocalVar local = null;
521
TypeWrapper type = VerifierTypeWrapper.Null;
522
int localIndex = method.Instructions[instructionIndex].NormalizedArg1;
524
foreach (int store in storeSites.Keys)
528
// it's a method argument, it has no initial store, but the type is simply the parameter type
529
type = InstructionState.FindCommonBaseType(type, codeInfo.GetLocalTypeWrapper(0, localIndex));
534
if (method.Instructions[store].NormalizedOpCode == NormalizedByteCode.__invokespecial)
536
type = InstructionState.FindCommonBaseType(type, codeInfo.GetLocalTypeWrapper(store + 1, localIndex));
538
else if (method.Instructions[store].NormalizedOpCode == NormalizedByteCode.__static_error)
540
// it's an __invokespecial that turned into a __static_error
541
// (since a __static_error doesn't continue, we don't need to set type)
545
Debug.Assert(IsStoreLocal(method.Instructions[store].NormalizedOpCode));
546
type = InstructionState.FindCommonBaseType(type, codeInfo.GetStackTypeWrapper(store, 0));
549
// we can't have an invalid type, because that would have failed verification earlier
550
Debug.Assert(type != VerifierTypeWrapper.Invalid);
553
if (localByStoreSite.TryGetValue(MakeKey(store, localIndex), out l))
561
// If we've already defined a LocalVar and we find another one, then we merge them
563
// This happens for the following code fragment:
566
// try { i = 0; for(; ; ) System.out.println(i); } catch(Exception x) {}
567
// try { i = 0; for(; ; ) System.out.println(i); } catch(Exception x) {}
568
// System.out.println(i);
570
local = MergeLocals(locals, localByStoreSite, local, l);
576
local = new LocalVar();
577
local.local = localIndex;
578
if (VerifierTypeWrapper.IsThis(type))
580
local.type = ((VerifierTypeWrapper)type).UnderlyingType;
589
FindLvtEntry(local, method, instructionIndex);
595
local.isArg |= isArg;
596
local.type = InstructionState.FindCommonBaseType(local.type, type);
597
Debug.Assert(local.type != VerifierTypeWrapper.Invalid);
599
foreach (int store in storeSites.Keys)
602
if (!localByStoreSite.TryGetValue(MakeKey(store, localIndex), out v))
604
localByStoreSite[MakeKey(store, localIndex)] = local;
608
local = MergeLocals(locals, localByStoreSite, local, v);
613
private static long MakeKey(int i, int j)
615
return (((long)(uint)i) << 32) + (uint)j;
618
private static LocalVar MergeLocals(List<LocalVar> locals, Dictionary<long, LocalVar> localByStoreSite, LocalVar l1, LocalVar l2)
620
Debug.Assert(l1 != l2);
621
Debug.Assert(l1.local == l2.local);
622
for (int i = 0; i < locals.Count; i++)
630
Dictionary<long, LocalVar> temp = new Dictionary<long, LocalVar>(localByStoreSite);
631
localByStoreSite.Clear();
632
foreach (KeyValuePair<long, LocalVar> kv in temp)
634
localByStoreSite[kv.Key] = kv.Value == l2 ? l1 : kv.Value;
636
l1.isArg |= l2.isArg;
637
l1.type = InstructionState.FindCommonBaseType(l1.type, l2.type);
638
Debug.Assert(l1.type != VerifierTypeWrapper.Invalid);