// // 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); }
// // 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); } }
public static bool CheckCast(CilType castType, bool @throw, JavaCode code) { if (object.ReferenceEquals(castType, GenericArrayType) || (castType.IsArray && (castType.IsInterface || castType.IsGenericParameter || castType.ClassName == JavaType.ObjectType.ClassName || castType.ClassName == CilType.SystemValueType.ClassName))) { // if casting to Object[], ValueType[], to an array of interface type, // or to an array of a generic parameter, we can't rely on a simple // 'checkcast' or 'instanceof', because the jvm will permit the cast // of a value type array to the aforementioned reference types. // // instead, we generate a call to system.Array.CheckCast in baselib, // except if we are generating code for the system.Array class itself. if (code.Method.Class.Name.StartsWith("system.Array")) { return(false); } // note the caller of this method already popped the stack once. // which we have to undo that, before we push anything else. code.StackMap.PushStack(JavaType.ObjectType); // array var method = new JavaMethodRef("CheckCast", JavaType.ObjectType); method.Parameters.Add(new JavaFieldRef("", JavaType.ObjectType)); if (object.ReferenceEquals(castType, GenericArrayType) || castType.IsGenericParameter) { method.Parameters.Add(new JavaFieldRef("", CilType.SystemTypeType)); GenericUtil.LoadMaybeGeneric(castType, code); // CilType.SystemTypeType pushed to the stack } else { method.Parameters.Add(new JavaFieldRef("", JavaType.ClassType)); code.NewInstruction(0x12 /* ldc */, castType.AdjustRank(-1), null); code.StackMap.PushStack(JavaType.ClassType); } method.Parameters.Add(new JavaFieldRef("", JavaType.BooleanType)); code.NewInstruction(0x12 /* ldc */, null, (int)(@throw ? 1 : 0)); code.StackMap.PushStack(JavaType.IntegerType); // boolean code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, method); code.StackMap.PopStack(CilMain.Where); // boolean code.StackMap.PopStack(CilMain.Where); // class/type code.StackMap.PopStack(CilMain.Where); // array return(true); } return(false); }
internal static JavaCode CreateHelperMethod(JavaClass theClass, JavaMethodRef methodType, int maxLocals, int maxStack) { var newMethod = new JavaMethod(theClass, methodType); newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_BRIDGE; var code = newMethod.Code = new JavaCode(); code.Method = newMethod; code.Instructions = new List <JavaCode.Instruction>(); code.MaxLocals = maxLocals; code.MaxStack = maxStack; theClass.Methods.Add(newMethod); return(code); }
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 CodeArrays() { var parameters = new List <JavaFieldRef>(4); parameters.Add(new JavaFieldRef("", JavaType.ObjectType)); parameters.Add(new JavaFieldRef("", CilType.SystemTypeType)); parameters.Add(new JavaFieldRef("", CilType.SystemValueType)); parameters.Add(new JavaFieldRef("", JavaType.IntegerType)); InitArrayMethod = new JavaMethodRef("Initialize", JavaType.VoidType, parameters); parameters = new List <JavaFieldRef>(3); parameters.Add(new JavaFieldRef("", JavaType.ObjectType)); parameters.Add(new JavaFieldRef("", JavaType.IntegerType)); parameters.Add(new JavaFieldRef("", JavaType.ObjectType)); StoreArrayMethod = new JavaMethodRef("Store", JavaType.VoidType, parameters); parameters = new List <JavaFieldRef>(2); parameters.Add(new JavaFieldRef("", JavaType.ObjectType)); parameters.Add(new JavaFieldRef("", JavaType.IntegerType)); LoadArrayMethod = new JavaMethodRef("Load", JavaType.ObjectType, parameters); }
public void EnterMethod(CilType type, JavaMethodRef method, bool instanceMethod) { // // we expect the method definition to contain the suffix type parameters // (added by CilMethod.ImportGenericParameters), so we subtract their // number to determine the index of the first type parameter. if this // is an instance method, we add one to account for 'this' argument. // var argumentIndex = method.Parameters.Count - type.GenericParameters.Count; if (instanceMethod) { argumentIndex++; } foreach (var genericParameter in type.GenericParameters) { var item = new GenericStackItem(); item.Name = genericParameter.JavaName; item.Index = ++argumentIndex; // index always +1 items.Add(item); } }
public static void Indirection(JavaCode code, Code cilOp) { var(name, opcodeType) = IndirectOpCodeToNameAndType(cilOp); bool isRef = opcodeType.Equals(JavaType.ObjectType); bool isLoad; if (cilOp >= Code.Ldind_I1 && cilOp <= Code.Ldind_Ref) { isLoad = true; } else { isLoad = false; var valueType = code.StackMap.PopStack(CilMain.Where); if (valueType.IsReference != isRef) { throw new InvalidProgramException(); } } var stackTop = (CilType)code.StackMap.PopStack(CilMain.Where); if (stackTop.IsGenericParameter) { if (isLoad) { var resultType = GenericUtil.CastMaybeGeneric(stackTop, false, code); if (resultType == stackTop && (!stackTop.Equals(JavaType.ObjectType))) { code.NewInstruction(0xC0 /* checkcast */, stackTop.AsWritableClass, null); resultType = stackTop; } code.StackMap.PushStack(resultType); } else { code.NewInstruction(0x5F /* swap */, null, null); GenericUtil.ValueCopy(stackTop, code); } return; } // // non-generic object reference // var boxedType = stackTop as BoxedType; if (boxedType == null || boxedType.IsBoxedReference != isRef) { if (CodeSpan.LoadStore(isLoad, stackTop, opcodeType, null, code)) { return; } if (object.ReferenceEquals(stackTop, CodeArrays.GenericArrayType)) { // a byref parameter T[] gets translated to java.lang.Object, // so we have to explicitly cast it to system.Reference boxedType = new BoxedType(stackTop, false); code.NewInstruction(0x5F /* swap */, null, null); code.NewInstruction(0xC0 /* checkcast */, boxedType.AsWritableClass, null); code.NewInstruction(0x5F /* swap */, null, null); } else { throw new ArgumentException($"incompatible type '{stackTop}'"); } } var unboxedType = boxedType.UnboxedType; var unboxedTypeCode = unboxedType.IsReference ? 0 : unboxedType.PrimitiveType; JavaMethodRef method; if (CompareIndirectTypes(unboxedTypeCode, opcodeType.PrimitiveType)) { // indirect access to a primitive or reference type, with a // reference type that represents the boxed form of the same type. if (isLoad) { boxedType.GetValue(code); if (unboxedType.IsReference) { // if we know the type of indirected value, cast to it if (!unboxedType.Equals(JavaType.ObjectType)) { code.NewInstruction(0xC0 /* checkcast */, unboxedType.AsWritableClass, null); } } else { unboxedType = CilType.From(opcodeType); } code.StackMap.PushStack(unboxedType); } else { // if we are storing a real array into a boxed reference of // e.g., system.Array, then we have to create an array proxy CodeArrays.MaybeGetProxy(CodeArrays.GenericArrayType, unboxedType, code); boxedType.SetValueOV(code); } return; } // indirect access to a primitive value from a reference type that // represents some other a primitive value; for example ldind.r4 // from a system.Int32. we call the "CodeNumber.Indirection methods" // helpers, defined in all baselib primitives, to assist. if (opcodeType.IsIntLike) { opcodeType = JavaType.IntegerType; } if (isLoad) { method = new JavaMethodRef("Get_" + name, opcodeType); code.StackMap.PushStack(CilType.From(opcodeType)); } else { method = new JavaMethodRef("Set_" + name, JavaType.VoidType, opcodeType); } code.NewInstruction(0xB6 /* invokevirtual */, boxedType, method); }
public static void LoadFunction(JavaCode code, Mono.Cecil.Cil.Instruction cilInst) { if (cilInst.Operand is MethodReference implMethodRef) { var implMethod = CilMethod.From(implMethodRef); var(declType, declMethod, dlgMethodName) = FindInterfaceType(cilInst); if (dlgMethodName != null) { // if this is an artificial delegate for a functional interface, // then we can't actually instantiate this particular delegate, // because its definition only exists in the DLL created by // DotNetImporter; see BuildDelegate there. we fix the Newobj // instruction to instantiate system.FunctionalInterfaceDelegate. cilInst.Next.Operand = DelegateConstructor(cilInst.Next.Operand); declMethod = new JavaMethodRef(dlgMethodName, implMethod.ReturnType, implMethod.Parameters); } if (IsPrimitive(declMethod.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 MakeInterface and GenerateInvoke declMethod = new JavaMethodRef( declMethod.Name, JavaType.ObjectType, declMethod.Parameters); } // select the method handle kind for the dynamic call site. // for a non-static invocation, we need to push an object reference. JavaMethodHandle.HandleKind callKind; if (implMethod.DeclType.IsInterface) { callKind = JavaMethodHandle.HandleKind.InvokeInterface; } else { callKind = JavaMethodHandle.HandleKind.InvokeVirtual; } if (cilInst.OpCode.Code == Code.Ldftn) { if (implMethod.IsStatic) { callKind = JavaMethodHandle.HandleKind.InvokeStatic; } else { var implMethodDef = CilMethod.AsDefinition(implMethodRef); bool isVirtual = implMethodDef.IsVirtual; if (isVirtual && implMethodDef.DeclaringType.IsSealed) { isVirtual = false; } if (!isVirtual) { // non-virtual instance method code.NewInstruction(0x59 /* dup */, null, null); code.StackMap.PushStack(JavaType.ObjectType); } else { // virtual method should only be specified in 'ldvirtftn' throw new Exception("virtual method referenced"); } } } // // the method may take generic type parameters, i.e. the System.Type // parameters that are added at the end of the parameter list, by // CilMethod::ImportGenericParameters. but when the delegate is // called, these parameters will not be pushed. therefore we do // the following when assigning such a method to a delegate: // // (1) we select a different implementing method, i.e. the bridge // method generated by CreateCapturingBridgeMethod (see below), // which moves the type arguments to the head of the parameter list. // (2) we generate and push the type arguments at this time, via // calls to GenericUtil.LoadGeneric. // (3) we specify the type arguments as capture parameters of the // call site. (see also JavaCallSite.) this means the generated // proxy will inject these parameters when it calls the bridge // method from step (1), and that bridge method will push these // parameters at the end of the parameter list, when it invokes // the actual target method. // List <JavaFieldRef> TypeArgs = null; JavaMethodRef implMethod2 = implMethod; var implGenericMethod = implMethod.WithGenericParameters; if (implGenericMethod != implMethod) { implMethod2 = new JavaMethodRef( "delegate-bridge-" + implGenericMethod.Name, implMethod.ReturnType, implMethod.Parameters); var count1 = implMethod.Parameters.Count; var count2 = implGenericMethod.Parameters.Count; TypeArgs = implGenericMethod.Parameters.GetRange(count1, count2 - count1); var callMethod = CilMain.GenericStack.EnterMethod(implMethodRef); for (int i = count1; i < count2; i++) { GenericUtil.LoadGeneric(implGenericMethod.Parameters[i].Name, code); } for (int i = count1; i < count2; i++) { code.StackMap.PopStack(CilMain.Where); } } // create a CallSite that implements the method signature // declMethod, in the functional interface declType, in order // to proxy-invoke the method implMethod.DeclType::implMethod. var callSite = new JavaCallSite(declType, declMethod, implMethod.DeclType, implMethod2, TypeArgs, callKind); code.NewInstruction(0xBA /* invokedynamic */, null, callSite); if (callKind != JavaMethodHandle.HandleKind.InvokeStatic) { code.StackMap.PopStack(CilMain.Where); } code.StackMap.PushStack(CilType.From(declType)); } else { throw new InvalidProgramException(); } // // ldftn or ldvirtftn should be followed by newobj, which we can use // to identify the delegate, and therefore, the interface // (JavaType, JavaMethodRef, string) FindInterfaceType( Mono.Cecil.Cil.Instruction cilInst) { cilInst = cilInst.Next; if (cilInst != null && cilInst.OpCode.Code == Code.Newobj && cilInst.Operand is MethodReference constructorRef) { var dlgType = CilType.From(constructorRef.DeclaringType); if (dlgType.IsDelegate) { // // check for an artificial delegate, generated to represent a // java functional interface: (BuildDelegate in DotNetImporter) // // delegate type is marked [java.lang.attr.AsInterface], // and is child of an interface type. // interface type is marked [java.lang.attr.RetainName], // and has one method. // var dlgType0 = CilType.AsDefinition(constructorRef.DeclaringType); if (dlgType0.HasCustomAttribute("AsInterface")) { var ifcType0 = dlgType0.DeclaringType; var ifcType = CilType.From(ifcType0); if (ifcType.IsInterface && ifcType.IsRetainName && ifcType0.HasMethods && ifcType0.Methods.Count == 1) { return(ifcType, null, ifcType0.Methods[0].Name); } } // // otherwise, a normal delegate, which may be generic, or plain. // interface name is DelegateType$interface. // we look for a method named Invoke. // foreach (var method in dlgType0.Methods) { if (method.Name == "Invoke") { return(InterfaceType(dlgType), CilMethod.From(method), null); } } } } throw new InvalidProgramException(); } // // create a method reference to delegate constructor from baselib: // system.MulticastDelegate::.ctor(object, object) // CilMethod DelegateConstructor(object Operand) { var baseType = CilType.AsDefinition(((MethodReference)Operand) .DeclaringType).BaseType; if (baseType.Namespace != "System" || baseType.Name != "MulticastDelegate") { throw CilMain.Where.Exception( $"delegate base type is '{baseType.Name}', " + "but expected 'System.MulticastDelegate'"); } return(CilMethod.CreateDelegateConstructor()); } }
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() { } }
bool Translate_Newobj(CilMethod callMethod) { if (callMethod.IsStatic) { return(false); } var newClass = callMethod.DeclType; if (callMethod.IsArrayMethod) { arrays.New(newClass, callMethod.Parameters.Count); return(true); } if (newClass.IsValueClass) { if (!callMethod.IsValueInit) { return(false); } } else { if (!callMethod.IsConstructor) { return(false); } if (newClass.Equals(JavaType.StringType) && code.Method.Class.Name != newClass.JavaName) { // redirect the System.String constructor to system.String.New var newMethod = new JavaMethodRef("New", newClass); newMethod.Parameters = callMethod.Parameters; code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, newClass.JavaName), newMethod); int n = callMethod.Parameters.Count; while (n-- > 0) { stackMap.PopStack(CilMain.Where); } stackMap.PushStack(newClass); return(true); } if (newClass.Equals(JavaType.ThrowableType)) { // generally we translate System.Exception to java.lang.Throwable, // but not when allocating a new object newClass = CilType.From(new JavaType(0, 0, newClass.JavaName)); } } // // save any parameters pushed for the call to the constructor // int localIndex = SaveMethodArguments(callMethod); code.NewInstruction(0xBB /* new */, newClass.AsWritableClass, null); stackMap.PushStack(newClass); code.NewInstruction(0x59 /* dup */, null, null); stackMap.PushStack(newClass); if (newClass.IsValueClass) { // new value type object. first call the default constructor, // to allocate the object. then call the constructor provided // in the Newobj instruction, to initialize the new object var defaultConstructor = new CilMethod(newClass); PushGenericArguments(defaultConstructor); code.NewInstruction(0xB7 /* invokespecial */, newClass, defaultConstructor.WithGenericParameters); // the defaul constructor accepted generic parameters, if any, // and they should be discarded now int numGeneric = defaultConstructor.WithGenericParameters.Parameters.Count; while (numGeneric-- > 0) { stackMap.PopStack(CilMain.Where); } // if Newobj specifies a non-default constructor, then call it int numNonGeneric = callMethod.Parameters.Count; if (numNonGeneric != 0) { code.NewInstruction(0x59 /* dup */, null, null); LoadMethodArguments(callMethod, localIndex); code.NewInstruction(0xB6 /* invokevirtual */, newClass, callMethod); while (numNonGeneric-- > 0) { stackMap.PopStack(CilMain.Where); } } stackMap.PopStack(CilMain.Where); } else { // // new reference type object // LoadMethodArguments(callMethod, localIndex); PushGenericArguments(callMethod); code.NewInstruction(0xB7 /* invokespecial */, newClass.AsWritableClass, callMethod.WithGenericParameters); ClearMethodArguments(callMethod, false); } return(true); }
bool ConvertVirtualToStaticCall(CilType callClass, CilMethod callMethod) { if (callClass.ClassName == callClass.JavaName) { if (callClass.ClassName == "system.FunctionalInterfaceDelegate" && callMethod.Name == "AsInterface") { // a call to system.FunctionalInterfaceDelegate::AsInterface() // should be fixed to expect an 'object' return type, and then // cast that return type to the proper interface var tempMethod = new JavaMethodRef(callMethod.Name, JavaType.ObjectType); code.NewInstruction(0xB6 /* invokevirtual */, callClass, tempMethod); code.NewInstruction(0xC0 /* checkcast */, callMethod.ReturnType, null); stackMap.PopStack(CilMain.Where); stackMap.PushStack(callMethod.ReturnType); return(true); } if (callMethod.IsExternal && NativeMethodClasses.TryGetValue(callClass.ClassName, out var altClassName)) { // a call to an instance method System.NameSpace.Class::Method, // which is implemented only as a native method, is translated // into a static call to some other class where the method is // implemented. such implementing classes are listed in the // NativeMethodClasses dictionary the bottom of this file. var altMethodName = callMethod.Name; int altMethodNameIdx = altMethodName.IndexOf(CilMain.OPEN_PARENS); if (altMethodNameIdx != -1) { altMethodName = altMethodName.Substring(0, altMethodNameIdx); } var tempMethod = new JavaMethodRef(altMethodName, callMethod.ReturnType); var parameters = new List <JavaFieldRef>(callMethod.Parameters); parameters.Insert(0, new JavaFieldRef("this", callClass)); tempMethod.Parameters = parameters; code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, altClassName), tempMethod); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } // don't bother adjusting the call if the program explicitly // refers to the java class rather than the simulated wrapper, // e.g. to java.lang.Throwable rather than System.Exception return(false); } if (callClass.Equals(JavaType.StringType) || callClass.Equals(JavaType.ThrowableType) || (callClass.Equals(JavaType.ObjectType) && callMethod.Name == "GetType" && callMethod.ToDescriptor() == "()Lsystem/Type;")) { // we map some basic .Net types their java counterparts, so we // can't invoke .Net instance methods on them directly. instead, // we invoke a static method on our helper class. for example: // ((System.String)x).CompareTo(y) -> system.String.CompareTo(x,y) var tempMethod = new JavaMethodRef(callMethod.Name, callMethod.ReturnType); var parameters = new List <JavaFieldRef>(callMethod.Parameters); parameters.Insert(0, new JavaFieldRef("this", callClass)); tempMethod.Parameters = parameters; if (callClass.Equals(JavaType.ThrowableType) && callMethod.Name == "system-Exception-GetType") { // undo the effect of CilMethod::MethodIsShadowing upon calling // the virtual/overriding GetType() from System.Exception, and // instead call the static system.Object.GetType() tempMethod.Name = "GetType"; callClass = CilType.From(new JavaType(0, 0, "system.Object")); parameters[0].Type = JavaType.ObjectType; } else { CilMethod.FixNameForVirtualToStaticCall(tempMethod, callClass); } code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, callClass.JavaName), tempMethod); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } if (callClass.JavaName == null && callClass.Equals(JavaType.ClassType) && callMethod.Name == "GetRuntimeType") { // convert virtual call to RuntimeTypeHandle.GetRuntimeType // to a static call to system.RuntimeType code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType, new JavaMethodRef( callMethod.Name, callMethod.ReturnType, JavaType.ObjectType)); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } return(false); }