public void Translate(Mono.Cecil.Cil.Instruction inst) { switch (inst.OpCode.Code) { case Code.Throw: case Code.Rethrow: Translate_Throw(inst); break; case Code.Leave: case Code.Leave_S: Translate_Leave(inst); break; case Code.Endfinally: Translate_Endfinally(inst.Offset); break; case Code.Endfilter: Translate_Endfilter(inst); return; default: throw new InvalidProgramException(); } // // these instructions break the normal flow of execution and clear the // stack frame. if the following instruction already has a stack frame, // due to some earlier forward jump, we load that frame // CilMain.LoadFrameOrClearStack(stackMap, inst); locals.TrackUnconditionalBranch(inst); }
static string ImportName(TypeReference fromType, int numGeneric) { string name = CilMain.MakeValidTypeName(fromType.Name); if (fromType.IsNested) { var parent = CilType.From(fromType.DeclaringType); name = parent.ClassName + "$" + name; } else if (!string.IsNullOrEmpty(fromType.Namespace)) { name = CilMain.MakeValidTypeName(fromType.Namespace.ToLowerInvariant()) + "." + name; } if (numGeneric != 0) { // the apostrophe character may get replaced in MakeValidTypeName(), // so we look for it in the original name string int index = fromType.Name.IndexOf('`'); if (index != -1) { index = index - fromType.Name.Length + name.Length; name = name.Substring(0, index) + "$$" + numGeneric; } } return(name); }
// // create the IGenericObject methods // public static void BuildGetTypeMethod(JavaClass theClass, CilType theType) { theClass.AddInterface("system.IGenericObject"); var methodRef = new JavaMethodRef("system-IGenericObject-GetType", CilType.SystemTypeType); var code = CilMain.CreateHelperMethod(theClass, methodRef, 1, 1); if (theType.HasGenericParameters) { // this is a proper generic type with a -generic-type field // created by MakeGenericClass, which also invoked us. code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0xB4 /* getfield */, theType, ConcreteTypeField); code.NewInstruction(JavaType.ObjectType.ReturnOpcode, null, null); } else { // this is a non-generic type, but has a generic base type // which implements the GetType method, so we want to provide // an overriding implementation that returns the correct type. // we are invoked by TypeBuilder. code.StackMap = new JavaStackMap(); LoadMaybeGeneric(theType, code); code.NewInstruction(JavaType.ObjectType.ReturnOpcode, null, null); } }
static int ConvertToLong(JavaCode code, TypeCode oldType, TypeCode newType) { if (oldType == TypeCode.Int64 || oldType == TypeCode.UInt64) { return(0x00); // nop } if (oldType == TypeCode.Double) { return(0x8F); // d2l } if (oldType == TypeCode.Single) { return(0x8C); // f2l } if (newType == TypeCode.UInt64) { code.NewInstruction(0x85 /* i2l */, null, null); code.StackMap.PushStack(JavaType.LongType); long maskValue = (oldType == TypeCode.Byte) ? 0xFF : (oldType == TypeCode.UInt16) ? 0xFFFF : 0xFFFFFFFF; code.NewInstruction(0x12 /* ldc */, null, (long)maskValue); code.StackMap.PushStack(JavaType.LongType); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); return(0x7F); // land } CilMain.MakeRoomForCategory2ValueOnStack(code); return(0x85); // i2l }
// // create a dummy method that is used to extract information // about this generic type: the number of type arguments // it takes, and the data class type. this is used by the // CreateGeneric method in the system.RuntimeType constructor. // public static void CreateGenericInfoMethod(JavaClass theClass, JavaClass dataClass, CilType fromType) { int numGeneric = fromType.GenericParameters.Count; var parameters = new List <JavaFieldRef>(numGeneric); for (int i = 0; i < numGeneric; i++) { parameters.Add(new JavaFieldRef("", SystemGenericType)); } var(returnType, maxStack) = (dataClass == null) ? (JavaType.VoidType, 0) : (new JavaType(0, 0, dataClass.Name), 1); var methodRef = new JavaMethodRef("-generic-info-method", returnType, parameters); var code = CilMain.CreateHelperMethod(theClass, methodRef, numGeneric, maxStack); code.Method.Flags |= JavaAccessFlags.ACC_STATIC | JavaAccessFlags.ACC_FINAL | JavaAccessFlags.ACC_SYNTHETIC; if (dataClass != null) { code.NewInstruction(0x01 /* aconst_null */, null, null); } code.NewInstruction(returnType.ReturnOpcode, null, null); }
static byte TestBool(JavaCode code, JavaType stackTop) { if (stackTop.IsReference) { return(0xC7); // ifnonnull } if (stackTop.PrimitiveType == TypeCode.Int64 || stackTop.PrimitiveType == TypeCode.UInt64 || stackTop.PrimitiveType == TypeCode.Int32 || stackTop.PrimitiveType == TypeCode.UInt32 || stackTop.PrimitiveType == TypeCode.Int16 || stackTop.PrimitiveType == TypeCode.UInt16 || stackTop.PrimitiveType == TypeCode.SByte || stackTop.PrimitiveType == TypeCode.Byte || stackTop.PrimitiveType == TypeCode.Char || stackTop.PrimitiveType == TypeCode.Boolean) { if (stackTop.PrimitiveType == TypeCode.Int64 || stackTop.PrimitiveType == TypeCode.UInt64) { CilMain.MakeRoomForCategory2ValueOnStack(code, JavaType.LongType); code.NewInstruction(0x09 /* lconst_0 (long) */, null, null); code.NewInstruction(0x94 /* lcmp (long) */, null, null); } return(0x9A); // ifne != zero } throw new InvalidProgramException(); }
void LoadStoreField(Code op, object data) { bool ok = false; if (data is FieldReference fieldRef) { var fldName = CilMain.MakeValidMemberName(fieldRef.Name); var fldClass = CilMain.GenericStack.EnterType(fieldRef.DeclaringType); var fldType = ValueUtil.GetBoxedFieldType(fldClass, fieldRef); if (fldClass.Equals(JavaType.StringType) || fldClass.Equals(JavaType.ThrowableType)) { // generally we translate System.String to java.lang.String, // and System.Exception to java.lang.Throwable, // but not in the case of a field reference fldClass = CilType.From(new JavaType(0, 0, fldClass.JavaName)); } bool isLoad = (op != Code.Stfld && op != Code.Stsfld); bool isStatic = (op == Code.Ldsfld || op == Code.Ldsflda || op == Code.Stsfld); bool isAddress = (op == Code.Ldflda || op == Code.Ldsflda); bool isVolatile = CheckVolatile(fldType); if (isLoad) { if (isAddress) { ok = LoadFieldAddress(fldName, fldType, fldClass, isStatic); } else { ok = LoadFieldValue(fldName, fldType, fldClass, isStatic, isVolatile); } } else { ok = StoreFieldValue(fldName, fldType, fldClass, isStatic, isVolatile); } } if (!ok) { throw new InvalidProgramException(); } }
public static void CreateToStringMethod(JavaClass theClass) { // we add an explicit ToString method if it is missing in a // non-interface class, which derives directly from java.lang.Object if ((theClass.Flags & JavaAccessFlags.ACC_INTERFACE) != 0) { return; } if (!theClass.Super.Equals(JavaType.ObjectType.ClassName)) { return; } foreach (var m in theClass.Methods) { if (m.Name == "toString" && (m.Flags & JavaAccessFlags.ACC_STATIC) == 0 && m.ReturnType.Equals(JavaType.StringType) && m.Parameters.Count == 0) { return; } } // create method: string ToString() => GetType().ToString(); var toStringMethod = new JavaMethodRef("toString", JavaType.StringType); var code = CilMain.CreateHelperMethod(theClass, toStringMethod, 1, 1); code.Method.Flags &= ~JavaAccessFlags.ACC_BRIDGE; code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, "system.Object"), new JavaMethodRef("GetType", CilType.SystemTypeType, JavaType.ObjectType)); code.NewInstruction(0xB6 /* invokevirtual */, JavaType.ObjectType, toStringMethod); code.NewInstruction(JavaType.StringType.ReturnOpcode, null, null); }
static void CreateValueMethods(JavaClass valueClass, CilType fromType, int numCastableInterfaces) { CreateValueClearMethod(valueClass, fromType); CreateValueCopyToMethod(valueClass, fromType); CreateValueCloneMethod(valueClass, fromType, numCastableInterfaces); // // system-ValueMethod-Clear() resets all fields to their default value // void CreateValueClearMethod(JavaClass valueClass, CilType fromType) { var code = CilMain.CreateHelperMethod(valueClass, CilMethod.ValueClear, 1, 2); if (valueClass.Fields != null && valueClass.Fields.Count != 0) { foreach (var fld in valueClass.Fields) { if ((fld.Flags & JavaAccessFlags.ACC_STATIC) != 0) { continue; } if (fld.Type is CilType fldType && fldType.IsValueClass) { code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0xB4 /* getfield */, fromType, fld); code.NewInstruction(0xB6 /* invokevirtual */, fldType, CilMethod.ValueClear); } else { code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(fld.Type.InitOpcode, null, null); code.NewInstruction(0xB5 /* putfield */, fromType, fld); } } }
public static JavaClass FixClass(JavaClass fromClass, CilType fromType) { JavaType interfaceType = InterfaceType(fromType); JavaClass interfaceClass = null; var fromMethods = fromClass.Methods; for (int i = fromMethods.Count; i-- > 0;) { var nm = fromMethods[i].Name; if (nm == "BeginInvoke" || nm == "EndInvoke") { fromClass.Methods.RemoveAt(i); } } foreach (var method in fromMethods) { if ((method.Flags & JavaAccessFlags.ACC_STATIC) == 0 && method.Code == null) { if (method.Name == "<init>") { GenerateConstructor(method, fromType); } else if (method.Name == "Invoke") { if (interfaceClass != null) { break; } interfaceClass = MakeInterface(fromClass, method, interfaceType.ClassName); GenerateInvoke(method, fromType, interfaceType); } /*else if (method.Name == "BeginInvoke") * { * if (interfaceClass == null) * break; * * GenerateBeginInvoke(); * continue; * } * else if (method.Name == "EndInvoke") * { * GenerateEndInvoke(); * continue; * }*/ else { break; } method.Flags &= ~JavaAccessFlags.ACC_ABSTRACT; } } if (interfaceClass == null) { throw CilMain.Where.Exception("invalid delegate class"); } return(interfaceClass); // // // JavaClass MakeInterface(JavaClass fromClass, JavaMethod fromMethod, string newName) { var newClass = CilMain.CreateInnerClass(fromClass, newName, JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_ABSTRACT | JavaAccessFlags.ACC_INTERFACE); var newMethod = new JavaMethod(newClass, fromMethod); newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_ABSTRACT; if (IsPrimitive(newMethod.ReturnType)) { // primitive return value is translated to java.lang.Object, // because the delegate target may return a generic type that // is specialized for the primitive type in the delegate. // see also GenerateInvoke and LoadFunction newMethod.ReturnType = JavaType.ObjectType; } newClass.Methods.Add(newMethod); return(newClass); } // // // void GenerateConstructor(JavaMethod method, CilType dlgType) { var code = method.Code = new JavaCode(); code.Method = method; code.Instructions = new List <Instruction>(); if (dlgType.HasGenericParameters) { code.StackMap = new JavaStackMap(); var genericMark = CilMain.GenericStack.Mark(); CilMain.GenericStack.EnterMethod(dlgType, method, true); // initialize the generic type field GenericUtil.InitializeTypeField(dlgType, code); CilMain.GenericStack.Release(genericMark); code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where); } code.NewInstruction(0x19 /* aload_0 */, null, (int)0); code.NewInstruction(0x19 /* aload_1 */, null, (int)1); code.NewInstruction(0x19 /* aload_2 */, null, (int)2); code.NewInstruction(0xB7 /* invokespecial */, new JavaType(0, 0, method.Class.Super), new JavaMethodRef("<init>", JavaType.VoidType, JavaType.ObjectType, JavaType.ObjectType)); code.NewInstruction(0xB1 /* return (void) */, null, null); if (code.MaxStack < 3) { code.MaxStack = 3; } code.MaxLocals = 3 + dlgType.GenericParametersCount; } // // // void GenerateInvoke(JavaMethod method, CilType dlgType, JavaType ifcType) { var delegateType = new JavaType(0, 0, "system.Delegate"); var code = method.Code = new JavaCode(); code.Method = method; code.Instructions = new List <Instruction>(); code.StackMap = new JavaStackMap(); code.StackMap.SetLocal(0, dlgType); int index = 1; for (int i = 0; i < method.Parameters.Count; i++) { var paramType = (CilType)method.Parameters[i].Type; code.StackMap.SetLocal(index, paramType); index += paramType.Category; } code.StackMap.SaveFrame((ushort)0, false, CilMain.Where); // // step 1, call the target method through the interface reference // stored in the 'invokable' field of the system.Delegate class // code.NewInstruction(0x19 /* aload_0 */, null, (int)0); code.NewInstruction(0xB4 /* getfield */, delegateType, new JavaFieldRef("invokable", JavaType.ObjectType)); code.NewInstruction(0xC0 /* checkcast */, ifcType, null); code.StackMap.PushStack(JavaType.ObjectType); // push method parameters 'invokeinterface' index = 1; for (int i = 0; i < method.Parameters.Count; i++) { var paramType = (CilType)method.Parameters[i].Type; code.NewInstruction(paramType.LoadOpcode, null, index); code.StackMap.PushStack(paramType); if (paramType.IsGenericParameter && (!paramType.IsByReference)) { // invoke the helper method which identifies our boxed // primitives and re-boxes them as java boxed values. // see also: GenericType::DelegateParameterin baselib. GenericUtil.LoadMaybeGeneric( paramType.GetMethodGenericParameter(), code); code.NewInstruction(0xB8 /* invokestatic */, SystemDelegateUtilType, DelegateParameterMethod); code.StackMap.PopStack(CilMain.Where); } index += paramType.Category; } var returnType = (CilType)method.ReturnType; if (IsPrimitive(returnType)) { // primitive return value is translated to java.lang.Object, // because the delegate target may return a generic type that // is specialized for the primitive type in the delegate. // see also GenerateInvoke and LoadFunction var adjustedMethod = new JavaMethodRef( method.Name, JavaType.ObjectType, method.Parameters); code.NewInstruction(0xB9 /* invokeinterface */, ifcType, adjustedMethod); } else { code.NewInstruction(0xB9 /* invokeinterface */, ifcType, method); } code.StackMap.ClearStack(); code.StackMap.PushStack(returnType); // check if this delegate has a 'following' delegate, // attached via system.MulticastDelegate.CombineImpl code.NewInstruction(0x19 /* aload_0 */, null, (int)0); code.NewInstruction(0xB4 /* getfield */, delegateType, new JavaFieldRef("following", delegateType)); code.NewInstruction(0xC7 /* ifnonnull */, null, (ushort)1); if (returnType.IsGenericParameter && (!returnType.IsByReference)) { GenericUtil.LoadMaybeGeneric( returnType.GetMethodGenericParameter(), code); code.NewInstruction(0xB8 /* invokestatic */, SystemDelegateUtilType, DelegateReturnValueMethod); code.StackMap.PopStack(CilMain.Where); } else if (IsPrimitive(returnType)) { // if the delegate returns a primitive type, we need to unbox // the object returned by the method we just called. it will be // a java boxed primitive (e.g. java.lang.Integer) if the called // method actually returns a primitive, due to the boxing done // by the invokedynamic mechanism. if the called method returns // a generic type, it will be our boxed type, e.g. system.Int32. // // see also DelegateReturnValueX helper methods in baselib, // and MakeInterface and LoadFunction in this file. var helperMethod = new JavaMethodRef( DelegateReturnValueMethod.Name + returnType.ToDescriptor(), returnType, JavaType.ObjectType); code.NewInstruction(0xB8 /* invokestatic */, SystemDelegateUtilType, helperMethod); } code.NewInstruction(returnType.ReturnOpcode, null, index); code.StackMap.PopStack(CilMain.Where); // // step 2 // // if this delegate has a 'following' delegate, then we need to // call it, but first get rid of the return value on the stack // byte popOpcode; if (returnType.Equals(JavaType.VoidType)) { popOpcode = 0x00; // nop } else // select 0x57 pop, or 0x58 pop2 { var adjustedReturnType = IsPrimitive(returnType) ? JavaType.ObjectType : returnType; code.StackMap.PushStack(adjustedReturnType); popOpcode = (byte)(0x56 + returnType.Category); } code.NewInstruction(popOpcode, null, null, (ushort)1); code.StackMap.SaveFrame((ushort)1, true, CilMain.Where); // now call the Invoke method on the 'following' delegate code.NewInstruction(0x19 /* aload_0 */, null, (int)0); code.NewInstruction(0xB4 /* getfield */, delegateType, new JavaFieldRef("following", delegateType)); code.NewInstruction(0xC0 /* checkcast */, dlgType, null); // push all method parameters for 'invokevirtual' index = 1; for (int i = 0; i < method.Parameters.Count; i++) { var paramType = (CilType)method.Parameters[i].Type; code.NewInstruction(paramType.LoadOpcode, null, index); if (paramType.IsGenericParameter && (!paramType.IsByReference)) { // invoke the helper method which identifies our boxed // primitives and re-boxes them as java boxed values. // see also: GenericType::DelegateParameterin baselib. GenericUtil.LoadMaybeGeneric( paramType.GetMethodGenericParameter(), code); code.NewInstruction(0xB8 /* invokestatic */, SystemDelegateUtilType, DelegateParameterMethod); } index += paramType.Category; } code.NewInstruction(0xB6 /* invokevirtual */, dlgType, method); code.NewInstruction(returnType.ReturnOpcode, null, index); code.StackMap.ClearStack(); code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where); code.MaxLocals = index; } // // // /*void GenerateBeginInvoke() * { * }*/ // // // /*void GenerateEndInvoke() * { * }*/ }
public static JavaClass MakeGenericClass(JavaClass fromClass, CilType fromType) { // if the generic class has static fields or a static initializer // then we need to move those into a separate class that can be // instantiated multiple times for multiple separate instances, // one for each concrete implementation of the generic type. int numGeneric = fromType.GenericParameters.Count; var dataClass = MoveStaticFields(fromClass, null); dataClass = MoveStaticInit(fromClass, dataClass); if (dataClass != null) { FixConstructorInData(dataClass, numGeneric); } // a generic class implements the IGenericObject interface, // and has a generic-type field for the concrete implementation // of the generic type and generic arguments CreateGenericTypeFields(fromClass, numGeneric); BuildGetTypeMethod(fromClass, fromType); return(dataClass); // // move any static fields from the generic class, // as instance fields in a new class // JavaClass MoveStaticFields(JavaClass fromClass, JavaClass dataClass) { var fields = fromClass.Fields; if (fields == null) { return(dataClass); } int n = fields.Count; for (int i = 0; i < n;) { var fld = fields[i]; if ((fld.Flags & JavaAccessFlags.ACC_STATIC) == 0) { i++; continue; } if (((CilType)fld.Type).IsLiteral) { i++; continue; } if (dataClass == null) { dataClass = CreateClass(fromClass); } if (fld.Constant != null) { throw CilMain.Where.Exception($"initializer in static field '{fld.Name}' in generic class"); } fields.RemoveAt(i); n--; fld.Flags &= ~JavaAccessFlags.ACC_STATIC; dataClass.Fields.Add(fld); } return(dataClass); } // // move the static constructor/initializer // from the generic class to the new data class // JavaClass MoveStaticInit(JavaClass fromClass, JavaClass dataClass) { var methods = fromClass.Methods; int n = methods.Count; for (int i = 0; i < n;) { var mth = methods[i]; if (mth.Name != "<clinit>") { i++; continue; } if (dataClass == null) { dataClass = CreateClass(fromClass); } methods.RemoveAt(i); n--; mth.Name = "<init>"; mth.Class = dataClass; mth.Flags = JavaAccessFlags.ACC_PUBLIC; dataClass.Methods.Add(mth); } return(dataClass); } // // create a constructor if there was no static initializer, // or inject a call to super class constructor // void FixConstructorInData(JavaClass dataClass, int numGeneric) { JavaCode code; bool insertReturn; if (dataClass.Methods.Count == 0) { code = CilMethod.CreateConstructor(dataClass, numGeneric, true); insertReturn = true; code.MaxStack = 1; code.MaxLocals = 1 + numGeneric; } else { code = dataClass.Methods[0].Code; if (code.MaxStack < 1) { code.MaxStack = 1; } // we are injecting a call to super constructor at the very top, // so local 0 should have the proper type, not uninitializedThis code.StackMap.SetLocalInAllFrames( 0, CilType.From(new JavaType(0, 0, dataClass.Name)), null); insertReturn = false; } code.Instructions.Insert(0, new Instruction( 0x19 /* aload */, null, (int)0, 0xFFFF)); code.Instructions.Insert(1, new Instruction( 0xB7 /* invokespecial */, JavaType.ObjectType, new JavaMethodRef("<init>", JavaType.VoidType), 0xFFFF)); // the static initializer can call static methods on its own type, // and those methods can invoke system.RuntimeType.GetType() to get // a reference to the generic type that is still being initialized. // and more importantly, a reference to the the static-generic data // object that is constructed by this method. to make the object // available to such access, we call system.RuntimeType.SetStatic(). // see also system.RuntimeType.MakeGenericType/MakeGenericType(). code.Instructions.Insert(2, new Instruction( 0x19 /* aload */, null, (int)0, 0xFFFF)); code.Instructions.Insert(3, new Instruction( 0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType, new JavaMethodRef("SetStatic", JavaType.VoidType, JavaType.ObjectType), 0xFFFF)); if (insertReturn) { code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null, 0xFFFF); } } // // create the new data class // JavaClass CreateClass(JavaClass fromClass) => CilMain.CreateInnerClass(fromClass, fromClass.Name + "$$static", 0, markGenericEntity: true); // // create a private instance field to hold the runtime type // for a particular combination of generic type and arguments // void CreateGenericTypeFields(JavaClass fromClass, int numGeneric) { var fld = new JavaField(); fld.Name = ConcreteTypeField.Name; fld.Type = ConcreteTypeField.Type; fld.Class = fromClass; fld.Flags = JavaAccessFlags.ACC_PRIVATE; if (fromClass.Fields == null) { fromClass.Fields = new List <JavaField>(); } fromClass.Fields.Add(fld); } }
static int ConvertToFloat(JavaCode code, TypeCode oldType, TypeCode newType, bool unsigned) { if (newType == TypeCode.Single) { // // convert to float // if (oldType == TypeCode.Single) { return(0x00); // nop } if (oldType == TypeCode.Double) { return(0x90); // d2f } if (oldType == TypeCode.Int64) { return(0x89); // l2f } if (oldType != TypeCode.UInt64) { return(0x86); // i2f } CallUnsignedLongToDouble(code); code.NewInstruction(0x8D /* d2f */, null, null); return(-1); // no output } else if (!unsigned) { // // convert to double // if (oldType == TypeCode.Double) { return(0x00); // nop } if (oldType == TypeCode.Single) { CilMain.MakeRoomForCategory2ValueOnStack(code); return(0x8D); // f2d } if (oldType == TypeCode.Int64) { return(0x8A); // l2d } if (oldType != TypeCode.UInt64) { return(0x87); // i2d } CallUnsignedLongToDouble(code); return(-1); // no output } else { // // convert integer, interpreted as unsigned, to a double // if (oldType == TypeCode.Single || oldType == TypeCode.Double) { throw new InvalidProgramException(); } bool fromInt32 = (oldType != TypeCode.Int64 && oldType != TypeCode.UInt64); if (fromInt32) { CilMain.MakeRoomForCategory2ValueOnStack(code); code.NewInstruction(0x85 /* i2l */, null, null); code.StackMap.PushStack(JavaType.LongType); if (oldType == TypeCode.UInt32) { code.NewInstruction(0x12 /* ldc */, null, (long)0xFFFFFFFF); code.StackMap.PushStack(JavaType.LongType); code.NewInstruction(0x7F /* land */, null, null); code.StackMap.PopStack(CilMain.Where); } } CallUnsignedLongToDouble(code); if (fromInt32) { code.StackMap.PopStack(CilMain.Where); } return(-1); // no output } void CallUnsignedLongToDouble(JavaCode code) { code.NewInstruction(0xB8 /* invokestatic */, CilType.From(JavaType.DoubleType).AsWritableClass, new JavaMethodRef("UnsignedLongToDouble", JavaType.DoubleType, JavaType.LongType)); } }
public static List <JavaClass> BuildProxyMethods(List <CilInterface> allInterfaces, TypeDefinition fromType, CilType intoType, JavaClass theClass) { // // process only if the class (or interface) has any methods or super interfaces // var classMethods = theClass.Methods; if (classMethods.Count == 0) { return(null); } bool isInterface = intoType.IsInterface; if ((!isInterface) && theClass.Interfaces == null) { return(null); } var theMethods = CilInterfaceMethod.CollectAll(fromType); // // if any interfaces are marked [RetainName], make sure that // all corresponding methods are also marked [RetainName] // CheckRetainNameMethods(theMethods, allInterfaces, intoType); // // if this is an abstract class but forced to an interface via [AddInterface] // decoration, then we need to remove all constructors generated for the class // if (intoType.IsInterface) { if (!fromType.IsInterface) { for (int i = classMethods.Count; i-- > 0;) { if (classMethods[i].Name == "<init>") { classMethods.RemoveAt(i); } } } if (intoType.HasGenericParameters) { // the RuntimeType constructor in baselib uses IGenericEntity // marker interface to identify generic classes. note that // real generic types implement IGenericObject -> IGenericEntity. theClass.AddInterface("system.IGenericEntity"); } return(null); } // // for each implemented interface, build proxy methods // List <JavaClass> output = null; int ifcNumber = 0; foreach (var ifc in allInterfaces) { if ((!ifc.DirectReference) && ifc.SuperImplements) { // we don't have to build proxy for an interface if it is // implemented by a super type and not by our primary type continue; } if (ifc.GenericTypes == null) { foreach (var ifcMethod in ifc.Methods) { // build proxy methods: interface$method -> method var newMethod = BuildPlainProxy(ifcMethod, intoType, theMethods); if (newMethod != null) { newMethod.Class = theClass; theClass.Methods.Add(newMethod); } } } else { var ifcClass = CreateInnerClass(theClass, intoType, ++ifcNumber); ifcClass.AddInterface(ifc.InterfaceType.JavaName); if (output == null) { output = new List <JavaClass>(); CreateInterfaceArrayField(theClass); } output.Add(ifcClass); // if the class implements a generic interface for multiple types, // then we need a method suffix to differentiate between the methods. // see also: CilMethod::InsertMethodNamePrefix string methodSuffix = ""; foreach (var genericType in ifc.GenericTypes) { methodSuffix += "--" + CilMethod.GenericParameterSuffixName(genericType); } foreach (var ifcMethod in ifc.Methods) { // build proxy classes: proxy sub-class -> this class BuildGenericProxy(ifcMethod, methodSuffix, intoType, theMethods, ifcClass); } } } return(output); JavaClass CreateInnerClass(JavaClass parentClass, CilType parentType, int ifcNumber) { // generic interfaces are implemented as proxy sub-classes which // call methods on the parent class object. we need to define // an inner class. this class has one instance field which is a // reference to the parent class. the constructor takes this // reference as a parameter and initializes the instance field. var newClass = CilMain.CreateInnerClass(parentClass, parentClass.Name + "$$generic" + ifcNumber.ToString()); var fld = new JavaField(); fld.Name = ParentFieldName; fld.Type = parentType; fld.Class = newClass; fld.Flags = JavaAccessFlags.ACC_PRIVATE; newClass.Fields.Add(fld); var code = CilMain.CreateHelperMethod(newClass, new JavaMethodRef("<init>", JavaType.VoidType, JavaType.ObjectType), 2, 2); code.Method.Flags &= ~JavaAccessFlags.ACC_BRIDGE; // invalid for constructor code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0xB7 /* invokespecial */, JavaType.ObjectType, new JavaMethodRef("<init>", JavaType.VoidType)); code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0x19 /* aload */, null, (int)1); code.NewInstruction(0xC0 /* checkcast */, parentType, null); code.NewInstruction(0xB5 /* putfield */, new JavaType(0, 0, newClass.Name), new JavaFieldRef(ParentFieldName, parentType)); code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null); return(newClass); } void CreateInterfaceArrayField(JavaClass parentClass) { // the parent class has a helper array field that is used to track // the proxy objects generated for implemented generic interfaces. // see also: InitInterfaceArrayField, below. var fld = new JavaField(); fld.Name = InterfaceArrayField.Name; fld.Type = InterfaceArrayField.Type; fld.Class = parentClass; fld.Flags = JavaAccessFlags.ACC_PRIVATE; if (parentClass.Fields == null) { parentClass.Fields = new List <JavaField>(1); } parentClass.Fields.Add(fld); } }
internal static void BuildJavaClass(TypeDefinition cilType, JavaClass parentClass) { CilMain.Where.Push($"class '{cilType.FullName}'"); var genericMark = CilMain.GenericStack.Mark(); var myType = CilMain.GenericStack.EnterType(cilType); var jclass = new JavaClass(); jclass.Name = myType.JavaName; jclass.Flags = AttributesToAccessFlags(cilType.Attributes, myType.IsInterface); if (myType.IsInterface) { jclass.Super = JavaType.ObjectType.ClassName; // java.lang.Object } else if (cilType.BaseType != null) { var myBaseType = CilType.From(cilType.BaseType); jclass.Super = myBaseType.Equals(JavaType.ObjectType) ? JavaType.ObjectType.ClassName // java.lang.Object : myBaseType.JavaName; } else { throw CilMain.Where.Exception("missing base class"); } var myInterfaces = ImportInterfaces(jclass, myType, cilType); int numCastableInterfaces = myType.IsGenericThisOrSuper ? InterfaceBuilder.CastableInterfaceCount(myInterfaces) : 0; ImportFields(jclass, cilType, myType.IsRetainName); ImportMethods(jclass, cilType, numCastableInterfaces); if (myType.JavaName == "system.Convert") { DiscardBase64MethodsInConvertClass(jclass); } ValueUtil.InitializeStaticFields(jclass, myType); if (myType.IsValueClass) { ValueUtil.MakeValueClass(jclass, myType, numCastableInterfaces); } else if (myType.IsEnum) { ValueUtil.MakeEnumClass(jclass, myType, cilType.HasCustomAttribute("System.FlagsAttribute", true)); } else if (myType.IsDelegate) { var delegateInterface = Delegate.FixClass(jclass, myType); CilMain.JavaClasses.Add(delegateInterface); } // if derives directly from object, and does not implement ToString CodeBuilder.CreateToStringMethod(jclass); ResetFieldReferences(jclass); LinkClasses(jclass, parentClass, cilType); var interfaceClasses = InterfaceBuilder.BuildProxyMethods( myInterfaces, cilType, myType, jclass); if (interfaceClasses != null) { foreach (var childClass in interfaceClasses) { CilMain.JavaClasses.Add(childClass); } } if (myType.HasGenericParameters) { JavaClass dataClass; if (!myType.IsInterface) { dataClass = GenericUtil.MakeGenericClass(jclass, myType); if (dataClass != null) { CilMain.JavaClasses.Add(dataClass); } } else { dataClass = null; } JavaClass infoClass = jclass; if (myType.IsInterface) { // Android 'D8' desugars static methods on an interface by // moving into a separate class, so we do it ourselves. // see also system.RuntimeType.CreateGeneric() in baselib infoClass = CilMain.CreateInnerClass(jclass, jclass.Name + "$$info"); CilMain.JavaClasses.Add(infoClass); } GenericUtil.CreateGenericInfoMethod(infoClass, dataClass, myType); GenericUtil.CreateGenericVarianceField(infoClass, myType, cilType); } if (myType.IsGenericThisOrSuper) { jclass.Signature = GenericUtil.MakeGenericSignature(cilType, jclass.Super); if (!myInterfaces.Exists(x => x.InterfaceType.JavaName == "system.IGenericObject")) { if (!myType.IsInterface) { // create IGenericObject methods GetType and TryCast // only if class did not already implement IGenericObject if (!myType.HasGenericParameters) { GenericUtil.BuildGetTypeMethod(jclass, myType); } InterfaceBuilder.BuildTryCastMethod( myInterfaces, myType, numCastableInterfaces, jclass); } } } CilMain.GenericStack.Release(genericMark); CilMain.Where.Pop(); }
public static void ImportMethods(JavaClass jclass, TypeDefinition cilType, int numCastableInterfaces) { if (cilType.HasMethods) { int n = cilType.Methods.Count; if (n > 0) { jclass.Methods = new List <JavaMethod>(n); for (int i = 0; i < n; i++) { var defMethod = cilType.Methods[i]; /* * if (defMethod.HasCustomAttribute("Discard")) * continue; // if decorated with [java.attr.Discard], don't export to java */ var genericMark = CilMain.GenericStack.Mark(); var myMethod = CilMain.GenericStack.EnterMethod(defMethod); var newMethod = new JavaMethod(jclass, myMethod.WithGenericParameters); newMethod.Flags = AttributesToAccessFlags( defMethod.Attributes, defMethod.HasOverrides, (cilType.HasNestedTypes || cilType.HasGenericParameters)); if (myMethod.IsStatic & myMethod.IsConstructor) { newMethod.Flags &= ~(JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_PRIVATE | JavaAccessFlags.ACC_PROTECTED); } if (defMethod.HasBody) { CilMain.Where.Push($"method '{defMethod.Name}'"); CodeBuilder.BuildJavaCode(newMethod, myMethod, defMethod, numCastableInterfaces); if ((defMethod.ImplAttributes & MethodImplAttributes.Synchronized) != 0) { // if method is decorated with [MethodImplOptions.Synchronized], // create a wrapper method that locks appropriately jclass.Methods.Add( CodeBuilder.CreateSyncWrapper(newMethod, myMethod.DeclType)); } else if (defMethod.Name == "Finalize" && (!defMethod.HasParameters) && defMethod.IsVirtual) { // if method is a finalizer, create a wrapper method that // checks if finalization was suppressed for the object CodeBuilder.CreateSuppressibleFinalize( newMethod, myMethod.DeclType, jclass); } if (defMethod.IsVirtual) { InterfaceBuilder.BuildOverloadProxy( cilType, defMethod, myMethod, jclass); } else if (!myMethod.IsConstructor) { newMethod.Flags |= JavaAccessFlags.ACC_FINAL; } CilMain.Where.Pop(); } else { if ((!defMethod.IsAbstract) && (defMethod.IsInternalCall || defMethod.IsPInvokeImpl)) { // skip native methods continue; } // clear ACC_STATIC and access, set ACC_ABSTRACT and ACC_PUBLIC newMethod.Flags = (newMethod.Flags | JavaAccessFlags.ACC_ABSTRACT | JavaAccessFlags.ACC_PUBLIC) & ~(JavaAccessFlags.ACC_STATIC | JavaAccessFlags.ACC_PRIVATE | JavaAccessFlags.ACC_PROTECTED); } jclass.Methods.Add(newMethod); CilMain.GenericStack.Release(genericMark); if (myMethod.IsConstructor) { var dummyClass = CreateDummyClassForConstructor(myMethod, jclass); if (dummyClass != null) { CilMain.JavaClasses.Add(dummyClass); } } else if (myMethod.WithGenericParameters != myMethod && (!myMethod.IsRetainName)) { jclass.Methods.Add( Delegate.CreateCapturingBridgeMethod( newMethod, myMethod.Parameters, cilType.IsInterface)); } } } } else { jclass.Methods = new List <JavaMethod>(0); } JavaClass CreateDummyClassForConstructor(CilMethod theMethod, JavaClass theClass) { if (!theMethod.HasDummyClassArg) { return(null); } return(CilMain.CreateInnerClass( theClass, theMethod.Parameters[ theMethod.Parameters.Count - 1].Type.ClassName)); } }
public static void ImportFields(JavaClass jclass, TypeDefinition cilType, bool isRetainName) { if (cilType.HasFields) { int n = cilType.Fields.Count; if (n > 0) { if (isRetainName) { throw CilMain.Where.Exception("fields not supported in a [RetainName] type"); } jclass.Fields = new List <JavaField>(n); for (int i = 0; i < n; i++) { var cilField = cilType.Fields[i]; CilMain.Where.Push($"field '{cilField.Name}'"); if (cilField.InitialValue.Length != 0) { throw CilMain.Where.Exception("unsupported InitialValue in field"); } var myField = new JavaField(); myField.Name = CilMain.MakeValidMemberName(cilField.Name); myField.Class = jclass; myField.Flags = AttributesToAccessFlags( cilField.Attributes, (cilType.HasNestedTypes || cilType.HasGenericParameters)); if (cilType.IsEnum) { myField.Type = CilType.From(cilField.FieldType); if (cilField.Constant != null) { myField.InitConstant(cilField.Constant, CilMain.Where); } } else { myField.Type = ValueUtil.GetBoxedFieldType(null, cilField); if (((CilType)myField.Type).IsValueClass) { myField.Constant = cilField; } else { if (cilField.Constant != null) { myField.InitConstant(cilField.Constant, CilMain.Where); } if (((CilType)myField.Type).IsVolatile) { myField.Flags |= JavaAccessFlags.ACC_VOLATILE; } } } jclass.Fields.Add(myField); CilMain.Where.Pop(); } } } }
public static JavaClass FixClass(JavaClass fromClass, CilType fromType) { JavaType interfaceType = InterfaceType(fromType); JavaClass interfaceClass = null; foreach (var method in fromClass.Methods) { if ((method.Flags & JavaAccessFlags.ACC_STATIC) == 0 && method.Code == null) { if (method.Name == "<init>") { GenerateConstructor(method, fromType); } else if (method.Name == "Invoke") { if (interfaceClass != null) { break; } interfaceClass = MakeInterface(fromClass, method, interfaceType.ClassName); GenerateInvoke(method, fromType, interfaceType); } else if (method.Name == "BeginInvoke") { if (interfaceClass == null) { break; } GenerateBeginInvoke(); continue; } else if (method.Name == "EndInvoke") { GenerateEndInvoke(); continue; } else { break; } method.Flags &= ~JavaAccessFlags.ACC_ABSTRACT; } } if (interfaceClass == null) { throw CilMain.Where.Exception("invalid delegate class"); } return(interfaceClass); // // // JavaClass MakeInterface(JavaClass fromClass, JavaMethod fromMethod, string newName) { var newClass = CilMain.CreateInnerClass(fromClass, newName, JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_ABSTRACT | JavaAccessFlags.ACC_INTERFACE); var newMethod = new JavaMethod(newClass, fromMethod); newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_ABSTRACT; if (IsPrimitive(newMethod.ReturnType)) { // primitive return value is translated to java.lang.Object, // because the delegate target may return a generic type that // is specialized for the primitive type in the delegate. // see also GenerateInvoke and LoadFunction newMethod.ReturnType = JavaType.ObjectType; } newClass.Methods.Add(newMethod); return(newClass); } // // // void GenerateConstructor(JavaMethod method, CilType dlgType) { var code = method.Code = new JavaCode(); code.Method = method; code.Instructions = new List <Instruction>(); if (dlgType.HasGenericParameters) { code.StackMap = new JavaStackMap(); var genericMark = CilMain.GenericStack.Mark(); CilMain.GenericStack.EnterMethod(dlgType, method, true); // initialize the generic type field GenericUtil.InitializeTypeField(dlgType, code); CilMain.GenericStack.Release(genericMark); code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where); } code.NewInstruction(0x19 /* aload_0 */, null, (int)0); code.NewInstruction(0x19 /* aload_1 */, null, (int)1); code.NewInstruction(0x19 /* aload_2 */, null, (int)2); code.NewInstruction(0xB7 /* invokespecial */, new JavaType(0, 0, method.Class.Super), new JavaMethodRef("<init>", JavaType.VoidType, JavaType.ObjectType, JavaType.ObjectType)); code.NewInstruction(0xB1 /* return (void) */, null, null); if (code.MaxStack < 3) { code.MaxStack = 3; } code.MaxLocals = 3 + dlgType.GenericParametersCount; } // // // void GenerateInvoke(JavaMethod method, CilType dlgType, JavaType ifcType) { var code = method.Code = new JavaCode(); code.Method = method; code.Instructions = new List <Instruction>(); code.StackMap = new JavaStackMap(); code.NewInstruction(0x19 /* aload_0 */, null, (int)0); code.NewInstruction(0xB4 /* getfield */, new JavaType(0, 0, "system.Delegate"), new JavaFieldRef("invokable", JavaType.ObjectType)); code.NewInstruction(0xC0 /* checkcast */, ifcType, null); code.StackMap.PushStack(JavaType.ObjectType); int index = 1; for (int i = 0; i < method.Parameters.Count; i++) { var paramType = (CilType)method.Parameters[i].Type; code.NewInstruction(paramType.LoadOpcode, null, index); code.StackMap.PushStack(paramType); if (paramType.IsGenericParameter && (!paramType.IsByReference)) { // invoke the helper method which identifies our boxed // primitives and re-boxes them as java boxed values. // see also: GenericType::DelegateParameterin baselib. GenericUtil.LoadMaybeGeneric( paramType.GetMethodGenericParameter(), code); code.NewInstruction(0xB8 /* invokestatic */, SystemDelegateUtilType, DelegateParameterMethod); code.StackMap.PopStack(CilMain.Where); } index += paramType.Category; } var returnType = (CilType)method.ReturnType; if (IsPrimitive(returnType)) { // primitive return value is translated to java.lang.Object, // because the delegate target may return a generic type that // is specialized for the primitive type in the delegate. // see also GenerateInvoke and LoadFunction var adjustedMethod = new JavaMethodRef( method.Name, JavaType.ObjectType, method.Parameters); code.NewInstruction(0xB9 /* invokeinterface */, ifcType, adjustedMethod); } else { code.NewInstruction(0xB9 /* invokeinterface */, ifcType, method); } code.StackMap.ClearStack(); code.StackMap.PushStack(returnType); if (returnType.IsGenericParameter && (!returnType.IsByReference)) { GenericUtil.LoadMaybeGeneric( returnType.GetMethodGenericParameter(), code); code.NewInstruction(0xB8 /* invokestatic */, SystemDelegateUtilType, DelegateReturnValueMethod); code.StackMap.PopStack(CilMain.Where); } else if (IsPrimitive(returnType)) { // if the delegate returns a primitive type, we need to unbox // the object returned by the method we just called. it will be // a java boxed primitive (e.g. java.lang.Integer) if the called // method actually returns a primitive, due to the boxing done // by the invokedynamic mechanism. if the called method returns // a generic type, it will be our boxed type, e.g. system.Int32. // // see also DelegateReturnValueX helper methods in baselib, // and MakeInterface and LoadFunction in this file. var helperMethod = new JavaMethodRef( DelegateReturnValueMethod.Name + returnType.ToDescriptor(), returnType, JavaType.ObjectType); code.NewInstruction(0xB8 /* invokestatic */, SystemDelegateUtilType, helperMethod); } code.NewInstruction(returnType.ReturnOpcode, null, index); code.StackMap.PopStack(CilMain.Where); code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where); code.MaxLocals = index; } // // // void GenerateBeginInvoke() { } // // // void GenerateEndInvoke() { } }