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); } }
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 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); } }
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 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() { } }