зеркало из https://github.com/mono/ikvm-fork.git
Implemented codegen improvement to use CLR fault handlers for Java catch all handlers, whenever possible.
This commit is contained in:
Родитель
3ca0627114
Коммит
0ed13314f0
|
@ -4620,14 +4620,22 @@ namespace IKVM.Internal
|
||||||
sealed class VerifierTypeWrapper : TypeWrapper
|
sealed class VerifierTypeWrapper : TypeWrapper
|
||||||
{
|
{
|
||||||
internal static readonly TypeWrapper Invalid = null;
|
internal static readonly TypeWrapper Invalid = null;
|
||||||
internal static readonly TypeWrapper Null = new VerifierTypeWrapper("null", 0, null);
|
internal static readonly TypeWrapper Null = new VerifierTypeWrapper("null", 0, null, null);
|
||||||
internal static readonly TypeWrapper UninitializedThis = new VerifierTypeWrapper("uninitialized-this", 0, null);
|
internal static readonly TypeWrapper UninitializedThis = new VerifierTypeWrapper("uninitialized-this", 0, null, null);
|
||||||
internal static readonly TypeWrapper Unloadable = new UnloadableTypeWrapper("<verifier>");
|
internal static readonly TypeWrapper Unloadable = new UnloadableTypeWrapper("<verifier>");
|
||||||
internal static readonly TypeWrapper ExtendedFloat = new VerifierTypeWrapper("<extfloat>", 0, null);
|
internal static readonly TypeWrapper ExtendedFloat = new VerifierTypeWrapper("<extfloat>", 0, null, null);
|
||||||
internal static readonly TypeWrapper ExtendedDouble = new VerifierTypeWrapper("<extdouble>", 0, null);
|
internal static readonly TypeWrapper ExtendedDouble = new VerifierTypeWrapper("<extdouble>", 0, null, null);
|
||||||
|
|
||||||
private int index;
|
private int index;
|
||||||
private TypeWrapper underlyingType;
|
private TypeWrapper underlyingType;
|
||||||
|
private MethodAnalyzer methodAnalyzer;
|
||||||
|
|
||||||
|
#if STUB_GENERATOR
|
||||||
|
internal class MethodAnalyzer
|
||||||
|
{
|
||||||
|
internal void ClearFaultBlockException(int dummy) { }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
@ -4636,7 +4644,12 @@ namespace IKVM.Internal
|
||||||
|
|
||||||
internal static TypeWrapper MakeNew(TypeWrapper type, int bytecodeIndex)
|
internal static TypeWrapper MakeNew(TypeWrapper type, int bytecodeIndex)
|
||||||
{
|
{
|
||||||
return new VerifierTypeWrapper("new", bytecodeIndex, type);
|
return new VerifierTypeWrapper("new", bytecodeIndex, type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TypeWrapper MakeFaultBlockException(MethodAnalyzer ma, int handlerIndex)
|
||||||
|
{
|
||||||
|
return new VerifierTypeWrapper("<fault>", handlerIndex, null, ma);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE the "this" type is special, it can only exist in local[0] and on the stack
|
// NOTE the "this" type is special, it can only exist in local[0] and on the stack
|
||||||
|
@ -4646,7 +4659,7 @@ namespace IKVM.Internal
|
||||||
// stack (using ldarg_0).
|
// stack (using ldarg_0).
|
||||||
internal static TypeWrapper MakeThis(TypeWrapper type)
|
internal static TypeWrapper MakeThis(TypeWrapper type)
|
||||||
{
|
{
|
||||||
return new VerifierTypeWrapper("this", 0, type);
|
return new VerifierTypeWrapper("this", 0, type, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsNew(TypeWrapper w)
|
internal static bool IsNew(TypeWrapper w)
|
||||||
|
@ -4654,6 +4667,11 @@ namespace IKVM.Internal
|
||||||
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "new");
|
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "new");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static bool IsFaultBlockException(TypeWrapper w)
|
||||||
|
{
|
||||||
|
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "<fault>");
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool IsNullOrUnloadable(TypeWrapper w)
|
internal static bool IsNullOrUnloadable(TypeWrapper w)
|
||||||
{
|
{
|
||||||
return w == Null || w.IsUnloadable;
|
return w == Null || w.IsUnloadable;
|
||||||
|
@ -4664,6 +4682,12 @@ namespace IKVM.Internal
|
||||||
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "this");
|
return w != null && w.IsVerifierType && ReferenceEquals(w.Name, "this");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void ClearFaultBlockException(TypeWrapper w)
|
||||||
|
{
|
||||||
|
VerifierTypeWrapper vtw = (VerifierTypeWrapper)w;
|
||||||
|
vtw.methodAnalyzer.ClearFaultBlockException(vtw.Index);
|
||||||
|
}
|
||||||
|
|
||||||
internal int Index
|
internal int Index
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -4680,11 +4704,12 @@ namespace IKVM.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private VerifierTypeWrapper(string name, int index, TypeWrapper underlyingType)
|
private VerifierTypeWrapper(string name, int index, TypeWrapper underlyingType, MethodAnalyzer methodAnalyzer)
|
||||||
: base(TypeWrapper.VerifierTypeModifiersHack, name, null)
|
: base(TypeWrapper.VerifierTypeModifiersHack, name, null)
|
||||||
{
|
{
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.underlyingType = underlyingType;
|
this.underlyingType = underlyingType;
|
||||||
|
this.methodAnalyzer = methodAnalyzer;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override ClassLoaderWrapper GetClassLoader()
|
internal override ClassLoaderWrapper GetClassLoader()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2002-2009 Jeroen Frijters
|
Copyright (C) 2002-2010 Jeroen Frijters
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
This software is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the authors be held liable for any damages
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
@ -173,8 +173,6 @@ sealed class Compiler
|
||||||
private readonly ClassFile.Method m;
|
private readonly ClassFile.Method m;
|
||||||
private readonly CodeEmitter ilGenerator;
|
private readonly CodeEmitter ilGenerator;
|
||||||
private readonly MethodAnalyzer ma;
|
private readonly MethodAnalyzer ma;
|
||||||
private readonly ClassFile.Method.InstructionFlags[] flags;
|
|
||||||
private readonly ExceptionTableEntry[] exceptions;
|
|
||||||
private readonly ISymbolDocumentWriter symboldocument;
|
private readonly ISymbolDocumentWriter symboldocument;
|
||||||
private readonly LineNumberTableAttribute.LineNumberWriter lineNumbers;
|
private readonly LineNumberTableAttribute.LineNumberWriter lineNumbers;
|
||||||
private bool nonleaf;
|
private bool nonleaf;
|
||||||
|
@ -300,9 +298,6 @@ sealed class Compiler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flags = ma.ComputePartialReachability(0, m.ExceptionTable);
|
|
||||||
exceptions = MethodAnalyzer.UntangleExceptionBlocks(classFile, m.Instructions, flags, m.ExceptionTable);
|
|
||||||
|
|
||||||
// if we're emitting debugging information, we need to use scopes for local variables
|
// if we're emitting debugging information, we need to use scopes for local variables
|
||||||
if(debug)
|
if(debug)
|
||||||
{
|
{
|
||||||
|
@ -414,6 +409,7 @@ sealed class Compiler
|
||||||
New,
|
New,
|
||||||
This,
|
This,
|
||||||
UnitializedThis,
|
UnitializedThis,
|
||||||
|
FaultBlockException,
|
||||||
Other
|
Other
|
||||||
}
|
}
|
||||||
private Compiler compiler;
|
private Compiler compiler;
|
||||||
|
@ -466,6 +462,10 @@ sealed class Compiler
|
||||||
// uninitialized references cannot be stored in a local, but we can reload them
|
// uninitialized references cannot be stored in a local, but we can reload them
|
||||||
types[i] = StackType.UnitializedThis;
|
types[i] = StackType.UnitializedThis;
|
||||||
}
|
}
|
||||||
|
else if (VerifierTypeWrapper.IsFaultBlockException(type))
|
||||||
|
{
|
||||||
|
types[i] = StackType.FaultBlockException;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
types[i] = StackType.Other;
|
types[i] = StackType.Other;
|
||||||
|
@ -481,7 +481,8 @@ sealed class Compiler
|
||||||
compiler.ilGenerator.Emit(OpCodes.Ldnull);
|
compiler.ilGenerator.Emit(OpCodes.Ldnull);
|
||||||
break;
|
break;
|
||||||
case StackType.New:
|
case StackType.New:
|
||||||
// new objects aren't really there on the stack
|
case StackType.FaultBlockException:
|
||||||
|
// objects aren't really there on the stack
|
||||||
break;
|
break;
|
||||||
case StackType.This:
|
case StackType.This:
|
||||||
case StackType.UnitializedThis:
|
case StackType.UnitializedThis:
|
||||||
|
@ -505,7 +506,8 @@ sealed class Compiler
|
||||||
compiler.ilGenerator.LazyEmitPop();
|
compiler.ilGenerator.LazyEmitPop();
|
||||||
break;
|
break;
|
||||||
case StackType.New:
|
case StackType.New:
|
||||||
// new objects aren't really there on the stack
|
case StackType.FaultBlockException:
|
||||||
|
// objects aren't really there on the stack
|
||||||
break;
|
break;
|
||||||
case StackType.Other:
|
case StackType.Other:
|
||||||
compiler.ilGenerator.Emit(OpCodes.Stloc, locals[i]);
|
compiler.ilGenerator.Emit(OpCodes.Stloc, locals[i]);
|
||||||
|
@ -615,7 +617,7 @@ sealed class Compiler
|
||||||
ilGenerator.Emit(OpCodes.Call, monitorEnterMethod);
|
ilGenerator.Emit(OpCodes.Call, monitorEnterMethod);
|
||||||
ilGenerator.BeginExceptionBlock();
|
ilGenerator.BeginExceptionBlock();
|
||||||
Block b = new Block(c, 0, int.MaxValue, -1, new List<object>(), true);
|
Block b = new Block(c, 0, int.MaxValue, -1, new List<object>(), true);
|
||||||
c.Compile(b);
|
c.Compile(b, c.ma.ComputePartialReachability(0, true));
|
||||||
b.Leave();
|
b.Leave();
|
||||||
ilGenerator.BeginFinallyBlock();
|
ilGenerator.BeginFinallyBlock();
|
||||||
ilGenerator.Emit(OpCodes.Ldloc, monitor);
|
ilGenerator.Emit(OpCodes.Ldloc, monitor);
|
||||||
|
@ -626,14 +628,15 @@ sealed class Compiler
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Block b = new Block(c, 0, int.MaxValue, -1, null, false);
|
Block b = new Block(c, 0, int.MaxValue, -1, null, false);
|
||||||
c.Compile(b);
|
c.Compile(b, c.ma.ComputePartialReachability(0, true));
|
||||||
b.Leave();
|
b.Leave();
|
||||||
}
|
}
|
||||||
if(c.lineNumbers != null)
|
if(c.lineNumbers != null)
|
||||||
{
|
{
|
||||||
|
InstructionFlags[] flags = c.ma.ComputePartialReachability(0, false);
|
||||||
for(int i = 0; i < m.Instructions.Length; i++)
|
for(int i = 0; i < m.Instructions.Length; i++)
|
||||||
{
|
{
|
||||||
if((c.flags[i] & InstructionFlags.Reachable) == 0)
|
if((flags[i] & InstructionFlags.Reachable) == 0)
|
||||||
{
|
{
|
||||||
// skip unreachable instructions
|
// skip unreachable instructions
|
||||||
}
|
}
|
||||||
|
@ -885,31 +888,9 @@ sealed class Compiler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsGuardedBlock(Stack<Block> blockStack, int instructionIndex, int instructionCount)
|
private void Compile(Block block, InstructionFlags[] flags)
|
||||||
{
|
|
||||||
int start = instructionIndex;
|
|
||||||
int end = instructionIndex + instructionCount;
|
|
||||||
for(int i = 0; i < exceptions.Length; i++)
|
|
||||||
{
|
|
||||||
ExceptionTableEntry e = exceptions[i];
|
|
||||||
if(e.endIndex > start && e.startIndex < end)
|
|
||||||
{
|
|
||||||
foreach(Block block in blockStack)
|
|
||||||
{
|
|
||||||
if(block.ExceptionIndex == i)
|
|
||||||
{
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
next:;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Compile(Block block)
|
|
||||||
{
|
{
|
||||||
|
ExceptionTableEntry[] exceptions = ma.GetExceptionTableFor(flags);
|
||||||
int exceptionIndex = 0;
|
int exceptionIndex = 0;
|
||||||
Instruction[] code = m.Instructions;
|
Instruction[] code = m.Instructions;
|
||||||
Stack<Block> blockStack = new Stack<Block>();
|
Stack<Block> blockStack = new Stack<Block>();
|
||||||
|
@ -948,32 +929,10 @@ sealed class Compiler
|
||||||
|
|
||||||
int handlerIndex = exc.handlerIndex;
|
int handlerIndex = exc.handlerIndex;
|
||||||
|
|
||||||
if(exc.catch_type == 0
|
if(exc.catch_type == 0 && VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(handlerIndex, 0)))
|
||||||
&& handlerIndex + 2 < m.Instructions.Length
|
|
||||||
&& m.Instructions[handlerIndex].NormalizedOpCode == NormalizedByteCode.__aload
|
|
||||||
&& m.Instructions[handlerIndex + 1].NormalizedOpCode == NormalizedByteCode.__monitorexit
|
|
||||||
&& m.Instructions[handlerIndex + 2].NormalizedOpCode == NormalizedByteCode.__athrow
|
|
||||||
&& !IsGuardedBlock(blockStack, handlerIndex, 3))
|
|
||||||
{
|
{
|
||||||
// this is the Jikes & Eclipse Java Compiler synchronization block exit
|
|
||||||
ilGenerator.BeginFaultBlock();
|
ilGenerator.BeginFaultBlock();
|
||||||
LoadLocal(handlerIndex);
|
Compile(new Block(this, 0, block.EndIndex, exceptionIndex, null, false), ma.ComputePartialReachability(handlerIndex, true));
|
||||||
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
|
|
||||||
ilGenerator.EndExceptionBlockNoFallThrough();
|
|
||||||
}
|
|
||||||
else if(exc.catch_type == 0
|
|
||||||
&& handlerIndex + 3 < m.Instructions.Length
|
|
||||||
&& m.Instructions[handlerIndex].NormalizedOpCode == NormalizedByteCode.__astore
|
|
||||||
&& m.Instructions[handlerIndex + 1].NormalizedOpCode == NormalizedByteCode.__aload
|
|
||||||
&& m.Instructions[handlerIndex + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit
|
|
||||||
&& m.Instructions[handlerIndex + 3].NormalizedOpCode == NormalizedByteCode.__aload
|
|
||||||
&& m.Instructions[handlerIndex + 4].NormalizedOpCode == NormalizedByteCode.__athrow
|
|
||||||
&& !IsGuardedBlock(blockStack, handlerIndex, 5))
|
|
||||||
{
|
|
||||||
// this is the javac synchronization block exit
|
|
||||||
ilGenerator.BeginFaultBlock();
|
|
||||||
LoadLocal(handlerIndex + 1);
|
|
||||||
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
|
|
||||||
ilGenerator.EndExceptionBlockNoFallThrough();
|
ilGenerator.EndExceptionBlockNoFallThrough();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1802,6 +1761,10 @@ sealed class Compiler
|
||||||
// so we don't have to worry about that.
|
// so we don't have to worry about that.
|
||||||
ilGenerator.Emit(OpCodes.Ldarg_0);
|
ilGenerator.Emit(OpCodes.Ldarg_0);
|
||||||
}
|
}
|
||||||
|
else if (VerifierTypeWrapper.IsFaultBlockException(type))
|
||||||
|
{
|
||||||
|
// not really there
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LocalVar v = LoadLocal(i);
|
LocalVar v = LoadLocal(i);
|
||||||
|
@ -1827,6 +1790,10 @@ sealed class Compiler
|
||||||
// the unitialized ref is loaded we redirect to the this reference
|
// the unitialized ref is loaded we redirect to the this reference
|
||||||
ilGenerator.LazyEmitPop();
|
ilGenerator.LazyEmitPop();
|
||||||
}
|
}
|
||||||
|
else if(VerifierTypeWrapper.IsFaultBlockException(type))
|
||||||
|
{
|
||||||
|
// not really there
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StoreLocal(i);
|
StoreLocal(i);
|
||||||
|
@ -1866,7 +1833,7 @@ sealed class Compiler
|
||||||
ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name);
|
ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name);
|
||||||
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewCheckOnly);
|
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewCheckOnly);
|
||||||
}
|
}
|
||||||
else if(wrapper != clazz && RequiresExplicitClassInit(wrapper, i + 1))
|
else if(wrapper != clazz && RequiresExplicitClassInit(wrapper, i + 1, flags))
|
||||||
{
|
{
|
||||||
// trigger cctor (as the spec requires)
|
// trigger cctor (as the spec requires)
|
||||||
wrapper.EmitRunClassConstructor(ilGenerator);
|
wrapper.EmitRunClassConstructor(ilGenerator);
|
||||||
|
@ -2572,12 +2539,19 @@ sealed class Compiler
|
||||||
ilGenerator.Emit(OpCodes.Throw);
|
ilGenerator.Emit(OpCodes.Throw);
|
||||||
break;
|
break;
|
||||||
case NormalizedByteCode.__athrow:
|
case NormalizedByteCode.__athrow:
|
||||||
if(ma.GetRawStackTypeWrapper(i, 0).IsUnloadable)
|
if (VerifierTypeWrapper.IsFaultBlockException(ma.GetRawStackTypeWrapper(i, 0)))
|
||||||
|
{
|
||||||
|
ilGenerator.Emit(OpCodes.Endfinally);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ma.GetRawStackTypeWrapper(i, 0).IsUnloadable)
|
||||||
{
|
{
|
||||||
ilGenerator.Emit(OpCodes.Castclass, Types.Exception);
|
ilGenerator.Emit(OpCodes.Castclass, Types.Exception);
|
||||||
}
|
}
|
||||||
ilGenerator.Emit(OpCodes.Call, unmapExceptionMethod);
|
ilGenerator.Emit(OpCodes.Call, unmapExceptionMethod);
|
||||||
ilGenerator.Emit(OpCodes.Throw);
|
ilGenerator.Emit(OpCodes.Throw);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NormalizedByteCode.__tableswitch:
|
case NormalizedByteCode.__tableswitch:
|
||||||
{
|
{
|
||||||
|
@ -2736,7 +2710,7 @@ sealed class Compiler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool RequiresExplicitClassInit(TypeWrapper tw, int index)
|
private bool RequiresExplicitClassInit(TypeWrapper tw, int index, InstructionFlags[] flags)
|
||||||
{
|
{
|
||||||
ClassFile.Method.Instruction[] code = m.Instructions;
|
ClassFile.Method.Instruction[] code = m.Instructions;
|
||||||
for (; index < code.Length; index++)
|
for (; index < code.Length; index++)
|
||||||
|
|
|
@ -307,6 +307,16 @@ class InstructionState
|
||||||
{
|
{
|
||||||
return VerifierTypeWrapper.Invalid;
|
return VerifierTypeWrapper.Invalid;
|
||||||
}
|
}
|
||||||
|
if(VerifierTypeWrapper.IsFaultBlockException(type1))
|
||||||
|
{
|
||||||
|
VerifierTypeWrapper.ClearFaultBlockException(type1);
|
||||||
|
return FindCommonBaseType(CoreClasses.java.lang.Throwable.Wrapper, type2);
|
||||||
|
}
|
||||||
|
if(VerifierTypeWrapper.IsFaultBlockException(type2))
|
||||||
|
{
|
||||||
|
VerifierTypeWrapper.ClearFaultBlockException(type2);
|
||||||
|
return FindCommonBaseType(type1, CoreClasses.java.lang.Throwable.Wrapper);
|
||||||
|
}
|
||||||
if(type1.IsPrimitive || type2.IsPrimitive)
|
if(type1.IsPrimitive || type2.IsPrimitive)
|
||||||
{
|
{
|
||||||
return VerifierTypeWrapper.Invalid;
|
return VerifierTypeWrapper.Invalid;
|
||||||
|
@ -677,6 +687,11 @@ class InstructionState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal TypeWrapper PopFaultBlockException()
|
||||||
|
{
|
||||||
|
return stack[--stackSize];
|
||||||
|
}
|
||||||
|
|
||||||
internal TypeWrapper PopAnyType()
|
internal TypeWrapper PopAnyType()
|
||||||
{
|
{
|
||||||
if(stackSize == 0)
|
if(stackSize == 0)
|
||||||
|
@ -692,6 +707,11 @@ class InstructionState
|
||||||
{
|
{
|
||||||
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
||||||
}
|
}
|
||||||
|
if(VerifierTypeWrapper.IsFaultBlockException(type))
|
||||||
|
{
|
||||||
|
VerifierTypeWrapper.ClearFaultBlockException(type);
|
||||||
|
type = CoreClasses.java.lang.Throwable.Wrapper;
|
||||||
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,6 +898,16 @@ class InstructionState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ClearFaultBlockException()
|
||||||
|
{
|
||||||
|
if(VerifierTypeWrapper.IsFaultBlockException(stack[0]))
|
||||||
|
{
|
||||||
|
StackCopyOnWrite();
|
||||||
|
changed = true;
|
||||||
|
stack[0] = CoreClasses.java.lang.Throwable.Wrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StackState
|
struct StackState
|
||||||
|
@ -916,6 +946,11 @@ struct StackState
|
||||||
{
|
{
|
||||||
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
||||||
}
|
}
|
||||||
|
if(VerifierTypeWrapper.IsFaultBlockException(type))
|
||||||
|
{
|
||||||
|
VerifierTypeWrapper.ClearFaultBlockException(type);
|
||||||
|
type = CoreClasses.java.lang.Throwable.Wrapper;
|
||||||
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1118,6 +1153,7 @@ class MethodAnalyzer
|
||||||
private LocalVar[/*instructionIndex*/][/*localIndex*/] invokespecialLocalVars;
|
private LocalVar[/*instructionIndex*/][/*localIndex*/] invokespecialLocalVars;
|
||||||
private LocalVar[/*index*/] allLocalVars;
|
private LocalVar[/*index*/] allLocalVars;
|
||||||
private List<string> errorMessages;
|
private List<string> errorMessages;
|
||||||
|
private ExceptionTableEntry[] exceptions;
|
||||||
|
|
||||||
static MethodAnalyzer()
|
static MethodAnalyzer()
|
||||||
{
|
{
|
||||||
|
@ -1147,6 +1183,7 @@ class MethodAnalyzer
|
||||||
|
|
||||||
// HACK because types have to have identity, the new types are cached here
|
// HACK because types have to have identity, the new types are cached here
|
||||||
Dictionary<int, TypeWrapper> newTypes = new Dictionary<int,TypeWrapper>();
|
Dictionary<int, TypeWrapper> newTypes = new Dictionary<int,TypeWrapper>();
|
||||||
|
Dictionary<int, TypeWrapper> faultTypes = new Dictionary<int, TypeWrapper>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1205,15 +1242,21 @@ class MethodAnalyzer
|
||||||
firstNonArgLocalIndex++;
|
firstNonArgLocalIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnalyzeTypeFlow(wrapper, thisType, mw, localStoreReaders, newTypes);
|
AnalyzeTypeFlow(wrapper, thisType, mw, localStoreReaders, newTypes, faultTypes);
|
||||||
|
exceptions = UntangleExceptionBlocks(classFile, method.ExceptionTable);
|
||||||
OptimizationPass(wrapper, classLoader);
|
OptimizationPass(wrapper, classLoader);
|
||||||
FinalCodePatchup(wrapper, mw);
|
FinalCodePatchup(wrapper, mw);
|
||||||
|
if (AnalyzePotentialFaultBlocks())
|
||||||
|
{
|
||||||
|
AnalyzeTypeFlow(wrapper, thisType, mw, localStoreReaders, newTypes, faultTypes);
|
||||||
|
}
|
||||||
AnalyzeLocalVariables(localStoreReaders, classLoader);
|
AnalyzeLocalVariables(localStoreReaders, classLoader);
|
||||||
|
ComputePartialReachability(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AnalyzeTypeFlow(TypeWrapper wrapper, TypeWrapper thisType, MethodWrapper mw, Dictionary<int, string>[] localStoreReaders, Dictionary<int, TypeWrapper> newTypes)
|
private void AnalyzeTypeFlow(TypeWrapper wrapper, TypeWrapper thisType, MethodWrapper mw, Dictionary<int, string>[] localStoreReaders, Dictionary<int, TypeWrapper> newTypes, Dictionary<int, TypeWrapper> faultTypes)
|
||||||
{
|
{
|
||||||
InstructionState s = state[0].Copy();
|
InstructionState s = new InstructionState(method.MaxLocals, method.MaxStack);
|
||||||
bool done = false;
|
bool done = false;
|
||||||
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
||||||
while(!done)
|
while(!done)
|
||||||
|
@ -1233,11 +1276,18 @@ class MethodAnalyzer
|
||||||
{
|
{
|
||||||
if(method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex)
|
if(method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex)
|
||||||
{
|
{
|
||||||
|
int idx = method.ExceptionTable[j].handlerIndex;
|
||||||
InstructionState ex = state[i].CopyLocals();
|
InstructionState ex = state[i].CopyLocals();
|
||||||
int catch_type = method.ExceptionTable[j].catch_type;
|
int catch_type = method.ExceptionTable[j].catch_type;
|
||||||
if(catch_type == 0)
|
if(catch_type == 0)
|
||||||
{
|
{
|
||||||
ex.PushType(CoreClasses.java.lang.Throwable.Wrapper);
|
TypeWrapper tw;
|
||||||
|
if (!faultTypes.TryGetValue(idx, out tw))
|
||||||
|
{
|
||||||
|
tw = VerifierTypeWrapper.MakeFaultBlockException(this, idx);
|
||||||
|
faultTypes.Add(idx, tw);
|
||||||
|
}
|
||||||
|
ex.PushType(tw);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1245,7 +1295,6 @@ class MethodAnalyzer
|
||||||
// Throwable as the type and recording a loader constraint
|
// Throwable as the type and recording a loader constraint
|
||||||
ex.PushType(GetConstantPoolClassType(catch_type));
|
ex.PushType(GetConstantPoolClassType(catch_type));
|
||||||
}
|
}
|
||||||
int idx = method.ExceptionTable[j].handlerIndex;
|
|
||||||
state[idx] += ex;
|
state[idx] += ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1265,6 +1314,11 @@ class MethodAnalyzer
|
||||||
}
|
}
|
||||||
case NormalizedByteCode.__astore:
|
case NormalizedByteCode.__astore:
|
||||||
{
|
{
|
||||||
|
if(VerifierTypeWrapper.IsFaultBlockException(s.PeekType()))
|
||||||
|
{
|
||||||
|
s.SetLocalType(instr.NormalizedArg1, s.PopFaultBlockException(), i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
// NOTE since the reference can be uninitialized, we cannot use PopObjectType
|
// NOTE since the reference can be uninitialized, we cannot use PopObjectType
|
||||||
TypeWrapper type = s.PopType();
|
TypeWrapper type = s.PopType();
|
||||||
if(type.IsPrimitive)
|
if(type.IsPrimitive)
|
||||||
|
@ -2058,7 +2112,14 @@ class MethodAnalyzer
|
||||||
s.GetLocalInt(instr.Arg1, ref localStoreReaders[i]);
|
s.GetLocalInt(instr.Arg1, ref localStoreReaders[i]);
|
||||||
break;
|
break;
|
||||||
case NormalizedByteCode.__athrow:
|
case NormalizedByteCode.__athrow:
|
||||||
|
if (VerifierTypeWrapper.IsFaultBlockException(s.PeekType()))
|
||||||
|
{
|
||||||
|
s.PopFaultBlockException();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
s.PopObjectType(CoreClasses.java.lang.Throwable.Wrapper);
|
s.PopObjectType(CoreClasses.java.lang.Throwable.Wrapper);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NormalizedByteCode.__tableswitch:
|
case NormalizedByteCode.__tableswitch:
|
||||||
case NormalizedByteCode.__lookupswitch:
|
case NormalizedByteCode.__lookupswitch:
|
||||||
|
@ -2229,7 +2290,7 @@ class MethodAnalyzer
|
||||||
if (assertionsDisabled != null)
|
if (assertionsDisabled != null)
|
||||||
{
|
{
|
||||||
// compute branch targets
|
// compute branch targets
|
||||||
InstructionFlags[] flags = ComputePartialReachability(0, method.ExceptionTable);
|
InstructionFlags[] flags = ComputePartialReachability(0, false);
|
||||||
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
||||||
for (int i = 0; i < instructions.Length; i++)
|
for (int i = 0; i < instructions.Length; i++)
|
||||||
{
|
{
|
||||||
|
@ -2465,7 +2526,7 @@ class MethodAnalyzer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal InstructionFlags[] ComputePartialReachability(int initialInstructionIndex, ClassFile.Method.ExceptionTableEntry[] exceptionTable)
|
internal InstructionFlags[] ComputePartialReachability(int initialInstructionIndex, bool skipFaultBlocks)
|
||||||
{
|
{
|
||||||
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
||||||
InstructionFlags[] flags = new InstructionFlags[instructions.Length];
|
InstructionFlags[] flags = new InstructionFlags[instructions.Length];
|
||||||
|
@ -2481,14 +2542,17 @@ class MethodAnalyzer
|
||||||
done = false;
|
done = false;
|
||||||
flags[i] |= InstructionFlags.Processed;
|
flags[i] |= InstructionFlags.Processed;
|
||||||
// mark the exception handlers reachable from this instruction
|
// mark the exception handlers reachable from this instruction
|
||||||
for (int j = 0; j < exceptionTable.Length; j++)
|
for (int j = 0; j < exceptions.Length; j++)
|
||||||
{
|
{
|
||||||
if (exceptionTable[j].startIndex <= i && i < exceptionTable[j].endIndex)
|
if (exceptions[j].startIndex <= i && i < exceptions[j].endIndex)
|
||||||
|
{
|
||||||
|
int idx = exceptions[j].handlerIndex;
|
||||||
|
if (!skipFaultBlocks || !VerifierTypeWrapper.IsFaultBlockException(state[idx].GetStackByIndex(0)))
|
||||||
{
|
{
|
||||||
int idx = exceptionTable[j].handlerIndex;
|
|
||||||
flags[idx] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
flags[idx] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// mark the successor instructions
|
// mark the successor instructions
|
||||||
switch (instructions[i].NormalizedOpCode)
|
switch (instructions[i].NormalizedOpCode)
|
||||||
{
|
{
|
||||||
|
@ -2531,6 +2595,7 @@ class MethodAnalyzer
|
||||||
case NormalizedByteCode.__areturn:
|
case NormalizedByteCode.__areturn:
|
||||||
case NormalizedByteCode.__return:
|
case NormalizedByteCode.__return:
|
||||||
case NormalizedByteCode.__athrow:
|
case NormalizedByteCode.__athrow:
|
||||||
|
case NormalizedByteCode.__athrow_no_unmap:
|
||||||
case NormalizedByteCode.__static_error:
|
case NormalizedByteCode.__static_error:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -2559,7 +2624,7 @@ class MethodAnalyzer
|
||||||
Dictionary<LocalVar, LocalVar> forwarders = new Dictionary<LocalVar,LocalVar>();
|
Dictionary<LocalVar, LocalVar> forwarders = new Dictionary<LocalVar,LocalVar>();
|
||||||
if(classLoader.EmitDebugInfo)
|
if(classLoader.EmitDebugInfo)
|
||||||
{
|
{
|
||||||
InstructionFlags[] flags = ComputePartialReachability(0, method.ExceptionTable);
|
InstructionFlags[] flags = ComputePartialReachability(0, false);
|
||||||
// if we're emitting debug info, we need to keep dead stores as well...
|
// if we're emitting debug info, we need to keep dead stores as well...
|
||||||
for(int i = 0; i < instructions.Length; i++)
|
for(int i = 0; i < instructions.Length; i++)
|
||||||
{
|
{
|
||||||
|
@ -2668,8 +2733,9 @@ class MethodAnalyzer
|
||||||
this.allLocalVars = locals.ToArray();
|
this.allLocalVars = locals.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ExceptionTableEntry[] UntangleExceptionBlocks(ClassFile classFile, ClassFile.Method.Instruction[] instructions, InstructionFlags[] flags, ExceptionTableEntry[] exceptionTable)
|
private ExceptionTableEntry[] UntangleExceptionBlocks(ClassFile classFile, ExceptionTableEntry[] exceptionTable)
|
||||||
{
|
{
|
||||||
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
||||||
List<ExceptionTableEntry> ar = new List<ExceptionTableEntry>(exceptionTable);
|
List<ExceptionTableEntry> ar = new List<ExceptionTableEntry>(exceptionTable);
|
||||||
|
|
||||||
// This optimization removes the recursive exception handlers that Java compiler place around
|
// This optimization removes the recursive exception handlers that Java compiler place around
|
||||||
|
@ -2711,6 +2777,14 @@ class MethodAnalyzer
|
||||||
ar.RemoveAt(i);
|
ar.RemoveAt(i);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
|
else if (index + 1 < instructions.Length
|
||||||
|
&& ei.endIndex == index + 1
|
||||||
|
&& instructions[index].NormalizedOpCode == NormalizedByteCode.__astore)
|
||||||
|
{
|
||||||
|
// this is the finally guard that javac produces
|
||||||
|
ar.RemoveAt(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2916,18 +2990,6 @@ class MethodAnalyzer
|
||||||
next: ;
|
next: ;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove unreachable exception handlers (because the code gen depends on that)
|
|
||||||
for (int i = 0; i < ar.Count; i++)
|
|
||||||
{
|
|
||||||
// if the first instruction is unreachable, the entire block is unreachable,
|
|
||||||
// because you can't jump into a block (we've just split the blocks to ensure that)
|
|
||||||
if ((flags[ar[i].startIndex] & InstructionFlags.Reachable) == 0)
|
|
||||||
{
|
|
||||||
ar.RemoveAt(i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExceptionTableEntry[] exceptions = ar.ToArray();
|
ExceptionTableEntry[] exceptions = ar.ToArray();
|
||||||
Array.Sort(exceptions, new ExceptionSorter());
|
Array.Sort(exceptions, new ExceptionSorter());
|
||||||
|
|
||||||
|
@ -2957,6 +3019,79 @@ class MethodAnalyzer
|
||||||
return exceptions;
|
return exceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool AnalyzePotentialFaultBlocks()
|
||||||
|
{
|
||||||
|
ClassFile.Method.Instruction[] code = method.Instructions;
|
||||||
|
bool changed = false;
|
||||||
|
bool done = false;
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
Stack<ExceptionTableEntry> stack = new Stack<ExceptionTableEntry>();
|
||||||
|
ExceptionTableEntry current = new ExceptionTableEntry(0, code.Length, -1, ushort.MaxValue, -1);
|
||||||
|
stack.Push(current);
|
||||||
|
for (int i = 0; i < exceptions.Length; i++)
|
||||||
|
{
|
||||||
|
while (exceptions[i].startIndex >= current.endIndex)
|
||||||
|
{
|
||||||
|
current = stack.Pop();
|
||||||
|
}
|
||||||
|
Debug.Assert(exceptions[i].startIndex >= current.startIndex && exceptions[i].endIndex <= current.endIndex);
|
||||||
|
if (exceptions[i].catch_type == 0
|
||||||
|
&& state[exceptions[i].handlerIndex] != null
|
||||||
|
&& VerifierTypeWrapper.IsFaultBlockException(GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0)))
|
||||||
|
{
|
||||||
|
InstructionFlags[] flags = ComputePartialReachability(exceptions[i].handlerIndex, true);
|
||||||
|
for (int j = 0; j < code.Length; j++)
|
||||||
|
{
|
||||||
|
if ((flags[j] & InstructionFlags.Reachable) != 0)
|
||||||
|
{
|
||||||
|
switch (code[j].NormalizedOpCode)
|
||||||
|
{
|
||||||
|
case NormalizedByteCode.__return:
|
||||||
|
case NormalizedByteCode.__areturn:
|
||||||
|
case NormalizedByteCode.__ireturn:
|
||||||
|
case NormalizedByteCode.__lreturn:
|
||||||
|
case NormalizedByteCode.__freturn:
|
||||||
|
case NormalizedByteCode.__dreturn:
|
||||||
|
goto not_fault_block;
|
||||||
|
case NormalizedByteCode.__athrow:
|
||||||
|
for (int k = i + 1; k < exceptions.Length; k++)
|
||||||
|
{
|
||||||
|
if (exceptions[k].startIndex <= j && j < exceptions[k].endIndex)
|
||||||
|
{
|
||||||
|
goto not_fault_block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j < current.startIndex || j >= current.endIndex)
|
||||||
|
{
|
||||||
|
goto not_fault_block;
|
||||||
|
}
|
||||||
|
else if (exceptions[i].startIndex <= j && j < exceptions[i].endIndex)
|
||||||
|
{
|
||||||
|
goto not_fault_block;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
not_fault_block:
|
||||||
|
VerifierTypeWrapper.ClearFaultBlockException(GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0));
|
||||||
|
done = false;
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack.Push(current);
|
||||||
|
current = exceptions[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
private void SetHardError(ref ClassFile.Method.Instruction instruction, HardError hardError, string message, params object[] args)
|
private void SetHardError(ref ClassFile.Method.Instruction instruction, HardError hardError, string message, params object[] args)
|
||||||
{
|
{
|
||||||
string text = string.Format(message, args);
|
string text = string.Format(message, args);
|
||||||
|
@ -3652,4 +3787,26 @@ class MethodAnalyzer
|
||||||
{
|
{
|
||||||
return allLocalVars;
|
return allLocalVars;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ClearFaultBlockException(int instructionIndex)
|
||||||
|
{
|
||||||
|
Debug.Assert(state[instructionIndex].GetStackHeight() == 1);
|
||||||
|
state[instructionIndex].ClearFaultBlockException();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ExceptionTableEntry[] GetExceptionTableFor(InstructionFlags[] flags)
|
||||||
|
{
|
||||||
|
List<ExceptionTableEntry> list = new List<ExceptionTableEntry>();
|
||||||
|
// return only reachable exception handlers (because the code gen depends on that)
|
||||||
|
for (int i = 0; i < exceptions.Length; i++)
|
||||||
|
{
|
||||||
|
// if the first instruction is unreachable, the entire block is unreachable,
|
||||||
|
// because you can't jump into a block (we've just split the blocks to ensure that)
|
||||||
|
if ((flags[exceptions[i].startIndex] & InstructionFlags.Reachable) != 0)
|
||||||
|
{
|
||||||
|
list.Add(exceptions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list.ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче