зеркало из https://github.com/mono/ikvm-fork.git
*** empty log message ***
This commit is contained in:
Родитель
e94332d37d
Коммит
41f7f8b1e6
|
@ -233,7 +233,6 @@ enum NormalizedByteCode : byte
|
|||
{
|
||||
__nop = 0,
|
||||
__aconst_null = 1,
|
||||
__iconst = 255,
|
||||
__lconst_0 = 9,
|
||||
__lconst_1 = 10,
|
||||
__fconst_0 = 11,
|
||||
|
@ -379,7 +378,13 @@ enum NormalizedByteCode : byte
|
|||
__multianewarray = 197,
|
||||
__ifnull = 198,
|
||||
__ifnonnull = 199,
|
||||
__static_error = 254 // not a real instruction, this signals an instruction that is compiled as an exception
|
||||
// This is where the pseudo-bytecodes start
|
||||
__dynamic_invokeinterface = 250,
|
||||
__dynamic_invokestatic = 251,
|
||||
__dynamic_invokevirtual = 252,
|
||||
__clone_array = 253,
|
||||
__static_error = 254, // not a real instruction, this signals an instruction that is compiled as an exception
|
||||
__iconst = 255
|
||||
}
|
||||
|
||||
enum ByteCodeMode : byte
|
||||
|
@ -493,8 +498,19 @@ struct ByteCodeMetaData
|
|||
|
||||
internal static bool CanThrowException(NormalizedByteCode bc)
|
||||
{
|
||||
// special case __iconst, because that's the only normalized opcode that doesn't correspond to a real opcode
|
||||
return bc != NormalizedByteCode.__iconst && (data[(int)bc].flags & ByteCodeFlags.CannotThrow) == 0;
|
||||
switch(bc)
|
||||
{
|
||||
case NormalizedByteCode.__dynamic_invokeinterface:
|
||||
case NormalizedByteCode.__dynamic_invokestatic:
|
||||
case NormalizedByteCode.__dynamic_invokevirtual:
|
||||
case NormalizedByteCode.__clone_array:
|
||||
case NormalizedByteCode.__static_error:
|
||||
return true;
|
||||
case NormalizedByteCode.__iconst:
|
||||
return false;
|
||||
default:
|
||||
return (data[(int)bc].flags & ByteCodeFlags.CannotThrow) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ByteCodeMetaData()
|
||||
|
|
|
@ -2098,6 +2098,11 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal void PatchOpCode(NormalizedByteCode bc)
|
||||
{
|
||||
this.normopcode = bc;
|
||||
}
|
||||
|
||||
internal void SetTermNop(ushort pc)
|
||||
{
|
||||
// TODO what happens if we already have exactly the maximum number of instructions?
|
||||
|
|
|
@ -786,6 +786,7 @@ namespace IKVM.Internal
|
|||
|
||||
internal static void FinishAll()
|
||||
{
|
||||
JVM.FinishingForDebugSave = true;
|
||||
while(dynamicTypes.Count > 0)
|
||||
{
|
||||
ArrayList l = new ArrayList(dynamicTypes.Values);
|
||||
|
@ -793,7 +794,7 @@ namespace IKVM.Internal
|
|||
{
|
||||
string name = tw.TypeAsTBD.FullName;
|
||||
Tracer.Info(Tracer.Runtime, "Finishing {0}", name);
|
||||
tw.Finish(true);
|
||||
tw.Finish();
|
||||
dynamicTypes.Remove(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1496,12 +1496,7 @@ namespace IKVM.Internal
|
|||
get;
|
||||
}
|
||||
|
||||
internal void Finish()
|
||||
{
|
||||
Finish(false);
|
||||
}
|
||||
|
||||
internal abstract void Finish(bool forDebugSave);
|
||||
internal abstract void Finish();
|
||||
|
||||
private void ImplementInterfaceMethodStubImpl(MethodWrapper ifmethod, TypeBuilder typeBuilder, DynamicTypeWrapper wrapper)
|
||||
{
|
||||
|
@ -1964,7 +1959,7 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
throw new InvalidOperationException("Finish called on UnloadableTypeWrapper: " + Name);
|
||||
}
|
||||
|
@ -2059,7 +2054,7 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -2302,14 +2297,14 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
Profiler.Enter("DynamicTypeWrapper.Finish");
|
||||
try
|
||||
{
|
||||
impl = impl.Finish(forDebugSave);
|
||||
impl = impl.Finish();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -2339,7 +2334,7 @@ namespace IKVM.Internal
|
|||
internal abstract TypeWrapper[] InnerClasses { get; }
|
||||
internal abstract TypeWrapper DeclaringTypeWrapper { get; }
|
||||
internal abstract Modifiers ReflectiveModifiers { get; }
|
||||
internal abstract DynamicImpl Finish(bool forDebugSave);
|
||||
internal abstract DynamicImpl Finish();
|
||||
internal abstract MethodBase LinkMethod(MethodWrapper mw);
|
||||
internal abstract FieldInfo LinkField(FieldWrapper fw);
|
||||
}
|
||||
|
@ -2352,7 +2347,6 @@ namespace IKVM.Internal
|
|||
private MethodWrapper[] methods;
|
||||
private MethodWrapper[] baseMethods;
|
||||
private FieldWrapper[] fields;
|
||||
private bool finishingForDebugSave;
|
||||
private FinishedTypeImpl finishedType;
|
||||
private readonly DynamicTypeWrapper outerClassWrapper;
|
||||
private Hashtable memberclashtable;
|
||||
|
@ -2843,9 +2837,9 @@ namespace IKVM.Internal
|
|||
// check the loader constraints
|
||||
if(mw.ReturnType != baseMethod.ReturnType)
|
||||
{
|
||||
if(baseMethod.ReturnType.IsUnloadable || finishingForDebugSave)
|
||||
if(baseMethod.ReturnType.IsUnloadable || JVM.FinishingForDebugSave)
|
||||
{
|
||||
if(!mw.ReturnType.IsUnloadable || (!baseMethod.ReturnType.IsUnloadable && finishingForDebugSave))
|
||||
if(!mw.ReturnType.IsUnloadable || (!baseMethod.ReturnType.IsUnloadable && JVM.FinishingForDebugSave))
|
||||
{
|
||||
unloadableOverrideStub = true;
|
||||
}
|
||||
|
@ -2861,9 +2855,9 @@ namespace IKVM.Internal
|
|||
{
|
||||
if(here[i] != there[i])
|
||||
{
|
||||
if(there[i].IsUnloadable || finishingForDebugSave)
|
||||
if(there[i].IsUnloadable || JVM.FinishingForDebugSave)
|
||||
{
|
||||
if(!here[i].IsUnloadable || (!there[i].IsUnloadable && finishingForDebugSave))
|
||||
if(!here[i].IsUnloadable || (!there[i].IsUnloadable && JVM.FinishingForDebugSave))
|
||||
{
|
||||
unloadableOverrideStub = true;
|
||||
}
|
||||
|
@ -3034,16 +3028,15 @@ namespace IKVM.Internal
|
|||
return field;
|
||||
}
|
||||
|
||||
internal override DynamicImpl Finish(bool forDebugSave)
|
||||
internal override DynamicImpl Finish()
|
||||
{
|
||||
this.finishingForDebugSave = forDebugSave;
|
||||
if(wrapper.BaseTypeWrapper != null)
|
||||
{
|
||||
wrapper.BaseTypeWrapper.Finish(forDebugSave);
|
||||
wrapper.BaseTypeWrapper.Finish();
|
||||
}
|
||||
if(outerClassWrapper != null)
|
||||
{
|
||||
outerClassWrapper.Finish(forDebugSave);
|
||||
outerClassWrapper.Finish();
|
||||
}
|
||||
// NOTE there is a bug in the CLR (.NET 1.0 & 1.1 [1.2 is not yet available]) that
|
||||
// causes the AppDomain.TypeResolve event to receive the incorrect type name for nested types.
|
||||
|
@ -3057,7 +3050,7 @@ namespace IKVM.Internal
|
|||
// turned up no other cases of the TypeResolve event firing.
|
||||
for(int i = 0; i < wrapper.Interfaces.Length; i++)
|
||||
{
|
||||
wrapper.Interfaces[i].Finish(forDebugSave);
|
||||
wrapper.Interfaces[i].Finish();
|
||||
}
|
||||
// make sure all classes are loaded, before we start finishing the type. During finishing, we
|
||||
// may not run any Java code, because that might result in a request to finish the type that we
|
||||
|
@ -3868,7 +3861,7 @@ namespace IKVM.Internal
|
|||
Debug.Assert(baseMethods[index].DeclaringType.IsInterface);
|
||||
string name = GenerateUniqueMethodName(methods[index].Name, baseMethods[index]);
|
||||
// TODO if the interface is not public, we probably shouldn't make the Miranda method public
|
||||
MethodBuilder mb = typeBuilder.DefineMethod(methods[index].Name, MethodAttributes.NewSlot | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract | MethodAttributes.CheckAccessOnOverride, baseMethods[index].ReturnTypeForDefineMethod, baseMethods[index].GetParametersForDefineMethod());
|
||||
MethodBuilder mb = typeBuilder.DefineMethod(methods[index].Name, MethodAttributes.NewSlot | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract | MethodAttributes.CheckAccessOnOverride, methods[index].ReturnTypeForDefineMethod, methods[index].GetParametersForDefineMethod());
|
||||
AttributeHelper.MirandaMethod(mb);
|
||||
if(unloadableOverrideStub || name != methods[index].Name)
|
||||
{
|
||||
|
@ -4069,20 +4062,23 @@ namespace IKVM.Internal
|
|||
needFinalize = false;
|
||||
}
|
||||
}
|
||||
if(unloadableOverrideStub)
|
||||
{
|
||||
attribs |= MethodAttributes.NewSlot;
|
||||
}
|
||||
if(setNameSig || memberclashtable != null)
|
||||
{
|
||||
// TODO we really should make sure that the name we generate doesn't already exist in a
|
||||
// base class (not in the Java method namespace, but in the CLR method namespace)
|
||||
name = GenerateUniqueMethodName(name, methods[index]);
|
||||
}
|
||||
bool needMethodImpl = baseMce != null && (explicitOverride || baseMce.RealName != name) && !needFinalize;
|
||||
if(unloadableOverrideStub || needMethodImpl)
|
||||
{
|
||||
attribs |= MethodAttributes.NewSlot;
|
||||
}
|
||||
mb = typeBuilder.DefineMethod(name, attribs, methods[index].ReturnTypeForDefineMethod, methods[index].GetParametersForDefineMethod());
|
||||
if(unloadableOverrideStub)
|
||||
{
|
||||
GenerateUnloadableOverrideStub(baseMce, mb, methods[index].ReturnTypeForDefineMethod, methods[index].GetParametersForDefineMethod());
|
||||
}
|
||||
else if(baseMce != null && (explicitOverride || baseMce.RealName != name) && !needFinalize)
|
||||
else if(needMethodImpl)
|
||||
{
|
||||
// assert that the method we're overriding is in fact virtual and not final!
|
||||
Debug.Assert(baseMce.GetMethod().IsVirtual && !baseMce.GetMethod().IsFinal);
|
||||
|
@ -4286,7 +4282,7 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal override DynamicImpl Finish(bool forDebugSave)
|
||||
internal override DynamicImpl Finish()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
@ -6139,7 +6135,7 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -7250,7 +7246,7 @@ namespace IKVM.Internal
|
|||
EmitHelper.Castclass(ilgen, type);
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
// TODO instead of linking here, we should just pre-link in LazyPublishMembers
|
||||
foreach(MethodWrapper mw in GetMethods())
|
||||
|
@ -7278,13 +7274,21 @@ namespace IKVM.Internal
|
|||
this.reflectiveModifiers = reflectiveModifiers;
|
||||
}
|
||||
|
||||
internal static MethodInfo CloneMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
if(clone == null)
|
||||
{
|
||||
clone = typeof(Array).GetMethod("Clone", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LazyPublishMembers()
|
||||
{
|
||||
if(clone == null)
|
||||
{
|
||||
clone = typeof(Array).GetMethod("Clone", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
|
||||
}
|
||||
MethodWrapper mw = new SimpleCallMethodWrapper(this, "clone", "()Ljava.lang.Object;", clone, CoreClasses.java.lang.Object.Wrapper, TypeWrapper.EmptyArray, Modifiers.Public, MemberFlags.HideFromReflection, SimpleOpCode.Callvirt, SimpleOpCode.Callvirt);
|
||||
MethodWrapper mw = new SimpleCallMethodWrapper(this, "clone", "()Ljava.lang.Object;", CloneMethod, CoreClasses.java.lang.Object.Wrapper, TypeWrapper.EmptyArray, Modifiers.Public, MemberFlags.HideFromReflection, SimpleOpCode.Callvirt, SimpleOpCode.Callvirt);
|
||||
mw.Link();
|
||||
SetMethods(new MethodWrapper[] { mw });
|
||||
SetFields(FieldWrapper.EmptyArray);
|
||||
|
@ -7367,7 +7371,7 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
|
|
2954
runtime/compiler.cs
2954
runtime/compiler.cs
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -800,6 +800,14 @@ class InstructionState
|
|||
return stack[stackSize - 1];
|
||||
}
|
||||
|
||||
internal void MultiPopAnyType(int count)
|
||||
{
|
||||
while(count-- != 0)
|
||||
{
|
||||
PopAnyType();
|
||||
}
|
||||
}
|
||||
|
||||
internal TypeWrapper PopAnyType()
|
||||
{
|
||||
if(stackSize == 0)
|
||||
|
@ -864,6 +872,11 @@ class InstructionState
|
|||
return stack[stackSize - 1 - pos];
|
||||
}
|
||||
|
||||
internal TypeWrapper GetStackByIndex(int index)
|
||||
{
|
||||
return stack[index];
|
||||
}
|
||||
|
||||
private void PushHelper(TypeWrapper type)
|
||||
{
|
||||
if(type.IsWidePrimitive)
|
||||
|
@ -962,6 +975,139 @@ class InstructionState
|
|||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
return state.GetStackByIndex(sp - 1);
|
||||
}
|
||||
|
||||
internal TypeWrapper PopAnyType()
|
||||
{
|
||||
if(sp == 0)
|
||||
{
|
||||
throw new VerifyError("Unable to pop operand off an empty stack");
|
||||
}
|
||||
return state.GetStackByIndex(--sp);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
if(PopAnyType() != PrimitiveTypeWrapper.FLOAT)
|
||||
{
|
||||
throw new VerifyError("Float expected on stack");
|
||||
}
|
||||
}
|
||||
|
||||
internal void PopDouble()
|
||||
{
|
||||
if(PopAnyType() != PrimitiveTypeWrapper.DOUBLE)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a container for the special verifier TypeWrappers
|
||||
class VerifierTypeWrapper : TypeWrapper
|
||||
{
|
||||
|
@ -1071,7 +1217,7 @@ class VerifierTypeWrapper : TypeWrapper
|
|||
}
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
throw new InvalidOperationException("Finish called on " + this);
|
||||
}
|
||||
|
@ -1571,97 +1717,26 @@ class MethodAnalyzer
|
|||
case NormalizedByteCode.__invokestatic:
|
||||
{
|
||||
ClassFile.ConstantPoolItemMI cpi = GetMethodref(instr.Arg1);
|
||||
if((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface))
|
||||
{
|
||||
throw new VerifyError("Illegal constant pool index");
|
||||
}
|
||||
if(instr.NormalizedOpCode != NormalizedByteCode.__invokespecial && cpi.Name == "<init>")
|
||||
{
|
||||
throw new VerifyError("Must call initializers using invokespecial");
|
||||
}
|
||||
if(cpi.Name == "<clinit>")
|
||||
{
|
||||
throw new VerifyError("Illegal call to internal method");
|
||||
}
|
||||
TypeWrapper[] args = cpi.GetArgTypes();
|
||||
for(int j = args.Length - 1; j >= 0; j--)
|
||||
{
|
||||
s.PopType(args[j]);
|
||||
}
|
||||
if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface)
|
||||
{
|
||||
int argcount = args.Length + 1;
|
||||
for(int j = 0; j < args.Length; j++)
|
||||
{
|
||||
if(args[j].IsWidePrimitive)
|
||||
{
|
||||
argcount++;
|
||||
}
|
||||
}
|
||||
if(instr.Arg2 != argcount)
|
||||
{
|
||||
throw new VerifyError("Inconsistent args size");
|
||||
}
|
||||
}
|
||||
s.MultiPopAnyType(cpi.GetArgTypes().Length);
|
||||
if(instr.NormalizedOpCode != NormalizedByteCode.__invokestatic)
|
||||
{
|
||||
TypeWrapper type = s.PopType();
|
||||
if(cpi.Name == "<init>")
|
||||
{
|
||||
TypeWrapper type = s.PopType();
|
||||
if((VerifierTypeWrapper.IsNew(type) && ((VerifierTypeWrapper)type).UnderlyingType != cpi.GetClassType()) ||
|
||||
(type == VerifierTypeWrapper.UninitializedThis && cpi.GetClassType() != wrapper.BaseTypeWrapper && cpi.GetClassType() != wrapper) ||
|
||||
(!VerifierTypeWrapper.IsNew(type) && 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 (or so I believe)
|
||||
throw new VerifyError("Call to wrong initialization method");
|
||||
}
|
||||
// after the constructor invocation, the uninitialized reference, is now
|
||||
// suddenly initialized
|
||||
// after we've invoked the constructor, the uninitialized references
|
||||
// are now initialized
|
||||
if(type == VerifierTypeWrapper.UninitializedThis)
|
||||
{
|
||||
s.MarkInitialized(type, wrapper, i);
|
||||
s.SetUnitializedThis(false);
|
||||
}
|
||||
else
|
||||
else if(VerifierTypeWrapper.IsNew(type))
|
||||
{
|
||||
s.MarkInitialized(type, ((VerifierTypeWrapper)type).UnderlyingType, i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(instr.NormalizedOpCode != NormalizedByteCode.__invokeinterface)
|
||||
else
|
||||
{
|
||||
TypeWrapper refType = s.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(instr.NormalizedOpCode == 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 previously we checked the type here, but it turns out that
|
||||
// the JVM throws an IncompatibleClassChangeError at runtime instead
|
||||
// of a VerifyError if this doesn't match
|
||||
s.PopObjectType();
|
||||
// This is a VerifyError, but it will be caught by our second pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2204,22 +2279,10 @@ class MethodAnalyzer
|
|||
case NormalizedByteCode.__if_acmpne:
|
||||
case NormalizedByteCode.__ifnull:
|
||||
case NormalizedByteCode.__ifnonnull:
|
||||
if(instr.Arg1 < 0)
|
||||
{
|
||||
// backward branches cannot have uninitialized objects on
|
||||
// the stack or in local variables
|
||||
s.CheckUninitializedObjRefs();
|
||||
}
|
||||
state[i + 1] += s;
|
||||
state[method.PcIndexMap[instr.PC + instr.Arg1]] += s;
|
||||
break;
|
||||
case NormalizedByteCode.__goto:
|
||||
if(instr.Arg1 < 0)
|
||||
{
|
||||
// backward branches cannot have uninitialized objects on
|
||||
// the stack or in local variables
|
||||
s.CheckUninitializedObjRefs();
|
||||
}
|
||||
state[method.PcIndexMap[instr.PC + instr.Arg1]] += s;
|
||||
break;
|
||||
case NormalizedByteCode.__jsr:
|
||||
|
@ -2300,79 +2363,124 @@ class MethodAnalyzer
|
|||
{
|
||||
done = false;
|
||||
instructions[i].flags |= InstructionFlags.Processed;
|
||||
StackState stack = new StackState(state[i]);
|
||||
switch(instructions[i].NormalizedOpCode)
|
||||
{
|
||||
case NormalizedByteCode.__invokeinterface:
|
||||
case NormalizedByteCode.__invokespecial:
|
||||
case NormalizedByteCode.__invokestatic:
|
||||
case NormalizedByteCode.__invokevirtual:
|
||||
VerifyInvoke(wrapper, ref instructions[i], stack);
|
||||
break;
|
||||
case NormalizedByteCode.__getfield:
|
||||
case NormalizedByteCode.__putfield:
|
||||
case NormalizedByteCode.__getstatic:
|
||||
case NormalizedByteCode.__putstatic:
|
||||
VerifyFieldAccess(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 && JVM.DisableDynamicBinding)
|
||||
{
|
||||
instructions[i].SetHardError(HardError.NoClassDefFoundError, AllocErrorMessage(tw.Name));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NormalizedByteCode.__new:
|
||||
{
|
||||
ClassFile.ConstantPoolItemMI cpi = GetMethodref(instructions[i].Arg1);
|
||||
if((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (instructions[i].NormalizedOpCode == NormalizedByteCode.__invokeinterface))
|
||||
TypeWrapper tw = classFile.GetConstantPoolClassType(instructions[i].Arg1);
|
||||
if(tw.IsUnloadable)
|
||||
{
|
||||
throw new VerifyError("Illegal constant pool index");
|
||||
}
|
||||
if(instructions[i].NormalizedOpCode != NormalizedByteCode.__invokespecial && cpi.Name == "<init>")
|
||||
{
|
||||
throw new VerifyError("Must call initializers using invokespecial");
|
||||
}
|
||||
if(cpi.Name == "<clinit>")
|
||||
{
|
||||
throw new VerifyError("Illegal call to internal method");
|
||||
}
|
||||
NormalizedByteCode invoke = instructions[i].NormalizedOpCode;
|
||||
TypeWrapper thisType;
|
||||
if(invoke == NormalizedByteCode.__invokestatic)
|
||||
{
|
||||
thisType = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
thisType = SigTypeToClassName(GetRawStackTypeWrapper(i, cpi.GetArgTypes().Length), cpi.GetClassType(), wrapper);
|
||||
}
|
||||
MethodWrapper targetMethod = invoke == NormalizedByteCode.__invokespecial ? cpi.GetMethodForInvokespecial() : cpi.GetMethod();
|
||||
if(targetMethod != null)
|
||||
{
|
||||
string errmsg = CheckLoaderConstraints(cpi, targetMethod);
|
||||
if(errmsg != null)
|
||||
if(JVM.DisableDynamicBinding)
|
||||
{
|
||||
instructions[i].SetHardError(HardError.LinkageError, AllocErrorMessage(errmsg));
|
||||
instructions[i].SetHardError(HardError.NoClassDefFoundError, AllocErrorMessage(tw.Name));
|
||||
}
|
||||
else if(targetMethod.IsStatic == (invoke == NormalizedByteCode.__invokestatic))
|
||||
}
|
||||
else if(!tw.IsAccessibleFrom(wrapper))
|
||||
{
|
||||
instructions[i].SetHardError(HardError.IllegalAccessError, AllocErrorMessage("Try to access class " + tw.Name + " from class " + wrapper.Name));
|
||||
}
|
||||
else if(tw.IsAbstract)
|
||||
{
|
||||
instructions[i].SetHardError(HardError.InstantiationError, AllocErrorMessage(tw.Name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NormalizedByteCode.__multianewarray:
|
||||
case NormalizedByteCode.__anewarray:
|
||||
{
|
||||
TypeWrapper tw = classFile.GetConstantPoolClassType(instructions[i].Arg1);
|
||||
if(tw.IsUnloadable)
|
||||
{
|
||||
if(JVM.DisableDynamicBinding)
|
||||
{
|
||||
if(targetMethod.IsAbstract && invoke == NormalizedByteCode.__invokespecial)
|
||||
{
|
||||
instructions[i].SetHardError(HardError.AbstractMethodError, AllocErrorMessage(cpi.Class + "." + cpi.Name + cpi.Signature));
|
||||
}
|
||||
else if(targetMethod.IsAccessibleFrom(cpi.GetClassType(), wrapper, thisType))
|
||||
{
|
||||
break;
|
||||
}
|
||||
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 && cpi.Name == "clone")
|
||||
{
|
||||
// NOTE since thisType is an array, we can be sure that the method is already linked
|
||||
targetMethod = thisType.GetMethodWrapper(cpi.Name, cpi.Signature, false);
|
||||
if(targetMethod != null && targetMethod.IsPublic)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
instructions[i].SetHardError(HardError.IllegalAccessError, AllocErrorMessage("Try to access method " + targetMethod.DeclaringType.Name + "." + cpi.Name + cpi.Signature + " from class " + wrapper.Name));
|
||||
}
|
||||
instructions[i].SetHardError(HardError.NoClassDefFoundError, AllocErrorMessage(tw.Name));
|
||||
}
|
||||
else
|
||||
}
|
||||
else if(!tw.IsAccessibleFrom(wrapper))
|
||||
{
|
||||
instructions[i].SetHardError(HardError.IllegalAccessError, AllocErrorMessage("Try to access class " + tw.Name + " from class " + 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))
|
||||
{
|
||||
instructions[i].SetHardError(HardError.IllegalAccessError, AllocErrorMessage("Try to access class " + tw.Name + " from class " + wrapper.Name));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NormalizedByteCode.__aaload:
|
||||
{
|
||||
stack.PopInt();
|
||||
TypeWrapper tw = stack.PopArrayType();
|
||||
if(tw == VerifierTypeWrapper.Null)
|
||||
{
|
||||
}
|
||||
else if(tw.IsUnloadable)
|
||||
{
|
||||
if(JVM.DisableDynamicBinding)
|
||||
{
|
||||
instructions[i].SetHardError(HardError.IncompatibleClassChangeError, AllocErrorMessage("static call to non-static method (or v.v.)"));
|
||||
instructions[i].SetHardError(HardError.NoClassDefFoundError, AllocErrorMessage(tw.Name));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instructions[i].SetHardError(HardError.NoSuchMethodError, AllocErrorMessage(cpi.Class + "." + cpi.Name + cpi.Signature));
|
||||
tw = tw.ElementTypeWrapper;
|
||||
if(tw.IsPrimitive)
|
||||
{
|
||||
throw new VerifyError("Object array expected");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NormalizedByteCode.__aastore:
|
||||
{
|
||||
stack.PopObjectType();
|
||||
stack.PopInt();
|
||||
TypeWrapper tw = stack.PopArrayType();
|
||||
if(tw.IsUnloadable)
|
||||
{
|
||||
if(JVM.DisableDynamicBinding)
|
||||
{
|
||||
instructions[i].SetHardError(HardError.NoClassDefFoundError, AllocErrorMessage(tw.Name));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO do we need any other tests?
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2392,13 +2500,30 @@ class MethodAnalyzer
|
|||
{
|
||||
case NormalizedByteCode.__tableswitch:
|
||||
case NormalizedByteCode.__lookupswitch:
|
||||
{
|
||||
bool hasbackbranch = false;
|
||||
for(int j = 0; j < instructions[i].SwitchEntryCount; j++)
|
||||
{
|
||||
hasbackbranch |= instructions[i].GetSwitchTargetOffset(j) < 0;
|
||||
instructions[method.PcIndexMap[instructions[i].PC + instructions[i].GetSwitchTargetOffset(j)]].flags |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
||||
}
|
||||
hasbackbranch |= instructions[i].DefaultOffset < 0;
|
||||
instructions[method.PcIndexMap[instructions[i].PC + instructions[i].DefaultOffset]].flags |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
||||
if(hasbackbranch)
|
||||
{
|
||||
// backward branches cannot have uninitialized objects on
|
||||
// the stack or in local variables
|
||||
state[i].CheckUninitializedObjRefs();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NormalizedByteCode.__goto:
|
||||
if(instructions[i].Arg1 < 0)
|
||||
{
|
||||
// backward branches cannot have uninitialized objects on
|
||||
// the stack or in local variables
|
||||
state[i].CheckUninitializedObjRefs();
|
||||
}
|
||||
instructions[method.PcIndexMap[instructions[i].PC + instructions[i].Arg1]].flags |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
||||
break;
|
||||
case NormalizedByteCode.__ifeq:
|
||||
|
@ -2417,10 +2542,17 @@ class MethodAnalyzer
|
|||
case NormalizedByteCode.__if_acmpne:
|
||||
case NormalizedByteCode.__ifnull:
|
||||
case NormalizedByteCode.__ifnonnull:
|
||||
if(instructions[i].Arg1 < 0)
|
||||
{
|
||||
// backward branches cannot have uninitialized objects on
|
||||
// the stack or in local variables
|
||||
state[i].CheckUninitializedObjRefs();
|
||||
}
|
||||
instructions[method.PcIndexMap[instructions[i].PC + instructions[i].Arg1]].flags |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
||||
instructions[i + 1].flags |= InstructionFlags.Reachable;
|
||||
break;
|
||||
case NormalizedByteCode.__jsr:
|
||||
// TODO should we check for unitialized objects?
|
||||
instructions[method.PcIndexMap[instructions[i].PC + instructions[i].Arg1]].flags |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
|
||||
didjsr = true;
|
||||
break;
|
||||
|
@ -2585,6 +2717,300 @@ class MethodAnalyzer
|
|||
this.allLocalVars = (LocalVar[])locals.ToArray(typeof(LocalVar));
|
||||
}
|
||||
|
||||
private void VerifyInvoke(TypeWrapper wrapper, ref ClassFile.Method.Instruction instr, StackState stack)
|
||||
{
|
||||
ClassFile.ConstantPoolItemMI cpi = GetMethodref(instr.Arg1);
|
||||
if((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface))
|
||||
{
|
||||
throw new VerifyError("Illegal constant pool index");
|
||||
}
|
||||
if(instr.NormalizedOpCode != NormalizedByteCode.__invokespecial && cpi.Name == "<init>")
|
||||
{
|
||||
throw new VerifyError("Must call initializers using invokespecial");
|
||||
}
|
||||
if(cpi.Name == "<clinit>")
|
||||
{
|
||||
throw new VerifyError("Illegal call to internal method");
|
||||
}
|
||||
NormalizedByteCode invoke = instr.NormalizedOpCode;
|
||||
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(instr.Arg2 != argcount)
|
||||
{
|
||||
throw new VerifyError("Inconsistent args size");
|
||||
}
|
||||
}
|
||||
TypeWrapper thisType;
|
||||
if(invoke == NormalizedByteCode.__invokestatic)
|
||||
{
|
||||
thisType = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
thisType = SigTypeToClassName(stack.PeekType(), cpi.GetClassType(), wrapper);
|
||||
if(cpi.Name == "<init>")
|
||||
{
|
||||
TypeWrapper type = stack.PopType();
|
||||
if((VerifierTypeWrapper.IsNew(type) && ((VerifierTypeWrapper)type).UnderlyingType != cpi.GetClassType()) ||
|
||||
(type == VerifierTypeWrapper.UninitializedThis && cpi.GetClassType() != wrapper.BaseTypeWrapper && cpi.GetClassType() != wrapper) ||
|
||||
(!VerifierTypeWrapper.IsNew(type) && 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 (or so I believe)
|
||||
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 previously we checked the type here, but it turns out that
|
||||
// the JVM throws an IncompatibleClassChangeError at runtime instead
|
||||
// of a VerifyError if this doesn't match
|
||||
stack.PopObjectType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(cpi.GetClassType().IsUnloadable || (thisType != null && thisType.IsUnloadable))
|
||||
{
|
||||
if(JVM.DisableDynamicBinding)
|
||||
{
|
||||
instr.SetHardError(HardError.NoClassDefFoundError, AllocErrorMessage(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:
|
||||
instr.SetHardError(HardError.LinkageError, AllocErrorMessage("Base class no longer loadable"));
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(cpi.GetClassType().IsInterface != (invoke == NormalizedByteCode.__invokeinterface))
|
||||
{
|
||||
instr.SetHardError(HardError.IncompatibleClassChangeError, AllocErrorMessage("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)
|
||||
{
|
||||
instr.SetHardError(HardError.LinkageError, AllocErrorMessage(errmsg));
|
||||
}
|
||||
else if(targetMethod.IsStatic == (invoke == NormalizedByteCode.__invokestatic))
|
||||
{
|
||||
if(targetMethod.IsAbstract && invoke == NormalizedByteCode.__invokespecial)
|
||||
{
|
||||
instr.SetHardError(HardError.AbstractMethodError, AllocErrorMessage(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 && cpi.Name == "clone")
|
||||
{
|
||||
// Patch the instruction, so that the compiler doesn't need to do this test again.
|
||||
instr.PatchOpCode(NormalizedByteCode.__clone_array);
|
||||
return;
|
||||
}
|
||||
instr.SetHardError(HardError.IllegalAccessError, AllocErrorMessage("Try to access method " + targetMethod.DeclaringType.Name + "." + cpi.Name + cpi.Signature + " from class " + wrapper.Name));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instr.SetHardError(HardError.IncompatibleClassChangeError, AllocErrorMessage("static call to non-static method (or v.v.)"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instr.SetHardError(HardError.NoSuchMethodError, AllocErrorMessage(cpi.Class + "." + cpi.Name + cpi.Signature));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyFieldAccess(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)
|
||||
{
|
||||
if(JVM.DisableDynamicBinding)
|
||||
{
|
||||
instr.SetHardError(HardError.NoClassDefFoundError, AllocErrorMessage(cpi.GetClassType().Name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldWrapper field = cpi.GetField();
|
||||
if(field == null)
|
||||
{
|
||||
instr.SetHardError(HardError.NoSuchFieldError, AllocErrorMessage(cpi.Class + "." + cpi.Name));
|
||||
return;
|
||||
}
|
||||
if(cpi.GetFieldType() != field.FieldTypeWrapper && !field.FieldTypeWrapper.IsUnloadable)
|
||||
{
|
||||
instr.SetHardError(HardError.LinkageError, AllocErrorMessage("Loader constraints violated: " + field.DeclaringType.Name + "." + field.Name));
|
||||
return;
|
||||
}
|
||||
if(field.IsStatic != isStatic)
|
||||
{
|
||||
instr.SetHardError(HardError.IncompatibleClassChangeError, AllocErrorMessage("Static field access to non-static field (or v.v.)"));
|
||||
return;
|
||||
}
|
||||
if(!field.IsAccessibleFrom(cpi.GetClassType(), wrapper, thisType))
|
||||
{
|
||||
instr.SetHardError(HardError.IllegalAccessError, AllocErrorMessage("Try to access field " + field.DeclaringType.Name + "." + field.Name + " from class " + 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) || (JVM.StrictFinalFieldSemantics && (isStatic ? (mw != null && mw.Name != "<clinit>") : (mw == null || mw.Name != "<init>")))))
|
||||
{
|
||||
instr.SetHardError(HardError.IllegalAccessError, AllocErrorMessage("Field " + field.DeclaringType.Name + "." + field.Name + " is final"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this method should have a better name
|
||||
private TypeWrapper SigTypeToClassName(TypeWrapper type, TypeWrapper nullType, TypeWrapper wrapper)
|
||||
{
|
||||
|
|
|
@ -291,6 +291,7 @@ namespace IKVM.Internal
|
|||
private static bool enableReflectionOnMethodsWithUnloadableTypeParameters;
|
||||
private static ikvm.@internal.LibraryVMInterface lib;
|
||||
private static bool strictFinalFieldSemantics;
|
||||
private static bool finishingForDebugSave;
|
||||
|
||||
internal static Version SafeGetAssemblyVersion(Assembly asm)
|
||||
{
|
||||
|
@ -454,6 +455,18 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal static bool FinishingForDebugSave
|
||||
{
|
||||
get
|
||||
{
|
||||
return finishingForDebugSave;
|
||||
}
|
||||
set
|
||||
{
|
||||
finishingForDebugSave = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool CompileInnerClassesAsNestedTypes
|
||||
{
|
||||
get
|
||||
|
@ -1969,7 +1982,7 @@ namespace IKVM.Internal
|
|||
}
|
||||
}
|
||||
|
||||
internal override void Finish(bool forDebugSave)
|
||||
internal override void Finish()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче