зеркало из https://github.com/mono/ikvm-fork.git
3767 строки
112 KiB
C#
3767 строки
112 KiB
C#
/*
|
|
Copyright (C) 2002-2010 Jeroen Frijters
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jeroen Frijters
|
|
jeroen@frijters.net
|
|
|
|
*/
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using IKVM.Internal;
|
|
using InstructionFlags = IKVM.Internal.ClassFile.Method.InstructionFlags;
|
|
using ExceptionTableEntry = IKVM.Internal.ClassFile.Method.ExceptionTableEntry;
|
|
|
|
sealed class InstructionState
|
|
{
|
|
private struct LocalStoreSites
|
|
{
|
|
private int[] data;
|
|
private int count;
|
|
private bool shared;
|
|
|
|
internal LocalStoreSites Copy()
|
|
{
|
|
LocalStoreSites n = new LocalStoreSites();
|
|
n.data = data;
|
|
n.count = count;
|
|
n.shared = true;
|
|
return n;
|
|
}
|
|
|
|
internal static LocalStoreSites Alloc()
|
|
{
|
|
LocalStoreSites n = new LocalStoreSites();
|
|
n.data = new int[4];
|
|
return n;
|
|
}
|
|
|
|
internal void Add(int store)
|
|
{
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
if(data[i] == store)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if(count == data.Length)
|
|
{
|
|
int[] newarray = new int[data.Length * 2];
|
|
Buffer.BlockCopy(data, 0, newarray, 0, data.Length * 4);
|
|
data = newarray;
|
|
shared = false;
|
|
}
|
|
if(shared)
|
|
{
|
|
shared = false;
|
|
data = (int[])data.Clone();
|
|
}
|
|
data[count++] = store;
|
|
}
|
|
|
|
internal int this[int index]
|
|
{
|
|
get
|
|
{
|
|
return data[index];
|
|
}
|
|
}
|
|
|
|
internal int Count
|
|
{
|
|
get
|
|
{
|
|
return count;
|
|
}
|
|
}
|
|
|
|
internal static void MarkShared(LocalStoreSites[] localStoreSites)
|
|
{
|
|
for(int i = 0; i < localStoreSites.Length; i++)
|
|
{
|
|
localStoreSites[i].shared = true;
|
|
}
|
|
}
|
|
}
|
|
private TypeWrapper[] stack;
|
|
private int stackSize;
|
|
private int stackEnd;
|
|
private TypeWrapper[] locals;
|
|
private bool unitializedThis;
|
|
internal bool changed = true;
|
|
private enum ShareFlags : byte
|
|
{
|
|
None = 0,
|
|
Stack = 1,
|
|
Locals = 2,
|
|
All = Stack | Locals
|
|
}
|
|
private ShareFlags flags;
|
|
|
|
private InstructionState(TypeWrapper[] stack, int stackSize, int stackEnd, TypeWrapper[] locals, bool unitializedThis)
|
|
{
|
|
this.flags = ShareFlags.All;
|
|
this.stack = stack;
|
|
this.stackSize = stackSize;
|
|
this.stackEnd = stackEnd;
|
|
this.locals = locals;
|
|
this.unitializedThis = unitializedThis;
|
|
}
|
|
|
|
internal InstructionState(int maxLocals, int maxStack)
|
|
{
|
|
this.flags = ShareFlags.None;
|
|
this.stack = new TypeWrapper[maxStack];
|
|
this.stackEnd = maxStack;
|
|
this.locals = new TypeWrapper[maxLocals];
|
|
}
|
|
|
|
internal InstructionState Copy()
|
|
{
|
|
return new InstructionState(stack, stackSize, stackEnd, locals, unitializedThis);
|
|
}
|
|
|
|
internal void CopyTo(InstructionState target)
|
|
{
|
|
target.flags = ShareFlags.All;
|
|
target.stack = stack;
|
|
target.stackSize = stackSize;
|
|
target.stackEnd = stackEnd;
|
|
target.locals = locals;
|
|
target.unitializedThis = unitializedThis;
|
|
target.changed = true;
|
|
}
|
|
|
|
internal InstructionState CopyLocals()
|
|
{
|
|
InstructionState copy = new InstructionState(new TypeWrapper[stack.Length], 0, stack.Length, locals, unitializedThis);
|
|
copy.flags &= ~ShareFlags.Stack;
|
|
return copy;
|
|
}
|
|
|
|
public static InstructionState operator+(InstructionState s1, InstructionState s2)
|
|
{
|
|
if(s1 == null)
|
|
{
|
|
return s2.Copy();
|
|
}
|
|
if(s1.stackSize != s2.stackSize || s1.stackEnd != s2.stackEnd)
|
|
{
|
|
throw new VerifyError(string.Format("Inconsistent stack height: {0} != {1}",
|
|
s1.stackSize + s1.stack.Length - s1.stackEnd,
|
|
s2.stackSize + s2.stack.Length - s2.stackEnd));
|
|
}
|
|
InstructionState s = s1.Copy();
|
|
s.changed = s1.changed;
|
|
for(int i = 0; i < s.stackSize; i++)
|
|
{
|
|
TypeWrapper type = s.stack[i];
|
|
TypeWrapper type2 = s2.stack[i];
|
|
if(type == type2)
|
|
{
|
|
// perfect match, nothing to do
|
|
}
|
|
else if((type == VerifierTypeWrapper.ExtendedDouble && type2 == PrimitiveTypeWrapper.DOUBLE)
|
|
|| (type2 == VerifierTypeWrapper.ExtendedDouble && type == PrimitiveTypeWrapper.DOUBLE))
|
|
{
|
|
if(type != VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
s.StackCopyOnWrite();
|
|
s.stack[i] = VerifierTypeWrapper.ExtendedDouble;
|
|
s.changed = true;
|
|
}
|
|
}
|
|
else if((type == VerifierTypeWrapper.ExtendedFloat && type2 == PrimitiveTypeWrapper.FLOAT)
|
|
|| (type2 == VerifierTypeWrapper.ExtendedFloat && type == PrimitiveTypeWrapper.FLOAT))
|
|
{
|
|
if(type != VerifierTypeWrapper.ExtendedFloat)
|
|
{
|
|
s.StackCopyOnWrite();
|
|
s.stack[i] = VerifierTypeWrapper.ExtendedFloat;
|
|
s.changed = true;
|
|
}
|
|
}
|
|
else if(!type.IsPrimitive)
|
|
{
|
|
TypeWrapper baseType = InstructionState.FindCommonBaseType(type, type2);
|
|
if(baseType == VerifierTypeWrapper.Invalid)
|
|
{
|
|
throw new VerifyError(string.Format("cannot merge {0} and {1}", type.Name, type2.Name));
|
|
}
|
|
if(type != baseType)
|
|
{
|
|
s.StackCopyOnWrite();
|
|
s.stack[i] = baseType;
|
|
s.changed = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new VerifyError(string.Format("cannot merge {0} and {1}", type.Name, type2.Name));
|
|
}
|
|
}
|
|
for(int i = 0; i < s.locals.Length; i++)
|
|
{
|
|
TypeWrapper type = s.locals[i];
|
|
TypeWrapper type2 = s2.locals[i];
|
|
TypeWrapper baseType = InstructionState.FindCommonBaseType(type, type2);
|
|
if(type != baseType)
|
|
{
|
|
s.LocalsCopyOnWrite();
|
|
s.locals[i] = baseType;
|
|
s.changed = true;
|
|
}
|
|
}
|
|
if(!s.unitializedThis && s2.unitializedThis)
|
|
{
|
|
s.unitializedThis = true;
|
|
s.changed = true;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
private static LocalStoreSites MergeStoreSites(LocalStoreSites h1, LocalStoreSites h2)
|
|
{
|
|
if(h1.Count == 0)
|
|
{
|
|
return h2.Copy();
|
|
}
|
|
if(h2.Count == 0)
|
|
{
|
|
return h1.Copy();
|
|
}
|
|
LocalStoreSites h = h1.Copy();
|
|
for(int i = 0; i < h2.Count; i++)
|
|
{
|
|
h.Add(h2[i]);
|
|
}
|
|
return h;
|
|
}
|
|
|
|
internal void SetUnitializedThis(bool state)
|
|
{
|
|
unitializedThis = state;
|
|
}
|
|
|
|
internal void CheckUninitializedThis()
|
|
{
|
|
if(unitializedThis)
|
|
{
|
|
throw new VerifyError("Base class constructor wasn't called");
|
|
}
|
|
}
|
|
|
|
internal static TypeWrapper FindCommonBaseType(TypeWrapper type1, TypeWrapper type2)
|
|
{
|
|
if(type1 == type2)
|
|
{
|
|
return type1;
|
|
}
|
|
if(type1 == VerifierTypeWrapper.Null)
|
|
{
|
|
return type2;
|
|
}
|
|
if(type2 == VerifierTypeWrapper.Null)
|
|
{
|
|
return type1;
|
|
}
|
|
if(type1 == VerifierTypeWrapper.Invalid || type2 == 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)
|
|
{
|
|
return VerifierTypeWrapper.Invalid;
|
|
}
|
|
if(type1 == VerifierTypeWrapper.UninitializedThis || type2 == VerifierTypeWrapper.UninitializedThis)
|
|
{
|
|
return VerifierTypeWrapper.Invalid;
|
|
}
|
|
if(VerifierTypeWrapper.IsNew(type1) || VerifierTypeWrapper.IsNew(type2))
|
|
{
|
|
return VerifierTypeWrapper.Invalid;
|
|
}
|
|
if(VerifierTypeWrapper.IsThis(type1))
|
|
{
|
|
type1 = ((VerifierTypeWrapper)type1).UnderlyingType;
|
|
}
|
|
if(VerifierTypeWrapper.IsThis(type2))
|
|
{
|
|
type2 = ((VerifierTypeWrapper)type2).UnderlyingType;
|
|
}
|
|
if(type1.IsUnloadable || type2.IsUnloadable)
|
|
{
|
|
return VerifierTypeWrapper.Unloadable;
|
|
}
|
|
if(type1.ArrayRank > 0 && type2.ArrayRank > 0)
|
|
{
|
|
int rank = 1;
|
|
int rank1 = type1.ArrayRank - 1;
|
|
int rank2 = type2.ArrayRank - 1;
|
|
TypeWrapper elem1 = type1.ElementTypeWrapper;
|
|
TypeWrapper elem2 = type2.ElementTypeWrapper;
|
|
while(rank1 != 0 && rank2 != 0)
|
|
{
|
|
elem1 = elem1.ElementTypeWrapper;
|
|
elem2 = elem2.ElementTypeWrapper;
|
|
rank++;
|
|
rank1--;
|
|
rank2--;
|
|
}
|
|
// NOTE arrays of value types have special merging semantics!
|
|
// NOTE we don't have to test for the case where the element types are the same, because that
|
|
// is only relevant if the ranks are the same, but if that is the case the types are completely
|
|
// identical, in which case the identity test at the top of this method already returned.
|
|
TypeWrapper baseType;
|
|
if(elem1.IsPrimitive || elem2.IsPrimitive || elem1.IsNonPrimitiveValueType || elem2.IsNonPrimitiveValueType)
|
|
{
|
|
baseType = CoreClasses.java.lang.Object.Wrapper;
|
|
rank--;
|
|
if(rank == 0)
|
|
{
|
|
return baseType;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
baseType = FindCommonBaseTypeHelper(elem1, elem2);
|
|
}
|
|
return baseType.MakeArrayType(rank);
|
|
}
|
|
return FindCommonBaseTypeHelper(type1, type2);
|
|
}
|
|
|
|
private static TypeWrapper FindCommonBaseTypeHelper(TypeWrapper t1, TypeWrapper t2)
|
|
{
|
|
if(t1 == t2)
|
|
{
|
|
return t1;
|
|
}
|
|
if(t1.IsInterface || t2.IsInterface)
|
|
{
|
|
// NOTE according to a paper by Alessandro Coglio & Allen Goldberg titled
|
|
// "Type Safety in the JVM: Some Problems in Java 2 SDK 1.2 and Proposed Solutions"
|
|
// the common base of two interfaces is java.lang.Object, and there is special
|
|
// treatment for java.lang.Object types that allow it to be assigned to any interface
|
|
// type, the JVM's typesafety then depends on the invokeinterface instruction to make
|
|
// sure that the reference actually implements the interface.
|
|
// NOTE the ECMA CLI spec also specifies this interface merging algorithm, so we can't
|
|
// really do anything more clever than this.
|
|
return CoreClasses.java.lang.Object.Wrapper;
|
|
}
|
|
Stack<TypeWrapper> st1 = new Stack<TypeWrapper>();
|
|
Stack<TypeWrapper> st2 = new Stack<TypeWrapper>();
|
|
while(t1 != null)
|
|
{
|
|
st1.Push(t1);
|
|
t1 = t1.BaseTypeWrapper;
|
|
}
|
|
while(t2 != null)
|
|
{
|
|
st2.Push(t2);
|
|
t2 = t2.BaseTypeWrapper;
|
|
}
|
|
TypeWrapper type = null;
|
|
for(;;)
|
|
{
|
|
t1 = st1.Count > 0 ? st1.Pop() : null;
|
|
t2 = st2.Count > 0 ? st2.Pop() : null;
|
|
if(t1 != t2)
|
|
{
|
|
return type;
|
|
}
|
|
type = t1;
|
|
}
|
|
}
|
|
|
|
private void SetLocal1(int index, TypeWrapper type)
|
|
{
|
|
try
|
|
{
|
|
LocalsCopyOnWrite();
|
|
if(index > 0 && locals[index - 1] != VerifierTypeWrapper.Invalid && locals[index - 1].IsWidePrimitive)
|
|
{
|
|
locals[index - 1] = VerifierTypeWrapper.Invalid;
|
|
}
|
|
locals[index] = type;
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
throw new VerifyError("Illegal local variable number");
|
|
}
|
|
}
|
|
|
|
private void SetLocal2(int index, TypeWrapper type)
|
|
{
|
|
try
|
|
{
|
|
LocalsCopyOnWrite();
|
|
if(index > 0 && locals[index - 1] != VerifierTypeWrapper.Invalid && locals[index - 1].IsWidePrimitive)
|
|
{
|
|
locals[index - 1] = VerifierTypeWrapper.Invalid;
|
|
}
|
|
locals[index] = type;
|
|
locals[index + 1] = VerifierTypeWrapper.Invalid;
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
throw new VerifyError("Illegal local variable number");
|
|
}
|
|
}
|
|
|
|
internal void GetLocalInt(int index)
|
|
{
|
|
if(GetLocalType(index) != PrimitiveTypeWrapper.INT)
|
|
{
|
|
throw new VerifyError("Invalid local type");
|
|
}
|
|
}
|
|
|
|
internal void SetLocalInt(int index, int instructionIndex)
|
|
{
|
|
SetLocal1(index, PrimitiveTypeWrapper.INT);
|
|
}
|
|
|
|
internal void GetLocalLong(int index)
|
|
{
|
|
if(GetLocalType(index) != PrimitiveTypeWrapper.LONG)
|
|
{
|
|
throw new VerifyError("incorrect local type, not long");
|
|
}
|
|
}
|
|
|
|
internal void SetLocalLong(int index, int instructionIndex)
|
|
{
|
|
SetLocal2(index, PrimitiveTypeWrapper.LONG);
|
|
}
|
|
|
|
internal void GetLocalFloat(int index)
|
|
{
|
|
if(GetLocalType(index) != PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
throw new VerifyError("incorrect local type, not float");
|
|
}
|
|
}
|
|
|
|
internal void SetLocalFloat(int index, int instructionIndex)
|
|
{
|
|
SetLocal1(index, PrimitiveTypeWrapper.FLOAT);
|
|
}
|
|
|
|
internal void GetLocalDouble(int index)
|
|
{
|
|
if(GetLocalType(index) != PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
throw new VerifyError("incorrect local type, not double");
|
|
}
|
|
}
|
|
|
|
internal void SetLocalDouble(int index, int instructionIndex)
|
|
{
|
|
SetLocal2(index, PrimitiveTypeWrapper.DOUBLE);
|
|
}
|
|
|
|
internal TypeWrapper GetLocalType(int index)
|
|
{
|
|
try
|
|
{
|
|
return locals[index];
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
throw new VerifyError("Illegal local variable number");
|
|
}
|
|
}
|
|
|
|
// this is used by the compiler (indirectly, through MethodAnalyzer.GetLocalTypeWrapper),
|
|
// we've already verified the code so we know we won't run outside the array boundary,
|
|
// and we don't need to record the fact that we're reading the local.
|
|
internal TypeWrapper GetLocalTypeEx(int index)
|
|
{
|
|
return locals[index];
|
|
}
|
|
|
|
internal void SetLocalType(int index, TypeWrapper type, int instructionIndex)
|
|
{
|
|
if(type.IsWidePrimitive)
|
|
{
|
|
SetLocal2(index, type);
|
|
}
|
|
else
|
|
{
|
|
SetLocal1(index, type);
|
|
}
|
|
}
|
|
|
|
internal void PushType(TypeWrapper type)
|
|
{
|
|
if(type.IsIntOnStackPrimitive)
|
|
{
|
|
type = PrimitiveTypeWrapper.INT;
|
|
}
|
|
PushHelper(type);
|
|
}
|
|
|
|
internal void PushInt()
|
|
{
|
|
PushHelper(PrimitiveTypeWrapper.INT);
|
|
}
|
|
|
|
internal void PushLong()
|
|
{
|
|
PushHelper(PrimitiveTypeWrapper.LONG);
|
|
}
|
|
|
|
internal void PushFloat()
|
|
{
|
|
PushHelper(PrimitiveTypeWrapper.FLOAT);
|
|
}
|
|
|
|
internal void PushExtendedFloat()
|
|
{
|
|
PushHelper(VerifierTypeWrapper.ExtendedFloat);
|
|
}
|
|
|
|
internal void PushDouble()
|
|
{
|
|
PushHelper(PrimitiveTypeWrapper.DOUBLE);
|
|
}
|
|
|
|
internal void PushExtendedDouble()
|
|
{
|
|
PushHelper(VerifierTypeWrapper.ExtendedDouble);
|
|
}
|
|
|
|
internal void PopInt()
|
|
{
|
|
if(PopAnyType() != PrimitiveTypeWrapper.INT)
|
|
{
|
|
throw new VerifyError("Int expected on stack");
|
|
}
|
|
}
|
|
|
|
internal bool PopFloat()
|
|
{
|
|
TypeWrapper tw = PopAnyType();
|
|
if(tw != PrimitiveTypeWrapper.FLOAT && tw != VerifierTypeWrapper.ExtendedFloat)
|
|
{
|
|
throw new VerifyError("Float expected on stack");
|
|
}
|
|
return tw == VerifierTypeWrapper.ExtendedFloat;
|
|
}
|
|
|
|
internal bool PopDouble()
|
|
{
|
|
TypeWrapper tw = PopAnyType();
|
|
if(tw != PrimitiveTypeWrapper.DOUBLE && tw != VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
throw new VerifyError("Double expected on stack");
|
|
}
|
|
return tw == VerifierTypeWrapper.ExtendedDouble;
|
|
}
|
|
|
|
internal void PopLong()
|
|
{
|
|
if(PopAnyType() != PrimitiveTypeWrapper.LONG)
|
|
{
|
|
throw new VerifyError("Long expected on stack");
|
|
}
|
|
}
|
|
|
|
internal TypeWrapper PopArrayType()
|
|
{
|
|
TypeWrapper type = PopAnyType();
|
|
if(!VerifierTypeWrapper.IsNullOrUnloadable(type) && type.ArrayRank == 0)
|
|
{
|
|
throw new VerifyError("Array reference expected on stack");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// null or an initialized object reference
|
|
internal TypeWrapper PopObjectType()
|
|
{
|
|
TypeWrapper type = PopType();
|
|
if(type.IsPrimitive || VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
|
|
{
|
|
throw new VerifyError("Expected object reference on stack");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// null or an initialized object reference derived from baseType (or baseType)
|
|
internal TypeWrapper PopObjectType(TypeWrapper baseType)
|
|
{
|
|
TypeWrapper type = PopObjectType();
|
|
// HACK because of the way interfaces references works, if baseType
|
|
// is an interface or array of interfaces, any reference will be accepted
|
|
if(!baseType.IsUnloadable && !baseType.IsInterfaceOrInterfaceArray && !(type.IsUnloadable || type.IsAssignableTo(baseType)))
|
|
{
|
|
throw new VerifyError("Unexpected type " + type.Name + " where " + baseType.Name + " was expected");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal TypeWrapper PeekType()
|
|
{
|
|
if(stackSize == 0)
|
|
{
|
|
throw new VerifyError("Unable to pop operand off an empty stack");
|
|
}
|
|
return stack[stackSize - 1];
|
|
}
|
|
|
|
internal void MultiPopAnyType(int count)
|
|
{
|
|
while(count-- != 0)
|
|
{
|
|
PopAnyType();
|
|
}
|
|
}
|
|
|
|
internal TypeWrapper PopFaultBlockException()
|
|
{
|
|
return stack[--stackSize];
|
|
}
|
|
|
|
internal TypeWrapper PopAnyType()
|
|
{
|
|
if(stackSize == 0)
|
|
{
|
|
throw new VerifyError("Unable to pop operand off an empty stack");
|
|
}
|
|
TypeWrapper type = stack[--stackSize];
|
|
if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
stackEnd++;
|
|
}
|
|
if(VerifierTypeWrapper.IsThis(type))
|
|
{
|
|
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
|
}
|
|
if(VerifierTypeWrapper.IsFaultBlockException(type))
|
|
{
|
|
VerifierTypeWrapper.ClearFaultBlockException(type);
|
|
type = CoreClasses.java.lang.Throwable.Wrapper;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// NOTE this can *not* be used to pop double or long
|
|
internal TypeWrapper PopType()
|
|
{
|
|
TypeWrapper type = PopAnyType();
|
|
if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
throw new VerifyError("Attempt to split long or double on the stack");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// this will accept null, a primitive type of the specified type or an initialized reference of the
|
|
// specified type or derived from it
|
|
// NOTE this can also be used to pop double or long
|
|
internal TypeWrapper PopType(TypeWrapper baseType)
|
|
{
|
|
if(baseType.IsIntOnStackPrimitive)
|
|
{
|
|
baseType = PrimitiveTypeWrapper.INT;
|
|
}
|
|
TypeWrapper type = PopAnyType();
|
|
if(VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
|
|
{
|
|
throw new VerifyError("Expecting to find object/array on stack");
|
|
}
|
|
if(type != baseType &&
|
|
!((type.IsUnloadable && !baseType.IsPrimitive) || (baseType.IsUnloadable && !type.IsPrimitive) ||
|
|
type.IsAssignableTo(baseType)))
|
|
{
|
|
// HACK because of the way interfaces references works, if baseType
|
|
// is an interface or array of interfaces, any reference will be accepted
|
|
if(baseType.IsInterfaceOrInterfaceArray && !type.IsPrimitive)
|
|
{
|
|
return type;
|
|
}
|
|
if(type == VerifierTypeWrapper.ExtendedDouble && baseType == PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
return type;
|
|
}
|
|
if(type == VerifierTypeWrapper.ExtendedFloat && baseType == PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
return type;
|
|
}
|
|
throw new VerifyError("Unexpected type " + type.Name + " where " + baseType.Name + " was expected");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal int GetStackHeight()
|
|
{
|
|
return stackSize;
|
|
}
|
|
|
|
internal TypeWrapper GetStackSlot(int pos)
|
|
{
|
|
TypeWrapper tw = stack[stackSize - 1 - pos];
|
|
if(tw == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
tw = PrimitiveTypeWrapper.DOUBLE;
|
|
}
|
|
else if(tw == VerifierTypeWrapper.ExtendedFloat)
|
|
{
|
|
tw = PrimitiveTypeWrapper.FLOAT;
|
|
}
|
|
return tw;
|
|
}
|
|
|
|
internal TypeWrapper GetStackSlotEx(int pos)
|
|
{
|
|
return stack[stackSize - 1 - pos];
|
|
}
|
|
|
|
internal TypeWrapper GetStackByIndex(int index)
|
|
{
|
|
return stack[index];
|
|
}
|
|
|
|
private void PushHelper(TypeWrapper type)
|
|
{
|
|
if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
stackEnd--;
|
|
}
|
|
if(stackSize >= stackEnd)
|
|
{
|
|
throw new VerifyError("Stack overflow");
|
|
}
|
|
StackCopyOnWrite();
|
|
stack[stackSize++] = type;
|
|
}
|
|
|
|
internal void MarkInitialized(TypeWrapper type, TypeWrapper initType, int instructionIndex)
|
|
{
|
|
System.Diagnostics.Debug.Assert(type != null && initType != null);
|
|
|
|
for(int i = 0; i < locals.Length; i++)
|
|
{
|
|
if(locals[i] == type)
|
|
{
|
|
LocalsCopyOnWrite();
|
|
locals[i] = initType;
|
|
}
|
|
}
|
|
for(int i = 0; i < stackSize; i++)
|
|
{
|
|
if(stack[i] == type)
|
|
{
|
|
StackCopyOnWrite();
|
|
stack[i] = initType;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void StackCopyOnWrite()
|
|
{
|
|
if((flags & ShareFlags.Stack) != 0)
|
|
{
|
|
flags &= ~ShareFlags.Stack;
|
|
stack = (TypeWrapper[])stack.Clone();
|
|
}
|
|
}
|
|
|
|
private void LocalsCopyOnWrite()
|
|
{
|
|
if((flags & ShareFlags.Locals) != 0)
|
|
{
|
|
flags &= ~ShareFlags.Locals;
|
|
locals = (TypeWrapper[])locals.Clone();
|
|
}
|
|
}
|
|
|
|
internal void DumpLocals()
|
|
{
|
|
Console.Write("// ");
|
|
string sep = "";
|
|
for(int i = 0; i < locals.Length; i++)
|
|
{
|
|
Console.Write(sep);
|
|
Console.Write(locals[i]);
|
|
sep = ", ";
|
|
}
|
|
Console.WriteLine();
|
|
}
|
|
|
|
internal void DumpStack()
|
|
{
|
|
Console.Write("// ");
|
|
string sep = "";
|
|
for(int i = 0; i < stackSize; i++)
|
|
{
|
|
Console.Write(sep);
|
|
Console.Write(stack[i]);
|
|
sep = ", ";
|
|
}
|
|
Console.WriteLine();
|
|
}
|
|
|
|
// this method ensures that no uninitialized object are in the current state
|
|
internal void CheckUninitializedObjRefs()
|
|
{
|
|
for(int i = 0; i < locals.Length; i++)
|
|
{
|
|
TypeWrapper type = locals[i];
|
|
if(VerifierTypeWrapper.IsNew(type))
|
|
{
|
|
throw new VerifyError("uninitialized object ref in local (2)");
|
|
}
|
|
}
|
|
for(int i = 0; i < stackSize; i++)
|
|
{
|
|
TypeWrapper type = stack[i];
|
|
if(VerifierTypeWrapper.IsNew(type))
|
|
{
|
|
throw new VerifyError("uninitialized object ref on stack");
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void ClearFaultBlockException()
|
|
{
|
|
if(VerifierTypeWrapper.IsFaultBlockException(stack[0]))
|
|
{
|
|
StackCopyOnWrite();
|
|
changed = true;
|
|
stack[0] = CoreClasses.java.lang.Throwable.Wrapper;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct StackState
|
|
{
|
|
private InstructionState state;
|
|
private int sp;
|
|
|
|
internal StackState(InstructionState state)
|
|
{
|
|
this.state = state;
|
|
sp = state.GetStackHeight();
|
|
}
|
|
|
|
internal TypeWrapper PeekType()
|
|
{
|
|
if(sp == 0)
|
|
{
|
|
throw new VerifyError("Unable to pop operand off an empty stack");
|
|
}
|
|
TypeWrapper type = state.GetStackByIndex(sp - 1);
|
|
if(VerifierTypeWrapper.IsThis(type))
|
|
{
|
|
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal TypeWrapper PopAnyType()
|
|
{
|
|
if(sp == 0)
|
|
{
|
|
throw new VerifyError("Unable to pop operand off an empty stack");
|
|
}
|
|
TypeWrapper type = state.GetStackByIndex(--sp);
|
|
if(VerifierTypeWrapper.IsThis(type))
|
|
{
|
|
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
|
}
|
|
if(VerifierTypeWrapper.IsFaultBlockException(type))
|
|
{
|
|
VerifierTypeWrapper.ClearFaultBlockException(type);
|
|
type = CoreClasses.java.lang.Throwable.Wrapper;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal TypeWrapper PopType(TypeWrapper baseType)
|
|
{
|
|
if(baseType.IsIntOnStackPrimitive)
|
|
{
|
|
baseType = PrimitiveTypeWrapper.INT;
|
|
}
|
|
TypeWrapper type = PopAnyType();
|
|
if(VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
|
|
{
|
|
throw new VerifyError("Expecting to find object/array on stack");
|
|
}
|
|
if(type != baseType &&
|
|
!((type.IsUnloadable && !baseType.IsPrimitive) || (baseType.IsUnloadable && !type.IsPrimitive) ||
|
|
type.IsAssignableTo(baseType)))
|
|
{
|
|
// HACK because of the way interfaces references works, if baseType
|
|
// is an interface or array of interfaces, any reference will be accepted
|
|
if(baseType.IsInterfaceOrInterfaceArray && !type.IsPrimitive)
|
|
{
|
|
return type;
|
|
}
|
|
if(type == VerifierTypeWrapper.ExtendedDouble && baseType == PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
return type;
|
|
}
|
|
if(type == VerifierTypeWrapper.ExtendedFloat && baseType == PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
return type;
|
|
}
|
|
throw new VerifyError("Unexpected type " + type.Name + " where " + baseType.Name + " was expected");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// NOTE this can *not* be used to pop double or long
|
|
internal TypeWrapper PopType()
|
|
{
|
|
TypeWrapper type = PopAnyType();
|
|
if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
throw new VerifyError("Attempt to split long or double on the stack");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal void PopInt()
|
|
{
|
|
if(PopAnyType() != PrimitiveTypeWrapper.INT)
|
|
{
|
|
throw new VerifyError("Int expected on stack");
|
|
}
|
|
}
|
|
|
|
internal void PopFloat()
|
|
{
|
|
TypeWrapper tw = PopAnyType();
|
|
if(tw != PrimitiveTypeWrapper.FLOAT && tw != VerifierTypeWrapper.ExtendedFloat)
|
|
{
|
|
throw new VerifyError("Float expected on stack");
|
|
}
|
|
}
|
|
|
|
internal void PopDouble()
|
|
{
|
|
TypeWrapper tw = PopAnyType();
|
|
if(tw != PrimitiveTypeWrapper.DOUBLE && tw != VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
throw new VerifyError("Double expected on stack");
|
|
}
|
|
}
|
|
|
|
internal void PopLong()
|
|
{
|
|
if(PopAnyType() != PrimitiveTypeWrapper.LONG)
|
|
{
|
|
throw new VerifyError("Long expected on stack");
|
|
}
|
|
}
|
|
|
|
internal TypeWrapper PopArrayType()
|
|
{
|
|
TypeWrapper type = PopAnyType();
|
|
if(!VerifierTypeWrapper.IsNullOrUnloadable(type) && type.ArrayRank == 0)
|
|
{
|
|
throw new VerifyError("Array reference expected on stack");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// either null or an initialized object reference
|
|
internal TypeWrapper PopObjectType()
|
|
{
|
|
TypeWrapper type = PopAnyType();
|
|
if(type.IsPrimitive || VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
|
|
{
|
|
throw new VerifyError("Expected object reference on stack");
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// null or an initialized object reference derived from baseType (or baseType)
|
|
internal TypeWrapper PopObjectType(TypeWrapper baseType)
|
|
{
|
|
TypeWrapper type = PopObjectType();
|
|
// HACK because of the way interfaces references works, if baseType
|
|
// is an interface or array of interfaces, any reference will be accepted
|
|
if(!baseType.IsUnloadable && !baseType.IsInterfaceOrInterfaceArray && !(type.IsUnloadable || type.IsAssignableTo(baseType)))
|
|
{
|
|
throw new VerifyError("Unexpected type " + type + " where " + baseType + " was expected");
|
|
}
|
|
return type;
|
|
}
|
|
}
|
|
|
|
sealed class ExceptionSorter : IComparer<ExceptionTableEntry>
|
|
{
|
|
public int Compare(ExceptionTableEntry e1, ExceptionTableEntry e2)
|
|
{
|
|
if (e1.startIndex < e2.startIndex)
|
|
{
|
|
return -1;
|
|
}
|
|
if (e1.startIndex == e2.startIndex)
|
|
{
|
|
if (e1.endIndex == e2.endIndex)
|
|
{
|
|
if (e1.ordinal > e2.ordinal)
|
|
{
|
|
return -1;
|
|
}
|
|
if (e1.ordinal == e2.ordinal)
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
if (e1.endIndex > e2.endIndex)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
struct UntangledExceptionTable
|
|
{
|
|
private readonly ExceptionTableEntry[] exceptions;
|
|
|
|
internal UntangledExceptionTable(ExceptionTableEntry[] exceptions)
|
|
{
|
|
#if DEBUG
|
|
for (int i = 0; i < exceptions.Length; i++)
|
|
{
|
|
for (int j = i + 1; j < exceptions.Length; j++)
|
|
{
|
|
// check for partially overlapping try blocks (which is legal for the JVM, but not the CLR)
|
|
if (exceptions[i].startIndex < exceptions[j].startIndex &&
|
|
exceptions[j].startIndex < exceptions[i].endIndex &&
|
|
exceptions[i].endIndex < exceptions[j].endIndex)
|
|
{
|
|
throw new InvalidOperationException("Partially overlapping try blocks is broken");
|
|
}
|
|
// check that we didn't destroy the ordering, when sorting
|
|
if (exceptions[i].startIndex <= exceptions[j].startIndex &&
|
|
exceptions[i].endIndex >= exceptions[j].endIndex &&
|
|
exceptions[i].ordinal < exceptions[j].ordinal)
|
|
{
|
|
throw new InvalidOperationException("Non recursive try blocks is broken");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
this.exceptions = exceptions;
|
|
}
|
|
|
|
internal ExceptionTableEntry this[int index]
|
|
{
|
|
get { return exceptions[index]; }
|
|
}
|
|
|
|
internal int Length
|
|
{
|
|
get { return exceptions.Length; }
|
|
}
|
|
|
|
internal void SetFinally(int index)
|
|
{
|
|
exceptions[index] = new ExceptionTableEntry(exceptions[index].startIndex, exceptions[index].endIndex, exceptions[index].handlerIndex, exceptions[index].catch_type, exceptions[index].ordinal, true);
|
|
}
|
|
}
|
|
|
|
struct CodeInfo
|
|
{
|
|
private readonly InstructionState[] state;
|
|
|
|
internal CodeInfo(InstructionState[] state)
|
|
{
|
|
this.state = state;
|
|
}
|
|
|
|
internal bool HasState(int index)
|
|
{
|
|
return state[index] != null;
|
|
}
|
|
|
|
internal int GetStackHeight(int index)
|
|
{
|
|
return state[index].GetStackHeight();
|
|
}
|
|
|
|
internal TypeWrapper GetStackTypeWrapper(int index, int pos)
|
|
{
|
|
TypeWrapper type = state[index].GetStackSlot(pos);
|
|
if (VerifierTypeWrapper.IsThis(type))
|
|
{
|
|
type = ((VerifierTypeWrapper)type).UnderlyingType;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
internal TypeWrapper GetRawStackTypeWrapper(int index, int pos)
|
|
{
|
|
return state[index].GetStackSlot(pos);
|
|
}
|
|
|
|
internal bool IsStackTypeExtendedDouble(int index, int pos)
|
|
{
|
|
return state[index].GetStackSlotEx(pos) == VerifierTypeWrapper.ExtendedDouble;
|
|
}
|
|
|
|
internal TypeWrapper GetLocalTypeWrapper(int index, int local)
|
|
{
|
|
return state[index].GetLocalTypeEx(local);
|
|
}
|
|
}
|
|
|
|
sealed class MethodAnalyzer
|
|
{
|
|
private readonly static TypeWrapper ByteArrayType;
|
|
private readonly static TypeWrapper BooleanArrayType;
|
|
private readonly static TypeWrapper ShortArrayType;
|
|
private readonly static TypeWrapper CharArrayType;
|
|
private readonly static TypeWrapper IntArrayType;
|
|
private readonly static TypeWrapper FloatArrayType;
|
|
private readonly static TypeWrapper DoubleArrayType;
|
|
private readonly static TypeWrapper LongArrayType;
|
|
private readonly static TypeWrapper java_lang_ThreadDeath;
|
|
private readonly TypeWrapper wrapper;
|
|
private readonly MethodWrapper mw;
|
|
private readonly ClassFile classFile;
|
|
private readonly ClassFile.Method method;
|
|
private readonly ClassLoaderWrapper classLoader;
|
|
private readonly TypeWrapper thisType;
|
|
private readonly InstructionState[] state;
|
|
private List<string> errorMessages;
|
|
private readonly Dictionary<int, TypeWrapper> newTypes = new Dictionary<int, TypeWrapper>();
|
|
private readonly Dictionary<int, TypeWrapper> faultTypes = new Dictionary<int, TypeWrapper>();
|
|
|
|
static MethodAnalyzer()
|
|
{
|
|
ByteArrayType = PrimitiveTypeWrapper.BYTE.MakeArrayType(1);
|
|
BooleanArrayType = PrimitiveTypeWrapper.BOOLEAN.MakeArrayType(1);
|
|
ShortArrayType = PrimitiveTypeWrapper.SHORT.MakeArrayType(1);
|
|
CharArrayType = PrimitiveTypeWrapper.CHAR.MakeArrayType(1);
|
|
IntArrayType = PrimitiveTypeWrapper.INT.MakeArrayType(1);
|
|
FloatArrayType = PrimitiveTypeWrapper.FLOAT.MakeArrayType(1);
|
|
DoubleArrayType = PrimitiveTypeWrapper.DOUBLE.MakeArrayType(1);
|
|
LongArrayType = PrimitiveTypeWrapper.LONG.MakeArrayType(1);
|
|
java_lang_ThreadDeath = ClassLoaderWrapper.LoadClassCritical("java.lang.ThreadDeath");
|
|
}
|
|
|
|
internal MethodAnalyzer(TypeWrapper wrapper, MethodWrapper mw, ClassFile classFile, ClassFile.Method method, ClassLoaderWrapper classLoader)
|
|
{
|
|
if(method.VerifyError != null)
|
|
{
|
|
throw new VerifyError(method.VerifyError);
|
|
}
|
|
|
|
this.wrapper = wrapper;
|
|
this.mw = mw;
|
|
this.classFile = classFile;
|
|
this.method = method;
|
|
this.classLoader = classLoader;
|
|
state = new InstructionState[method.Instructions.Length];
|
|
|
|
try
|
|
{
|
|
// ensure that exception blocks and handlers start and end at instruction boundaries
|
|
for(int i = 0; i < method.ExceptionTable.Length; i++)
|
|
{
|
|
int start = method.ExceptionTable[i].startIndex;
|
|
int end = method.ExceptionTable[i].endIndex;
|
|
int handler = method.ExceptionTable[i].handlerIndex;
|
|
if(start >= end || start == -1 || end == -1 || handler <= 0)
|
|
{
|
|
throw new IndexOutOfRangeException();
|
|
}
|
|
}
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
// TODO figure out if we should throw this during class loading
|
|
throw new ClassFormatError(string.Format("Illegal exception table (class: {0}, method: {1}, signature: {2}", classFile.Name, method.Name, method.Signature));
|
|
}
|
|
|
|
// start by computing the initial state, the stack is empty and the locals contain the arguments
|
|
state[0] = new InstructionState(method.MaxLocals, method.MaxStack);
|
|
int firstNonArgLocalIndex = 0;
|
|
if(!method.IsStatic)
|
|
{
|
|
thisType = VerifierTypeWrapper.MakeThis(wrapper);
|
|
// this reference. If we're a constructor, the this reference is uninitialized.
|
|
if(ReferenceEquals(method.Name, StringConstants.INIT))
|
|
{
|
|
state[0].SetLocalType(firstNonArgLocalIndex++, VerifierTypeWrapper.UninitializedThis, -1);
|
|
state[0].SetUnitializedThis(true);
|
|
}
|
|
else
|
|
{
|
|
state[0].SetLocalType(firstNonArgLocalIndex++, thisType, -1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
thisType = null;
|
|
}
|
|
// mw can be null when we're invoked from IsSideEffectFreeStaticInitializer
|
|
TypeWrapper[] argTypeWrappers = mw == null ? TypeWrapper.EmptyArray : mw.GetParameters();
|
|
for(int i = 0; i < argTypeWrappers.Length; i++)
|
|
{
|
|
TypeWrapper type = argTypeWrappers[i];
|
|
if(type.IsIntOnStackPrimitive)
|
|
{
|
|
type = PrimitiveTypeWrapper.INT;
|
|
}
|
|
state[0].SetLocalType(firstNonArgLocalIndex++, type, -1);
|
|
if(type.IsWidePrimitive)
|
|
{
|
|
firstNonArgLocalIndex++;
|
|
}
|
|
}
|
|
AnalyzeTypeFlow();
|
|
VerifyPassTwo();
|
|
}
|
|
|
|
internal CodeInfo GetCodeInfoAndErrors(UntangledExceptionTable exceptions, out List<string> errors)
|
|
{
|
|
CodeInfo codeInfo = new CodeInfo(state);
|
|
OptimizationPass(codeInfo, classFile, method, exceptions, wrapper, classLoader);
|
|
PatchHardErrorsAndDynamicMemberAccess(wrapper, mw);
|
|
errors = errorMessages;
|
|
if (AnalyzePotentialFaultBlocks(codeInfo, method, exceptions))
|
|
{
|
|
AnalyzeTypeFlow();
|
|
}
|
|
ConvertFinallyBlocks(codeInfo, method, exceptions);
|
|
return codeInfo;
|
|
}
|
|
|
|
private void AnalyzeTypeFlow()
|
|
{
|
|
InstructionState s = new InstructionState(method.MaxLocals, method.MaxStack);
|
|
bool done = false;
|
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
|
while(!done)
|
|
{
|
|
done = true;
|
|
for(int i = 0; i < instructions.Length; i++)
|
|
{
|
|
if(state[i] != null && state[i].changed)
|
|
{
|
|
try
|
|
{
|
|
//Console.WriteLine(method.Instructions[i].PC + ": " + method.Instructions[i].OpCode.ToString());
|
|
done = false;
|
|
state[i].changed = false;
|
|
// mark the exception handlers reachable from this instruction
|
|
for(int j = 0; j < method.ExceptionTable.Length; j++)
|
|
{
|
|
if(method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex)
|
|
{
|
|
int idx = method.ExceptionTable[j].handlerIndex;
|
|
InstructionState ex = state[i].CopyLocals();
|
|
int catch_type = method.ExceptionTable[j].catch_type;
|
|
if(catch_type == 0)
|
|
{
|
|
TypeWrapper tw;
|
|
if (!faultTypes.TryGetValue(idx, out tw))
|
|
{
|
|
tw = VerifierTypeWrapper.MakeFaultBlockException(this, idx);
|
|
faultTypes.Add(idx, tw);
|
|
}
|
|
ex.PushType(tw);
|
|
}
|
|
else
|
|
{
|
|
// TODO if the exception type is unloadable we should consider pushing
|
|
// Throwable as the type and recording a loader constraint
|
|
ex.PushType(GetConstantPoolClassType(catch_type));
|
|
}
|
|
state[idx] += ex;
|
|
}
|
|
}
|
|
state[i].CopyTo(s);
|
|
ClassFile.Method.Instruction instr = instructions[i];
|
|
switch(instr.NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__aload:
|
|
{
|
|
TypeWrapper type = s.GetLocalType(instr.NormalizedArg1);
|
|
if(type == VerifierTypeWrapper.Invalid || type.IsPrimitive)
|
|
{
|
|
throw new VerifyError("Object reference expected");
|
|
}
|
|
s.PushType(type);
|
|
break;
|
|
}
|
|
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
|
|
TypeWrapper type = s.PopType();
|
|
if(type.IsPrimitive)
|
|
{
|
|
throw new VerifyError("Object reference expected");
|
|
}
|
|
s.SetLocalType(instr.NormalizedArg1, type, i);
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__aconst_null:
|
|
s.PushType(VerifierTypeWrapper.Null);
|
|
break;
|
|
case NormalizedByteCode.__aaload:
|
|
{
|
|
s.PopInt();
|
|
TypeWrapper type = s.PopArrayType();
|
|
if(type == VerifierTypeWrapper.Null)
|
|
{
|
|
// if the array is null, we have use null as the element type, because
|
|
// otherwise the rest of the code will not verify correctly
|
|
s.PushType(VerifierTypeWrapper.Null);
|
|
}
|
|
else if(type.IsUnloadable)
|
|
{
|
|
s.PushType(VerifierTypeWrapper.Unloadable);
|
|
}
|
|
else
|
|
{
|
|
type = type.ElementTypeWrapper;
|
|
if(type.IsPrimitive)
|
|
{
|
|
throw new VerifyError("Object array expected");
|
|
}
|
|
s.PushType(type);
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__aastore:
|
|
s.PopObjectType();
|
|
s.PopInt();
|
|
s.PopArrayType();
|
|
// TODO check that elem is assignable to the array
|
|
break;
|
|
case NormalizedByteCode.__baload:
|
|
{
|
|
s.PopInt();
|
|
TypeWrapper type = s.PopArrayType();
|
|
if(!VerifierTypeWrapper.IsNullOrUnloadable(type) &&
|
|
type != ByteArrayType &&
|
|
type != BooleanArrayType)
|
|
{
|
|
throw new VerifyError();
|
|
}
|
|
s.PushInt();
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__bastore:
|
|
{
|
|
s.PopInt();
|
|
s.PopInt();
|
|
TypeWrapper type = s.PopArrayType();
|
|
if(!VerifierTypeWrapper.IsNullOrUnloadable(type) &&
|
|
type != ByteArrayType &&
|
|
type != BooleanArrayType)
|
|
{
|
|
throw new VerifyError();
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__caload:
|
|
s.PopInt();
|
|
s.PopObjectType(CharArrayType);
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__castore:
|
|
s.PopInt();
|
|
s.PopInt();
|
|
s.PopObjectType(CharArrayType);
|
|
break;
|
|
case NormalizedByteCode.__saload:
|
|
s.PopInt();
|
|
s.PopObjectType(ShortArrayType);
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__sastore:
|
|
s.PopInt();
|
|
s.PopInt();
|
|
s.PopObjectType(ShortArrayType);
|
|
break;
|
|
case NormalizedByteCode.__iaload:
|
|
s.PopInt();
|
|
s.PopObjectType(IntArrayType);
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__iastore:
|
|
s.PopInt();
|
|
s.PopInt();
|
|
s.PopObjectType(IntArrayType);
|
|
break;
|
|
case NormalizedByteCode.__laload:
|
|
s.PopInt();
|
|
s.PopObjectType(LongArrayType);
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__lastore:
|
|
s.PopLong();
|
|
s.PopInt();
|
|
s.PopObjectType(LongArrayType);
|
|
break;
|
|
case NormalizedByteCode.__daload:
|
|
s.PopInt();
|
|
s.PopObjectType(DoubleArrayType);
|
|
s.PushDouble();
|
|
break;
|
|
case NormalizedByteCode.__dastore:
|
|
s.PopDouble();
|
|
s.PopInt();
|
|
s.PopObjectType(DoubleArrayType);
|
|
break;
|
|
case NormalizedByteCode.__faload:
|
|
s.PopInt();
|
|
s.PopObjectType(FloatArrayType);
|
|
s.PushFloat();
|
|
break;
|
|
case NormalizedByteCode.__fastore:
|
|
s.PopFloat();
|
|
s.PopInt();
|
|
s.PopObjectType(FloatArrayType);
|
|
break;
|
|
case NormalizedByteCode.__arraylength:
|
|
s.PopArrayType();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__iconst:
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__if_icmpeq:
|
|
case NormalizedByteCode.__if_icmpne:
|
|
case NormalizedByteCode.__if_icmplt:
|
|
case NormalizedByteCode.__if_icmpge:
|
|
case NormalizedByteCode.__if_icmpgt:
|
|
case NormalizedByteCode.__if_icmple:
|
|
s.PopInt();
|
|
s.PopInt();
|
|
break;
|
|
case NormalizedByteCode.__ifeq:
|
|
case NormalizedByteCode.__ifge:
|
|
case NormalizedByteCode.__ifgt:
|
|
case NormalizedByteCode.__ifle:
|
|
case NormalizedByteCode.__iflt:
|
|
case NormalizedByteCode.__ifne:
|
|
s.PopInt();
|
|
break;
|
|
case NormalizedByteCode.__ifnonnull:
|
|
case NormalizedByteCode.__ifnull:
|
|
// TODO it might be legal to use an unitialized ref here
|
|
s.PopObjectType();
|
|
break;
|
|
case NormalizedByteCode.__if_acmpeq:
|
|
case NormalizedByteCode.__if_acmpne:
|
|
// TODO it might be legal to use an unitialized ref here
|
|
s.PopObjectType();
|
|
s.PopObjectType();
|
|
break;
|
|
case NormalizedByteCode.__getstatic:
|
|
case NormalizedByteCode.__dynamic_getstatic:
|
|
// special support for when we're being called from IsSideEffectFreeStaticInitializer
|
|
if(mw == null)
|
|
{
|
|
switch(GetFieldref(instr.Arg1).Signature[0])
|
|
{
|
|
case 'B':
|
|
case 'Z':
|
|
case 'C':
|
|
case 'S':
|
|
case 'I':
|
|
s.PushInt();
|
|
break;
|
|
case 'F':
|
|
s.PushFloat();
|
|
break;
|
|
case 'D':
|
|
s.PushDouble();
|
|
break;
|
|
case 'J':
|
|
s.PushLong();
|
|
break;
|
|
case 'L':
|
|
case '[':
|
|
throw new VerifyError();
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ClassFile.ConstantPoolItemFieldref cpi = GetFieldref(instr.Arg1);
|
|
if(cpi.GetField() != null && cpi.GetField().FieldTypeWrapper.IsUnloadable)
|
|
{
|
|
s.PushType(cpi.GetField().FieldTypeWrapper);
|
|
}
|
|
else
|
|
{
|
|
s.PushType(cpi.GetFieldType());
|
|
}
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__putstatic:
|
|
case NormalizedByteCode.__dynamic_putstatic:
|
|
// special support for when we're being called from IsSideEffectFreeStaticInitializer
|
|
if(mw == null)
|
|
{
|
|
switch(GetFieldref(instr.Arg1).Signature[0])
|
|
{
|
|
case 'B':
|
|
case 'Z':
|
|
case 'C':
|
|
case 'S':
|
|
case 'I':
|
|
s.PopInt();
|
|
break;
|
|
case 'F':
|
|
s.PopFloat();
|
|
break;
|
|
case 'D':
|
|
s.PopDouble();
|
|
break;
|
|
case 'J':
|
|
s.PopLong();
|
|
break;
|
|
case 'L':
|
|
case '[':
|
|
if(s.PopAnyType() != VerifierTypeWrapper.Null)
|
|
{
|
|
throw new VerifyError();
|
|
}
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s.PopType(GetFieldref(instr.Arg1).GetFieldType());
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__getfield:
|
|
case NormalizedByteCode.__dynamic_getfield:
|
|
{
|
|
s.PopObjectType(GetFieldref(instr.Arg1).GetClassType());
|
|
ClassFile.ConstantPoolItemFieldref cpi = GetFieldref(instr.Arg1);
|
|
if(cpi.GetField() != null && cpi.GetField().FieldTypeWrapper.IsUnloadable)
|
|
{
|
|
s.PushType(cpi.GetField().FieldTypeWrapper);
|
|
}
|
|
else
|
|
{
|
|
s.PushType(cpi.GetFieldType());
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__putfield:
|
|
case NormalizedByteCode.__dynamic_putfield:
|
|
s.PopType(GetFieldref(instr.Arg1).GetFieldType());
|
|
// putfield is allowed to access the uninitialized this
|
|
if(s.PeekType() == VerifierTypeWrapper.UninitializedThis
|
|
&& wrapper.IsAssignableTo(GetFieldref(instr.Arg1).GetClassType()))
|
|
{
|
|
s.PopType();
|
|
}
|
|
else
|
|
{
|
|
s.PopObjectType(GetFieldref(instr.Arg1).GetClassType());
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__ldc:
|
|
{
|
|
switch(GetConstantPoolConstantType(instr.Arg1))
|
|
{
|
|
case ClassFile.ConstantType.Double:
|
|
s.PushDouble();
|
|
break;
|
|
case ClassFile.ConstantType.Float:
|
|
s.PushFloat();
|
|
break;
|
|
case ClassFile.ConstantType.Integer:
|
|
s.PushInt();
|
|
break;
|
|
case ClassFile.ConstantType.Long:
|
|
s.PushLong();
|
|
break;
|
|
case ClassFile.ConstantType.String:
|
|
s.PushType(CoreClasses.java.lang.String.Wrapper);
|
|
break;
|
|
case ClassFile.ConstantType.Class:
|
|
if(classFile.MajorVersion < 49)
|
|
{
|
|
throw new VerifyError("Illegal type in constant pool");
|
|
}
|
|
s.PushType(CoreClasses.java.lang.Class.Wrapper);
|
|
break;
|
|
default:
|
|
// NOTE this is not a VerifyError, because it cannot happen (unless we have
|
|
// a bug in ClassFile.GetConstantPoolConstantType)
|
|
throw new InvalidOperationException();
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__clone_array:
|
|
case NormalizedByteCode.__invokevirtual:
|
|
case NormalizedByteCode.__invokespecial:
|
|
case NormalizedByteCode.__invokeinterface:
|
|
case NormalizedByteCode.__invokestatic:
|
|
case NormalizedByteCode.__dynamic_invokevirtual:
|
|
case NormalizedByteCode.__dynamic_invokespecial:
|
|
case NormalizedByteCode.__dynamic_invokeinterface:
|
|
case NormalizedByteCode.__dynamic_invokestatic:
|
|
{
|
|
ClassFile.ConstantPoolItemMI cpi = GetMethodref(instr.Arg1);
|
|
s.MultiPopAnyType(cpi.GetArgTypes().Length);
|
|
if(instr.NormalizedOpCode != NormalizedByteCode.__invokestatic
|
|
&& instr.NormalizedOpCode != NormalizedByteCode.__dynamic_invokestatic)
|
|
{
|
|
TypeWrapper type = s.PopType();
|
|
if(ReferenceEquals(cpi.Name, StringConstants.INIT))
|
|
{
|
|
// after we've invoked the constructor, the uninitialized references
|
|
// are now initialized
|
|
if(type == VerifierTypeWrapper.UninitializedThis)
|
|
{
|
|
if(s.GetLocalTypeEx(0) == type)
|
|
{
|
|
s.SetLocalType(0, thisType, i);
|
|
}
|
|
s.MarkInitialized(type, wrapper, i);
|
|
s.SetUnitializedThis(false);
|
|
}
|
|
else if(VerifierTypeWrapper.IsNew(type))
|
|
{
|
|
s.MarkInitialized(type, ((VerifierTypeWrapper)type).UnderlyingType, i);
|
|
}
|
|
else
|
|
{
|
|
// This is a VerifyError, but it will be caught by our second pass
|
|
}
|
|
}
|
|
}
|
|
TypeWrapper retType = cpi.GetRetType();
|
|
if(retType != PrimitiveTypeWrapper.VOID)
|
|
{
|
|
if(cpi.GetMethod() != null && cpi.GetMethod().ReturnType.IsUnloadable)
|
|
{
|
|
s.PushType(cpi.GetMethod().ReturnType);
|
|
}
|
|
else if(retType == PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
s.PushExtendedDouble();
|
|
}
|
|
else if(retType == PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
s.PushExtendedFloat();
|
|
}
|
|
else
|
|
{
|
|
s.PushType(retType);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__goto:
|
|
break;
|
|
case NormalizedByteCode.__istore:
|
|
s.PopInt();
|
|
s.SetLocalInt(instr.NormalizedArg1, i);
|
|
break;
|
|
case NormalizedByteCode.__iload:
|
|
s.GetLocalInt(instr.NormalizedArg1);
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__ineg:
|
|
s.PopInt();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__iadd:
|
|
case NormalizedByteCode.__isub:
|
|
case NormalizedByteCode.__imul:
|
|
case NormalizedByteCode.__idiv:
|
|
case NormalizedByteCode.__irem:
|
|
case NormalizedByteCode.__iand:
|
|
case NormalizedByteCode.__ior:
|
|
case NormalizedByteCode.__ixor:
|
|
case NormalizedByteCode.__ishl:
|
|
case NormalizedByteCode.__ishr:
|
|
case NormalizedByteCode.__iushr:
|
|
s.PopInt();
|
|
s.PopInt();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__lneg:
|
|
s.PopLong();
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__ladd:
|
|
case NormalizedByteCode.__lsub:
|
|
case NormalizedByteCode.__lmul:
|
|
case NormalizedByteCode.__ldiv:
|
|
case NormalizedByteCode.__lrem:
|
|
case NormalizedByteCode.__land:
|
|
case NormalizedByteCode.__lor:
|
|
case NormalizedByteCode.__lxor:
|
|
s.PopLong();
|
|
s.PopLong();
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__lshl:
|
|
case NormalizedByteCode.__lshr:
|
|
case NormalizedByteCode.__lushr:
|
|
s.PopInt();
|
|
s.PopLong();
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__fneg:
|
|
if(s.PopFloat())
|
|
{
|
|
s.PushExtendedFloat();
|
|
}
|
|
else
|
|
{
|
|
s.PushFloat();
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__fadd:
|
|
case NormalizedByteCode.__fsub:
|
|
case NormalizedByteCode.__fmul:
|
|
case NormalizedByteCode.__fdiv:
|
|
case NormalizedByteCode.__frem:
|
|
s.PopFloat();
|
|
s.PopFloat();
|
|
s.PushExtendedFloat();
|
|
break;
|
|
case NormalizedByteCode.__dneg:
|
|
if(s.PopDouble())
|
|
{
|
|
s.PushExtendedDouble();
|
|
}
|
|
else
|
|
{
|
|
s.PushDouble();
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__dadd:
|
|
case NormalizedByteCode.__dsub:
|
|
case NormalizedByteCode.__dmul:
|
|
case NormalizedByteCode.__ddiv:
|
|
case NormalizedByteCode.__drem:
|
|
s.PopDouble();
|
|
s.PopDouble();
|
|
s.PushExtendedDouble();
|
|
break;
|
|
case NormalizedByteCode.__new:
|
|
{
|
|
// mark the type, so that we can ascertain that it is a "new object"
|
|
TypeWrapper type;
|
|
if(!newTypes.TryGetValue(i, out type))
|
|
{
|
|
type = GetConstantPoolClassType(instr.Arg1);
|
|
if(type.IsArray)
|
|
{
|
|
throw new VerifyError("Illegal use of array type");
|
|
}
|
|
type = VerifierTypeWrapper.MakeNew(type, i);
|
|
newTypes[i] = type;
|
|
}
|
|
s.PushType(type);
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__multianewarray:
|
|
{
|
|
if(instr.Arg2 < 1)
|
|
{
|
|
throw new VerifyError("Illegal dimension argument");
|
|
}
|
|
for(int j = 0; j < instr.Arg2; j++)
|
|
{
|
|
s.PopInt();
|
|
}
|
|
TypeWrapper type = GetConstantPoolClassType(instr.Arg1);
|
|
if(type.ArrayRank < instr.Arg2)
|
|
{
|
|
throw new VerifyError("Illegal dimension argument");
|
|
}
|
|
s.PushType(type);
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__anewarray:
|
|
{
|
|
s.PopInt();
|
|
TypeWrapper type = GetConstantPoolClassType(instr.Arg1);
|
|
if(type.IsUnloadable)
|
|
{
|
|
s.PushType(new UnloadableTypeWrapper("[" + type.SigName));
|
|
}
|
|
else
|
|
{
|
|
s.PushType(type.MakeArrayType(1));
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__newarray:
|
|
s.PopInt();
|
|
switch(instr.Arg1)
|
|
{
|
|
case 4:
|
|
s.PushType(BooleanArrayType);
|
|
break;
|
|
case 5:
|
|
s.PushType(CharArrayType);
|
|
break;
|
|
case 6:
|
|
s.PushType(FloatArrayType);
|
|
break;
|
|
case 7:
|
|
s.PushType(DoubleArrayType);
|
|
break;
|
|
case 8:
|
|
s.PushType(ByteArrayType);
|
|
break;
|
|
case 9:
|
|
s.PushType(ShortArrayType);
|
|
break;
|
|
case 10:
|
|
s.PushType(IntArrayType);
|
|
break;
|
|
case 11:
|
|
s.PushType(LongArrayType);
|
|
break;
|
|
default:
|
|
throw new VerifyError("Bad type");
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__swap:
|
|
{
|
|
TypeWrapper t1 = s.PopType();
|
|
TypeWrapper t2 = s.PopType();
|
|
s.PushType(t1);
|
|
s.PushType(t2);
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__dup:
|
|
{
|
|
TypeWrapper t = s.PopType();
|
|
s.PushType(t);
|
|
s.PushType(t);
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__dup2:
|
|
{
|
|
TypeWrapper t = s.PopAnyType();
|
|
if(t.IsWidePrimitive || t == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
s.PushType(t);
|
|
s.PushType(t);
|
|
}
|
|
else
|
|
{
|
|
TypeWrapper t2 = s.PopType();
|
|
s.PushType(t2);
|
|
s.PushType(t);
|
|
s.PushType(t2);
|
|
s.PushType(t);
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__dup_x1:
|
|
{
|
|
TypeWrapper value1 = s.PopType();
|
|
TypeWrapper value2 = s.PopType();
|
|
s.PushType(value1);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__dup2_x1:
|
|
{
|
|
TypeWrapper value1 = s.PopAnyType();
|
|
if(value1.IsWidePrimitive || value1 == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
TypeWrapper value2 = s.PopType();
|
|
s.PushType(value1);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
else
|
|
{
|
|
TypeWrapper value2 = s.PopType();
|
|
TypeWrapper value3 = s.PopType();
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
s.PushType(value3);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__dup_x2:
|
|
{
|
|
TypeWrapper value1 = s.PopType();
|
|
TypeWrapper value2 = s.PopAnyType();
|
|
if(value2.IsWidePrimitive || value2 == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
s.PushType(value1);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
else
|
|
{
|
|
TypeWrapper value3 = s.PopType();
|
|
s.PushType(value1);
|
|
s.PushType(value3);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__dup2_x2:
|
|
{
|
|
TypeWrapper value1 = s.PopAnyType();
|
|
if(value1.IsWidePrimitive || value1 == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
TypeWrapper value2 = s.PopAnyType();
|
|
if(value2.IsWidePrimitive || value2 == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
// Form 4
|
|
s.PushType(value1);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
else
|
|
{
|
|
// Form 2
|
|
TypeWrapper value3 = s.PopType();
|
|
s.PushType(value1);
|
|
s.PushType(value3);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TypeWrapper value2 = s.PopType();
|
|
TypeWrapper value3 = s.PopAnyType();
|
|
if(value3.IsWidePrimitive || value3 == VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
// Form 3
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
s.PushType(value3);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
else
|
|
{
|
|
// Form 4
|
|
TypeWrapper value4 = s.PopType();
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
s.PushType(value4);
|
|
s.PushType(value3);
|
|
s.PushType(value2);
|
|
s.PushType(value1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__pop:
|
|
s.PopType();
|
|
break;
|
|
case NormalizedByteCode.__pop2:
|
|
{
|
|
TypeWrapper type = s.PopAnyType();
|
|
if(!type.IsWidePrimitive && type != VerifierTypeWrapper.ExtendedDouble)
|
|
{
|
|
s.PopType();
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__monitorenter:
|
|
case NormalizedByteCode.__monitorexit:
|
|
// TODO these bytecodes are allowed on an uninitialized object, but
|
|
// we don't support that at the moment...
|
|
s.PopObjectType();
|
|
break;
|
|
case NormalizedByteCode.__return:
|
|
// mw is null if we're called from IsSideEffectFreeStaticInitializer
|
|
if(mw != null)
|
|
{
|
|
if(mw.ReturnType != PrimitiveTypeWrapper.VOID)
|
|
{
|
|
throw new VerifyError("Wrong return type in function");
|
|
}
|
|
// if we're a constructor, make sure we called the base class constructor
|
|
s.CheckUninitializedThis();
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__areturn:
|
|
s.PopObjectType(mw.ReturnType);
|
|
break;
|
|
case NormalizedByteCode.__ireturn:
|
|
{
|
|
s.PopInt();
|
|
if(!mw.ReturnType.IsIntOnStackPrimitive)
|
|
{
|
|
throw new VerifyError("Wrong return type in function");
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__lreturn:
|
|
s.PopLong();
|
|
if(mw.ReturnType != PrimitiveTypeWrapper.LONG)
|
|
{
|
|
throw new VerifyError("Wrong return type in function");
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__freturn:
|
|
s.PopFloat();
|
|
if(mw.ReturnType != PrimitiveTypeWrapper.FLOAT)
|
|
{
|
|
throw new VerifyError("Wrong return type in function");
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__dreturn:
|
|
s.PopDouble();
|
|
if(mw.ReturnType != PrimitiveTypeWrapper.DOUBLE)
|
|
{
|
|
throw new VerifyError("Wrong return type in function");
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__fload:
|
|
s.GetLocalFloat(instr.NormalizedArg1);
|
|
s.PushFloat();
|
|
break;
|
|
case NormalizedByteCode.__fstore:
|
|
s.PopFloat();
|
|
s.SetLocalFloat(instr.NormalizedArg1, i);
|
|
break;
|
|
case NormalizedByteCode.__dload:
|
|
s.GetLocalDouble(instr.NormalizedArg1);
|
|
s.PushDouble();
|
|
break;
|
|
case NormalizedByteCode.__dstore:
|
|
s.PopDouble();
|
|
s.SetLocalDouble(instr.NormalizedArg1, i);
|
|
break;
|
|
case NormalizedByteCode.__lload:
|
|
s.GetLocalLong(instr.NormalizedArg1);
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__lstore:
|
|
s.PopLong();
|
|
s.SetLocalLong(instr.NormalizedArg1, i);
|
|
break;
|
|
case NormalizedByteCode.__lconst_0:
|
|
case NormalizedByteCode.__lconst_1:
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__fconst_0:
|
|
case NormalizedByteCode.__fconst_1:
|
|
case NormalizedByteCode.__fconst_2:
|
|
s.PushFloat();
|
|
break;
|
|
case NormalizedByteCode.__dconst_0:
|
|
case NormalizedByteCode.__dconst_1:
|
|
s.PushDouble();
|
|
break;
|
|
case NormalizedByteCode.__lcmp:
|
|
s.PopLong();
|
|
s.PopLong();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__fcmpl:
|
|
case NormalizedByteCode.__fcmpg:
|
|
s.PopFloat();
|
|
s.PopFloat();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__dcmpl:
|
|
case NormalizedByteCode.__dcmpg:
|
|
s.PopDouble();
|
|
s.PopDouble();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__checkcast:
|
|
s.PopObjectType();
|
|
s.PushType(GetConstantPoolClassType(instr.Arg1));
|
|
break;
|
|
case NormalizedByteCode.__instanceof:
|
|
s.PopObjectType();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__iinc:
|
|
s.GetLocalInt(instr.Arg1);
|
|
break;
|
|
case NormalizedByteCode.__athrow:
|
|
if (VerifierTypeWrapper.IsFaultBlockException(s.PeekType()))
|
|
{
|
|
s.PopFaultBlockException();
|
|
}
|
|
else
|
|
{
|
|
s.PopObjectType(CoreClasses.java.lang.Throwable.Wrapper);
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__tableswitch:
|
|
case NormalizedByteCode.__lookupswitch:
|
|
s.PopInt();
|
|
break;
|
|
case NormalizedByteCode.__i2b:
|
|
s.PopInt();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__i2c:
|
|
s.PopInt();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__i2s:
|
|
s.PopInt();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__i2l:
|
|
s.PopInt();
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__i2f:
|
|
s.PopInt();
|
|
s.PushFloat();
|
|
break;
|
|
case NormalizedByteCode.__i2d:
|
|
s.PopInt();
|
|
s.PushDouble();
|
|
break;
|
|
case NormalizedByteCode.__l2i:
|
|
s.PopLong();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__l2f:
|
|
s.PopLong();
|
|
s.PushFloat();
|
|
break;
|
|
case NormalizedByteCode.__l2d:
|
|
s.PopLong();
|
|
s.PushDouble();
|
|
break;
|
|
case NormalizedByteCode.__f2i:
|
|
s.PopFloat();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__f2l:
|
|
s.PopFloat();
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__f2d:
|
|
s.PopFloat();
|
|
s.PushDouble();
|
|
break;
|
|
case NormalizedByteCode.__d2i:
|
|
s.PopDouble();
|
|
s.PushInt();
|
|
break;
|
|
case NormalizedByteCode.__d2f:
|
|
s.PopDouble();
|
|
s.PushFloat();
|
|
break;
|
|
case NormalizedByteCode.__d2l:
|
|
s.PopDouble();
|
|
s.PushLong();
|
|
break;
|
|
case NormalizedByteCode.__nop:
|
|
if(i + 1 == instructions.Length)
|
|
{
|
|
throw new VerifyError("Falling off the end of the code");
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__static_error:
|
|
break;
|
|
default:
|
|
throw new NotImplementedException(instr.NormalizedOpCode.ToString());
|
|
}
|
|
if(s.GetStackHeight() > method.MaxStack)
|
|
{
|
|
throw new VerifyError("Stack size too large");
|
|
}
|
|
try
|
|
{
|
|
switch(ByteCodeMetaData.GetFlowControl(instr.NormalizedOpCode))
|
|
{
|
|
case ByteCodeFlowControl.Switch:
|
|
for(int j = 0; j < instr.SwitchEntryCount; j++)
|
|
{
|
|
state[instr.GetSwitchTargetIndex(j)] += s;
|
|
}
|
|
state[instr.DefaultTarget] += s;
|
|
break;
|
|
case ByteCodeFlowControl.CondBranch:
|
|
state[i + 1] += s;
|
|
state[instr.TargetIndex] += s;
|
|
break;
|
|
case ByteCodeFlowControl.Branch:
|
|
state[instr.TargetIndex] += s;
|
|
break;
|
|
case ByteCodeFlowControl.Return:
|
|
case ByteCodeFlowControl.Throw:
|
|
break;
|
|
case ByteCodeFlowControl.Next:
|
|
state[i + 1] += s;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
// we're going to assume that this always means that we have an invalid branch target
|
|
// NOTE because PcIndexMap returns -1 for illegal PCs (in the middle of an instruction) and
|
|
// we always use that value as an index into the state array, any invalid PC will result
|
|
// in an IndexOutOfRangeException
|
|
throw new VerifyError("Illegal target of jump or branch");
|
|
}
|
|
}
|
|
catch(VerifyError x)
|
|
{
|
|
string opcode = instructions[i].NormalizedOpCode.ToString();
|
|
if(opcode.StartsWith("__"))
|
|
{
|
|
opcode = opcode.Substring(2);
|
|
}
|
|
throw new VerifyError(string.Format("{5} (class: {0}, method: {1}, signature: {2}, offset: {3}, instruction: {4})",
|
|
classFile.Name, method.Name, method.Signature, instructions[i].PC, opcode, x.Message), x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// this verification pass must run on the unmodified bytecode stream
|
|
private void VerifyPassTwo()
|
|
{
|
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
|
for (int i = 0; i < instructions.Length; i++)
|
|
{
|
|
if (state[i] != null)
|
|
{
|
|
try
|
|
{
|
|
switch (instructions[i].NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__invokeinterface:
|
|
case NormalizedByteCode.__invokespecial:
|
|
case NormalizedByteCode.__invokestatic:
|
|
case NormalizedByteCode.__invokevirtual:
|
|
VerifyInvokePassTwo(i);
|
|
break;
|
|
}
|
|
// verify backward branches
|
|
switch (ByteCodeMetaData.GetFlowControl(instructions[i].NormalizedOpCode))
|
|
{
|
|
case ByteCodeFlowControl.Switch:
|
|
{
|
|
bool hasbackbranch = false;
|
|
for (int j = 0; j < instructions[i].SwitchEntryCount; j++)
|
|
{
|
|
hasbackbranch |= instructions[i].GetSwitchTargetIndex(j) < i;
|
|
}
|
|
hasbackbranch |= instructions[i].DefaultTarget < i;
|
|
if (hasbackbranch)
|
|
{
|
|
// backward branches cannot have uninitialized objects on
|
|
// the stack or in local variables
|
|
state[i].CheckUninitializedObjRefs();
|
|
}
|
|
break;
|
|
}
|
|
case ByteCodeFlowControl.Branch:
|
|
case ByteCodeFlowControl.CondBranch:
|
|
if (instructions[i].TargetIndex < i)
|
|
{
|
|
// backward branches cannot have uninitialized objects on
|
|
// the stack or in local variables
|
|
state[i].CheckUninitializedObjRefs();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch (VerifyError x)
|
|
{
|
|
string opcode = instructions[i].NormalizedOpCode.ToString();
|
|
if (opcode.StartsWith("__"))
|
|
{
|
|
opcode = opcode.Substring(2);
|
|
}
|
|
throw new VerifyError(string.Format("{5} (class: {0}, method: {1}, signature: {2}, offset: {3}, instruction: {4})",
|
|
classFile.Name, method.Name, method.Signature, instructions[i].PC, opcode, x.Message), x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void VerifyInvokePassTwo(int index)
|
|
{
|
|
StackState stack = new StackState(state[index]);
|
|
NormalizedByteCode invoke = method.Instructions[index].NormalizedOpCode;
|
|
ClassFile.ConstantPoolItemMI cpi = GetMethodref(method.Instructions[index].Arg1);
|
|
if ((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (invoke == NormalizedByteCode.__invokeinterface))
|
|
{
|
|
throw new VerifyError("Illegal constant pool index");
|
|
}
|
|
if (invoke != NormalizedByteCode.__invokespecial && ReferenceEquals(cpi.Name, StringConstants.INIT))
|
|
{
|
|
throw new VerifyError("Must call initializers using invokespecial");
|
|
}
|
|
if (ReferenceEquals(cpi.Name, StringConstants.CLINIT))
|
|
{
|
|
throw new VerifyError("Illegal call to internal method");
|
|
}
|
|
TypeWrapper[] args = cpi.GetArgTypes();
|
|
for (int j = args.Length - 1; j >= 0; j--)
|
|
{
|
|
stack.PopType(args[j]);
|
|
}
|
|
if (invoke == NormalizedByteCode.__invokeinterface)
|
|
{
|
|
int argcount = args.Length + 1;
|
|
for (int j = 0; j < args.Length; j++)
|
|
{
|
|
if (args[j].IsWidePrimitive)
|
|
{
|
|
argcount++;
|
|
}
|
|
}
|
|
if (method.Instructions[index].Arg2 != argcount)
|
|
{
|
|
throw new VerifyError("Inconsistent args size");
|
|
}
|
|
}
|
|
bool isnew = false;
|
|
TypeWrapper thisType;
|
|
if (invoke == NormalizedByteCode.__invokestatic)
|
|
{
|
|
thisType = null;
|
|
}
|
|
else
|
|
{
|
|
thisType = SigTypeToClassName(stack.PeekType(), cpi.GetClassType(), wrapper);
|
|
if (ReferenceEquals(cpi.Name, StringConstants.INIT))
|
|
{
|
|
TypeWrapper type = stack.PopType();
|
|
isnew = VerifierTypeWrapper.IsNew(type);
|
|
if ((isnew && ((VerifierTypeWrapper)type).UnderlyingType != cpi.GetClassType()) ||
|
|
(type == VerifierTypeWrapper.UninitializedThis && cpi.GetClassType() != wrapper.BaseTypeWrapper && cpi.GetClassType() != wrapper) ||
|
|
(!isnew && type != VerifierTypeWrapper.UninitializedThis))
|
|
{
|
|
// TODO oddly enough, Java fails verification for the class without
|
|
// even running the constructor, so maybe constructors are always
|
|
// verified...
|
|
// NOTE when a constructor isn't verifiable, the static initializer
|
|
// doesn't run either
|
|
throw new VerifyError("Call to wrong initialization method");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (invoke != NormalizedByteCode.__invokeinterface)
|
|
{
|
|
TypeWrapper refType = stack.PopObjectType();
|
|
TypeWrapper targetType = cpi.GetClassType();
|
|
if (!VerifierTypeWrapper.IsNullOrUnloadable(refType) &&
|
|
!targetType.IsUnloadable &&
|
|
!refType.IsAssignableTo(targetType))
|
|
{
|
|
throw new VerifyError("Incompatible object argument for function call");
|
|
}
|
|
// for invokespecial we also need to make sure we're calling ourself or a base class
|
|
if (invoke == NormalizedByteCode.__invokespecial)
|
|
{
|
|
if (!VerifierTypeWrapper.IsNullOrUnloadable(refType) && !refType.IsSubTypeOf(wrapper))
|
|
{
|
|
throw new VerifyError("Incompatible target object for invokespecial");
|
|
}
|
|
if (!targetType.IsUnloadable && !wrapper.IsSubTypeOf(targetType))
|
|
{
|
|
throw new VerifyError("Invokespecial cannot call subclass methods");
|
|
}
|
|
}
|
|
}
|
|
else /* __invokeinterface */
|
|
{
|
|
// NOTE unlike in the above case, we also allow *any* interface target type
|
|
// regardless of whether it is compatible or not, because if it is not compatible
|
|
// we want an IncompatibleClassChangeError at runtime
|
|
TypeWrapper refType = stack.PopObjectType();
|
|
TypeWrapper targetType = cpi.GetClassType();
|
|
if (!VerifierTypeWrapper.IsNullOrUnloadable(refType)
|
|
&& !targetType.IsUnloadable
|
|
&& !refType.IsAssignableTo(targetType)
|
|
&& !targetType.IsInterface)
|
|
{
|
|
throw new VerifyError("Incompatible object argument for function call");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void OptimizationPass(CodeInfo codeInfo, ClassFile classFile, ClassFile.Method method, UntangledExceptionTable exceptions, TypeWrapper wrapper, ClassLoaderWrapper classLoader)
|
|
{
|
|
// Optimization pass
|
|
if (classLoader.RemoveAsserts)
|
|
{
|
|
FieldWrapper assertionsDisabled = null;
|
|
foreach (FieldWrapper fw in wrapper.GetFields())
|
|
{
|
|
// HACK we assume that all compilers use the same name for this field (ecj and javac do)
|
|
if (fw.Name == "$assertionsDisabled" && fw.Signature == "Z"
|
|
&& (fw.Modifiers & (IKVM.Attributes.Modifiers.AccessMask | IKVM.Attributes.Modifiers.Final | IKVM.Attributes.Modifiers.Static | IKVM.Attributes.Modifiers.Synthetic))
|
|
== (IKVM.Attributes.Modifiers.Static | IKVM.Attributes.Modifiers.Final | IKVM.Attributes.Modifiers.Synthetic))
|
|
{
|
|
assertionsDisabled = fw;
|
|
break;
|
|
}
|
|
}
|
|
if (assertionsDisabled != null)
|
|
{
|
|
// compute branch targets
|
|
InstructionFlags[] flags = MethodAnalyzer.ComputePartialReachability(codeInfo, method.Instructions, exceptions, 0, false);
|
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
|
for (int i = 0; i < instructions.Length; i++)
|
|
{
|
|
if (instructions[i].NormalizedOpCode == NormalizedByteCode.__getstatic
|
|
&& instructions[i + 1].NormalizedOpCode == NormalizedByteCode.__ifne
|
|
&& instructions[i + 1].TargetIndex > i
|
|
&& (flags[i + 1] & InstructionFlags.BranchTarget) == 0)
|
|
{
|
|
ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instructions[i].Arg1);
|
|
if (cpi.GetField() == assertionsDisabled)
|
|
{
|
|
// We've found an assertion. We patch the instruction to branch around it so that
|
|
// the assertion code will be unreachable (and hence optimized away).
|
|
// Note that the goto will be optimized away later by the code generator (which removes unnecessary branches).
|
|
instructions[i].PatchOpCode(NormalizedByteCode.__goto, instructions[i + 1].TargetIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PatchHardErrorsAndDynamicMemberAccess(TypeWrapper wrapper, MethodWrapper mw)
|
|
{
|
|
// Now we do another pass to find "hard error" instructions
|
|
if(true)
|
|
{
|
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
|
for(int i = 0; i < instructions.Length; i++)
|
|
{
|
|
if(state[i] != null)
|
|
{
|
|
StackState stack = new StackState(state[i]);
|
|
switch(instructions[i].NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__invokeinterface:
|
|
case NormalizedByteCode.__invokespecial:
|
|
case NormalizedByteCode.__invokestatic:
|
|
case NormalizedByteCode.__invokevirtual:
|
|
PatchInvoke(wrapper, ref instructions[i], stack);
|
|
break;
|
|
case NormalizedByteCode.__getfield:
|
|
case NormalizedByteCode.__putfield:
|
|
case NormalizedByteCode.__getstatic:
|
|
case NormalizedByteCode.__putstatic:
|
|
PatchFieldAccess(wrapper, mw, ref instructions[i], stack);
|
|
break;
|
|
case NormalizedByteCode.__ldc:
|
|
if(classFile.GetConstantPoolConstantType(instructions[i].Arg1) == ClassFile.ConstantType.Class)
|
|
{
|
|
TypeWrapper tw = classFile.GetConstantPoolClassType(instructions[i].Arg1);
|
|
if(tw.IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.NoClassDefFoundError, "{0}", tw.Name);
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__new:
|
|
{
|
|
TypeWrapper tw = classFile.GetConstantPoolClassType(instructions[i].Arg1);
|
|
if(tw.IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.NoClassDefFoundError, "{0}", tw.Name);
|
|
#endif
|
|
}
|
|
else if(!tw.IsAccessibleFrom(wrapper))
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.IllegalAccessError, "Try to access class {0} from class {1}", tw.Name, wrapper.Name);
|
|
}
|
|
else if(tw.IsAbstract)
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.InstantiationError, "{0}", tw.Name);
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__multianewarray:
|
|
case NormalizedByteCode.__anewarray:
|
|
{
|
|
TypeWrapper tw = classFile.GetConstantPoolClassType(instructions[i].Arg1);
|
|
if(tw.IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.NoClassDefFoundError, "{0}", tw.Name);
|
|
#endif
|
|
}
|
|
else if(!tw.IsAccessibleFrom(wrapper))
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.IllegalAccessError, "Try to access class {0} from class {1}", tw.Name, wrapper.Name);
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__checkcast:
|
|
case NormalizedByteCode.__instanceof:
|
|
{
|
|
TypeWrapper tw = classFile.GetConstantPoolClassType(instructions[i].Arg1);
|
|
if(tw.IsUnloadable)
|
|
{
|
|
// If the type is unloadable, we always generate the dynamic code
|
|
// (regardless of JVM.DisableDynamicBinding), because at runtime,
|
|
// null references should always pass thru without attempting
|
|
// to load the type (for Sun compatibility).
|
|
}
|
|
else if(!tw.IsAccessibleFrom(wrapper))
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.IllegalAccessError, "Try to access class {0} from class {1}", tw.Name, wrapper.Name);
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__aaload:
|
|
{
|
|
stack.PopInt();
|
|
TypeWrapper tw = stack.PopArrayType();
|
|
if(tw.IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.NoClassDefFoundError, "{0}", tw.Name);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case NormalizedByteCode.__aastore:
|
|
{
|
|
stack.PopObjectType();
|
|
stack.PopInt();
|
|
TypeWrapper tw = stack.PopArrayType();
|
|
if(tw.IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
SetHardError(wrapper.GetClassLoader(), ref instructions[i], HardError.NoClassDefFoundError, "{0}", tw.Name);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static InstructionFlags[] ComputePartialReachability(CodeInfo codeInfo, ClassFile.Method.Instruction[] instructions, UntangledExceptionTable exceptions, int initialInstructionIndex, bool skipFaultBlocks)
|
|
{
|
|
InstructionFlags[] flags = new InstructionFlags[instructions.Length];
|
|
flags[initialInstructionIndex] |= InstructionFlags.Reachable;
|
|
UpdatePartialReachability(flags, codeInfo, instructions, exceptions, skipFaultBlocks);
|
|
return flags;
|
|
}
|
|
|
|
private static void UpdatePartialReachability(InstructionFlags[] flags, CodeInfo codeInfo, ClassFile.Method.Instruction[] instructions, UntangledExceptionTable exceptions, bool skipFaultBlocks)
|
|
{
|
|
bool done = false;
|
|
while (!done)
|
|
{
|
|
done = true;
|
|
for (int i = 0; i < instructions.Length; i++)
|
|
{
|
|
if ((flags[i] & (InstructionFlags.Reachable | InstructionFlags.Processed)) == InstructionFlags.Reachable)
|
|
{
|
|
done = false;
|
|
flags[i] |= InstructionFlags.Processed;
|
|
// mark the exception handlers reachable from this instruction
|
|
for (int j = 0; j < exceptions.Length; j++)
|
|
{
|
|
if (exceptions[j].startIndex <= i && i < exceptions[j].endIndex)
|
|
{
|
|
int idx = exceptions[j].handlerIndex;
|
|
if (!skipFaultBlocks || !VerifierTypeWrapper.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(idx, 0)))
|
|
{
|
|
flags[idx] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
|
}
|
|
}
|
|
}
|
|
MarkSuccessors(instructions, flags, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void MarkSuccessors(ClassFile.Method.Instruction[] code, InstructionFlags[] flags, int index)
|
|
{
|
|
switch (ByteCodeMetaData.GetFlowControl(code[index].NormalizedOpCode))
|
|
{
|
|
case ByteCodeFlowControl.Switch:
|
|
{
|
|
for (int i = 0; i < code[index].SwitchEntryCount; i++)
|
|
{
|
|
flags[code[index].GetSwitchTargetIndex(i)] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
|
}
|
|
flags[code[index].DefaultTarget] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
|
break;
|
|
}
|
|
case ByteCodeFlowControl.Branch:
|
|
flags[code[index].TargetIndex] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
|
break;
|
|
case ByteCodeFlowControl.CondBranch:
|
|
flags[code[index].TargetIndex] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
|
flags[index + 1] |= InstructionFlags.Reachable;
|
|
break;
|
|
case ByteCodeFlowControl.Return:
|
|
case ByteCodeFlowControl.Throw:
|
|
break;
|
|
case ByteCodeFlowControl.Next:
|
|
flags[index + 1] |= InstructionFlags.Reachable;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
internal static UntangledExceptionTable UntangleExceptionBlocks(ClassFile classFile, ClassFile.Method method)
|
|
{
|
|
ClassFile.Method.Instruction[] instructions = method.Instructions;
|
|
ExceptionTableEntry[] exceptionTable = method.ExceptionTable;
|
|
List<ExceptionTableEntry> ar = new List<ExceptionTableEntry>(exceptionTable);
|
|
|
|
// This optimization removes the recursive exception handlers that Java compiler place around
|
|
// the exit of a synchronization block to be "safe" in the face of asynchronous exceptions.
|
|
// (see http://weblog.ikvm.net/PermaLink.aspx?guid=3af9548e-4905-4557-8809-65a205ce2cd6)
|
|
// We can safely remove them since the code we generate for this construct isn't async safe anyway,
|
|
// but there is another reason why this optimization may be slightly controversial. In some
|
|
// pathological cases it can cause observable differences, where the Sun JVM would spin in an
|
|
// infinite loop, but we will throw an exception. However, the perf benefit is large enough to
|
|
// warrant this "incompatibility".
|
|
// Note that there is also code in the exception handler handling code that detects these bytecode
|
|
// sequences to try to compile them into a fault block, instead of an exception handler.
|
|
for (int i = 0; i < ar.Count; i++)
|
|
{
|
|
ExceptionTableEntry ei = ar[i];
|
|
if (ei.startIndex == ei.handlerIndex && ei.catch_type == 0)
|
|
{
|
|
int index = ei.startIndex;
|
|
if (index + 2 < instructions.Length
|
|
&& ei.endIndex == index + 2
|
|
&& instructions[index].NormalizedOpCode == NormalizedByteCode.__aload
|
|
&& instructions[index + 1].NormalizedOpCode == NormalizedByteCode.__monitorexit
|
|
&& instructions[index + 2].NormalizedOpCode == NormalizedByteCode.__athrow)
|
|
{
|
|
// this is the async exception guard that Jikes and the Eclipse Java Compiler produce
|
|
ar.RemoveAt(i);
|
|
i--;
|
|
}
|
|
else if (index + 4 < instructions.Length
|
|
&& ei.endIndex == index + 3
|
|
&& instructions[index].NormalizedOpCode == NormalizedByteCode.__astore
|
|
&& instructions[index + 1].NormalizedOpCode == NormalizedByteCode.__aload
|
|
&& instructions[index + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit
|
|
&& instructions[index + 3].NormalizedOpCode == NormalizedByteCode.__aload
|
|
&& instructions[index + 4].NormalizedOpCode == NormalizedByteCode.__athrow
|
|
&& instructions[index].NormalizedArg1 == instructions[index + 3].NormalizedArg1)
|
|
{
|
|
// this is the async exception guard that javac produces
|
|
ar.RemoveAt(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--;
|
|
}
|
|
}
|
|
}
|
|
|
|
restart:
|
|
for (int i = 0; i < ar.Count; i++)
|
|
{
|
|
ExceptionTableEntry ei = ar[i];
|
|
for (int j = 0; j < ar.Count; j++)
|
|
{
|
|
ExceptionTableEntry ej = ar[j];
|
|
if (ei.startIndex <= ej.startIndex && ej.startIndex < ei.endIndex)
|
|
{
|
|
// 0006/test.j
|
|
if (ej.endIndex > ei.endIndex)
|
|
{
|
|
ExceptionTableEntry emi = new ExceptionTableEntry(ej.startIndex, ei.endIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ExceptionTableEntry emj = new ExceptionTableEntry(ej.startIndex, ei.endIndex, ej.handlerIndex, ej.catch_type, ej.ordinal);
|
|
ei = new ExceptionTableEntry(ei.startIndex, emi.startIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ej = new ExceptionTableEntry(emj.endIndex, ej.endIndex, ej.handlerIndex, ej.catch_type, ej.ordinal);
|
|
ar[i] = ei;
|
|
ar[j] = ej;
|
|
ar.Insert(j, emj);
|
|
ar.Insert(i + 1, emi);
|
|
goto restart;
|
|
}
|
|
// 0007/test.j
|
|
else if (j > i && ej.endIndex < ei.endIndex)
|
|
{
|
|
ExceptionTableEntry emi = new ExceptionTableEntry(ej.startIndex, ej.endIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ExceptionTableEntry eei = new ExceptionTableEntry(ej.endIndex, ei.endIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ei = new ExceptionTableEntry(ei.startIndex, emi.startIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ar[i] = ei;
|
|
ar.Insert(i + 1, eei);
|
|
ar.Insert(i + 1, emi);
|
|
goto restart;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Split try blocks at branch targets (branches from outside the try block)
|
|
restart_split:
|
|
for (int i = 0; i < ar.Count; i++)
|
|
{
|
|
ExceptionTableEntry ei = ar[i];
|
|
int start = ei.startIndex;
|
|
int end = ei.endIndex;
|
|
for (int j = 0; j < instructions.Length; j++)
|
|
{
|
|
if (j < start || j >= end)
|
|
{
|
|
switch (instructions[j].NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__tableswitch:
|
|
case NormalizedByteCode.__lookupswitch:
|
|
// start at -1 to have an opportunity to handle the default offset
|
|
for (int k = -1; k < instructions[j].SwitchEntryCount; k++)
|
|
{
|
|
int targetIndex = (k == -1 ? instructions[j].DefaultTarget : instructions[j].GetSwitchTargetIndex(k));
|
|
if (ei.startIndex < targetIndex && targetIndex < ei.endIndex)
|
|
{
|
|
ExceptionTableEntry en = new ExceptionTableEntry(targetIndex, ei.endIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ei = new ExceptionTableEntry(ei.startIndex, targetIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ar[i] = ei;
|
|
ar.Insert(i + 1, en);
|
|
goto restart_split;
|
|
}
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__ifeq:
|
|
case NormalizedByteCode.__ifne:
|
|
case NormalizedByteCode.__iflt:
|
|
case NormalizedByteCode.__ifge:
|
|
case NormalizedByteCode.__ifgt:
|
|
case NormalizedByteCode.__ifle:
|
|
case NormalizedByteCode.__if_icmpeq:
|
|
case NormalizedByteCode.__if_icmpne:
|
|
case NormalizedByteCode.__if_icmplt:
|
|
case NormalizedByteCode.__if_icmpge:
|
|
case NormalizedByteCode.__if_icmpgt:
|
|
case NormalizedByteCode.__if_icmple:
|
|
case NormalizedByteCode.__if_acmpeq:
|
|
case NormalizedByteCode.__if_acmpne:
|
|
case NormalizedByteCode.__ifnull:
|
|
case NormalizedByteCode.__ifnonnull:
|
|
case NormalizedByteCode.__goto:
|
|
{
|
|
int targetIndex = instructions[j].Arg1;
|
|
if (ei.startIndex < targetIndex && targetIndex < ei.endIndex)
|
|
{
|
|
ExceptionTableEntry en = new ExceptionTableEntry(targetIndex, ei.endIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ei = new ExceptionTableEntry(ei.startIndex, targetIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ar[i] = ei;
|
|
ar.Insert(i + 1, en);
|
|
goto restart_split;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// exception handlers are also a kind of jump, so we need to split try blocks around handlers as well
|
|
for (int i = 0; i < ar.Count; i++)
|
|
{
|
|
ExceptionTableEntry ei = ar[i];
|
|
for (int j = 0; j < ar.Count; j++)
|
|
{
|
|
ExceptionTableEntry ej = ar[j];
|
|
if (ei.startIndex < ej.handlerIndex && ej.handlerIndex < ei.endIndex)
|
|
{
|
|
ExceptionTableEntry en = new ExceptionTableEntry(ej.handlerIndex, ei.endIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ei = new ExceptionTableEntry(ei.startIndex, ej.handlerIndex, ei.handlerIndex, ei.catch_type, ei.ordinal);
|
|
ar[i] = ei;
|
|
ar.Insert(i + 1, en);
|
|
goto restart_split;
|
|
}
|
|
}
|
|
}
|
|
// filter out zero length try blocks
|
|
for (int i = 0; i < ar.Count; i++)
|
|
{
|
|
ExceptionTableEntry ei = ar[i];
|
|
if (ei.startIndex == ei.endIndex)
|
|
{
|
|
ar.RemoveAt(i);
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
// exception blocks that only contain harmless instructions (i.e. instructions that will *never* throw an exception)
|
|
// are also filtered out (to improve the quality of the generated code)
|
|
TypeWrapper exceptionType = ei.catch_type == 0 ? CoreClasses.java.lang.Throwable.Wrapper : classFile.GetConstantPoolClassType(ei.catch_type);
|
|
if (exceptionType.IsUnloadable)
|
|
{
|
|
// we can't remove handlers for unloadable types
|
|
}
|
|
else if (java_lang_ThreadDeath.IsAssignableTo(exceptionType))
|
|
{
|
|
// We only remove exception handlers that could catch ThreadDeath in limited cases, because it can be thrown
|
|
// asynchronously (and thus appear on any instruction). This is particularly important to ensure that
|
|
// we run finally blocks when a thread is killed.
|
|
// Note that even so, we aren't remotely async exception safe.
|
|
int start = ei.startIndex;
|
|
int end = ei.endIndex;
|
|
for (int j = start; j < end; j++)
|
|
{
|
|
switch (instructions[j].NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__aload:
|
|
case NormalizedByteCode.__iload:
|
|
case NormalizedByteCode.__lload:
|
|
case NormalizedByteCode.__fload:
|
|
case NormalizedByteCode.__dload:
|
|
case NormalizedByteCode.__astore:
|
|
case NormalizedByteCode.__istore:
|
|
case NormalizedByteCode.__lstore:
|
|
case NormalizedByteCode.__fstore:
|
|
case NormalizedByteCode.__dstore:
|
|
break;
|
|
case NormalizedByteCode.__dup:
|
|
case NormalizedByteCode.__dup_x1:
|
|
case NormalizedByteCode.__dup_x2:
|
|
case NormalizedByteCode.__dup2:
|
|
case NormalizedByteCode.__dup2_x1:
|
|
case NormalizedByteCode.__dup2_x2:
|
|
case NormalizedByteCode.__pop:
|
|
case NormalizedByteCode.__pop2:
|
|
break;
|
|
case NormalizedByteCode.__return:
|
|
case NormalizedByteCode.__areturn:
|
|
case NormalizedByteCode.__ireturn:
|
|
case NormalizedByteCode.__lreturn:
|
|
case NormalizedByteCode.__freturn:
|
|
case NormalizedByteCode.__dreturn:
|
|
break;
|
|
case NormalizedByteCode.__goto:
|
|
// if there is a branch that stays inside the block, we should keep the block
|
|
if (start <= instructions[j].TargetIndex && instructions[j].TargetIndex < end)
|
|
goto next;
|
|
break;
|
|
default:
|
|
goto next;
|
|
}
|
|
}
|
|
ar.RemoveAt(i);
|
|
i--;
|
|
}
|
|
else
|
|
{
|
|
int start = ei.startIndex;
|
|
int end = ei.endIndex;
|
|
for (int j = start; j < end; j++)
|
|
{
|
|
if (ByteCodeMetaData.CanThrowException(instructions[j].NormalizedOpCode))
|
|
{
|
|
goto next;
|
|
}
|
|
}
|
|
ar.RemoveAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
next: ;
|
|
}
|
|
|
|
ExceptionTableEntry[] exceptions = ar.ToArray();
|
|
Array.Sort(exceptions, new ExceptionSorter());
|
|
|
|
return new UntangledExceptionTable(exceptions);
|
|
}
|
|
|
|
private static bool AnalyzePotentialFaultBlocks(CodeInfo codeInfo, ClassFile.Method method, UntangledExceptionTable exceptions)
|
|
{
|
|
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
|
|
&& codeInfo.HasState(exceptions[i].handlerIndex)
|
|
&& VerifierTypeWrapper.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0)))
|
|
{
|
|
InstructionFlags[] flags = MethodAnalyzer.ComputePartialReachability(codeInfo, method.Instructions, exceptions, 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(codeInfo.GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0));
|
|
done = false;
|
|
changed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
stack.Push(current);
|
|
current = exceptions[i];
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
private static void ConvertFinallyBlocks(CodeInfo codeInfo, ClassFile.Method method, UntangledExceptionTable exceptions)
|
|
{
|
|
ClassFile.Method.Instruction[] code = method.Instructions;
|
|
InstructionFlags[] flags = ComputePartialReachability(codeInfo, code, exceptions, 0, false);
|
|
for (int i = 0; i < exceptions.Length; i++)
|
|
{
|
|
if (exceptions[i].catch_type == 0
|
|
&& codeInfo.HasState(exceptions[i].handlerIndex)
|
|
&& VerifierTypeWrapper.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0)))
|
|
{
|
|
int exit;
|
|
if (IsSynchronizedBlockHandler(code, exceptions[i].handlerIndex)
|
|
&& exceptions[i].endIndex - 2 >= exceptions[i].startIndex
|
|
&& TryFindSingleTryBlockExit(code, flags, exceptions, new ExceptionTableEntry(exceptions[i].startIndex, exceptions[i].endIndex - 2, exceptions[i].handlerIndex, 0, exceptions[i].ordinal), i, out exit)
|
|
&& exit == exceptions[i].endIndex - 2
|
|
&& (flags[exit + 1] & InstructionFlags.BranchTarget) == 0
|
|
&& MatchInstructions(code, exit, exceptions[i].handlerIndex + 1)
|
|
&& MatchInstructions(code, exit + 1, exceptions[i].handlerIndex + 2)
|
|
&& MatchExceptionCoverage(exceptions, i, exceptions[i].handlerIndex + 1, exceptions[i].handlerIndex + 3, exit, exit + 2)
|
|
&& exceptions[i].handlerIndex <= ushort.MaxValue)
|
|
{
|
|
code[exit].PatchOpCode(NormalizedByteCode.__goto_finally, exceptions[i].endIndex, (short)exceptions[i].handlerIndex);
|
|
exceptions.SetFinally(i);
|
|
}
|
|
else if (TryFindSingleTryBlockExit(code, flags, exceptions, exceptions[i], i, out exit)
|
|
// the stack must be empty
|
|
&& codeInfo.GetStackHeight(exit) == 0
|
|
// the exit code must not be reachable (except from within the try-block),
|
|
// because we're going to patch it to jump around the exit code
|
|
&& !IsReachableFromOutsideTryBlock(codeInfo, code, exceptions, exceptions[i], exit))
|
|
{
|
|
int exitHandlerEnd;
|
|
int faultHandlerEnd;
|
|
if (MatchFinallyBlock(codeInfo, code, exceptions, exceptions[i].handlerIndex, exit, out exitHandlerEnd, out faultHandlerEnd))
|
|
{
|
|
if (exit != exitHandlerEnd
|
|
&& codeInfo.GetStackHeight(exitHandlerEnd) == 0
|
|
&& MatchExceptionCoverage(exceptions, -1, exceptions[i].handlerIndex, faultHandlerEnd, exit, exitHandlerEnd))
|
|
{
|
|
// We use Arg2 (which is a short) to store the handler in the __goto_finally pseudo-opcode,
|
|
// so we can only do that if handlerIndex fits in a short (note that we can use the sign bit too).
|
|
if (exceptions[i].handlerIndex <= ushort.MaxValue)
|
|
{
|
|
code[exit].PatchOpCode(NormalizedByteCode.__goto_finally, exitHandlerEnd, (short)exceptions[i].handlerIndex);
|
|
exceptions.SetFinally(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool IsSynchronizedBlockHandler(ClassFile.Method.Instruction[] code, int index)
|
|
{
|
|
return code[index].NormalizedOpCode == NormalizedByteCode.__astore
|
|
&& code[index + 1].NormalizedOpCode == NormalizedByteCode.__aload
|
|
&& code[index + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit
|
|
&& code[index + 3].NormalizedOpCode == NormalizedByteCode.__aload && code[index + 3].Arg1 == code[index].Arg1
|
|
&& code[index + 4].NormalizedOpCode == NormalizedByteCode.__athrow;
|
|
}
|
|
|
|
private static bool MatchExceptionCoverage(UntangledExceptionTable exceptions, int skipException, int startFault, int endFault, int startExit, int endExit)
|
|
{
|
|
for (int j = 0; j < exceptions.Length; j++)
|
|
{
|
|
if (j != skipException && ExceptionCovers(exceptions[j], startFault, endFault) != ExceptionCovers(exceptions[j], startExit, endExit))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool ExceptionCovers(ExceptionTableEntry exception, int start, int end)
|
|
{
|
|
return exception.startIndex < end && exception.endIndex > start;
|
|
}
|
|
|
|
private static bool MatchFinallyBlock(CodeInfo codeInfo, ClassFile.Method.Instruction[] code, UntangledExceptionTable exceptions, int faultHandler, int exitHandler, out int exitHandlerEnd, out int faultHandlerEnd)
|
|
{
|
|
exitHandlerEnd = -1;
|
|
faultHandlerEnd = -1;
|
|
if (code[faultHandler].NormalizedOpCode != NormalizedByteCode.__astore)
|
|
{
|
|
return false;
|
|
}
|
|
int startFault = faultHandler;
|
|
int faultLocal = code[faultHandler++].NormalizedArg1;
|
|
for (; ; )
|
|
{
|
|
if (code[faultHandler].NormalizedOpCode == NormalizedByteCode.__aload
|
|
&& code[faultHandler].NormalizedArg1 == faultLocal
|
|
&& code[faultHandler + 1].NormalizedOpCode == NormalizedByteCode.__athrow)
|
|
{
|
|
// make sure that instructions that we haven't covered aren't reachable
|
|
InstructionFlags[] flags = ComputePartialReachability(codeInfo, code, exceptions, startFault, false);
|
|
for (int i = 0; i < flags.Length; i++)
|
|
{
|
|
if ((i < startFault || i > faultHandler + 1) && (flags[i] & InstructionFlags.Reachable) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
exitHandlerEnd = exitHandler;
|
|
faultHandlerEnd = faultHandler;
|
|
return true;
|
|
}
|
|
if (!MatchInstructions(code, faultHandler, exitHandler))
|
|
{
|
|
return false;
|
|
}
|
|
faultHandler++;
|
|
exitHandler++;
|
|
}
|
|
}
|
|
|
|
private static bool MatchInstructions(ClassFile.Method.Instruction[] code, int i, int j)
|
|
{
|
|
if (code[i].NormalizedOpCode != code[j].NormalizedOpCode)
|
|
{
|
|
return false;
|
|
}
|
|
switch (ByteCodeMetaData.GetFlowControl(code[i].NormalizedOpCode))
|
|
{
|
|
case ByteCodeFlowControl.Branch:
|
|
case ByteCodeFlowControl.CondBranch:
|
|
if (code[i].Arg1 - i != code[j].Arg1 - j)
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case ByteCodeFlowControl.Switch:
|
|
if (code[i].SwitchEntryCount != code[j].SwitchEntryCount)
|
|
{
|
|
return false;
|
|
}
|
|
for (int k = 0; k < code[i].SwitchEntryCount; k++)
|
|
{
|
|
if (code[i].GetSwitchTargetIndex(k) != code[j].GetSwitchTargetIndex(k))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (code[i].DefaultTarget != code[j].DefaultTarget)
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
if (code[i].Arg1 != code[j].Arg1)
|
|
{
|
|
return false;
|
|
}
|
|
if (code[i].Arg2 != code[j].Arg2)
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool IsReachableFromOutsideTryBlock(CodeInfo codeInfo, ClassFile.Method.Instruction[] code, UntangledExceptionTable exceptions, ExceptionTableEntry tryBlock, int instructionIndex)
|
|
{
|
|
InstructionFlags[] flags = new InstructionFlags[code.Length];
|
|
flags[0] |= InstructionFlags.Reachable;
|
|
// We mark the first instruction of the try-block as already processed, so that UpdatePartialReachability will skip the try-block.
|
|
// Note that we can do this, because it is not possible to jump into the middle of a try-block (after the exceptions have been untangled).
|
|
flags[tryBlock.startIndex] = InstructionFlags.Processed;
|
|
// We mark the successor instructions of the instruction we're examinining as reachable,
|
|
// to figure out if the code following the handler somehow branches back to it.
|
|
MarkSuccessors(code, flags, instructionIndex);
|
|
UpdatePartialReachability(flags, codeInfo, code, exceptions, false);
|
|
return (flags[instructionIndex] & InstructionFlags.Reachable) != 0;
|
|
}
|
|
|
|
private static bool TryFindSingleTryBlockExit(ClassFile.Method.Instruction[] code, InstructionFlags[] flags, UntangledExceptionTable exceptions, ExceptionTableEntry exception, int exceptionIndex, out int exit)
|
|
{
|
|
exit = -1;
|
|
bool fail = false;
|
|
bool nextIsReachable = false;
|
|
for (int i = exception.startIndex; !fail && i < exception.endIndex; i++)
|
|
{
|
|
if ((flags[i] & InstructionFlags.Reachable) != 0)
|
|
{
|
|
nextIsReachable = false;
|
|
for (int j = 0; j < exceptions.Length; j++)
|
|
{
|
|
if (j != exceptionIndex && exceptions[j].startIndex <= exception.startIndex && exception.endIndex <= exceptions[j].endIndex)
|
|
{
|
|
UpdateTryBlockExit(exception, exceptions[j].handlerIndex, ref exit, ref fail);
|
|
}
|
|
}
|
|
switch (ByteCodeMetaData.GetFlowControl(code[i].NormalizedOpCode))
|
|
{
|
|
case ByteCodeFlowControl.Switch:
|
|
{
|
|
for (int j = 0; j < code[i].SwitchEntryCount; j++)
|
|
{
|
|
UpdateTryBlockExit(exception, code[i].GetSwitchTargetIndex(j), ref exit, ref fail);
|
|
}
|
|
UpdateTryBlockExit(exception, code[i].DefaultTarget, ref exit, ref fail);
|
|
break;
|
|
}
|
|
case ByteCodeFlowControl.Branch:
|
|
UpdateTryBlockExit(exception, code[i].TargetIndex, ref exit, ref fail);
|
|
break;
|
|
case ByteCodeFlowControl.CondBranch:
|
|
UpdateTryBlockExit(exception, code[i].TargetIndex, ref exit, ref fail);
|
|
nextIsReachable = true;
|
|
break;
|
|
case ByteCodeFlowControl.Return:
|
|
fail = true;
|
|
break;
|
|
case ByteCodeFlowControl.Throw:
|
|
break;
|
|
case ByteCodeFlowControl.Next:
|
|
nextIsReachable = true;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
}
|
|
if (nextIsReachable)
|
|
{
|
|
UpdateTryBlockExit(exception, exception.endIndex, ref exit, ref fail);
|
|
}
|
|
return !fail && exit != -1;
|
|
}
|
|
|
|
private static void UpdateTryBlockExit(ExceptionTableEntry exception, int targetIndex, ref int exitIndex, ref bool fail)
|
|
{
|
|
if (exception.startIndex <= targetIndex && targetIndex < exception.endIndex)
|
|
{
|
|
// branch stays inside try block
|
|
}
|
|
else if (exitIndex == -1)
|
|
{
|
|
exitIndex = targetIndex;
|
|
}
|
|
else if (exitIndex != targetIndex)
|
|
{
|
|
fail = true;
|
|
}
|
|
}
|
|
|
|
private void SetHardError(ClassLoaderWrapper classLoader, ref ClassFile.Method.Instruction instruction, HardError hardError, string message, params object[] args)
|
|
{
|
|
string text = string.Format(message, args);
|
|
#if STATIC_COMPILER
|
|
Message msg;
|
|
switch (hardError)
|
|
{
|
|
case HardError.NoClassDefFoundError:
|
|
msg = Message.EmittedNoClassDefFoundError;
|
|
break;
|
|
case HardError.IllegalAccessError:
|
|
msg = Message.EmittedIllegalAccessError;
|
|
break;
|
|
case HardError.InstantiationError:
|
|
msg = Message.EmittedIllegalAccessError;
|
|
break;
|
|
case HardError.IncompatibleClassChangeError:
|
|
msg = Message.EmittedIncompatibleClassChangeError;
|
|
break;
|
|
case HardError.NoSuchFieldError:
|
|
msg = Message.EmittedNoSuchFieldError;
|
|
break;
|
|
case HardError.AbstractMethodError:
|
|
msg = Message.EmittedAbstractMethodError;
|
|
break;
|
|
case HardError.NoSuchMethodError:
|
|
msg = Message.EmittedNoSuchMethodError;
|
|
break;
|
|
case HardError.LinkageError:
|
|
msg = Message.EmittedLinkageError;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
classLoader.IssueMessage(msg, classFile.Name + "." + method.Name + method.Signature, text);
|
|
#endif
|
|
instruction.SetHardError(hardError, AllocErrorMessage(text));
|
|
}
|
|
|
|
private void PatchInvoke(TypeWrapper wrapper, ref ClassFile.Method.Instruction instr, StackState stack)
|
|
{
|
|
ClassFile.ConstantPoolItemMI cpi = GetMethodref(instr.Arg1);
|
|
NormalizedByteCode invoke = instr.NormalizedOpCode;
|
|
bool isnew = false;
|
|
TypeWrapper thisType;
|
|
if(invoke == NormalizedByteCode.__invokestatic)
|
|
{
|
|
thisType = null;
|
|
}
|
|
else
|
|
{
|
|
TypeWrapper[] args = cpi.GetArgTypes();
|
|
for (int j = args.Length - 1; j >= 0; j--)
|
|
{
|
|
stack.PopType(args[j]);
|
|
}
|
|
thisType = SigTypeToClassName(stack.PeekType(), cpi.GetClassType(), wrapper);
|
|
if(ReferenceEquals(cpi.Name, StringConstants.INIT))
|
|
{
|
|
TypeWrapper type = stack.PopType();
|
|
isnew = VerifierTypeWrapper.IsNew(type);
|
|
}
|
|
}
|
|
|
|
if(cpi.GetClassType().IsUnloadable || (thisType != null && thisType.IsUnloadable))
|
|
{
|
|
#if STATIC_COMPILER
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.NoClassDefFoundError, "{0}", cpi.GetClassType().Name);
|
|
#else
|
|
switch(invoke)
|
|
{
|
|
case NormalizedByteCode.__invokeinterface:
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_invokeinterface);
|
|
break;
|
|
case NormalizedByteCode.__invokestatic:
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_invokestatic);
|
|
break;
|
|
case NormalizedByteCode.__invokevirtual:
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_invokevirtual);
|
|
break;
|
|
case NormalizedByteCode.__invokespecial:
|
|
if(isnew)
|
|
{
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_invokespecial);
|
|
}
|
|
else
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.LinkageError, "Base class no longer loadable");
|
|
}
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
#endif
|
|
}
|
|
else if(cpi.GetClassType().IsInterface != (invoke == NormalizedByteCode.__invokeinterface))
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IncompatibleClassChangeError, "invokeinterface on non-interface");
|
|
}
|
|
else
|
|
{
|
|
MethodWrapper targetMethod = invoke == NormalizedByteCode.__invokespecial ? cpi.GetMethodForInvokespecial() : cpi.GetMethod();
|
|
if(targetMethod != null)
|
|
{
|
|
string errmsg = CheckLoaderConstraints(cpi, targetMethod);
|
|
if(errmsg != null)
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.LinkageError, "{0}", errmsg);
|
|
}
|
|
else if(targetMethod.IsStatic == (invoke == NormalizedByteCode.__invokestatic))
|
|
{
|
|
if(targetMethod.IsAbstract && invoke == NormalizedByteCode.__invokespecial)
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.AbstractMethodError, "{0}.{1}{2}", cpi.Class, cpi.Name, cpi.Signature);
|
|
}
|
|
else if(targetMethod.IsAccessibleFrom(cpi.GetClassType(), wrapper, thisType))
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// NOTE special case for incorrect invocation of Object.clone(), because this could mean
|
|
// we're calling clone() on an array
|
|
// (bug in javac, see http://developer.java.sun.com/developer/bugParade/bugs/4329886.html)
|
|
if(cpi.GetClassType() == CoreClasses.java.lang.Object.Wrapper
|
|
&& thisType.IsArray
|
|
&& ReferenceEquals(cpi.Name, StringConstants.CLONE))
|
|
{
|
|
// Patch the instruction, so that the compiler doesn't need to do this test again.
|
|
instr.PatchOpCode(NormalizedByteCode.__clone_array);
|
|
return;
|
|
}
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessError, "Try to access method {0}.{1}{2} from class {3}", targetMethod.DeclaringType.Name, cpi.Name, cpi.Signature, wrapper.Name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IncompatibleClassChangeError, "static call to non-static method (or v.v.)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.NoSuchMethodError, "{0}.{1}{2}", cpi.Class, cpi.Name, cpi.Signature);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PatchFieldAccess(TypeWrapper wrapper, MethodWrapper mw, ref ClassFile.Method.Instruction instr, StackState stack)
|
|
{
|
|
ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1);
|
|
bool isStatic;
|
|
bool write;
|
|
TypeWrapper thisType;
|
|
switch(instr.NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__getfield:
|
|
isStatic = false;
|
|
write = false;
|
|
thisType = SigTypeToClassName(stack.PopObjectType(GetFieldref(instr.Arg1).GetClassType()), cpi.GetClassType(), wrapper);
|
|
break;
|
|
case NormalizedByteCode.__putfield:
|
|
stack.PopType(GetFieldref(instr.Arg1).GetFieldType());
|
|
isStatic = false;
|
|
write = true;
|
|
// putfield is allowed to access the unintialized this
|
|
if(stack.PeekType() == VerifierTypeWrapper.UninitializedThis
|
|
&& wrapper.IsAssignableTo(GetFieldref(instr.Arg1).GetClassType()))
|
|
{
|
|
thisType = wrapper;
|
|
}
|
|
else
|
|
{
|
|
thisType = SigTypeToClassName(stack.PopObjectType(GetFieldref(instr.Arg1).GetClassType()), cpi.GetClassType(), wrapper);
|
|
}
|
|
break;
|
|
case NormalizedByteCode.__getstatic:
|
|
isStatic = true;
|
|
write = false;
|
|
thisType = null;
|
|
break;
|
|
case NormalizedByteCode.__putstatic:
|
|
// special support for when we're being called from IsSideEffectFreeStaticInitializer
|
|
if(mw == null)
|
|
{
|
|
switch(GetFieldref(instr.Arg1).Signature[0])
|
|
{
|
|
case 'B':
|
|
case 'Z':
|
|
case 'C':
|
|
case 'S':
|
|
case 'I':
|
|
stack.PopInt();
|
|
break;
|
|
case 'F':
|
|
stack.PopFloat();
|
|
break;
|
|
case 'D':
|
|
stack.PopDouble();
|
|
break;
|
|
case 'J':
|
|
stack.PopLong();
|
|
break;
|
|
case 'L':
|
|
case '[':
|
|
if(stack.PopAnyType() != VerifierTypeWrapper.Null)
|
|
{
|
|
throw new VerifyError();
|
|
}
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack.PopType(GetFieldref(instr.Arg1).GetFieldType());
|
|
}
|
|
isStatic = true;
|
|
write = true;
|
|
thisType = null;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
if(mw == null)
|
|
{
|
|
// We're being called from IsSideEffectFreeStaticInitializer,
|
|
// no further checks are possible (nor needed).
|
|
}
|
|
else if(cpi.GetClassType().IsUnloadable || (thisType != null && thisType.IsUnloadable))
|
|
{
|
|
#if STATIC_COMPILER
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.NoClassDefFoundError, "{0}", cpi.GetClassType().Name);
|
|
#else
|
|
switch(instr.NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__getstatic:
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_getstatic);
|
|
break;
|
|
case NormalizedByteCode.__putstatic:
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_putstatic);
|
|
break;
|
|
case NormalizedByteCode.__getfield:
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_getfield);
|
|
break;
|
|
case NormalizedByteCode.__putfield:
|
|
instr.PatchOpCode(NormalizedByteCode.__dynamic_putfield);
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
FieldWrapper field = cpi.GetField();
|
|
if(field == null)
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.NoSuchFieldError, "{0}.{1}", cpi.Class, cpi.Name);
|
|
return;
|
|
}
|
|
if(cpi.GetFieldType() != field.FieldTypeWrapper && !field.FieldTypeWrapper.IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
StaticCompiler.LinkageError("Field \"{2}.{3}\" is of type \"{0}\" instead of type \"{1}\" as expected by \"{4}\"", field.FieldTypeWrapper, cpi.GetFieldType(), cpi.GetClassType().Name, cpi.Name, wrapper.Name);
|
|
#endif
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.LinkageError, "Loader constraints violated: {0}.{1}", field.DeclaringType.Name, field.Name);
|
|
return;
|
|
}
|
|
if(field.IsStatic != isStatic)
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IncompatibleClassChangeError, "Static field access to non-static field (or v.v.)");
|
|
return;
|
|
}
|
|
if(!field.IsAccessibleFrom(cpi.GetClassType(), wrapper, thisType))
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessError, "Try to access field {0}.{1} from class {2}", field.DeclaringType.Name, field.Name, wrapper.Name);
|
|
return;
|
|
}
|
|
// are we trying to mutate a final field? (they are read-only from outside of the defining class)
|
|
if(write && field.IsFinal
|
|
&& ((isStatic ? wrapper != cpi.GetClassType() : wrapper != thisType) || (wrapper.GetClassLoader().StrictFinalFieldSemantics && (isStatic ? (mw != null && mw.Name != "<clinit>") : (mw == null || mw.Name != "<init>")))))
|
|
{
|
|
SetHardError(wrapper.GetClassLoader(), ref instr, HardError.IllegalAccessError, "Field {0}.{1} is final", field.DeclaringType.Name, field.Name);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO this method should have a better name
|
|
private TypeWrapper SigTypeToClassName(TypeWrapper type, TypeWrapper nullType, TypeWrapper wrapper)
|
|
{
|
|
if(type == VerifierTypeWrapper.UninitializedThis)
|
|
{
|
|
return wrapper;
|
|
}
|
|
else if(VerifierTypeWrapper.IsNew(type))
|
|
{
|
|
return ((VerifierTypeWrapper)type).UnderlyingType;
|
|
}
|
|
else if(type == VerifierTypeWrapper.Null)
|
|
{
|
|
return nullType;
|
|
}
|
|
else
|
|
{
|
|
return type;
|
|
}
|
|
}
|
|
|
|
private int AllocErrorMessage(string message)
|
|
{
|
|
if(errorMessages == null)
|
|
{
|
|
errorMessages = new List<string>();
|
|
}
|
|
int index = errorMessages.Count;
|
|
errorMessages.Add(message);
|
|
return index;
|
|
}
|
|
|
|
private string CheckLoaderConstraints(ClassFile.ConstantPoolItemMI cpi, MethodWrapper mw)
|
|
{
|
|
if(cpi.GetRetType() != mw.ReturnType && !mw.ReturnType.IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has a return type \"{0}\" instead of type \"{1}\" as expected by \"{5}\"", mw.ReturnType, cpi.GetRetType(), cpi.GetClassType().Name, cpi.Name, cpi.Signature, classFile.Name);
|
|
#endif
|
|
return "Loader constraints violated (return type): " + mw.DeclaringType.Name + "." + mw.Name + mw.Signature;
|
|
}
|
|
TypeWrapper[] here = cpi.GetArgTypes();
|
|
TypeWrapper[] there = mw.GetParameters();
|
|
for(int i = 0; i < here.Length; i++)
|
|
{
|
|
if(here[i] != there[i] && !there[i].IsUnloadable)
|
|
{
|
|
#if STATIC_COMPILER
|
|
StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has a argument type \"{0}\" instead of type \"{1}\" as expected by \"{5}\"", there[i], here[i], cpi.GetClassType().Name, cpi.Name, cpi.Signature, classFile.Name);
|
|
#endif
|
|
return "Loader constraints violated (arg " + i + "): " + mw.DeclaringType.Name + "." + mw.Name + mw.Signature;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal ClassFile.ConstantPoolItemMI GetMethodref(int index)
|
|
{
|
|
try
|
|
{
|
|
ClassFile.ConstantPoolItemMI item = classFile.GetMethodref(index);
|
|
if(item != null)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
catch(InvalidCastException)
|
|
{
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
}
|
|
throw new VerifyError("Illegal constant pool index");
|
|
}
|
|
|
|
private ClassFile.ConstantPoolItemFieldref GetFieldref(int index)
|
|
{
|
|
try
|
|
{
|
|
ClassFile.ConstantPoolItemFieldref item = classFile.GetFieldref(index);
|
|
if(item != null)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
catch(InvalidCastException)
|
|
{
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
}
|
|
throw new VerifyError("Illegal constant pool index");
|
|
}
|
|
|
|
private ClassFile.ConstantType GetConstantPoolConstantType(int index)
|
|
{
|
|
try
|
|
{
|
|
return classFile.GetConstantPoolConstantType(index);
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
// constant pool index out of range
|
|
}
|
|
catch(InvalidOperationException)
|
|
{
|
|
// specified constant pool entry doesn't contain a constant
|
|
}
|
|
catch(NullReferenceException)
|
|
{
|
|
// specified constant pool entry is empty (entry 0 or the filler following a wide entry)
|
|
}
|
|
throw new VerifyError("Illegal constant pool index");
|
|
}
|
|
|
|
private TypeWrapper GetConstantPoolClassType(int index)
|
|
{
|
|
try
|
|
{
|
|
return classFile.GetConstantPoolClassType(index);
|
|
}
|
|
catch(InvalidCastException)
|
|
{
|
|
}
|
|
catch(IndexOutOfRangeException)
|
|
{
|
|
}
|
|
catch(NullReferenceException)
|
|
{
|
|
}
|
|
throw new VerifyError("Illegal constant pool index");
|
|
}
|
|
|
|
internal void ClearFaultBlockException(int instructionIndex)
|
|
{
|
|
Debug.Assert(state[instructionIndex].GetStackHeight() == 1);
|
|
state[instructionIndex].ClearFaultBlockException();
|
|
}
|
|
|
|
private static void DumpMethod(CodeInfo codeInfo, ClassFile.Method method, UntangledExceptionTable exceptions)
|
|
{
|
|
ClassFile.Method.Instruction[] code = method.Instructions;
|
|
InstructionFlags[] flags = ComputePartialReachability(codeInfo, code, exceptions, 0, false);
|
|
for (int i = 0; i < code.Length; i++)
|
|
{
|
|
bool label = (flags[i] & InstructionFlags.BranchTarget) != 0;
|
|
if (!label)
|
|
{
|
|
for (int j = 0; j < exceptions.Length; j++)
|
|
{
|
|
if (exceptions[j].startIndex == i
|
|
|| exceptions[j].endIndex == i
|
|
|| exceptions[j].handlerIndex == i)
|
|
{
|
|
label = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (label)
|
|
{
|
|
Console.WriteLine("label{0}:", i);
|
|
}
|
|
if ((flags[i] & InstructionFlags.Reachable) != 0)
|
|
{
|
|
Console.Write(" {1}", i, code[i].NormalizedOpCode.ToString().Substring(2));
|
|
switch (ByteCodeMetaData.GetFlowControl(code[i].NormalizedOpCode))
|
|
{
|
|
case ByteCodeFlowControl.Branch:
|
|
case ByteCodeFlowControl.CondBranch:
|
|
Console.Write(" label{0}", code[i].Arg1);
|
|
break;
|
|
}
|
|
switch (code[i].NormalizedOpCode)
|
|
{
|
|
case NormalizedByteCode.__iload:
|
|
case NormalizedByteCode.__lload:
|
|
case NormalizedByteCode.__fload:
|
|
case NormalizedByteCode.__dload:
|
|
case NormalizedByteCode.__aload:
|
|
case NormalizedByteCode.__istore:
|
|
case NormalizedByteCode.__lstore:
|
|
case NormalizedByteCode.__fstore:
|
|
case NormalizedByteCode.__dstore:
|
|
case NormalizedByteCode.__astore:
|
|
case NormalizedByteCode.__iconst:
|
|
Console.Write(" {0}", code[i].Arg1);
|
|
break;
|
|
case NormalizedByteCode.__ldc:
|
|
case NormalizedByteCode.__getfield:
|
|
case NormalizedByteCode.__getstatic:
|
|
case NormalizedByteCode.__putfield:
|
|
case NormalizedByteCode.__putstatic:
|
|
case NormalizedByteCode.__invokeinterface:
|
|
case NormalizedByteCode.__invokespecial:
|
|
case NormalizedByteCode.__invokestatic:
|
|
case NormalizedByteCode.__invokevirtual:
|
|
case NormalizedByteCode.__new:
|
|
Console.Write(" #{0}", code[i].Arg1);
|
|
break;
|
|
}
|
|
Console.WriteLine();
|
|
}
|
|
}
|
|
for (int i = 0; i < exceptions.Length; i++)
|
|
{
|
|
Console.WriteLine(".catch #{0} from label{1} to label{2} using label{3}", exceptions[i].catch_type, exceptions[i].startIndex, exceptions[i].endIndex, exceptions[i].handlerIndex);
|
|
}
|
|
}
|
|
}
|