diff --git a/IK.VM.NET/ClassFile.cs b/IK.VM.NET/ClassFile.cs index 7f692c25..784fb0ed 100644 --- a/IK.VM.NET/ClassFile.cs +++ b/IK.VM.NET/ClassFile.cs @@ -49,9 +49,10 @@ class ClassFile { private ConstantPoolItem[] constantpool; private Modifiers access_flags; - private string name; - private string supername; - private string[] interfaces; + private ConstantPoolItemClass this_cpi; + private ConstantPoolItemClass super_cpi; + private ConstantPoolItemClass[] interfaces; + private TypeWrapper[] interfaceTypeWrappers; private Field[] fields; private Method[] methods; private Attribute[] attributes; @@ -114,7 +115,7 @@ class ClassFile int this_class = br.ReadUInt16(); try { - name = ((ConstantPoolItemClass)constantpool[this_class]).Name; + this_cpi = (ConstantPoolItemClass)constantpool[this_class]; } catch(Exception) { @@ -127,7 +128,7 @@ class ClassFile { try { - supername = ((ConstantPoolItemClass)constantpool[super_class]).Name; + super_cpi = (ConstantPoolItemClass)constantpool[super_class]; } catch(Exception) { @@ -141,12 +142,12 @@ class ClassFile throw JavaException.ClassFormatError("{0} (Bad superclass index)", Name); } } - if(IsInterface && (super_class == 0 || supername != "java/lang/Object")) + if(IsInterface && (super_class == 0 || super_cpi.Name != "java/lang/Object")) { throw JavaException.ClassFormatError("{0} (Interfaces must have java.lang.Object as superclass)", Name); } int interfaces_count = br.ReadUInt16(); - interfaces = new string[interfaces_count]; + interfaces = new ConstantPoolItemClass[interfaces_count]; Hashtable interfaceNames = new Hashtable(); for(int i = 0; i < interfaces_count; i++) { @@ -160,7 +161,7 @@ class ClassFile { throw JavaException.ClassFormatError("{0} (Interface name has bad constant type)", Name); } - interfaces[i] = ((ConstantPoolItemClass)GetConstantPoolItem(index)).Name; + interfaces[i] = (ConstantPoolItemClass)GetConstantPoolItem(index); if(interfaceNames.ContainsKey(interfaces[i])) { throw JavaException.ClassFormatError("{0} (Repetitive interface name)", Name); @@ -321,6 +322,11 @@ class ClassFile return ((ConstantPoolItemClass)constantpool[index]).Name; } + internal TypeWrapper GetConstantPoolClassType(int index, ClassLoaderWrapper classLoader) + { + return ((ConstantPoolItemClass)constantpool[index]).GetClassType(classLoader); + } + private string GetConstantPoolString(int index) { return ((ConstantPoolItemString)constantpool[index]).Value; @@ -364,7 +370,7 @@ class ClassFile { get { - return name; + return this_cpi.Name; } } @@ -372,6 +378,7 @@ class ClassFile { get { + string name = Name; int index = name.LastIndexOf('/'); if(index == -1) { @@ -381,11 +388,16 @@ class ClassFile } } + internal TypeWrapper GetSuperTypeWrapper(ClassLoaderWrapper classLoader) + { + return super_cpi.GetClassType(classLoader); + } + internal string SuperClass { get { - return supername; + return super_cpi.Name; } } @@ -405,11 +417,31 @@ class ClassFile } } + internal TypeWrapper[] GetInterfaceTypeWrappers(ClassLoaderWrapper classLoader) + { + if(interfaceTypeWrappers == null) + { + TypeWrapper[] tw = new TypeWrapper[interfaces.Length]; + for(int i = 0; i < tw.Length; i++) + { + tw[i] = interfaces[i].GetClassType(classLoader); + } + interfaceTypeWrappers = tw; + } + return interfaceTypeWrappers; + } + + // TODO this is legacy and needs to be removed in the future internal string[] Interfaces { get { - return interfaces; + string[] s = new string[interfaces.Length]; + for(int i = 0; i < s.Length; i++) + { + s[i] = interfaces[i].Name; + } + return s; } } @@ -539,12 +571,113 @@ class ClassFile { if(typeWrapper == null) { - typeWrapper = classLoader.LoadClassBySlashedName(name); + typeWrapper = LoadClassHelper(classLoader, name); } return typeWrapper; } } + private static TypeWrapper LoadClassHelper(ClassLoaderWrapper classLoader, string name) + { + try + { + return classLoader.LoadClassBySlashedName(name); + } + catch(Exception) + { + // TODO consider what to do with this error, may be a command line switch? + // TODO it might not be a good idea to catch .NET system exceptions here + return new UnloadableTypeWrapper(name); + } + } + + private static TypeWrapper SigDecoderWrapper(ClassLoaderWrapper classLoader, ref int index, string sig) + { + switch(sig[index++]) + { + case 'B': + return PrimitiveTypeWrapper.BYTE; + case 'C': + return PrimitiveTypeWrapper.CHAR; + case 'D': + return PrimitiveTypeWrapper.DOUBLE; + case 'F': + return PrimitiveTypeWrapper.FLOAT; + case 'I': + return PrimitiveTypeWrapper.INT; + case 'J': + return PrimitiveTypeWrapper.LONG; + case 'L': + { + int pos = index; + index = sig.IndexOf(';', index) + 1; + return LoadClassHelper(classLoader, sig.Substring(pos, index - pos - 1)); + } + case 'S': + return PrimitiveTypeWrapper.SHORT; + case 'Z': + return PrimitiveTypeWrapper.BOOLEAN; + case 'V': + return PrimitiveTypeWrapper.VOID; + case '[': + { + // TODO this can be optimized + string array = "["; + while(sig[index] == '[') + { + index++; + array += "["; + } + switch(sig[index]) + { + case 'L': + { + int pos = index; + index = sig.IndexOf(';', index) + 1; + return LoadClassHelper(classLoader, array + sig.Substring(pos, index - pos)); + } + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + return LoadClassHelper(classLoader, array + sig[index++]); + default: + // TODO this should never happen, because ClassFile should validate the descriptors + throw new InvalidOperationException(sig.Substring(index)); + } + } + default: + // TODO this should never happen, because ClassFile should validate the descriptors + throw new InvalidOperationException(sig.Substring(index)); + } + } + + private static TypeWrapper[] ArgTypeWrapperListFromSig(ClassLoaderWrapper classLoader, string sig) + { + if(sig[1] == ')') + { + return new TypeWrapper[0]; + } + ArrayList list = new ArrayList(); + for(int i = 1; sig[i] != ')';) + { + list.Add(SigDecoderWrapper(classLoader, ref i, sig)); + } + TypeWrapper[] types = new TypeWrapper[list.Count]; + list.CopyTo(types); + return types; + } + + private static TypeWrapper RetTypeWrapperFromSig(ClassLoaderWrapper classLoader, string sig) + { + int index = sig.IndexOf(')') + 1; + return SigDecoderWrapper(classLoader, ref index, sig); + } + private class ConstantPoolItemDouble : ConstantPoolItem { private double d; @@ -628,6 +761,11 @@ class ClassFile { return name_and_type.GetRetType(classLoader); } + + internal TypeWrapper GetFieldType(ClassLoaderWrapper classLoader) + { + return name_and_type.GetFieldType(classLoader); + } } internal class ConstantPoolItemFieldref : ConstantPoolItemFMI @@ -772,7 +910,7 @@ class ClassFile { if(argTypeWrappers == null) { - argTypeWrappers = classLoader.ArgTypeWrapperListFromSig(descriptor); + argTypeWrappers = ArgTypeWrapperListFromSig(classLoader, descriptor); } return argTypeWrappers; } @@ -781,7 +919,7 @@ class ClassFile { if(retTypeWrapper == null) { - retTypeWrapper = classLoader.RetTypeWrapperFromSig(descriptor); + retTypeWrapper = RetTypeWrapperFromSig(classLoader, descriptor); } return retTypeWrapper; } @@ -790,7 +928,8 @@ class ClassFile { if(fieldTypeWrapper == null) { - fieldTypeWrapper = classLoader.RetTypeWrapperFromSig("()" + descriptor); + // HACK + fieldTypeWrapper = RetTypeWrapperFromSig(classLoader, "()" + descriptor); } return fieldTypeWrapper; } @@ -920,6 +1059,9 @@ class ClassFile private ushort name_index; private ushort descriptor_index; private Attribute[] attributes; + private TypeWrapper[] argTypeWrappers; + private TypeWrapper retTypeWrapper; + private TypeWrapper fieldTypeWrapper; internal FieldOrMethod(ClassFile classFile, BigEndianBinaryReader br) { @@ -953,6 +1095,34 @@ class ClassFile } } + internal TypeWrapper[] GetArgTypes(ClassLoaderWrapper classLoader) + { + if(argTypeWrappers == null) + { + argTypeWrappers = ArgTypeWrapperListFromSig(classLoader, Signature); + } + return argTypeWrappers; + } + + internal TypeWrapper GetRetType(ClassLoaderWrapper classLoader) + { + if(retTypeWrapper == null) + { + retTypeWrapper = RetTypeWrapperFromSig(classLoader, Signature); + } + return retTypeWrapper; + } + + internal TypeWrapper GetFieldType(ClassLoaderWrapper classLoader) + { + if(fieldTypeWrapper == null) + { + // HACK + fieldTypeWrapper = RetTypeWrapperFromSig(classLoader, "()" + Signature); + } + return fieldTypeWrapper; + } + internal Modifiers Modifiers { get diff --git a/IK.VM.NET/ClassLoaderWrapper.cs b/IK.VM.NET/ClassLoaderWrapper.cs index 96bca9dd..1162e396 100644 --- a/IK.VM.NET/ClassLoaderWrapper.cs +++ b/IK.VM.NET/ClassLoaderWrapper.cs @@ -128,9 +128,9 @@ class ClassLoaderWrapper { nativeMethods = new Hashtable(); // TODO interfaces have java/lang/Object as the base type (do they really?) - types["java.lang.Cloneable"] = new RemappedTypeWrapper(ModifiersAttribute.GetModifiers(typeof(java.lang.Cloneable)), "java/lang/Cloneable", typeof(java.lang.Cloneable), new TypeWrapper[0], null); + types["java.lang.Cloneable"] = new RemappedTypeWrapper(this, ModifiersAttribute.GetModifiers(typeof(java.lang.Cloneable)), "java/lang/Cloneable", typeof(java.lang.Cloneable), new TypeWrapper[0], null); typeToTypeWrapper.Add(typeof(java.lang.Cloneable), types["java.lang.Cloneable"]); - types["java.io.Serializable"] = new RemappedTypeWrapper(ModifiersAttribute.GetModifiers(typeof(java.io.Serializable)), "java/io/Serializable", typeof(java.io.Serializable), new TypeWrapper[0], null); + types["java.io.Serializable"] = new RemappedTypeWrapper(this, ModifiersAttribute.GetModifiers(typeof(java.io.Serializable)), "java/io/Serializable", typeof(java.io.Serializable), new TypeWrapper[0], null); typeToTypeWrapper.Add(typeof(java.io.Serializable), types["java.io.Serializable"]); MapXml.Root map = null; using(Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("map.xml")) @@ -151,7 +151,7 @@ class ClassLoaderWrapper string name = c.Name; Modifiers modifiers = (Modifiers)c.Modifiers; // TODO specify interfaces - TypeWrapper tw = new RemappedTypeWrapper(modifiers, name.Replace('.', '/'), Type.GetType(c.Type, true), new TypeWrapper[0], baseWrapper); + TypeWrapper tw = new RemappedTypeWrapper(this, modifiers, name.Replace('.', '/'), Type.GetType(c.Type, true), new TypeWrapper[0], baseWrapper); types.Add(name, tw); typeToTypeWrapper.Add(tw.Type, tw); } @@ -365,7 +365,7 @@ class ClassLoaderWrapper MethodDescriptor mdClone = new MethodDescriptor(GetBootstrapClassLoader(), "clone", "()Ljava/lang/Object;"); Modifiers modifiers = Modifiers.Final | Modifiers.Public; // TODO copy accessibility from element type - wrapper = new RemappedTypeWrapper(modifiers, name, array, interfaces, GetBootstrapClassLoader().LoadClassByDottedName("java.lang.Object")); + wrapper = new RemappedTypeWrapper(this, modifiers, name, array, interfaces, GetBootstrapClassLoader().LoadClassByDottedName("java.lang.Object")); MethodInfo clone = typeof(Array).GetMethod("Clone"); MethodWrapper mw = new MethodWrapper(wrapper, mdClone, clone, Modifiers.Public); mw.EmitCall = CodeEmitter.Create(OpCodes.Callvirt, clone); @@ -415,7 +415,7 @@ class ClassLoaderWrapper } else { - type = new DynamicTypeWrapper(f.Name, f, baseType, this, nativeMethods); + type = new DynamicTypeWrapper(f, this, nativeMethods); dynamicTypes.Add(f.Name.Replace('/', '.'), type); } types.Add(f.Name.Replace('/', '.'), type); @@ -526,19 +526,18 @@ class ClassLoaderWrapper return moduleBuilder; } - internal Type ExpressionType(string type) + internal TypeWrapper ExpressionTypeWrapper(string type) { - // HACK to ease the burden of the compiler, we support the Lret pseudo type here if(type.StartsWith("Lret;")) { - return typeof(int); + throw new InvalidOperationException("ExpressionTypeWrapper for Lret; requested"); } if(type == "Lnull") { - throw new InvalidOperationException("ExpressionType for Lnull requested"); + throw new InvalidOperationException("ExpressionTypeWrapper for Lnull requested"); } int index = 0; - return SigDecoder(ref index, type); + return SigDecoderWrapper(ref index, type); } // NOTE: this will ignore anything following the sig marker (so that it can be used to decode method signatures) @@ -713,66 +712,6 @@ class ClassLoaderWrapper return types; } - // subType and baseType are Java class name (e.g. java/lang/Object) - internal bool IsSubType(string subType, string baseType) - { - return LoadClassBySlashedName(subType).IsSubTypeOf(LoadClassBySlashedName(baseType)); - } - - internal string FindCommonBaseType(string type1, string type2) - { - TypeWrapper t1 = LoadClassBySlashedName(type1); - TypeWrapper t2 = LoadClassBySlashedName(type2); - if(t1 == t2) - { - return type1; - } - if(t1.IsInterface || t2.IsInterface) - { - // TODO I don't know how finding the common base for interfaces is defined, but - // for now I'm just doing the naive thing - // UPDATE 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. - // So strictly speaking, the code below isn't correct, but it works, so for now it stays in. - if(t1.ImplementsInterface(t2)) - { - return t2.Name; - } - if(t2.ImplementsInterface(t1)) - { - return t1.Name; - } - return "java/lang/Object"; - } - Stack st1 = new Stack(); - Stack st2 = new Stack(); - 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 ? (TypeWrapper)st1.Pop() : null; - t2 = st2.Count > 0 ? (TypeWrapper)st2.Pop() : null; - if(t1 != t2) - { - return type.Name; - } - type = t1; - } - } - internal static ClassLoaderWrapper GetBootstrapClassLoader() { if(bootstrapClassLoader == null) @@ -821,7 +760,26 @@ class ClassLoaderWrapper internal static TypeWrapper GetWrapperFromTypeFast(Type type) { Debug.Assert(!(type is TypeBuilder)); - return (TypeWrapper)typeToTypeWrapper[type]; + TypeWrapper wrapper = (TypeWrapper)typeToTypeWrapper[type]; + if(wrapper == null && type.IsArray) + { + // it might be an array of a dynamically compiled Java type + int rank = 1; + Type elem = type.GetElementType(); + while(elem.IsArray) + { + rank++; + elem = elem.GetElementType(); + } + wrapper = (TypeWrapper)typeToTypeWrapper[elem]; + if(wrapper != null) + { + // HACK this is a lame way of creating the array wrapper + wrapper = wrapper.GetClassLoader().LoadClassBySlashedName(new String('[', rank) + "L" + wrapper.Name + ";"); + typeToTypeWrapper[type] = wrapper; + } + } + return wrapper; } internal static TypeWrapper GetWrapperFromType(Type type) diff --git a/IK.VM.NET/IK.VM.NET.csproj b/IK.VM.NET/IK.VM.NET.csproj index 4aa79e7e..1d052f3a 100644 --- a/IK.VM.NET/IK.VM.NET.csproj +++ b/IK.VM.NET/IK.VM.NET.csproj @@ -25,7 +25,7 @@ BaseAddress = "285212672" CheckForOverflowUnderflow = "false" ConfigurationOverrideFile = "" - DefineConstants = "DEBUG;TRACE;PROFILE" + DefineConstants = "DEBUG;TRACE" DocumentationFile = "" DebugSymbols = "true" FileAlignment = "4096" diff --git a/IK.VM.NET/TypeWrapper.cs b/IK.VM.NET/TypeWrapper.cs index 45acf99e..b816c6c3 100644 --- a/IK.VM.NET/TypeWrapper.cs +++ b/IK.VM.NET/TypeWrapper.cs @@ -34,6 +34,22 @@ sealed class MethodDescriptor private string sig; private Type[] args; private Type ret; + private TypeWrapper[] argTypeWrappers; + private TypeWrapper retTypeWrapper; + + internal MethodDescriptor(ClassLoaderWrapper classLoader, ClassFile.ConstantPoolItemFMI cpi) + : this(classLoader, cpi.Name, cpi.Signature) + { + argTypeWrappers = cpi.GetArgTypes(classLoader); + retTypeWrapper = cpi.GetRetType(classLoader); + } + + internal MethodDescriptor(ClassLoaderWrapper classLoader, ClassFile.Method method) + : this(classLoader, method.Name, method.Signature) + { + argTypeWrappers = method.GetArgTypes(classLoader); + retTypeWrapper = method.GetRetType(classLoader); + } internal MethodDescriptor(ClassLoaderWrapper classLoader, string name, string sig) { @@ -76,6 +92,18 @@ sealed class MethodDescriptor } } + internal TypeWrapper[] ArgTypeWrappers + { + get + { + if(argTypeWrappers == null) + { + argTypeWrappers = classLoader.ArgTypeWrapperListFromSig(sig); + } + return argTypeWrappers; + } + } + internal Type RetType { get @@ -88,6 +116,18 @@ sealed class MethodDescriptor } } + internal TypeWrapper RetTypeWrapper + { + get + { + if(retTypeWrapper == null) + { + retTypeWrapper = classLoader.RetTypeWrapperFromSig(sig); + } + return retTypeWrapper; + } + } + public override bool Equals(object o) { // TODO instead of comparing the signature strings, we should compare the actual types @@ -196,6 +236,51 @@ abstract class TypeWrapper this.classLoader = classLoader; } + public override string ToString() + { + return GetType().Name + "[" + name + "]"; + } + + // NOTE for non-array types this returns 0 + internal int ArrayRank + { + get + { + int i = 0; + while(name[i] == '[') + { + i++; + } + return i; + } + } + + internal virtual bool IsPrimitive + { + get + { + return false; + } + } + + internal bool IsUnloadable + { + get + { + // NOTE we abuse modifiers to note unloadable classes + return modifiers == Modifiers.Synthetic; + } + } + + internal bool IsVerifierType + { + get + { + // NOTE we abuse modifiers to note verifier types + return modifiers == (Modifiers.Final | Modifiers.Interface); + } + } + internal Modifiers Modifiers { get @@ -235,6 +320,7 @@ abstract class TypeWrapper protected abstract FieldWrapper GetFieldImpl(string fieldName); + // TODO this shouldn't just be based on the name, fields can be overloaded on type public FieldWrapper GetFieldWrapper(string fieldName) { FieldWrapper fae = (FieldWrapper)fields[fieldName]; @@ -364,6 +450,23 @@ abstract class TypeWrapper get; } + internal Type TypeOrUnloadableAsObject + { + get + { + if(IsUnloadable) + { + return typeof(object); + } + // HACK as a convenience to the compiler, we replace return address types with typeof(int) + if(VerifierTypeWrapper.IsRet(this)) + { + return typeof(int); + } + return Type; + } + } + public TypeWrapper BaseTypeWrapper { get @@ -372,6 +475,43 @@ abstract class TypeWrapper } } + internal TypeWrapper ElementTypeWrapper + { + get + { + if(name[0] != '[') + { + throw new InvalidOperationException(); + } + // TODO consider caching the element type + switch(name[1]) + { + case '[': + return classLoader.LoadClassBySlashedName(name.Substring(1)); + case 'L': + return classLoader.LoadClassBySlashedName(name.Substring(2, name.Length - 3)); + case 'Z': + return PrimitiveTypeWrapper.BOOLEAN; + case 'B': + return PrimitiveTypeWrapper.BYTE; + case 'S': + return PrimitiveTypeWrapper.SHORT; + case 'C': + return PrimitiveTypeWrapper.CHAR; + case 'I': + return PrimitiveTypeWrapper.INT; + case 'J': + return PrimitiveTypeWrapper.LONG; + case 'F': + return PrimitiveTypeWrapper.FLOAT; + case 'D': + return PrimitiveTypeWrapper.DOUBLE; + default: + throw new InvalidOperationException(name); + } + } + } + public bool ImplementsInterface(TypeWrapper interfaceWrapper) { TypeWrapper typeWrapper = this; @@ -415,6 +555,40 @@ abstract class TypeWrapper return true; } + internal bool IsAssignableTo(TypeWrapper wrapper) + { + if(this == wrapper) + { + return true; + } + if(wrapper.IsPrimitive) + { + return false; + } + if(this == VerifierTypeWrapper.Null) + { + return true; + } + int rank1 = this.ArrayRank; + int rank2 = wrapper.ArrayRank; + if(rank1 > 0 && rank2 > 0) + { + rank1--; + rank2--; + TypeWrapper elem1 = this.ElementTypeWrapper; + TypeWrapper elem2 = wrapper.ElementTypeWrapper; + while(rank1 != 0 && rank2 != 0) + { + elem1 = elem1.ElementTypeWrapper; + elem2 = elem2.ElementTypeWrapper; + rank1--; + rank2--; + } + return elem1.IsSubTypeOf(elem2); + } + return this.IsSubTypeOf(wrapper); + } + public abstract bool IsInterface { get; @@ -608,6 +782,53 @@ abstract class TypeWrapper } } +class UnloadableTypeWrapper : TypeWrapper +{ + internal UnloadableTypeWrapper(string name) + : base(Modifiers.Synthetic, name, null, null) + { + } + + protected override FieldWrapper GetFieldImpl(string fieldName) + { + throw new InvalidOperationException("GetFieldImpl called on UnloadableTypeWrapper"); + } + + protected override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + throw new InvalidOperationException("GetMethodImpl called on UnloadableTypeWrapper"); + } + + public override Type Type + { + get + { + throw new InvalidOperationException("get_Type called on UnloadableTypeWrapper"); + } + } + + public override bool IsInterface + { + get + { + throw new InvalidOperationException("get_IsInterface called on UnloadableTypeWrapper"); + } + } + + public override TypeWrapper[] Interfaces + { + get + { + throw new InvalidOperationException("get_Interfaces called on UnloadableTypeWrapper"); + } + } + + public override void Finish() + { + throw new InvalidOperationException("Finish called on UnloadableTypeWrapper"); + } +} + class PrimitiveTypeWrapper : TypeWrapper { internal static readonly PrimitiveTypeWrapper BYTE = new PrimitiveTypeWrapper(typeof(sbyte)); @@ -628,6 +849,14 @@ class PrimitiveTypeWrapper : TypeWrapper this.type = type; } + internal override bool IsPrimitive + { + get + { + return true; + } + } + public override Type Type { get @@ -673,12 +902,47 @@ class DynamicTypeWrapper : TypeWrapper private DynamicImpl impl; private TypeWrapper[] interfaces; - internal DynamicTypeWrapper(string name, ClassFile f, TypeWrapper baseType, ClassLoaderWrapper classLoader, Hashtable nativeMethods) - : base(f.Modifiers, name, baseType, classLoader) + internal DynamicTypeWrapper(ClassFile f, ClassLoaderWrapper classLoader, Hashtable nativeMethods) + : base(f.Modifiers, f.Name, f.GetSuperTypeWrapper(classLoader), classLoader) { - JavaTypeImpl impl = new JavaTypeImpl(f, this, baseType, nativeMethods); - this.impl = impl; - interfaces = impl.GetInterfaces(); + if(BaseTypeWrapper.IsUnloadable) + { + throw JavaException.NoClassDefFoundError(BaseTypeWrapper.Name); + } + // if the base type isn't public, it must be in the same package + if(!BaseTypeWrapper.IsPublic) + { + if(BaseTypeWrapper.GetClassLoader() != classLoader || f.PackageName != BaseTypeWrapper.PackageName) + { + throw JavaException.IllegalAccessError("Class {0} cannot access its superclass {1}", f.Name, BaseTypeWrapper.Name); + } + } + if(BaseTypeWrapper.IsFinal) + { + throw JavaException.VerifyError("Cannot inherit from final class"); + } + if(BaseTypeWrapper.IsInterface) + { + throw JavaException.IncompatibleClassChangeError("Class {0} has interface {1} as superclass", f.Name, BaseTypeWrapper.Name); + } + interfaces = f.GetInterfaceTypeWrappers(classLoader); + for(int i = 0; i < interfaces.Length; i++) + { + if(interfaces[i].IsUnloadable) + { + throw JavaException.NoClassDefFoundError(interfaces[i].Name); + } + if(!interfaces[i].IsInterface) + { + throw JavaException.IncompatibleClassChangeError("Implementing class"); + } + if(!interfaces[i].IsAccessibleFrom(this)) + { + throw JavaException.IllegalAccessError("Class {0} cannot access its superinterface {1}", f.Name, interfaces[i].Name); + } + } + + impl = new JavaTypeImpl(f, this, BaseTypeWrapper, interfaces, nativeMethods); } protected override FieldWrapper GetFieldImpl(string fieldName) @@ -747,12 +1011,13 @@ class DynamicTypeWrapper : TypeWrapper private FinishedTypeImpl finishedType; private Hashtable nativeMethods; - internal JavaTypeImpl(ClassFile f, DynamicTypeWrapper wrapper, TypeWrapper baseWrapper, Hashtable nativeMethods) + internal JavaTypeImpl(ClassFile f, DynamicTypeWrapper wrapper, TypeWrapper baseWrapper, TypeWrapper[] interfaces, Hashtable nativeMethods) { - // Console.WriteLine("constructing JavaTypeImpl for " + f.Name); + //Console.WriteLine("constructing JavaTypeImpl for " + f.Name); this.classFile = f; this.wrapper = wrapper; this.baseWrapper = baseWrapper; + this.interfaces = interfaces; this.nativeMethods = nativeMethods; TypeAttributes typeAttribs = 0; @@ -768,21 +1033,6 @@ class DynamicTypeWrapper : TypeWrapper { typeAttribs |= TypeAttributes.Public; } - interfaces = new TypeWrapper[f.Interfaces.Length]; - for(int i = 0; i < f.Interfaces.Length; i++) - { - interfaces[i] = wrapper.GetClassLoader().LoadClassBySlashedName(f.Interfaces[i]); - if(!interfaces[i].IsInterface) - { - throw JavaException.IncompatibleClassChangeError("Implementing class"); - } - if(!interfaces[i].IsAccessibleFrom(wrapper)) - { - throw JavaException.IllegalAccessError("Class {0} cannot access its superinterface {1}", wrapper.Name, interfaces[i].Name); - } - } - // NOTE we call DefineType after all the interfaces have been resolved, because otherwise - // we end up with a .NET type that cannot be completed if(f.IsInterface) { typeAttribs |= TypeAttributes.Interface | TypeAttributes.Abstract; @@ -799,11 +1049,6 @@ class DynamicTypeWrapper : TypeWrapper } } - internal TypeWrapper[] GetInterfaces() - { - return interfaces; - } - public override DynamicImpl Finish() { if(baseWrapper != null) @@ -850,7 +1095,7 @@ class DynamicTypeWrapper : TypeWrapper MethodDescriptor[] methodDescriptors = new MethodDescriptor[classFile.Methods.Length]; for(int i = 0; i < classFile.Methods.Length; i++) { - methodDescriptors[i] = new MethodDescriptor(wrapper.GetClassLoader(), classFile.Methods[i].Name, classFile.Methods[i].Signature); + methodDescriptors[i] = new MethodDescriptor(wrapper.GetClassLoader(), classFile.Methods[i]); } if(methodLookup == null) { @@ -1149,21 +1394,17 @@ class DynamicTypeWrapper : TypeWrapper { FieldBuilder field; ClassFile.Field fld = classFile.Fields[i]; - Type type = null; - try + TypeWrapper typeWrapper = fld.GetFieldType(wrapper.GetClassLoader()); + Type type; + if(typeWrapper.IsUnloadable) { - type = wrapper.GetClassLoader().ExpressionType(fld.Signature); + // TODO the field name should be mangled here, because otherwise it might conflict with another field + // with the same name and a different unloadable type (or java.lang.Object as its type) + type = typeof(object); } - catch(Exception x) + else { - if(x.GetType().FullName == "java.lang.ClassNotFoundException") - { - // TODO set fields[i] to a special FieldWrapper that does the appropriate thing (whatever that may be) - fields[i] = new FieldWrapper(this.wrapper, fld.Name, fld.Signature, fld.Modifiers); - Console.Error.WriteLine("Type " + fld.Signature + " of field " + fld.Name + " in class " + classFile.Name + " is unloadable"); - return; - } - throw; + type = typeWrapper.Type; } FieldAttributes attribs = 0; MethodAttributes methodAttribs = 0; @@ -1294,6 +1535,11 @@ class DynamicTypeWrapper : TypeWrapper fields[i] = FieldWrapper.Create(wrapper, field, fld.Signature, fld.Modifiers); } } + if(typeWrapper.IsUnloadable) + { + CustomAttributeBuilder attrib = new CustomAttributeBuilder(typeof(UnloadableTypeAttribute).GetConstructor(new Type[] { typeof(string) }), new object[] { typeWrapper.Name }); + field.SetCustomAttribute(attrib); + } // if the Java modifiers cannot be expressed in .NET, we emit the Modifiers attribute to store // the Java modifiers if(setModifiers) @@ -1310,7 +1556,7 @@ class DynamicTypeWrapper : TypeWrapper methodLookup = new Hashtable(); for(int i = 0; i < classFile.Methods.Length; i++) { - methodLookup[new MethodDescriptor(wrapper.GetClassLoader(), classFile.Methods[i].Name, classFile.Methods[i].Signature)] = i; + methodLookup[new MethodDescriptor(wrapper.GetClassLoader(), classFile.Methods[i])] = i; } } object index = methodLookup[md]; @@ -1332,25 +1578,36 @@ class DynamicTypeWrapper : TypeWrapper { throw new InvalidOperationException(); } + // TODO things to consider when we support unloadable types on the argument list on return type: + // - later on, the method can be overriden by a class that does have access to the type, so + // this should be detected and an appropriate override stub should be generated + // - overloading might conflict with the generalised argument list (unloadable types appear + // as System.Object). The nicest way to solve this would be to emit a modreq attribute on the parameter, + // but Reflection.Emit doesn't support this, so we'll probably have to use a name mangling scheme MethodBase method; ClassFile.Method m = classFile.Methods[index]; - Type[] args = null; - Type retType = null; - try + TypeWrapper[] argTypeWrappers = m.GetArgTypes(wrapper.GetClassLoader()); + TypeWrapper retTypeWrapper = m.GetRetType(wrapper.GetClassLoader()); + Type[] args = new Type[argTypeWrappers.Length]; + Type retType; + if(retTypeWrapper.IsUnloadable) { - args = wrapper.GetClassLoader().ArgTypeListFromSig(m.Signature); - retType = wrapper.GetClassLoader().RetTypeFromSig(m.Signature); + retType = typeof(object); } - catch(Exception x) + else { - if(x.GetType().FullName == "java.lang.ClassNotFoundException") + retType = retTypeWrapper.Type; + } + for(int i = 0; i < args.Length; i++) + { + if(argTypeWrappers[i].IsUnloadable) { - // TODO set methods[i] to a special MethodWrapper that does the appropriate thing (whatever that may be) - methods[index] = new MethodWrapper(this.wrapper, new MethodDescriptor(wrapper.GetClassLoader(), m.Name, m.Signature), null, m.Modifiers); - Console.Error.WriteLine("Type " + x.Message + " of method " + m.Name + m.Signature + " in class " + classFile.Name + " is unloadable"); - return; + args[i] = typeof(object); + } + else + { + args[i] = argTypeWrappers[i].Type; } - throw; } MethodAttributes attribs = 0; if(m.IsAbstract) @@ -1412,7 +1669,7 @@ class DynamicTypeWrapper : TypeWrapper attribs |= MethodAttributes.Virtual; } string name = m.Name; - MethodDescriptor md = new MethodDescriptor(wrapper.GetClassLoader(), name, m.Signature); + MethodDescriptor md = new MethodDescriptor(wrapper.GetClassLoader(), m); // if a method is virtual, we need to find the method it overrides (if any), for several reasons: // - if we're overriding a method that has a different name (e.g. some of the virtual methods // in System.Object [Equals <-> equals]) we need to add an explicit MethodOverride @@ -1506,7 +1763,27 @@ class DynamicTypeWrapper : TypeWrapper typeBuilder.DefineMethodOverride(mb, (MethodInfo)baseMethod); } } - methods[index] = MethodWrapper.Create(wrapper, new MethodDescriptor(wrapper.GetClassLoader(), m.Name, m.Signature), method, method, m.Modifiers); + if(retTypeWrapper.IsUnloadable) + { + CustomAttributeBuilder attrib = new CustomAttributeBuilder(typeof(UnloadableTypeAttribute).GetConstructor(new Type[] { typeof(string) }), new object[] { retTypeWrapper.Name }); + ((MethodBuilder)method).DefineParameter(0, ParameterAttributes.None, null).SetCustomAttribute(attrib); + } + for(int i = 0; i < argTypeWrappers.Length; i++) + { + if(argTypeWrappers[i].IsUnloadable) + { + CustomAttributeBuilder attrib = new CustomAttributeBuilder(typeof(UnloadableTypeAttribute).GetConstructor(new Type[] { typeof(string) }), new object[] { argTypeWrappers[i].Name }); + if(method is MethodBuilder) + { + ((MethodBuilder)method).DefineParameter(i + 1, ParameterAttributes.None, null).SetCustomAttribute(attrib); + } + else + { + ((ConstructorBuilder)method).DefineParameter(i + 1, ParameterAttributes.None, null).SetCustomAttribute(attrib); + } + } + } + methods[index] = MethodWrapper.Create(wrapper, new MethodDescriptor(wrapper.GetClassLoader(), m), method, method, m.Modifiers); } public override Type Type @@ -1568,8 +1845,8 @@ class RemappedTypeWrapper : TypeWrapper private Type virtualsInterface; private Type virtualsHelperHack; - public RemappedTypeWrapper(Modifiers modifiers, string name, Type type, TypeWrapper[] interfaces, TypeWrapper baseType) - : base(modifiers, name, baseType, ClassLoaderWrapper.GetBootstrapClassLoader()) + public RemappedTypeWrapper(ClassLoaderWrapper classLoader, Modifiers modifiers, string name, Type type, TypeWrapper[] interfaces, TypeWrapper baseType) + : base(modifiers, name, baseType, classLoader) { this.type = type; this.interfaces = interfaces; @@ -1709,9 +1986,8 @@ class RemappedTypeWrapper : TypeWrapper { if(method.@override != null) { - MethodDescriptor redir = new MethodDescriptor(GetClassLoader(), method.@override.Name, sig); BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - overrideMethod = type.GetMethod(redir.Name, binding, null, CallingConventions.Standard, redir.ArgTypes, null); + overrideMethod = type.GetMethod(method.@override.Name, binding, null, CallingConventions.Standard, GetClassLoader().ArgTypeListFromSig(sig), null); if(overrideMethod == null) { throw new InvalidOperationException("Override method not found: " + Name + "." + name + sig); @@ -2908,13 +3184,13 @@ sealed class MethodWrapper { get { - return declaringType.GetClassLoader().RetTypeWrapperFromSig(md.Signature); + return md.RetTypeWrapper; } } internal TypeWrapper[] GetParameters() { - return declaringType.GetClassLoader().ArgTypeWrapperListFromSig(md.Signature); + return md.ArgTypeWrappers; } internal Modifiers Modifiers @@ -3139,7 +3415,16 @@ sealed class FieldWrapper { get { - return declaringType.GetClassLoader().ExpressionType(sig); + return FieldTypeWrapper.Type; + } + } + + internal TypeWrapper FieldTypeWrapper + { + get + { + // HACK + return declaringType.GetClassLoader().RetTypeWrapperFromSig("()" + sig); } } diff --git a/IK.VM.NET/attributes.cs b/IK.VM.NET/attributes.cs index 93d7b7dd..44ad5752 100644 --- a/IK.VM.NET/attributes.cs +++ b/IK.VM.NET/attributes.cs @@ -35,6 +35,25 @@ public class IKVMAssemblyAttribute : Attribute { } +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] +public class UnloadableTypeAttribute : Attribute +{ + private string name; + + public UnloadableTypeAttribute(string name) + { + this.name = name; + } + + public string Name + { + get + { + return name; + } + } +} + [AttributeUsage(AttributeTargets.All)] public class ModifiersAttribute : Attribute { diff --git a/IK.VM.NET/classpath.cs b/IK.VM.NET/classpath.cs index 0f2a3101..395405dc 100644 --- a/IK.VM.NET/classpath.cs +++ b/IK.VM.NET/classpath.cs @@ -1100,7 +1100,25 @@ namespace NativeCode.java } // we need to finish the type otherwise all methods will not be in the method map yet wrapper.Finish(); - return wrapper.GetMethods(); + // we need to look through the array for unloadable types, because we may not let them + // escape into the 'wild' + MethodWrapper[] methods = wrapper.GetMethods(); + for(int i = 0; i < methods.Length; i++) + { + if(methods[i].ReturnType.IsUnloadable) + { + throw JavaException.NoClassDefFoundError(methods[i].ReturnType.Name); + } + TypeWrapper[] args = methods[i].GetParameters(); + for(int j = 0; j < args.Length; j++) + { + if(args[j].IsUnloadable) + { + throw JavaException.NoClassDefFoundError(args[j].Name); + } + } + } + return methods; } public static object[] GetDeclaredFields(Type type, object cwrapper) @@ -1112,7 +1130,17 @@ namespace NativeCode.java } // we need to finish the type otherwise all fields will not be in the field map yet wrapper.Finish(); - return wrapper.GetFields(); + // we need to look through the array for unloadable types, because we may not let them + // escape into the 'wild' + FieldWrapper[] fields = wrapper.GetFields(); + for(int i = 0; i < fields.Length; i++) + { + if(fields[i].FieldTypeWrapper.IsUnloadable) + { + throw JavaException.NoClassDefFoundError(fields[i].FieldTypeWrapper.Name); + } + } + return fields; } public static object[] GetDeclaredClasses(Type type, object cwrapper) diff --git a/IK.VM.NET/compiler.cs b/IK.VM.NET/compiler.cs index aa4b5fdc..25d7e426 100644 --- a/IK.VM.NET/compiler.cs +++ b/IK.VM.NET/compiler.cs @@ -81,6 +81,7 @@ class Compiler private static MethodInfo monitorEnterMethod = typeof(ByteCodeHelper).GetMethod("monitorenter"); private static MethodInfo monitorExitMethod = typeof(ByteCodeHelper).GetMethod("monitorexit"); private static MethodInfo throwHack = typeof(ExceptionHelper).GetMethod("ThrowHack"); + private static TypeWrapper java_lang_Throwable; private TypeWrapper clazz; private ClassFile.Method.Code m; private ILGenerator ilGenerator; @@ -105,17 +106,8 @@ class Compiler } } Profiler.Enter("MethodAnalyzer"); - ma = new MethodAnalyzer(m, classLoader); + ma = new MethodAnalyzer(clazz, m, classLoader); Profiler.Leave("MethodAnalyzer"); - // HACK force types of locals to be loaded, so we don't run into a problem later (GetLocal cannot handle an unloadable type) - for(int i = 0; i < m.MaxLocals; i++) - { - string t = ma.GetDeclaredLocalType(i); - if(t != null && t != "Lnull") - { - classLoader.ExpressionType(t); - } - } ArrayList ar = new ArrayList(m.ExceptionTable); // Console.WriteLine("before processing:"); // foreach(ExceptionTableEntry e in ar) @@ -360,29 +352,26 @@ class Compiler private struct DupHelper { - private ClassLoaderWrapper classLoader; private ILGenerator ilgen; private bool[] isnull; private LocalBuilder[] locals; - internal DupHelper(ClassLoaderWrapper classLoader, ILGenerator ilgen, int count) + internal DupHelper(ILGenerator ilgen, int count) { - this.classLoader = classLoader; this.ilgen = ilgen; isnull = new bool[count]; locals = new LocalBuilder[count]; } - internal DupHelper SetType(int i, string type) + internal DupHelper SetType(int i, TypeWrapper type) { - if(type == "Lnull") + if(type == VerifierTypeWrapper.Null) { isnull[i] = true; } - else if(type[0] != 'N') + else if(!VerifierTypeWrapper.IsNew(type)) { - // TODO handle class not found - locals[i] = ilgen.DeclareLocal(classLoader.ExpressionType(type)); + locals[i] = ilgen.DeclareLocal(type.TypeOrUnloadableAsObject); } return this; } @@ -439,21 +428,6 @@ class Compiler } return; } - catch(Exception x1) - { - // HACK because the verifier currently cannot deal with unloadable classes, we have - // to work around it by just generating code to throw a verify error - if(x1.GetType().FullName == "java.lang.ClassNotFoundException") - { - Type verifyError = ClassLoaderWrapper.GetType("java.lang.VerifyError"); - string msg = string.Format("{0}.{1}{2} cannot be verified due to unloadable class {3}", clazz.Name, m.Name, m.Signature, x1.Message); - ilGenerator.Emit(OpCodes.Ldstr, msg); - ilGenerator.Emit(OpCodes.Newobj, verifyError.GetConstructor(new Type[] { typeof(string) })); - ilGenerator.Emit(OpCodes.Throw); - return; - } - throw; - } Profiler.Enter("Compile"); c.Compile(0, 0, null); Profiler.Leave("Compile"); @@ -546,28 +520,24 @@ class Compiler int stackHeight = ma.GetStackHeight(i); for(int n = 0; n < stackHeight; n++) { - // TODO handle class not found - string t = ma.GetRawStackType(i, n); - if(t.Length > 1 && (t[0] == 'N' || t[0] == 'U')) + TypeWrapper t = ma.GetRawStackTypeWrapper(i, n); + if(VerifierTypeWrapper.IsNew(t)) { - if(t[0] == 'U') - { - // we're inside a constructor and the uninitialized this is passed into an exception block! - ilGenerator.Emit(OpCodes.Pop); - stack.Push("this"); - } - else - { - // unitialized references (new objects) aren't really there - } + // unitialized references (new objects) aren't really there } - else if(t == "Lnull") + else if(t == VerifierTypeWrapper.UninitializedThis) + { + // we're inside a constructor and the uninitialized this is passed into an exception block! + ilGenerator.Emit(OpCodes.Pop); + stack.Push("this"); + } + else if(t == VerifierTypeWrapper.Null) { stack.Push(null); } else { - LocalBuilder local = ilGenerator.DeclareLocal(classLoader.ExpressionType(t)); + LocalBuilder local = ilGenerator.DeclareLocal(t.TypeOrUnloadableAsObject); stack.Push(local); ilGenerator.Emit(OpCodes.Stloc, local); } @@ -606,24 +576,32 @@ class Compiler int stack = ma.GetStackHeight(bc.TargetIndex); for(int n = 0; n < stack; n++) { - // TODO handle class not found - string t = ma.GetRawStackType(bc.TargetIndex, n); - if((t.Length > 1 && (t[0] == 'N' || t[0] == 'U')) || t == "Lnull") + TypeWrapper t = ma.GetRawStackTypeWrapper(bc.TargetIndex, n); + if(VerifierTypeWrapper.IsNew(t)) { - if(t[0] == 'U') - { - // we're inside a constructor and the uninitialized this is passed into an exception block! - ilGenerator.Emit(OpCodes.Ldarg_0); - } // unitialized references aren't really there, but at the push site we // need to know that we have to skip this slot, so we push a null as well, // and then at the push site we'll look at the stack type to figure out // if it is a real null or an unitialized references bc.Stack.Push(null); } + else if(t == VerifierTypeWrapper.UninitializedThis) + { + // we're inside a constructor and the uninitialized this is passed into an exception block! + ilGenerator.Emit(OpCodes.Ldarg_0); + // unitialized references aren't really there, but at the push site we + // need to know that we have to skip this slot, so we push a null as well, + // and then at the push site we'll look at the stack type to figure out + // if it is a real null or an unitialized references + bc.Stack.Push(null); + } + else if(t == VerifierTypeWrapper.Null) + { + bc.Stack.Push(null); + } else { - LocalBuilder local = ilGenerator.DeclareLocal(classLoader.ExpressionType(t)); + LocalBuilder local = ilGenerator.DeclareLocal(t.TypeOrUnloadableAsObject); bc.Stack.Push(local); ilGenerator.Emit(OpCodes.Stloc, local); } @@ -739,12 +717,12 @@ class Compiler LocalBuilder local = (LocalBuilder)bc.Stack.Pop(); if(local == null) { - string t = ma.GetRawStackType(bc.TargetIndex, (stack - 1) - n); - if(t == "Lnull") + TypeWrapper t = ma.GetRawStackTypeWrapper(bc.TargetIndex, (stack - 1) - n); + if(t == VerifierTypeWrapper.Null) { ilGenerator.Emit(OpCodes.Ldnull); } - else if(t[0] == 'U') + else if(t == VerifierTypeWrapper.UninitializedThis) { // we're inside a constructor and the uninitialized this is passed into an exception block! ilGenerator.Emit(OpCodes.Ldarg_0); @@ -780,7 +758,7 @@ class Compiler } else { - EmitPlaceholder(cpi.Signature); + EmitPlaceholder(cpi.GetFieldType(classLoader)); } break; } @@ -790,9 +768,9 @@ class Compiler FieldWrapper field = GetField(cpi, true, null, true); if(field != null) { - // because of the way interface merging works, an object reference is valid + // because of the way interface merging works, any reference is valid // for any interface reference - if(field.FieldType != typeof(object) && ma.GetRawStackType(i, 0) == "Ljava/lang/Object;") + if(field.FieldTypeWrapper.IsInterface && !ma.GetRawStackTypeWrapper(i, 0).IsAssignableTo(field.FieldTypeWrapper)) { ilGenerator.Emit(OpCodes.Castclass, field.FieldType); } @@ -807,8 +785,8 @@ class Compiler case NormalizedByteCode.__getfield: { ClassFile.ConstantPoolItemFieldref cpi = m.Method.ClassFile.GetFieldref(instr.Arg1); - TypeWrapper thisType = LoadClass(SigTypeToClassName(ma.GetRawStackType(i, 0), cpi.Class)); - if(thisType != null) + TypeWrapper thisType = SigTypeToClassName(ma.GetRawStackTypeWrapper(i, 0), cpi.GetClassType(classLoader)); + if(!thisType.IsUnloadable) { FieldWrapper field = GetField(cpi, false, thisType, false); if(field != null) @@ -818,21 +796,21 @@ class Compiler } } ilGenerator.Emit(OpCodes.Pop); - EmitPlaceholder(cpi.Signature); + EmitPlaceholder(cpi.GetFieldType(classLoader)); break; } case NormalizedByteCode.__putfield: { ClassFile.ConstantPoolItemFieldref cpi = m.Method.ClassFile.GetFieldref(instr.Arg1); - TypeWrapper thisType = LoadClass(SigTypeToClassName(ma.GetRawStackType(i, 1), cpi.Class)); - if(thisType != null) + TypeWrapper thisType = SigTypeToClassName(ma.GetRawStackTypeWrapper(i, 1), cpi.GetClassType(classLoader)); + if(!thisType.IsUnloadable) { FieldWrapper field = GetField(cpi, false, thisType, true); if(field != null) { - // because of the way interface merging works, an object reference is valid + // because of the way interface merging works, any reference is valid // for any interface reference - if(field.FieldType != typeof(object) && ma.GetRawStackType(i, 0) == "Ljava/lang/Object;") + if(field.FieldTypeWrapper.IsInterface && !ma.GetRawStackTypeWrapper(i, 0).IsAssignableTo(field.FieldTypeWrapper)) { ilGenerator.Emit(OpCodes.Castclass, field.FieldType); } @@ -954,12 +932,12 @@ class Compiler } else { - SigEnumerator sig = new SigEnumerator(cpi.Signature); - while(sig.MoveNext()) + int argcount = cpi.GetArgTypes(classLoader).Length; + for(int j = 0; j < argcount; j++) { ilGenerator.Emit(OpCodes.Pop); } - EmitPlaceholder(cpi.Signature.Substring(cpi.Signature.LastIndexOf(')') + 1)); + EmitPlaceholder(cpi.GetRetType(classLoader)); } break; } @@ -972,50 +950,42 @@ class Compiler // TODO invokespecial should check for null "this" reference ClassFile.ConstantPoolItemFMI cpi = m.Method.ClassFile.GetMethodref(instr.Arg1); - SigEnumerator sig = new SigEnumerator(cpi.Signature); - int argcount = 0; - while(sig.MoveNext()) - { - argcount++; - } - string type = ma.GetRawStackType(i, argcount); - TypeWrapper thisType = LoadClass(SigTypeToClassName(type, cpi.Class)); + int argcount = cpi.GetArgTypes(classLoader).Length; + TypeWrapper type = ma.GetRawStackTypeWrapper(i, argcount); + TypeWrapper thisType = SigTypeToClassName(type, cpi.GetClassType(classLoader)); // invokeinterface needs to have special support for downcasting to the interface (because // the verifier may not be able to merge two interfaces, but the resulting code would still be valid) if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface && thisType == ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/Object")) { - thisType = LoadClass(cpi.Class); - if(thisType != null) + thisType = cpi.GetClassType(classLoader); + DupHelper dup = new DupHelper(ilGenerator, argcount); + for(int k = 0; k < argcount; k++) { - DupHelper dup = new DupHelper(classLoader, ilGenerator, argcount); - for(int k = 0; k < argcount; k++) - { - dup.SetType(k, ma.GetRawStackType(i, k)); - } - for(int k = 0; k < argcount; k++) - { - dup.Store(k); - } - // TODO this IncompatibleClassChangeError check should also be applied - // for other locations where we can "consume" an object reference in the - // place of an interface reference (putstatic / putfield / arguments for invoke*). - // TODO it turns out that when an interface ref is expected, *any* type will be accepted! - ilGenerator.Emit(OpCodes.Dup); - Label label = ilGenerator.DefineLabel(); - ilGenerator.Emit(OpCodes.Brfalse_S, label); - ilGenerator.Emit(OpCodes.Isinst, thisType.Type); - ilGenerator.Emit(OpCodes.Dup); - ilGenerator.Emit(OpCodes.Brtrue_S, label); - EmitError("java.lang.IncompatibleClassChangeError", null); - ilGenerator.MarkLabel(label); - for(int k = argcount - 1; k >= 0; k--) - { - dup.Load(k); - } + dup.SetType(k, ma.GetRawStackTypeWrapper(i, k)); + } + for(int k = 0; k < argcount; k++) + { + dup.Store(k); + } + // TODO this IncompatibleClassChangeError check should also be applied + // for other locations where we can "consume" an object reference in the + // place of an interface reference (putstatic / putfield / arguments for invoke*). + // TODO it turns out that when an interface ref is expected, *any* type will be accepted! + ilGenerator.Emit(OpCodes.Dup); + Label label = ilGenerator.DefineLabel(); + ilGenerator.Emit(OpCodes.Brfalse_S, label); + ilGenerator.Emit(OpCodes.Isinst, thisType.Type); + ilGenerator.Emit(OpCodes.Dup); + ilGenerator.Emit(OpCodes.Brtrue_S, label); + EmitError("java.lang.IncompatibleClassChangeError", null); + ilGenerator.MarkLabel(label); + for(int k = argcount - 1; k >= 0; k--) + { + dup.Load(k); } } - else if(thisType != null && !thisType.IsSubTypeOf(LoadClass(cpi.Class))) + else if(thisType != null && !thisType.IsSubTypeOf(cpi.GetClassType(classLoader))) { EmitError("java.lang.IncompatibleClassChangeError", null); thisType = null; @@ -1025,7 +995,7 @@ class Compiler { if(cpi.Name == "") { - if(type[0] == 'N') + if(VerifierTypeWrapper.IsNew(type)) { if(thisType != null && (thisType.IsAbstract || thisType.IsInterface)) { @@ -1043,7 +1013,7 @@ class Compiler bool[] localsfix = new bool[m.MaxLocals]; for(int j = 0; j < stackfix.Length; j++) { - if(ma.GetRawStackType(i, argcount + 1 + j) == type) + if(ma.GetRawStackTypeWrapper(i, argcount + 1 + j) == type) { stackfix[j] = true; if(trivcount == j) @@ -1061,7 +1031,7 @@ class Compiler } for(int j = 0; j < localsfix.Length; j++) { - if(ma.GetLocalType(i, j) == type) + if(ma.GetLocalTypeWrapper(i, j) == type) { localsfix[j] = true; nontrivial = true; @@ -1079,19 +1049,11 @@ class Compiler } ilGenerator.Emit(OpCodes.Ldnull); } - // TODO it is probably a better idea to do this in the constructor for each class - // derived from java.lang.Throwable, but if we do move this to the constructor, we - // should still call it here for non-Java exceptions (that aren't derived from Throwable) - Type t = ExpressionType(type.Substring(type.IndexOf(';') + 1)); - if(t == null) + if(java_lang_Throwable == null) { - // If the type couldn't be loaded, we continue we object to make sure - // the code remains verifiable (the ExpressionType call above already generated - // code to throw an exception, but the remaing code still needs to be verifiable, - // even though it is unreachable). - t = typeof(object); + java_lang_Throwable = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/Throwable"); } - if(typeof(Exception).IsAssignableFrom(t)) + if(thisType.IsSubTypeOf(java_lang_Throwable)) { // HACK if the next instruction isn't an athrow, we need to // call fillInStackTrace, because the object might be used @@ -1108,21 +1070,24 @@ class Compiler // this could be done a little more efficiently, but since in practice this // code never runs (for code compiled from Java source) it doesn't // really matter - LocalBuilder newobj = ilGenerator.DeclareLocal(t); + LocalBuilder newobj = ilGenerator.DeclareLocal(thisType.Type); ilGenerator.Emit(OpCodes.Stloc, newobj); LocalBuilder[] tempstack = new LocalBuilder[stackfix.Length]; for(int j = 0; j < stackfix.Length; j++) { if(!stackfix[j]) { - string stacktype = ma.GetRawStackType(i, argcount + 1 + j); + TypeWrapper stacktype = ma.GetRawStackTypeWrapper(i, argcount + 1 + j); // it could be another new object reference (not from current invokespecial // instruction) - if(stacktype[0] != 'N') + if(stacktype == VerifierTypeWrapper.Null) { - // TODO handle Lnull stack entries - // TODO handle class not found - LocalBuilder lb = ilGenerator.DeclareLocal(classLoader.ExpressionType(stacktype)); + // TODO handle null stack entries + throw new NotImplementedException(); + } + else if(!VerifierTypeWrapper.IsNew(stacktype)) + { + LocalBuilder lb = ilGenerator.DeclareLocal(stacktype.TypeOrUnloadableAsObject); ilGenerator.Emit(OpCodes.Stloc, lb); tempstack[j] = lb; } @@ -1201,7 +1166,7 @@ class Compiler { ilGenerator.Emit(OpCodes.Pop); } - EmitPlaceholder(cpi.Signature.Substring(cpi.Signature.LastIndexOf(')') + 1)); + EmitPlaceholder(cpi.GetRetType(classLoader)); } } } @@ -1217,7 +1182,7 @@ class Compiler { ilGenerator.Emit(OpCodes.Pop); } - EmitPlaceholder(cpi.Signature.Substring(cpi.Signature.LastIndexOf(')') + 1)); + EmitPlaceholder(cpi.GetRetType(classLoader)); } } break; @@ -1235,14 +1200,13 @@ class Compiler ReturnCookie rc = new ReturnCookie(); if(instr.NormalizedOpCode != NormalizedByteCode.__return) { - // TODO handle class not found - Type retType = classLoader.RetTypeFromSig(m.Method.Signature); - rc.Local = ilGenerator.DeclareLocal(retType); - // because of the way interface merging works, an object reference is valid + TypeWrapper retTypeWrapper = m.Method.GetRetType(classLoader); + rc.Local = ilGenerator.DeclareLocal(retTypeWrapper.TypeOrUnloadableAsObject); + // because of the way interface merging works, any reference is valid // for any interface reference - if(retType.IsInterface && m.Method.Signature.Substring(m.Method.Signature.LastIndexOf(')') + 1) != ma.GetRawStackType(i, 0)) + if(retTypeWrapper.IsInterface && !ma.GetRawStackTypeWrapper(i, 0).IsAssignableTo(retTypeWrapper)) { - ilGenerator.Emit(OpCodes.Castclass, retType); + ilGenerator.Emit(OpCodes.Castclass, retTypeWrapper.Type); } ilGenerator.Emit(OpCodes.Stloc, rc.Local); } @@ -1266,17 +1230,16 @@ class Compiler } else { - // TODO handle class not found - Type retType = classLoader.RetTypeFromSig(m.Method.Signature); - // because of the way interface merging works, an object reference is valid + TypeWrapper retTypeWrapper = m.Method.GetRetType(classLoader); + // because of the way interface merging works, any reference is valid // for any interface reference - if(retType.IsInterface && m.Method.Signature.Substring(m.Method.Signature.LastIndexOf(')') + 1) != ma.GetRawStackType(i, 0)) + if(retTypeWrapper.IsInterface && !ma.GetRawStackTypeWrapper(i, 0).IsAssignableTo(retTypeWrapper)) { - ilGenerator.Emit(OpCodes.Castclass, retType); + ilGenerator.Emit(OpCodes.Castclass, retTypeWrapper.Type); } if(stackHeight != 1) { - LocalBuilder local = ilGenerator.DeclareLocal(retType); + LocalBuilder local = ilGenerator.DeclareLocal(retTypeWrapper.TypeOrUnloadableAsObject); ilGenerator.Emit(OpCodes.Stloc, local); for(int j = 1; j < stackHeight; j++) { @@ -1291,17 +1254,17 @@ class Compiler } case NormalizedByteCode.__aload: { - string type = ma.GetLocalType(i, instr.NormalizedArg1); - if(type == "Lnull") + TypeWrapper type = ma.GetLocalTypeWrapper(i, instr.NormalizedArg1); + if(type == VerifierTypeWrapper.Null) { // if the local is known to be null, we just emit a null ilGenerator.Emit(OpCodes.Ldnull); } - else if(type[0] == 'N') + else if(VerifierTypeWrapper.IsNew(type)) { // since new objects aren't represented on the stack, we don't need to do anything here } - else if(type[0] == 'U') + else if(type == VerifierTypeWrapper.UninitializedThis) { // any unitialized reference has to be the this reference // TODO when we get support for overwriting the this reference, this code @@ -1315,10 +1278,9 @@ class Compiler { // HACK since, for now, all locals are of type object, we've got to cast them to the proper type // UPDATE the above is no longer true, we now have at least some idea of the type of the local - if(type != ma.GetDeclaredLocalType(instr.NormalizedArg1)) + if(type != ma.GetDeclaredLocalTypeWrapper(instr.NormalizedArg1) && !type.IsUnloadable) { - // TODO handle class not found - ilGenerator.Emit(OpCodes.Castclass, classLoader.ExpressionType(ma.GetLocalType(i, instr.NormalizedArg1))); + ilGenerator.Emit(OpCodes.Castclass, type.Type); } } } @@ -1326,13 +1288,13 @@ class Compiler } case NormalizedByteCode.__astore: { - string type = ma.GetRawStackType(i, 0); + TypeWrapper type = ma.GetRawStackTypeWrapper(i, 0); // HACK we use "int" to track the return address of a jsr - if(type.StartsWith("Lret;")) + if(VerifierTypeWrapper.IsRet(type)) { Store(instr, typeof(int)); } - else if(type[0] == 'N') + else if(VerifierTypeWrapper.IsNew(type)) { // NOTE new objects aren't really on the stack, so we can't copy them into the local. // We do store a null in the local, to prevent it from retaining an unintentional reference @@ -1340,7 +1302,7 @@ class Compiler ilGenerator.Emit(OpCodes.Ldnull); Store(instr, typeof(object)); } - else if(type[0] == 'U') + else if(type == VerifierTypeWrapper.UninitializedThis) { // any unitialized reference, is always the this reference, we don't store anything // here (because CLR wont allow unitialized references in locals) and then when @@ -1379,8 +1341,8 @@ class Compiler break; case NormalizedByteCode.__new: { - TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); - if(wrapper != null && (wrapper.IsAbstract || wrapper.IsInterface)) + TypeWrapper wrapper = instr.MethodCode.Method.ClassFile.GetConstantPoolClassType(instr.Arg1, classLoader); + if(!wrapper.IsUnloadable && (wrapper.IsAbstract || wrapper.IsInterface)) { EmitError("java.lang.InstantiationError", wrapper.Name); } @@ -1389,8 +1351,13 @@ class Compiler } case NormalizedByteCode.__multianewarray: { - TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); - if(wrapper != null) + TypeWrapper wrapper = instr.MethodCode.Method.ClassFile.GetConstantPoolClassType(instr.Arg1, classLoader); + if(wrapper.IsUnloadable) + { + // TODO + throw new NotImplementedException(); + } + else { LocalBuilder localArray = ilGenerator.DeclareLocal(typeof(int[])); LocalBuilder localInt = ilGenerator.DeclareLocal(typeof(int)); @@ -1415,8 +1382,13 @@ class Compiler } case NormalizedByteCode.__anewarray: { - TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); - if(wrapper != null) + TypeWrapper wrapper = instr.MethodCode.Method.ClassFile.GetConstantPoolClassType(instr.Arg1, classLoader); + if(wrapper.IsUnloadable) + { + // TODO + throw new NotImplementedException(); + } + else { ilGenerator.Emit(OpCodes.Newarr, wrapper.Type); } @@ -1455,8 +1427,13 @@ class Compiler break; case NormalizedByteCode.__checkcast: { - TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); - if(wrapper != null) + TypeWrapper wrapper = instr.MethodCode.Method.ClassFile.GetConstantPoolClassType(instr.Arg1, classLoader); + if(wrapper.IsUnloadable) + { + // TODO + throw new NotImplementedException(); + } + else { ilGenerator.Emit(OpCodes.Castclass, wrapper.Type); } @@ -1464,8 +1441,13 @@ class Compiler } case NormalizedByteCode.__instanceof: { - TypeWrapper wrapper = LoadClass(instr.MethodCode.Method.ClassFile.GetConstantPoolClass(instr.Arg1)); - if(wrapper != null) + TypeWrapper wrapper = instr.MethodCode.Method.ClassFile.GetConstantPoolClassType(instr.Arg1, classLoader); + if(wrapper.IsUnloadable) + { + // TODO + throw new NotImplementedException(); + } + else { ilGenerator.Emit(OpCodes.Isinst, wrapper.Type); ilGenerator.Emit(OpCodes.Ldnull); @@ -1818,9 +1800,9 @@ class Compiler ilGenerator.Emit(OpCodes.Shr); break; case NormalizedByteCode.__swap: - new DupHelper(classLoader, ilGenerator, 2) - .SetType(0, ma.GetRawStackType(i, 0)) - .SetType(1, ma.GetRawStackType(i, 1)) + new DupHelper(ilGenerator, 2) + .SetType(0, ma.GetRawStackTypeWrapper(i, 0)) + .SetType(1, ma.GetRawStackTypeWrapper(i, 1)) .Store(0) .Store(1) .Load(0) @@ -1828,23 +1810,23 @@ class Compiler break; case NormalizedByteCode.__dup: // if the TOS contains a "new" object, it isn't really there, so we wont dup it either - if(ma.GetRawStackType(i, 0)[0] != 'N') + if(!VerifierTypeWrapper.IsNew(ma.GetRawStackTypeWrapper(i, 0))) { ilGenerator.Emit(OpCodes.Dup); } break; case NormalizedByteCode.__dup2: { - string type1 = ma.GetRawStackType(i, 0); - if(type1 == "D" || type1 == "J") + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + if(type1 == PrimitiveTypeWrapper.DOUBLE || type1 == PrimitiveTypeWrapper.LONG) { ilGenerator.Emit(OpCodes.Dup); } else { - new DupHelper(classLoader, ilGenerator, 2) + new DupHelper(ilGenerator, 2) .SetType(0, type1) - .SetType(1, ma.GetRawStackType(i, 1)) + .SetType(1, ma.GetRawStackTypeWrapper(i, 1)) .Store(0) .Store(1) .Load(1) @@ -1855,9 +1837,9 @@ class Compiler break; } case NormalizedByteCode.__dup_x1: - new DupHelper(classLoader, ilGenerator, 2) - .SetType(0, ma.GetRawStackType(i, 0)) - .SetType(1, ma.GetRawStackType(i, 1)) + new DupHelper(ilGenerator, 2) + .SetType(0, ma.GetRawStackTypeWrapper(i, 0)) + .SetType(1, ma.GetRawStackTypeWrapper(i, 1)) .Store(0) .Store(1) .Load(0) @@ -1866,12 +1848,12 @@ class Compiler break; case NormalizedByteCode.__dup2_x1: { - string type1 = ma.GetRawStackType(i, 0); - if(type1 == "D" || type1 == "J") + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + if(type1 == PrimitiveTypeWrapper.DOUBLE || type1 == PrimitiveTypeWrapper.LONG) { - new DupHelper(classLoader, ilGenerator, 2) + new DupHelper(ilGenerator, 2) .SetType(0, type1) - .SetType(1, ma.GetRawStackType(i, 1)) + .SetType(1, ma.GetRawStackTypeWrapper(i, 1)) .Store(0) .Store(1) .Load(0) @@ -1880,10 +1862,10 @@ class Compiler } else { - new DupHelper(classLoader, ilGenerator, 3) + new DupHelper(ilGenerator, 3) .SetType(0, type1) - .SetType(1, ma.GetRawStackType(i, 1)) - .SetType(2, ma.GetRawStackType(i, 2)) + .SetType(1, ma.GetRawStackTypeWrapper(i, 1)) + .SetType(2, ma.GetRawStackTypeWrapper(i, 2)) .Store(0) .Store(1) .Store(2) @@ -1897,14 +1879,14 @@ class Compiler } case NormalizedByteCode.__dup2_x2: { - string type1 = ma.GetRawStackType(i, 0); - string type2 = ma.GetRawStackType(i, 1); - if(type1 == "D" || type1 == "J") + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + TypeWrapper type2 = ma.GetRawStackTypeWrapper(i, 1); + if(type1 == PrimitiveTypeWrapper.DOUBLE || type1 == PrimitiveTypeWrapper.LONG) { - if(type2 == "D" || type2 == "J") + if(type2 == PrimitiveTypeWrapper.DOUBLE || type2 == PrimitiveTypeWrapper.LONG) { // Form 4 - new DupHelper(classLoader, ilGenerator, 2) + new DupHelper(ilGenerator, 2) .SetType(0, type1) .SetType(1, type2) .Store(0) @@ -1916,10 +1898,10 @@ class Compiler else { // Form 2 - new DupHelper(classLoader, ilGenerator, 3) + new DupHelper(ilGenerator, 3) .SetType(0, type1) .SetType(1, type2) - .SetType(2, ma.GetRawStackType(i, 2)) + .SetType(2, ma.GetRawStackTypeWrapper(i, 2)) .Store(0) .Store(1) .Store(2) @@ -1931,11 +1913,11 @@ class Compiler } else { - string type3 = ma.GetRawStackType(i, 2); - if(type3 == "D" || type3 == "J") + TypeWrapper type3 = ma.GetRawStackTypeWrapper(i, 2); + if(type3 == PrimitiveTypeWrapper.DOUBLE || type3 == PrimitiveTypeWrapper.LONG) { // Form 3 - new DupHelper(classLoader, ilGenerator, 3) + new DupHelper(ilGenerator, 3) .SetType(0, type1) .SetType(1, type2) .SetType(2, type3) @@ -1951,11 +1933,11 @@ class Compiler else { // Form 1 - new DupHelper(classLoader, ilGenerator, 4) + new DupHelper(ilGenerator, 4) .SetType(0, type1) .SetType(1, type2) .SetType(2, type3) - .SetType(3, ma.GetRawStackType(i, 3)) + .SetType(3, ma.GetRawStackTypeWrapper(i, 3)) .Store(0) .Store(1) .Store(2) @@ -1971,10 +1953,10 @@ class Compiler break; } case NormalizedByteCode.__dup_x2: - new DupHelper(classLoader, ilGenerator, 3) - .SetType(0, ma.GetRawStackType(i, 0)) - .SetType(1, ma.GetRawStackType(i, 1)) - .SetType(2, ma.GetRawStackType(i, 2)) + new DupHelper(ilGenerator, 3) + .SetType(0, ma.GetRawStackTypeWrapper(i, 0)) + .SetType(1, ma.GetRawStackTypeWrapper(i, 1)) + .SetType(2, ma.GetRawStackTypeWrapper(i, 2)) .Store(0) .Store(1) .Store(2) @@ -1985,18 +1967,18 @@ class Compiler break; case NormalizedByteCode.__pop2: { - string type1 = ma.GetRawStackType(i, 0); - if(type1 == "D" || type1 == "J") + TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0); + if(type1 == PrimitiveTypeWrapper.DOUBLE || type1 == PrimitiveTypeWrapper.LONG) { ilGenerator.Emit(OpCodes.Pop); } else { - if(type1[0] != 'N') + if(!VerifierTypeWrapper.IsNew(type1)) { ilGenerator.Emit(OpCodes.Pop); } - if(ma.GetRawStackType(i, 1)[0] != 'N') + if(!VerifierTypeWrapper.IsNew(ma.GetRawStackTypeWrapper(i, 1))) { ilGenerator.Emit(OpCodes.Pop); } @@ -2005,7 +1987,7 @@ class Compiler } case NormalizedByteCode.__pop: // if the TOS is a new object, it isn't really there, so we don't need to pop it - if(ma.GetRawStackType(i, 0)[0] != 'N') + if(!VerifierTypeWrapper.IsNew(ma.GetRawStackTypeWrapper(i, 0))) { ilGenerator.Emit(OpCodes.Pop); } @@ -2088,8 +2070,8 @@ class Compiler { // NOTE using a OpCodes.Switch here is not efficient, because 99 out of a 100 cases // there are either one or two call sites. - string subid = ma.GetLocalType(i, instr.Arg1); - int[] callsites = ma.GetCallSites(int.Parse(subid.Substring("Lret;".Length))); + int subid = ((VerifierTypeWrapper)ma.GetLocalTypeWrapper(i, instr.Arg1)).Index; + int[] callsites = ma.GetCallSites(subid); for(int j = 0; j < callsites.Length - 1; j++) { Load(instr, typeof(int)); @@ -2144,8 +2126,14 @@ class Compiler private FieldWrapper GetField(ClassFile.ConstantPoolItemFieldref cpi, bool isStatic, TypeWrapper thisType, bool write) { - TypeWrapper wrapper = LoadClass(cpi.Class); - if(wrapper != null) + TypeWrapper wrapper = cpi.GetClassType(classLoader); + if(wrapper.IsUnloadable) + { + // TODO instead of this NoClassDefFoundError, we should return a dynamic FieldWrapper that + // dynamically tries to get/set the field + EmitError("java.lang.NoClassDefFoundError", wrapper.Name); + } + else { FieldWrapper field = wrapper.GetFieldWrapper(cpi.Name); if(field != null) @@ -2164,15 +2152,7 @@ class Compiler } else { - // HACK for the time being we handle fields of an unloadable type here - if(field.EmitGet == null && field.EmitSet == null) - { - EmitError("java.lang.NoClassDefFoundError", "Field " + cpi.Class + "." + cpi.Name + " is is of the unloadable type " + cpi.Signature); - } - else - { - return field; - } + return field; } } else @@ -2216,8 +2196,14 @@ class Compiler { // TODO when there is an error resolving a call to the super class constructor (in the constructor of this type), // we cannot use EmitError, because that will yield an invalid constructor (that doesn't call the superclass constructor) - TypeWrapper wrapper = LoadClass(cpi.Class); - if(wrapper != null) + TypeWrapper wrapper = cpi.GetClassType(classLoader); + if(wrapper.IsUnloadable) + { + // TODO instead of this NoClassDefFoundError, we should return a dynamic MethodWrapper that + // dynamically calls the method + EmitError("java.lang.NoClassDefFoundError", wrapper.Name); + } + else { if(wrapper.IsInterface != (invoke == NormalizedByteCode.__invokeinterface)) { @@ -2229,7 +2215,7 @@ class Compiler { wrapper = thisType.BaseTypeWrapper; } - MethodDescriptor md = new MethodDescriptor(classLoader, cpi.Name, cpi.Signature); + MethodDescriptor md = new MethodDescriptor(classLoader, cpi); MethodWrapper method = null; if(invoke == NormalizedByteCode.__invokeinterface) { @@ -2262,15 +2248,7 @@ class Compiler (method.IsPrivate && clazz == method.DeclaringType) || (!(method.IsPublic || method.IsPrivate) && clazz.IsInSamePackageAs(method.DeclaringType))) { - // HACK for the time being we handle methods with an unloadable type in the sig here - if(method.EmitCall == null && method.EmitCallvirt == null && method.EmitNewobj == null) - { - EmitError("java.lang.NoClassDefFoundError", "Method " + cpi.Class + "." + cpi.Name + cpi.Signature + " has an unloadable type in its signature"); - } - else - { - return method; - } + return method; } else { @@ -2279,7 +2257,7 @@ class Compiler // (bug in javac, see http://developer.java.sun.com/developer/bugParade/bugs/4329886.html) if(!method.IsStatic && cpi.Name == "clone" && wrapper.Type == typeof(object) && thisType.Type.IsArray) { - method = thisType.GetMethodWrapper(new MethodDescriptor(classLoader, cpi.Name, cpi.Signature), false); + method = thisType.GetMethodWrapper(new MethodDescriptor(classLoader, cpi), false); if(method != null && method.IsPublic) { return method; @@ -2312,8 +2290,8 @@ class Compiler Console.Error.WriteLine("\tat " + m.Method.ClassFile.Name + "." + m.Method.Name + m.Method.Signature); } ilGenerator.Emit(OpCodes.Ldstr, message); - TypeWrapper type = classLoader.LoadClassByDottedName(errorType); - MethodWrapper method = type.GetMethodWrapper(new MethodDescriptor(classLoader, "", "(Ljava/lang/String;)V"), false); + TypeWrapper type = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedName(errorType); + MethodWrapper method = type.GetMethodWrapper(new MethodDescriptor(type.GetClassLoader(), "", "(Ljava/lang/String;)V"), false); method.EmitNewobj.Emit(ilGenerator); } else @@ -2323,8 +2301,8 @@ class Compiler Console.Error.WriteLine(errorType); Console.Error.WriteLine("\tat " + m.Method.ClassFile.Name + "." + m.Method.Name + m.Method.Signature); } - TypeWrapper type = classLoader.LoadClassByDottedName(errorType); - MethodWrapper method = type.GetMethodWrapper(new MethodDescriptor(classLoader, "", "()V"), false); + TypeWrapper type = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassByDottedName(errorType); + MethodWrapper method = type.GetMethodWrapper(new MethodDescriptor(type.GetClassLoader(), "", "()V"), false); method.EmitNewobj.Emit(ilGenerator); } // we emit a call to ThrowHack instead of a throw instruction, because otherwise the verifier will know @@ -2332,7 +2310,9 @@ class Compiler ilGenerator.Emit(OpCodes.Call, throwHack); } - private TypeWrapper LoadClass(string classname) + // TODO since I no longer use LoadClass, I need to go back to each instruction and add a check to make sure + // the class is accessible from this class + private TypeWrapper unused__LoadClass(string classname) { try { @@ -2353,74 +2333,55 @@ class Compiler } } - private Type ExpressionType(string type) + // TODO this method should have a better name + private TypeWrapper SigTypeToClassName(TypeWrapper type, TypeWrapper nullType) { - try + if(type == VerifierTypeWrapper.UninitializedThis) { - return classLoader.ExpressionType(type); + return clazz; } - catch(Exception) + else if(VerifierTypeWrapper.IsNew(type)) { - // TODO we should freeze the exception here, instead of always throwing a NoClassDefFoundError - EmitError("java.lang.NoClassDefFoundError", type); - return null; + return ((VerifierTypeWrapper)type).UnderlyingType; + } + else if(type == VerifierTypeWrapper.Null) + { + return nullType; + } + else + { + return type; } } - private static string SigTypeToClassName(string type, string nullType) + private void EmitPlaceholder(TypeWrapper type) { - switch(type[0]) + if(type == PrimitiveTypeWrapper.BOOLEAN || + type == PrimitiveTypeWrapper.BYTE || + type == PrimitiveTypeWrapper.SHORT || + type == PrimitiveTypeWrapper.CHAR || + type == PrimitiveTypeWrapper.INT) { - case 'N': - case 'U': - { - string chop = type.Substring(type.IndexOf(';') + 2); - return chop.Substring(0, chop.Length - 1); - } - case 'L': - if(type == "Lnull") - { - return nullType; - } - else - { - return type.Substring(1, type.Length - 2); - } - case '[': - return type; - default: - throw new InvalidOperationException(); + ilGenerator.Emit(OpCodes.Ldc_I4_0); } - } - - private void EmitPlaceholder(string sig) - { - switch(sig[0]) + else if(type == PrimitiveTypeWrapper.LONG) { - case 'L': - case '[': - ilGenerator.Emit(OpCodes.Ldnull); - break; - case 'Z': - case 'B': - case 'S': - case 'C': - case 'I': - ilGenerator.Emit(OpCodes.Ldc_I4_0); - break; - case 'J': - ilGenerator.Emit(OpCodes.Ldc_I8, 0L); - break; - case 'F': - ilGenerator.Emit(OpCodes.Ldc_R4, 0.0f); - break; - case 'D': - ilGenerator.Emit(OpCodes.Ldc_R8, 0.0); - break; - case 'V': - break; - default: - throw new InvalidOperationException(); + ilGenerator.Emit(OpCodes.Ldc_I8, 0L); + } + else if(type == PrimitiveTypeWrapper.FLOAT) + { + ilGenerator.Emit(OpCodes.Ldc_R4, 0.0f); + } + else if(type == PrimitiveTypeWrapper.DOUBLE) + { + ilGenerator.Emit(OpCodes.Ldc_R4, 0.0f); + } + else if(type == PrimitiveTypeWrapper.VOID) + { + } + else + { + ilGenerator.Emit(OpCodes.Ldnull); } } @@ -2499,11 +2460,10 @@ class Compiler else { name = "Obj" + index; - string t = ma.GetDeclaredLocalType(index); - if(t != null && t != "Lnull") + TypeWrapper t = ma.GetDeclaredLocalTypeWrapper(index); + if(!t.IsUnloadable && t != VerifierTypeWrapper.Null) { - // TODO handle class not found - type = classLoader.ExpressionType(t); + type = t.Type; } } LocalBuilder lb = (LocalBuilder)locals[name]; diff --git a/IK.VM.NET/verifier.cs b/IK.VM.NET/verifier.cs index c278d6c9..2f5a5c6b 100644 --- a/IK.VM.NET/verifier.cs +++ b/IK.VM.NET/verifier.cs @@ -90,13 +90,13 @@ class InstructionState { private static ArrayList empty = new ArrayList(); private MethodAnalyzer ma; - private ArrayList stack; // each entry contains a string with a Java signature of the type - private string[] locals; // each entry contains a string with a Java signature of the type + private ArrayList stack; // each entry contains a TypeWrapper object + private TypeWrapper[] locals; // each entry contains a TypeWrapper object private ArrayList subroutines; private int callsites; internal bool changed = true; - private InstructionState(MethodAnalyzer ma, ArrayList stack, string[] locals, ArrayList subroutines, int callsites) + private InstructionState(MethodAnalyzer ma, ArrayList stack, TypeWrapper[] locals, ArrayList subroutines, int callsites) { this.ma = ma; this.stack = stack; @@ -109,18 +109,18 @@ class InstructionState { this.ma = ma; this.stack = new ArrayList(); - this.locals = new string[maxLocals]; + this.locals = new TypeWrapper[maxLocals]; this.subroutines = new ArrayList(); } internal InstructionState Copy() { - return new InstructionState(ma, (ArrayList)stack.Clone(), (string[])locals.Clone(), CopySubroutines(subroutines), callsites); + return new InstructionState(ma, (ArrayList)stack.Clone(), (TypeWrapper[])locals.Clone(), CopySubroutines(subroutines), callsites); } internal InstructionState CopyLocals() { - return new InstructionState(ma, new ArrayList(), (string[])locals.Clone(), new ArrayList(), callsites); + return new InstructionState(ma, new ArrayList(), (TypeWrapper[])locals.Clone(), new ArrayList(), callsites); } private ArrayList CopySubroutines(ArrayList l) @@ -201,14 +201,20 @@ class InstructionState s.changed = s1.changed; for(int i = 0; i < s.stack.Count; i++) { - string type = (string)s.stack[i]; - if(type == (string)s2.stack[i]) + TypeWrapper type = (TypeWrapper)s.stack[i]; + if(type == s2.stack[i]) { // perfect match, nothing to do } - else if(type[0] == 'L' || type[0] == '[' || type[0] == 'U' || type[0] == 'N') + else if(!type.IsPrimitive) { - string baseType = s.FindCommonBaseType(type, (string)s2.stack[i]); + TypeWrapper baseType = s.FindCommonBaseType(type, (TypeWrapper)s2.stack[i]); + if(baseType == VerifierTypeWrapper.Invalid) + { + Console.WriteLine("type = " + type); + Console.WriteLine("s2.stack[i] = " + s2.stack[i]); + throw new InvalidOperationException(); + } if(type != baseType) { s.stack[i] = baseType; @@ -217,13 +223,13 @@ class InstructionState } else { - throw new VerifyError(string.Format("cannot merge {0} and {1}", type, s2.stack[i])); + throw new VerifyError(string.Format("cannot merge {0} and {1}", type.Name, ((TypeWrapper)s2.stack[i]).Name)); } } for(int i = 0; i < s.locals.Length; i++) { - string type = s.locals[i]; - string type2; + TypeWrapper type = s.locals[i]; + TypeWrapper type2; if(locals_modified == null || locals_modified[i]) { type2 = s2.locals[i]; @@ -232,27 +238,11 @@ class InstructionState { type2 = s3.locals[i]; } - if(type == type2) + TypeWrapper baseType = s2.FindCommonBaseType(type, type2); + if(type != baseType) { - // perfect match, nothing to do - } - else if(type != null) - { - if(type[0] == 'L' || type[0] == '[') - { - string baseType = s2.FindCommonBaseType(type, type2); - if(type != baseType) - { - s.locals[i] = baseType; - s.changed = true; - } - } - else - { - // mark the slot as invalid - s.locals[i] = null; - s.changed = true; - } + s.locals[i] = baseType; + s.changed = true; } } s.MergeSubroutineHelper(s2); @@ -309,149 +299,134 @@ class InstructionState throw new VerifyError("inactive subroutine"); } - private bool IsSubType(string subType, string baseType) + internal TypeWrapper FindCommonBaseType(TypeWrapper type1, TypeWrapper type2) { - if(subType == baseType) - { - return true; - } - if(subType.Length == 1 || baseType.Length == 1) - { - // primitives can never be subtypes of another type - return false; - } - if(baseType == "Ljava/lang/Object;") - { - return true; - } - if(baseType[0] == '[') - { - if(subType[0] != '[') - { - return false; - } - int subDepth = 0; - while(subType[subDepth] == '[') - { - subDepth++; - } - int baseDepth = 0; - while(baseType[baseDepth] == '[') - { - baseDepth++; - } - if(baseDepth > subDepth) - { - return false; - } - if(baseDepth < subDepth) - { - return baseType.EndsWith("[Ljava/lang/Object;"); - } - if(subType[subDepth] == baseType[baseDepth]) - { - if(subType[subDepth] != 'L') - { - return baseDepth == subDepth; - } - string baseElemType = baseType.Substring(baseDepth + 1, baseType.Length - baseDepth - 2); - if(baseElemType == "java/lang/Object") - { - return true; - } - if(baseDepth == subDepth) - { - string subElemType = subType.Substring(subDepth + 1, subType.Length - subDepth - 2); - return ma.classLoader.IsSubType(subElemType, baseElemType); - } - } - return false; - } - else if(subType[0] == '[') - { - return false; - } - return ma.classLoader.IsSubType(subType.Substring(1, subType.Length - 2), baseType.Substring(1, baseType.Length - 2)); - } - - internal string FindCommonBaseType(string type1, string type2) - { -// Console.WriteLine("FindCommonBaseType: {0} v {1}", type1, type2); - if(type1 == "Lnull") - { - return type2; - } - if(type2 == "Lnull") - { - return type1; - } if(type1 == type2) { return type1; } - if(type1 == null || type2 == null) + if(type1 == VerifierTypeWrapper.Null) { - return null; + return type2; } - if(type1.Length == 1 || type2.Length == 1) + if(type2 == VerifierTypeWrapper.Null) { - return null; + return type1; } - if(type1[0] == '[' || type2[0] == '[') + if(type1 == VerifierTypeWrapper.Invalid || type2 == VerifierTypeWrapper.Invalid) { - int rank1 = 0; - while(type1[rank1] == '[') + return VerifierTypeWrapper.Invalid; + } + 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.IsRet(type1) || VerifierTypeWrapper.IsRet(type2)) + { + return VerifierTypeWrapper.Invalid; + } + 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) { - rank1++; + elem1 = elem1.ElementTypeWrapper; + elem2 = elem2.ElementTypeWrapper; + rank++; + rank1--; + rank2--; } - int rank2 = 0; - while(type2[rank2] == '[') + TypeWrapper baseType = FindCommonBaseTypeHelper(elem1, elem2); + if(baseType == VerifierTypeWrapper.Invalid) { - rank2++; - } - if(rank1 == 0 || rank2 == 0) - { - return "Ljava/lang/Object;"; - } - if(rank1 != rank2) - { - if(rank1 > rank2) + // TODO cache java.lang.Object type somewhere + baseType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/Object"); + rank--; + if(rank == 0) { - int temp = rank1; - rank1 = rank2; - rank2 = temp; - string temps = type1; - type1 = type2; - type2 = temps; + return baseType; } - if(type1.EndsWith("Ljava/lang/Object;")) - { - return type1; - } - return "Ljava/lang/Object;"; - } - else - { - if(type1[rank1] != type2[rank1]) - { - // two different primitive arrays, or a primitive and a reference array - return "Ljava/lang/Object;"; - } - return new String('[', rank1) + "L" + ma.classLoader.FindCommonBaseType(type1.Substring(1 + rank1, type1.Length - (2 + rank1)), type2.Substring(1 + rank2, type2.Length - (2 + rank2))) + ";"; } + // HACK load the array type + return baseType.GetClassLoader().LoadClassBySlashedName(new String('[', rank) + "L" + baseType.Name + ";"); } - if(type1.StartsWith("Lret;") || type2.StartsWith("Lret;")) - { - return null; - } - return "L" + ma.classLoader.FindCommonBaseType(type1.Substring(1, type1.Length - 2), type2.Substring(1, type2.Length - 2)) + ";"; + return FindCommonBaseTypeHelper(type1, type2); } - private void SetLocal1(int index, string type) + private TypeWrapper FindCommonBaseTypeHelper(TypeWrapper t1, TypeWrapper t2) { - if(index > 0 && (locals[index] == "D2" || locals[index] == "J2")) + if(t1 == t2) { - locals[index - 1] = null; + return t1; + } + if(t1.IsInterface || t2.IsInterface) + { + // TODO I don't know how finding the common base for interfaces is defined, but + // for now I'm just doing the naive thing + // UPDATE 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. + // So strictly speaking, the code below isn't correct, but it works, so for now it stays in. + if(t1.ImplementsInterface(t2)) + { + return t2; + } + if(t2.ImplementsInterface(t1)) + { + return t1; + } + // TODO cache java.lang.Object type somewhere + return ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/Object"); + } + Stack st1 = new Stack(); + Stack st2 = new Stack(); + 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 ? (TypeWrapper)st1.Pop() : null; + t2 = st2.Count > 0 ? (TypeWrapper)st2.Pop() : null; + if(t1 != t2) + { + return type; + } + type = t1; + } + } + + private void SetLocal1(int index, TypeWrapper type) + { + if(index > 0 && (locals[index - 1] == PrimitiveTypeWrapper.DOUBLE || locals[index - 1] == PrimitiveTypeWrapper.LONG)) + { + locals[index - 1] = VerifierTypeWrapper.Invalid; + foreach(Subroutine s in subroutines) + { + s.SetLocalModified(index - 1); + } } locals[index] = type; foreach(Subroutine s in subroutines) @@ -460,14 +435,18 @@ class InstructionState } } - private void SetLocal2(int index, string type) + private void SetLocal2(int index, TypeWrapper type) { - if(index > 0 && (locals[index] == "D2" || locals[index] == "J2")) + if(index > 0 && (locals[index - 1] == PrimitiveTypeWrapper.DOUBLE || locals[index - 1] == PrimitiveTypeWrapper.LONG)) { - locals[index - 1] = null; + locals[index - 1] = VerifierTypeWrapper.Invalid; + foreach(Subroutine s in subroutines) + { + s.SetLocalModified(index - 1); + } } locals[index] = type; - locals[index + 1] = type[0] == 'D' ? "D2" : "J2"; + locals[index + 1] = VerifierTypeWrapper.Invalid; foreach(Subroutine s in subroutines) { s.SetLocalModified(index); @@ -477,7 +456,7 @@ class InstructionState internal void GetLocalInt(int index) { - if(locals[index] != "I") + if(GetLocalType(index) != PrimitiveTypeWrapper.INT) { throw new VerifyError("Invalid local type"); } @@ -485,25 +464,25 @@ class InstructionState internal void SetLocalInt(int index) { - SetLocal1(index, "I"); - } - - internal void SetLocalLong(int index) - { - SetLocal2(index, "J"); + SetLocal1(index, PrimitiveTypeWrapper.INT); } internal void GetLocalLong(int index) { - if(locals[index] != "J" || locals[index + 1] != "J2") + if(GetLocalType(index) != PrimitiveTypeWrapper.LONG) { throw new VerifyError("incorrect local type, not long"); } } + internal void SetLocalLong(int index) + { + SetLocal2(index, PrimitiveTypeWrapper.LONG); + } + internal void GetLocalFloat(int index) { - if(locals[index] != "F") + if(GetLocalType(index) != PrimitiveTypeWrapper.FLOAT) { throw new VerifyError("incorrect local type, not float"); } @@ -511,241 +490,197 @@ class InstructionState internal void SetLocalFloat(int index) { - SetLocal1(index, "F"); - } - - internal void SetLocalDouble(int index) - { - SetLocal2(index, "D"); + SetLocal1(index, PrimitiveTypeWrapper.FLOAT); } internal void GetLocalDouble(int index) { - if(locals[index] != "D" || locals[index + 1] != "D2") + if(GetLocalType(index) != PrimitiveTypeWrapper.DOUBLE) { throw new VerifyError("incorrect local type, not double"); } } - internal string GetLocalType(int index) + internal void SetLocalDouble(int index) + { + SetLocal2(index, PrimitiveTypeWrapper.DOUBLE); + } + + internal TypeWrapper GetLocalType(int index) { return locals[index]; } internal int GetLocalRet(int index) { - string type = locals[index]; - if(!type.StartsWith("Lret;")) + TypeWrapper type = GetLocalType(index); + if(VerifierTypeWrapper.IsRet(type)) { - throw new VerifyError("incorrect local type, not ret"); + return ((VerifierTypeWrapper)type).Index; } - return int.Parse(type.Substring(5)); + throw new VerifyError("incorrect local type, not ret"); } - internal string GetLocalObject(int index) + internal void SetLocalType(int index, TypeWrapper type) { - string s = locals[index]; - if(s == null || (s[0] != 'L' && s[0] != '[' && s[0] != 'U' && s[0] != 'N') || s.StartsWith("Lret;")) + if(type == PrimitiveTypeWrapper.DOUBLE || type == PrimitiveTypeWrapper.LONG) { - throw new VerifyError("incorrect local type, not object"); + SetLocal2(index, type); + } + else + { + SetLocal1(index, type); } - return s; } - internal void SetLocalObject(int index, string type) + internal void PushType(TypeWrapper type) { - if(type[0] != 'L' && type[0] != '[' && type[0] != 'U' && type[0] != 'N') + if(type.IsPrimitive) { - throw new VerifyError("SetLocalObject"); - } - SetLocal1(index, type); - } - - internal void Push(string type) - { - if(type.Length == 1) - { - switch(type[0]) + if(type == PrimitiveTypeWrapper.BOOLEAN || + type == PrimitiveTypeWrapper.BYTE || + type == PrimitiveTypeWrapper.CHAR || + type == PrimitiveTypeWrapper.SHORT) { - case 'Z': - case 'B': - case 'C': - case 'S': - type = "I"; - break; + type = PrimitiveTypeWrapper.INT; } } PushHelper(type); } - internal void PushObject(string type) - { - if(type == null) - { - throw new VerifyError("PushObject null"); - } - if(type[0] == 'L' || type[0] == '[' || type[0] == 'U' || type[0] == 'N') - { - PushHelper(type); - return; - } - throw new VerifyError("PushObject not object"); - } - internal void PushInt() { - PushHelper("I"); + PushHelper(PrimitiveTypeWrapper.INT); } internal void PushLong() { - PushHelper("J"); + PushHelper(PrimitiveTypeWrapper.LONG); } internal void PushFloat() { - PushHelper("F"); + PushHelper(PrimitiveTypeWrapper.FLOAT); } internal void PushDouble() { - PushHelper("D"); + PushHelper(PrimitiveTypeWrapper.DOUBLE); } internal void PopInt() { - Pop('I'); + if(PopAnyType() != PrimitiveTypeWrapper.INT) + { + throw new VerifyError("Int expected on stack"); + } } internal void PopFloat() { - Pop('F'); + if(PopAnyType() != PrimitiveTypeWrapper.FLOAT) + { + throw new VerifyError("Float expected on stack"); + } } internal void PopDouble() { - Pop('D'); + if(PopAnyType() != PrimitiveTypeWrapper.DOUBLE) + { + throw new VerifyError("Double expected on stack"); + } } internal void PopLong() { - Pop('J'); - } - - internal string PopArray() - { - string s = PopHelper(); - if(s[0] == '[' || s == "Lnull") + if(PopAnyType() != PrimitiveTypeWrapper.LONG) { - return s; - } - throw new VerifyError("Array type expected"); - } - - internal string PopUninitializedObject(string type) - { - string s = PopHelper(); - string u = s; - if(s[0] != 'U' && s[0] != 'N') - { - throw new VerifyError("Expecting to find unitialized object on stack"); - } - s = s.Substring(s.IndexOf(';') + 1); - if(s != type) - { - if(IsSubType(s, type)) - { - // OK - } - else - { - throw new VerifyError(string.Format("popped {0} and expected {1}", s, type)); - } - } - return u; - } - - internal void Pop(string type) - { - if(type.Length == 1) - { - switch(type[0]) - { - case 'Z': - case 'B': - case 'C': - case 'S': - Pop('I'); - return; - } - } - string s = PopHelper(); - if(s != type) - { - if((type[0] == 'L' || type[0] == '[') && s == "Lnull") - { - } - else if(IsSubType(s, type)) - { - } - else - { - throw new VerifyError(string.Format("popped {0} and expected {1}", s, type)); - } + throw new VerifyError("Long expected on stack"); } } - internal string PopObject(string type) + internal TypeWrapper PopArrayType() { - string s = PopHelper(); - if(s != type) + TypeWrapper type = PopAnyType(); + if(type != VerifierTypeWrapper.Null && type.ArrayRank == 0) { - if(s == "Lnull") - { - // null can be used as any type - } - else if(s[0] == 'N' || s[0] == 'U') - { - throw new VerifyError("Unexpected unitialized objref " + s); - } - else if(IsSubType(s, type)) - { - // OK - } - else - { - throw new VerifyError(string.Format("popped {0} and expected {1}", s, type)); - } + throw new VerifyError("Array reference expected on stack"); } + return type; + } + + // null or an initialized object reference (or a subroutine return address) + 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, any reference will be accepted + if(!baseType.IsInterface && !type.IsAssignableTo(baseType)) + { + throw new VerifyError("Unexpected type " + type + " where " + baseType + " was expected"); + } + return type; + } + + internal TypeWrapper PopAnyType() + { + if(stack.Count == 0) + { + throw new VerifyError("Unable to pop operand off an empty stack"); + } + TypeWrapper s = (TypeWrapper)stack[stack.Count - 1]; + stack.RemoveAt(stack.Count - 1); return s; } - internal string PopAny() + // NOTE this can *not* be used to pop double or long + internal TypeWrapper PopType() { - return PopHelper(); - } - - internal string Pop() - { - string type = PopHelper(); - if(type == "D" || type == "J") + TypeWrapper type = PopAnyType(); + if(type == PrimitiveTypeWrapper.DOUBLE || type == PrimitiveTypeWrapper.LONG) { throw new VerifyError("Attempt to split long or double on the stack"); } return type; } - internal string Pop2() + // 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) { - string type = PopHelper(); - if(type == "D" || type == "J") + if(baseType.IsPrimitive) { - return type; + if(baseType == PrimitiveTypeWrapper.BOOLEAN || + baseType == PrimitiveTypeWrapper.BYTE || + baseType == PrimitiveTypeWrapper.CHAR || + baseType == PrimitiveTypeWrapper.SHORT) + { + baseType = PrimitiveTypeWrapper.INT; + } } - type = PopHelper(); - if(type == "D" || type == "J") + TypeWrapper type = PopAnyType(); + if(type != baseType && !type.IsAssignableTo(baseType)) { - throw new VerifyError("Attempt to split long or double on the stack"); + // HACK because of the way interfaces references works, if baseType + // is an interface, any reference will be accepted + if(baseType.IsInterface && !type.IsPrimitive) + { + return type; + } + throw new VerifyError("Unexpected type " + type.Name + " where " + baseType.Name + " was expected"); } return type; } @@ -755,70 +690,22 @@ class InstructionState return stack.Count; } - internal string GetStackSlot(int pos) + internal TypeWrapper GetStackSlot(int pos) { - return (string)stack[stack.Count - 1 - pos]; + return (TypeWrapper)stack[stack.Count - 1 - pos]; } - internal string Peek() + private void PushHelper(TypeWrapper type) { - if(stack.Count == 0) + stack.Add(type); + } + + internal void MarkInitialized(TypeWrapper type, TypeWrapper initType) + { + if(type == null || initType == null) { - // return null, if the stack is empty - return null; + throw new InvalidOperationException(); } - return (string)stack[stack.Count - 1]; - } - - private string PopHelper() - { - if(stack.Count == 0) - { - throw new VerifyError("Unable to pop operand off an empty stack"); - } - string s = (string)stack[stack.Count - 1]; - stack.RemoveAt(stack.Count - 1); - return s; - } - - private void PushHelper(string s) - { - if(s.IndexOf("L[") >= 0) - { - throw new VerifyError("Internal error: L[ type found"); - } - stack.Add(s); - } - - private string Pop(char type) - { - string s = PopHelper(); - if(s[0] != type) - { - switch(type) - { - case 'I': - throw new VerifyError("Expecting to find int on stack"); - case '[': - throw new VerifyError("Expecting to find array on stack"); - case 'L': - throw new VerifyError("Expecting to find object on stack"); - case 'F': - throw new VerifyError("Expecting to find float on stack"); - case 'D': - throw new VerifyError("Expecting to find double on stack"); - case 'J': - throw new VerifyError("Expecting to find long on stack"); - default: - throw new VerifyError("Expecting to find " + type + " on stack"); - } - } - return s; - } - - internal void MarkInitialized(string type) - { - string initType = type.Substring(type.IndexOf(';') + 1); for(int i = 0; i < locals.Length; i++) { if(locals[i] == type) @@ -828,7 +715,7 @@ class InstructionState } for(int i = 0; i < stack.Count; i++) { - if((string)stack[i] == type) + if(stack[i] == type) { stack[i] = initType; } @@ -882,147 +769,172 @@ class InstructionState { for(int i = 0; i < locals.Length; i++) { - if(locals[i] != null && (((locals[i])[0] == 'U') || ((locals[i])[0] == 'N'))) + TypeWrapper type = locals[i]; + if(type == VerifierTypeWrapper.UninitializedThis || VerifierTypeWrapper.IsNew(type)) { throw new VerifyError("uninitialized object ref in local (2)"); } } for(int i = 0; i < stack.Count; i++) { - if((((string)stack[i])[0] == 'U') || (((string)stack[i])[0] == 'N')) + TypeWrapper type = (TypeWrapper)stack[i]; + if(type == VerifierTypeWrapper.UninitializedThis || VerifierTypeWrapper.IsNew(type)) { throw new VerifyError("uninitialized object ref on stack"); } } } - - // this method ensures that no uninitialized objects, of the specified type are in the current state - internal void CheckUninitializedObjRefs(string type) - { - for(int i = 0; i < locals.Length; i++) - { - if(locals[i] == type) - { - throw new VerifyError("unininitialized " + type + " in locals"); - } - } - for(int i = 0; i < stack.Count; i++) - { - if((string)stack[i] == type) - { - throw new VerifyError("uninitialized " + type + " on stack"); - } - } - } } -class SigEnumerator +// this is a container for the special verifier TypeWrappers +class VerifierTypeWrapper : TypeWrapper { - private string sig; - private int pos; - private int length; + internal static readonly TypeWrapper Invalid = null; + internal static readonly TypeWrapper Null = new VerifierTypeWrapper("null", 0, null); + internal static readonly TypeWrapper UninitializedThis = new VerifierTypeWrapper("this", 0, null); - internal SigEnumerator(string sig) + private int index; + private TypeWrapper underlyingType; + + public override string ToString() { - this.sig = sig; - pos = 1; - length = 0; + return GetType().Name + "[" + Name + "," + index + "," + underlyingType + "]"; } - internal bool MoveNext() + internal static TypeWrapper MakeNew(TypeWrapper type, int bytecodeIndex) { - pos += length; - switch(sig[pos]) - { - case 'L': - { - length = sig.IndexOf(';', pos) - pos + 1; - break; - } - case '[': - { - length = 0; - while(sig[pos + length] == '[') length++; - if(sig[pos + length] == 'L') - { - length = sig.IndexOf(';', pos) - pos; - } - length++; - break; - } - case ')': - length = 0; - return false; - default: - length = 1; - break; - } - return true; + return new VerifierTypeWrapper("new", bytecodeIndex, type); } - internal string Current + internal static TypeWrapper MakeRet(int bytecodeIndex) + { + return new VerifierTypeWrapper("ret", bytecodeIndex, null); + } + + internal static bool IsNew(TypeWrapper w) + { + return w != null && w.IsVerifierType && w.Name == "new"; + } + + internal static bool IsRet(TypeWrapper w) + { + return w != null && w.IsVerifierType && w.Name == "ret"; + } + + internal int Index { get { - return sig.Substring(pos, length); + return index; } } -} -class ReverseSigEnumerator -{ - private string[] items; - private int pos; - - internal ReverseSigEnumerator(string sig) - { - ArrayList ar = new ArrayList(); - SigEnumerator se = new SigEnumerator(sig); - while(se.MoveNext()) - { - ar.Add(se.Current); - } - items = new String[ar.Count]; - ar.CopyTo(items); - pos = items.Length; - } - - internal bool MoveNext() - { - pos--; - return pos >= 0; - } - - internal string Current + internal TypeWrapper UnderlyingType { get { - return items[pos]; + return underlyingType; } } + + private VerifierTypeWrapper(string name, int index, TypeWrapper underlyingType) + : base(Modifiers.Final | Modifiers.Interface, name, null, null) + { + this.index = index; + this.underlyingType = underlyingType; + } + + protected override FieldWrapper GetFieldImpl(string fieldName) + { + throw new InvalidOperationException("GetFieldImpl called on " + this); + } + + protected override MethodWrapper GetMethodImpl(MethodDescriptor md) + { + throw new InvalidOperationException("GetMethodImpl called on " + this); + } + + public override Type Type + { + get + { + throw new InvalidOperationException("get_Type called on " + this); + } + } + + public override bool IsInterface + { + get + { + throw new InvalidOperationException("get_IsInterface called on " + this); + } + } + + public override TypeWrapper[] Interfaces + { + get + { + throw new InvalidOperationException("get_Interfaces called on " + this); + } + } + + public override void Finish() + { + throw new InvalidOperationException("Finish called on " + this); + } } class MethodAnalyzer { + private static TypeWrapper java_lang_Throwable; + private static TypeWrapper java_lang_String; + private static TypeWrapper ByteArrayType; + private static TypeWrapper BooleanArrayType; + private static TypeWrapper ShortArrayType; + private static TypeWrapper CharArrayType; + private static TypeWrapper IntArrayType; + private static TypeWrapper FloatArrayType; + private static TypeWrapper DoubleArrayType; + private static TypeWrapper LongArrayType; internal readonly ClassLoaderWrapper classLoader; private ClassFile.Method.Code method; private InstructionState[] state; private ArrayList[] callsites; - private string[] localTypes; + private TypeWrapper[] localTypes; private bool[] aload_used; - internal MethodAnalyzer(ClassFile.Method.Code method, ClassLoaderWrapper classLoader) + internal MethodAnalyzer(TypeWrapper wrapper, ClassFile.Method.Code method, ClassLoaderWrapper classLoader) { this.classLoader = classLoader; this.method = method; + lock(GetType()) + { + if(java_lang_Throwable == null) + { + java_lang_Throwable = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/Throwable"); + java_lang_String = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("java/lang/String"); + ByteArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[B"); + BooleanArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[Z"); + ShortArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[S"); + CharArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[C"); + IntArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[I"); + FloatArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[F"); + DoubleArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[D"); + LongArrayType = ClassLoaderWrapper.GetBootstrapClassLoader().LoadClassBySlashedName("[J"); + } + } state = new InstructionState[method.Instructions.Length]; callsites = new ArrayList[method.Instructions.Length]; - localTypes = new String[method.MaxLocals]; + localTypes = new TypeWrapper[method.MaxLocals]; // HACK aload_used is used to track whether aload is ever used on a particular local (a very lame way of // trying to determine if a local that contains an exception, is ever used) // TODO we really need real liveness analyses for the locals aload_used = new Boolean[method.MaxLocals]; + // HACK because types have to have identity, the subroutine return address and new types are cached here + Hashtable returnAddressTypes = new Hashtable(); + Hashtable newTypes = new Hashtable(); + // start by computing the initial state, the stack is empty and the locals contain the arguments state[0] = new InstructionState(this, method.MaxLocals); int arg = 0; @@ -1031,56 +943,33 @@ class MethodAnalyzer // this reference. If we're a constructor, the this reference is uninitialized. if(method.Method.Name == "") { - state[0].SetLocalObject(arg++, "U0;L" + method.Method.ClassFile.Name + ";"); + state[0].SetLocalType(arg++, VerifierTypeWrapper.UninitializedThis); } else { - state[0].SetLocalObject(arg++, "L" + method.Method.ClassFile.Name + ";"); + state[0].SetLocalType(arg++, wrapper); } } - string sig = method.Method.Signature; - for(int i = 1; sig[i] != ')'; i++) + // HACK articial scope to make "args" name reusable + if(true) { - switch(sig[i]) + TypeWrapper[] args = method.Method.GetArgTypes(classLoader); + for(int i = 0; i < args.Length; i++) { - case 'D': - state[0].SetLocalDouble(arg); - arg += 2; - break; - case 'J': - state[0].SetLocalLong(arg); - arg += 2; - break; - case 'L': + TypeWrapper type = args[i]; + if(type.IsPrimitive && + (type == PrimitiveTypeWrapper.BOOLEAN || + type == PrimitiveTypeWrapper.BYTE || + type == PrimitiveTypeWrapper.CHAR || + type == PrimitiveTypeWrapper.SHORT)) { - int pos = sig.IndexOf(';', i); - state[0].SetLocalObject(arg++, sig.Substring(i, pos - i + 1)); - i = pos; - break; + type = PrimitiveTypeWrapper.INT; } - case '[': + state[0].SetLocalType(arg++, type); + if(type == PrimitiveTypeWrapper.DOUBLE || type == PrimitiveTypeWrapper.LONG) { - int start = i; - while(sig[i] == '[') i++; - if(sig[i] == 'L') - { - i = sig.IndexOf(';', i); - } - state[0].SetLocalObject(arg++, sig.Substring(start, i - start + 1)); - break; + arg++; } - case 'F': - state[0].SetLocalFloat(arg++); - break; - case 'Z': - case 'B': - case 'S': - case 'C': - case 'I': - state[0].SetLocalInt(arg++); - break; - default: - throw new NotImplementedException(); } } bool done = false; @@ -1109,11 +998,13 @@ class MethodAnalyzer int catch_type = method.ExceptionTable[j].catch_type; if(catch_type == 0) { - ex.PushObject("Ljava/lang/Throwable;"); + ex.PushType(java_lang_Throwable); } else { - ex.PushObject("L" + GetConstantPoolClass(catch_type) + ";"); + // 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)); } int idx = method.PcIndexMap[method.ExceptionTable[j].handler_pc]; state[idx] += ex; @@ -1124,57 +1015,64 @@ class MethodAnalyzer switch(instr.NormalizedOpCode) { case NormalizedByteCode.__aload: + { aload_used[instr.NormalizedArg1] = true; - s.PushObject(s.GetLocalObject(instr.NormalizedArg1)); + TypeWrapper type = s.GetLocalType(instr.NormalizedArg1); + if(type.IsPrimitive || type == VerifierTypeWrapper.Invalid) + { + throw new VerifyError("Object reference expected"); + } + s.PushType(type); break; + } case NormalizedByteCode.__astore: { - string type = s.Pop(); - switch(type[0]) + // NOTE since the reference can be uninitialized, we cannot use PopObjectType + TypeWrapper type = s.PopType(); + if(type.IsPrimitive) { - case 'L': - case '[': - case 'N': - case 'U': - s.SetLocalObject(instr.NormalizedArg1, type); - break; - default: - throw new VerifyError("Object reference expected"); + throw new VerifyError("Object reference expected"); } + s.SetLocalType(instr.NormalizedArg1, type); break; } case NormalizedByteCode.__aconst_null: - s.PushObject("Lnull"); + s.PushType(VerifierTypeWrapper.Null); break; case NormalizedByteCode.__aaload: { s.PopInt(); - string type = s.PopArray(); - if(type == "Lnull") + 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.PushObject(type); + s.PushType(VerifierTypeWrapper.Null); } else { - s.PushObject(type.Substring(1)); + type = type.ElementTypeWrapper; + if(type.IsPrimitive) + { + throw new VerifyError("Object array expected"); + } + s.PushType(type); } break; } case NormalizedByteCode.__aastore: - { - string elem = s.PopObject("Ljava/lang/Object;"); + s.PopObjectType(); s.PopInt(); - string type = s.PopArray(); + s.PopArrayType(); // TODO check that elem is assignable to the array break; - } case NormalizedByteCode.__baload: { s.PopInt(); - string type = s.PopArray(); - if(type[1] != 'B' && type[1] != 'Z' && type != "Lnull") + TypeWrapper type = s.PopArrayType(); + if(type != VerifierTypeWrapper.Null && + type != ByteArrayType && + type != BooleanArrayType) { throw new VerifyError(); } @@ -1185,8 +1083,10 @@ class MethodAnalyzer { s.PopInt(); s.PopInt(); - string type = s.PopArray(); - if(type[1] != 'B' && type[1] != 'Z' && type != "Lnull") + TypeWrapper type = s.PopArrayType(); + if(type != VerifierTypeWrapper.Null && + type != ByteArrayType && + type != BooleanArrayType) { throw new VerifyError(); } @@ -1194,66 +1094,66 @@ class MethodAnalyzer } case NormalizedByteCode.__caload: s.PopInt(); - s.PopObject("[C"); + s.PopObjectType(CharArrayType); s.PushInt(); break; case NormalizedByteCode.__castore: s.PopInt(); s.PopInt(); - s.PopObject("[C"); + s.PopObjectType(CharArrayType); break; case NormalizedByteCode.__saload: s.PopInt(); - s.PopObject("[S"); + s.PopObjectType(ShortArrayType); s.PushInt(); break; case NormalizedByteCode.__sastore: s.PopInt(); s.PopInt(); - s.PopObject("[S"); + s.PopObjectType(ShortArrayType); break; case NormalizedByteCode.__iaload: s.PopInt(); - s.PopObject("[I"); + s.PopObjectType(IntArrayType); s.PushInt(); break; case NormalizedByteCode.__iastore: s.PopInt(); s.PopInt(); - s.PopObject("[I"); + s.PopObjectType(IntArrayType); break; case NormalizedByteCode.__laload: s.PopInt(); - s.PopObject("[J"); + s.PopObjectType(LongArrayType); s.PushLong(); break; case NormalizedByteCode.__lastore: s.PopLong(); s.PopInt(); - s.PopObject("[J"); + s.PopObjectType(LongArrayType); break; case NormalizedByteCode.__daload: s.PopInt(); - s.PopObject("[D"); + s.PopObjectType(DoubleArrayType); s.PushDouble(); break; case NormalizedByteCode.__dastore: s.PopDouble(); s.PopInt(); - s.PopObject("[D"); + s.PopObjectType(DoubleArrayType); break; case NormalizedByteCode.__faload: s.PopInt(); - s.PopObject("[F"); + s.PopObjectType(FloatArrayType); s.PushFloat(); break; case NormalizedByteCode.__fastore: s.PopFloat(); s.PopInt(); - s.PopObject("[F"); + s.PopObjectType(FloatArrayType); break; case NormalizedByteCode.__arraylength: - s.PopArray(); + s.PopArrayType(); s.PushInt(); break; case NormalizedByteCode.__iconst: @@ -1279,54 +1179,28 @@ class MethodAnalyzer case NormalizedByteCode.__ifnonnull: case NormalizedByteCode.__ifnull: // TODO it might be legal to use an unitialized ref here - s.PopObject("Ljava/lang/Object;"); + s.PopObjectType(); break; case NormalizedByteCode.__if_acmpeq: case NormalizedByteCode.__if_acmpne: // TODO it might be legal to use an unitialized ref here - s.PopObject("Ljava/lang/Object;"); - s.PopObject("Ljava/lang/Object;"); + s.PopObjectType(); + s.PopObjectType(); break; case NormalizedByteCode.__getstatic: - s.Push((GetFieldref(instr.Arg1)).Signature); + s.PushType((GetFieldref(instr.Arg1)).GetFieldType(classLoader)); break; case NormalizedByteCode.__putstatic: - { - // HACK because of the way interface merging works, if the - // type on the stack is Object, it can be assigned to anything - // (the compiler will emit a cast) - string type = (GetFieldref(instr.Arg1)).Signature; - if(type[0] == 'L' && s.Peek() == "Ljava/lang/Object;") - { - s.Pop(); - } - else - { - s.Pop(type); - } + s.PopType(GetFieldref(instr.Arg1).GetFieldType(classLoader)); break; - } case NormalizedByteCode.__getfield: - s.PopObject("L" + (GetFieldref(instr.Arg1)).Class + ";"); - s.Push((GetFieldref(instr.Arg1)).Signature); + s.PopObjectType(GetFieldref(instr.Arg1).GetClassType(classLoader)); + s.PushType(GetFieldref(instr.Arg1).GetFieldType(classLoader)); break; case NormalizedByteCode.__putfield: - { - // HACK because of the way interface merging works, if the - // type on the stack is Object, it can be assigned to anything - // (the compiler will emit a cast) - string type = (GetFieldref(instr.Arg1)).Signature; - if(type[0] == 'L' && s.Peek() == "Ljava/lang/Object;") - { - s.Pop(); - } - else - { - s.Pop(type); - } - s.PopObject("L" + (GetFieldref(instr.Arg1)).Class + ";"); + s.PopType(GetFieldref(instr.Arg1).GetFieldType(classLoader)); + s.PopObjectType(GetFieldref(instr.Arg1).GetClassType(classLoader)); break; - } case NormalizedByteCode.__ldc: { object o = GetConstantPoolConstant(instr.Arg1); @@ -1336,7 +1210,7 @@ class MethodAnalyzer } else if(o is string) { - s.PushObject("Ljava/lang/String;"); + s.PushType(java_lang_String); } else if(o is long) { @@ -1374,48 +1248,10 @@ class MethodAnalyzer { throw new VerifyError("Illegal call to internal method"); } - ReverseSigEnumerator rse = new ReverseSigEnumerator(cpi.Signature); - while(rse.MoveNext()) + TypeWrapper[] args = cpi.GetArgTypes(classLoader); + for(int j = args.Length - 1; j >= 0; j--) { - switch(rse.Current[0]) - { - case 'Z': - case 'B': - case 'S': - case 'C': - case 'I': - s.PopInt(); - break; - case 'J': - s.PopLong(); - break; - case 'D': - s.PopDouble(); - break; - case 'F': - s.PopFloat(); - break; - case 'L': - { - // HACK if the return type is an interface, any object is legal - if(classLoader.RetTypeFromSig("()" + rse.Current).IsInterface) - { - // TODO implement support in the compiler for this condition (the code that - // is currently generated works, but isn't verifiable) - s.PopObject("Ljava/lang/Object;"); - } - else - { - s.PopObject(rse.Current); - } - break; - } - case '[': - s.PopObject(rse.Current); - break; - default: - throw new NotImplementedException(rse.Current); - } + s.PopType(args[j]); } if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface) { @@ -1424,11 +1260,13 @@ class MethodAnalyzer } if(instr.NormalizedOpCode != NormalizedByteCode.__invokestatic) { + // TODO we should verify that in a constructor, the base class constructor is actually called if(cpi.Name == "") { - string type = s.PopUninitializedObject("L" + cpi.Class + ";"); - if((type[0] == 'N' && !type.EndsWith("L" + cpi.Class + ";")) || - (type[0] == 'U' && cpi.Class != method.Method.ClassFile.SuperClass && cpi.Class != method.Method.ClassFile.Name)) + TypeWrapper type = s.PopType(); + if((VerifierTypeWrapper.IsNew(type) && ((VerifierTypeWrapper)type).UnderlyingType != cpi.GetClassType(classLoader)) || + (type == VerifierTypeWrapper.UninitializedThis && cpi.GetClassType(classLoader) != method.Method.ClassFile.GetSuperTypeWrapper(classLoader) && cpi.GetClassType(classLoader) != 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 @@ -1439,63 +1277,27 @@ class MethodAnalyzer } // after the constructor invocation, the uninitialized reference, is now // suddenly initialized - s.MarkInitialized(type); + if(type == VerifierTypeWrapper.UninitializedThis) + { + s.MarkInitialized(type, wrapper); + } + else + { + s.MarkInitialized(type, ((VerifierTypeWrapper)type).UnderlyingType); + } } else { // 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.PopObject("Ljava/lang/Object;"); - /* - string type = cpi.Class; - if(type[0] != '[') - { - type = "L" + type + ";"; - } - // invokeinterface is allowed on java/lang/Object (because merging interfaces is - // complicated), this will generate a runtime cast in the compiler - if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface && - s.Peek() == "Ljava/lang/Object;") - { - s.PopAny(); - } - else - { - // TODO if this fails it shouldn't generate a VerifyError, but instead - // an IncompatibleClassChangeError (at run time) - s.PopObject(type); - } - */ + s.PopObjectType(); } } - string ret = cpi.Signature.Substring(cpi.Signature.IndexOf(')') + 1); - switch(ret[0]) + TypeWrapper retType = cpi.GetRetType(classLoader); + if(retType != PrimitiveTypeWrapper.VOID) { - case 'Z': - case 'B': - case 'S': - case 'C': - case 'I': - s.PushInt(); - break; - case 'J': - s.PushLong(); - break; - case 'D': - s.PushDouble(); - break; - case 'F': - s.PushFloat(); - break; - case 'L': - case '[': - s.PushObject(ret); - break; - case 'V': - break; - default: - throw new NotImplementedException(ret); + s.PushType(retType); } break; } @@ -1578,9 +1380,17 @@ class MethodAnalyzer s.PushDouble(); break; case NormalizedByteCode.__new: + { // mark the type, so that we can ascertain that it is a "new object" - s.PushObject(string.Format("N{0};L{1};", instr.PC, GetConstantPoolClass(instr.Arg1))); + TypeWrapper type = (TypeWrapper)newTypes[instr.PC]; + if(type == null) + { + type = VerifierTypeWrapper.MakeNew(GetConstantPoolClassType(instr.Arg1), instr.PC); + newTypes[instr.PC] = type; + } + s.PushType(type); break; + } case NormalizedByteCode.__multianewarray: { if(instr.Arg2 < 1) @@ -1591,23 +1401,31 @@ class MethodAnalyzer { s.PopInt(); } - string type = GetConstantPoolClass(instr.Arg1); - if(!type.StartsWith(new String('[', instr.Arg2))) + TypeWrapper type = GetConstantPoolClassType(instr.Arg1); + if(type.ArrayRank < instr.Arg2) { throw new VerifyError("Illegal dimension argument"); } - s.PushObject(type); + s.PushType(type); break; } case NormalizedByteCode.__anewarray: { s.PopInt(); - string type = GetConstantPoolClass(instr.Arg1); - if(type[0] != '[') + TypeWrapper type = GetConstantPoolClassType(instr.Arg1); + string name = type.Name; + if(name[0] != '[') { - type = "L" + type + ";"; + name = "L" + name + ";"; + } + if(type.IsUnloadable) + { + s.PushType(new UnloadableTypeWrapper(name)); + } + else + { + s.PushType(type.GetClassLoader().LoadClassBySlashedName("[" + name)); } - s.PushObject("[" + type); break; } case NormalizedByteCode.__newarray: @@ -1615,28 +1433,28 @@ class MethodAnalyzer switch(instr.Arg1) { case 4: - s.PushObject("[Z"); + s.PushType(BooleanArrayType); break; case 5: - s.PushObject("[C"); + s.PushType(CharArrayType); break; case 6: - s.PushObject("[F"); + s.PushType(FloatArrayType); break; case 7: - s.PushObject("[D"); + s.PushType(DoubleArrayType); break; case 8: - s.PushObject("[B"); + s.PushType(ByteArrayType); break; case 9: - s.PushObject("[S"); + s.PushType(ShortArrayType); break; case 10: - s.PushObject("[I"); + s.PushType(IntArrayType); break; case 11: - s.PushObject("[J"); + s.PushType(LongArrayType); break; default: throw new VerifyError("Bad type"); @@ -1644,190 +1462,186 @@ class MethodAnalyzer break; case NormalizedByteCode.__swap: { - string t1 = s.Pop(); - string t2 = s.Pop(); - s.Push(t1); - s.Push(t2); + TypeWrapper t1 = s.PopType(); + TypeWrapper t2 = s.PopType(); + s.PushType(t1); + s.PushType(t2); break; } case NormalizedByteCode.__dup: { - string t = s.Pop(); - s.Push(t); - s.Push(t); + TypeWrapper t = s.PopType(); + s.PushType(t); + s.PushType(t); break; } case NormalizedByteCode.__dup2: { - string t = s.PopAny(); - if(t == "D" || t == "J") + TypeWrapper t = s.PopAnyType(); + if(t == PrimitiveTypeWrapper.DOUBLE || t == PrimitiveTypeWrapper.LONG) { - s.Push(t); - s.Push(t); + s.PushType(t); + s.PushType(t); } else { - string t2 = s.Pop(); - s.Push(t2); - s.Push(t); - s.Push(t2); - s.Push(t); + TypeWrapper t2 = s.PopType(); + s.PushType(t2); + s.PushType(t); + s.PushType(t2); + s.PushType(t); } break; } case NormalizedByteCode.__dup_x1: { - string value1 = s.Pop(); - string value2 = s.Pop(); - s.Push(value1); - s.Push(value2); - s.Push(value1); + TypeWrapper value1 = s.PopType(); + TypeWrapper value2 = s.PopType(); + s.PushType(value1); + s.PushType(value2); + s.PushType(value1); break; } case NormalizedByteCode.__dup2_x1: { - string value1 = s.PopAny(); - if(value1 == "D" || value1 == "J") + TypeWrapper value1 = s.PopAnyType(); + if(value1 == PrimitiveTypeWrapper.DOUBLE || value1 == PrimitiveTypeWrapper.LONG) { - string value2 = s.Pop(); - s.Push(value1); - s.Push(value2); - s.Push(value1); + TypeWrapper value2 = s.PopType(); + s.PushType(value1); + s.PushType(value2); + s.PushType(value1); } else { - string value2 = s.Pop(); - string value3 = s.Pop(); - s.Push(value2); - s.Push(value1); - s.Push(value3); - s.Push(value2); - s.Push(value1); + 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: { - string value1 = s.Pop(); - string value2 = s.Pop(); - string value3 = s.Pop(); - s.Push(value1); - s.Push(value3); - s.Push(value2); - s.Push(value1); + TypeWrapper value1 = s.PopType(); + TypeWrapper value2 = s.PopType(); + TypeWrapper value3 = s.PopType(); + s.PushType(value1); + s.PushType(value3); + s.PushType(value2); + s.PushType(value1); break; } case NormalizedByteCode.__dup2_x2: { - string value1 = s.PopAny(); - if(value1 == "D" || value1 == "J") + TypeWrapper value1 = s.PopAnyType(); + if(value1 == PrimitiveTypeWrapper.DOUBLE || value1 == PrimitiveTypeWrapper.LONG) { - string value2 = s.PopAny(); - if(value2 == "D" || value2 == "J") + TypeWrapper value2 = s.PopAnyType(); + if(value2 == PrimitiveTypeWrapper.DOUBLE || value2 == PrimitiveTypeWrapper.LONG) { // Form 4 - s.Push(value1); - s.Push(value2); - s.Push(value1); + s.PushType(value1); + s.PushType(value2); + s.PushType(value1); } else { // Form 2 - string value3 = s.Pop(); - s.Push(value1); - s.Push(value3); - s.Push(value2); - s.Push(value1); + TypeWrapper value3 = s.PopType(); + s.PushType(value1); + s.PushType(value3); + s.PushType(value2); + s.PushType(value1); } } else { - string value2 = s.Pop(); - string value3 = s.PopAny(); - if(value3 == "D" || value3 == "J") + TypeWrapper value2 = s.PopType(); + TypeWrapper value3 = s.PopAnyType(); + if(value3 == PrimitiveTypeWrapper.DOUBLE || value3 == PrimitiveTypeWrapper.LONG) { // Form 3 - s.Push(value2); - s.Push(value1); - s.Push(value3); - s.Push(value2); - s.Push(value1); + s.PushType(value2); + s.PushType(value1); + s.PushType(value3); + s.PushType(value2); + s.PushType(value1); } else { // Form 4 - string value4 = s.Pop(); - s.Push(value2); - s.Push(value1); - s.Push(value4); - s.Push(value3); - s.Push(value2); - s.Push(value1); + 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.Pop(); + s.PopType(); break; case NormalizedByteCode.__pop2: - s.Pop2(); + { + TypeWrapper type = s.PopAnyType(); + if(type != PrimitiveTypeWrapper.DOUBLE && type != PrimitiveTypeWrapper.LONG) + { + s.PopType(); + } break; + } case NormalizedByteCode.__monitorenter: case NormalizedByteCode.__monitorexit: // TODO is this allowed to be an uninitialized object? - s.PopObject("Ljava/lang/Object;"); + s.PopObjectType(); break; case NormalizedByteCode.__return: - if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "V") + if(method.Method.GetRetType(classLoader) != PrimitiveTypeWrapper.VOID) { throw new VerifyError("Wrong return type in function"); } break; case NormalizedByteCode.__areturn: + s.PopObjectType(method.Method.GetRetType(classLoader)); + break; + case NormalizedByteCode.__ireturn: { - // HACK if the return type is an interface, any object is legal - if(classLoader.RetTypeFromSig(method.Method.Signature).IsInterface) + s.PopInt(); + TypeWrapper retType = method.Method.GetRetType(classLoader); + if(retType != PrimitiveTypeWrapper.BOOLEAN && + retType != PrimitiveTypeWrapper.BYTE && + retType != PrimitiveTypeWrapper.CHAR && + retType != PrimitiveTypeWrapper.SHORT && + retType != PrimitiveTypeWrapper.INT) { - s.PopObject("Ljava/lang/Object;"); - } - else - { - s.PopObject(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1)); + throw new VerifyError("Wrong return type in function"); } break; } - case NormalizedByteCode.__ireturn: - s.PopInt(); - switch(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1)) - { - case "Z": - case "B": - case "S": - case "C": - case "I": - break; - default: - throw new VerifyError("Wrong return type in function"); - } - break; case NormalizedByteCode.__lreturn: s.PopLong(); - if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "J") + if(method.Method.GetRetType(classLoader) != PrimitiveTypeWrapper.LONG) { throw new VerifyError("Wrong return type in function"); } break; case NormalizedByteCode.__freturn: s.PopFloat(); - if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "F") + if(method.Method.GetRetType(classLoader) != PrimitiveTypeWrapper.FLOAT) { throw new VerifyError("Wrong return type in function"); } break; case NormalizedByteCode.__dreturn: s.PopDouble(); - if(method.Method.Signature.Substring(method.Method.Signature.IndexOf(')') + 1) != "D") + if(method.Method.GetRetType(classLoader) != PrimitiveTypeWrapper.DOUBLE) { throw new VerifyError("Wrong return type in function"); } @@ -1887,25 +1701,18 @@ class MethodAnalyzer s.PushInt(); break; case NormalizedByteCode.__checkcast: - { - s.PopObject("Ljava/lang/Object;"); - string type = GetConstantPoolClass(instr.Arg1); - if(type[0] != '[') - { - type = "L" + type + ";"; - } - s.PushObject(type); + s.PopObjectType(); + s.PushType(GetConstantPoolClassType(instr.Arg1)); break; - } case NormalizedByteCode.__instanceof: - s.PopObject("Ljava/lang/Object;"); + s.PopObjectType(); s.PushInt(); break; case NormalizedByteCode.__iinc: s.GetLocalInt(instr.Arg1); break; case NormalizedByteCode.__athrow: - s.PopObject("Ljava/lang/Throwable;"); + s.PopObjectType(java_lang_Throwable); break; case NormalizedByteCode.__lookupswitch: s.PopInt(); @@ -2043,8 +1850,14 @@ class MethodAnalyzer case NormalizedByteCode.__jsr: { int index = method.PcIndexMap[instr.PC + instr.Arg1]; - s.Push("Lret;" + index); s.SetSubroutineId(index); + TypeWrapper retAddressType = (TypeWrapper)returnAddressTypes[index]; + if(retAddressType == null) + { + retAddressType = VerifierTypeWrapper.MakeRet(index); + returnAddressTypes[index] = retAddressType; + } + s.PushType(retAddressType); state[index] += s; AddCallSite(index, i); break; @@ -2087,20 +1900,27 @@ class MethodAnalyzer // HACK track the local types (but only for object references) for(int j = 0; j < localTypes.Length ; j++) { - string l = s.GetLocalType(j); - if(l != null && (l[0] == 'U' || l[0] == 'N' || l[0] == 'L' || l[0] == '[') && !l.StartsWith("Lret;")) + TypeWrapper l = s.GetLocalType(j); + if(l != VerifierTypeWrapper.Invalid) { - if(l[0] == 'U' || l[0] == 'N') + if(l == VerifierTypeWrapper.UninitializedThis) { - l = l.Substring(l.IndexOf(';') + 1); + localTypes[j] = wrapper; } - if(localTypes[j] == null) + else if(VerifierTypeWrapper.IsNew(l)) { - localTypes[j] = l; + localTypes[j] = ((VerifierTypeWrapper)l).UnderlyingType; } - else + else if(!VerifierTypeWrapper.IsRet(l) && !l.IsPrimitive) { - localTypes[j] = s.FindCommonBaseType(localTypes[j], l); + if(localTypes[j] == VerifierTypeWrapper.Invalid) + { + localTypes[j] = l; + } + else + { + localTypes[j] = s.FindCommonBaseType(localTypes[j], l); + } } } } @@ -2117,6 +1937,7 @@ class MethodAnalyzer opcode = opcode.Substring(2); } x.Instruction = opcode; + Console.WriteLine(x); /* for(int j = 0; j < method.Instructions.Length; j++) { @@ -2201,6 +2022,21 @@ class MethodAnalyzer throw new VerifyError("Illegal constant pool index"); } + private TypeWrapper GetConstantPoolClassType(int index) + { + try + { + return method.Method.ClassFile.GetConstantPoolClassType(index, classLoader); + } + catch(InvalidCastException) + { + } + catch(IndexOutOfRangeException) + { + } + throw new VerifyError("Illegal constant pool index"); + } + private void AddCallSite(int subroutineIndex, int callSiteIndex) { if(callsites[subroutineIndex] == null) @@ -2225,17 +2061,17 @@ class MethodAnalyzer return state[index].GetStackHeight(); } - internal string GetRawStackType(int index, int pos) + internal TypeWrapper GetRawStackTypeWrapper(int index, int pos) { return state[index].GetStackSlot(pos); } - internal string GetLocalType(int index, int local) + internal TypeWrapper GetLocalTypeWrapper(int index, int local) { return state[index].GetLocalType(local); } - internal string GetDeclaredLocalType(int local) + internal TypeWrapper GetDeclaredLocalTypeWrapper(int local) { return localTypes[local]; } diff --git a/classpath/java/io/FileDescriptor.java b/classpath/java/io/FileDescriptor.java index e8dfc43f..c50e1d3d 100644 --- a/classpath/java/io/FileDescriptor.java +++ b/classpath/java/io/FileDescriptor.java @@ -37,7 +37,7 @@ public final class FileDescriptor { } - private FileDescriptor(Stream stream) + public FileDescriptor(Stream stream) { this.stream = stream; }