public static JavaMethod CreateSyncWrapper(JavaMethod innerMethod, CilType declType) { // // if method is decorated with [MethodImplOptions.Synchronized], // create a wrapper method that locks the object (for instance methods) // or the type (for static methods), and then calls the original method, // which we make private and rename to have a unique suffix // if (innerMethod.Name == "<init>") { throw CilMain.Where.Exception("[Synchronized] is not supported on constructors"); } var outerMethod = new JavaMethod(innerMethod.Class, innerMethod); outerMethod.Flags = innerMethod.Flags; innerMethod.Flags &= ~(JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_PROTECTED); innerMethod.Flags |= JavaAccessFlags.ACC_PRIVATE; innerMethod.Name += "---inner"; // count the size of locals in the parameters, plus one. we have to // add one for an instance method, to account for the 'this' argument. // and if a static method, we have to add one for the lock object. var numLocals = 1; for (int i = outerMethod.Parameters.Count; i-- > 0;) { numLocals += outerMethod.Parameters[i].Type.Category; } // prepare to generate instructions var code = outerMethod.Code = new JavaCode(); code.Method = outerMethod; code.Instructions = new List <JavaCode.Instruction>(); code.MaxLocals = numLocals + 1; var exception = new JavaAttribute.Code.Exception(); exception.start = /* label */ 1; exception.endPlus1 = /* label */ 2; exception.handler = /* label */ 3; exception.catchType = CodeExceptions.ThrowableType.ClassName; code.Exceptions = new List <JavaAttribute.Code.Exception>(); code.Exceptions.Add(exception); code.StackMap = new JavaStackMap(); code.StackMap.SaveFrame((ushort)0, false, CilMain.Where); // get a reference to 'this' (for instance methods) // or to the type object (for static methods), // then lock on the reference pushed on the stack int lockedObjectIndex; if ((outerMethod.Flags & JavaAccessFlags.ACC_STATIC) == 0) { code.NewInstruction(0x19 /* aload */, null, (int)0); code.StackMap.PushStack(JavaType.ObjectType); code.StackMap.SetLocal(0, JavaType.ObjectType); lockedObjectIndex = 0; } else { GenericUtil.LoadMaybeGeneric(declType, code); code.NewInstruction(0x59 /* dup */, null, null); code.StackMap.PushStack(JavaType.ObjectType); code.StackMap.PopStack(CilMain.Where); code.NewInstruction(0x3A /* astore */, null, (int)numLocals); code.StackMap.SetLocal((int)numLocals, JavaType.ObjectType); lockedObjectIndex = (int)numLocals; } code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, "system.threading.Monitor"), new JavaMethodRef( "Enter", JavaType.VoidType, JavaType.ObjectType)); code.StackMap.PopStack(CilMain.Where); code.NewInstruction(0x00 /* nop */, null, null, /* label */ 1); code.StackMap.SaveFrame((ushort)1, false, CilMain.Where); // push all arguments for the call to the inner method byte callOpcode; int localIndex = 0; if ((outerMethod.Flags & JavaAccessFlags.ACC_STATIC) == 0) { code.StackMap.PushStack(JavaType.ObjectType); code.NewInstruction(0x19 /* aload */, null, (int)0); localIndex++; callOpcode = 0xB7; // invokespecial } else { callOpcode = 0xB8; // invokestatic } for (int i = 0; i < outerMethod.Parameters.Count; i++) { var paramType = outerMethod.Parameters[i].Type; code.StackMap.PushStack(paramType); code.NewInstruction(paramType.LoadOpcode, null, (int)localIndex); localIndex += paramType.Category; } code.NewInstruction(callOpcode, declType, innerMethod); // if we get here, then no exception was thrown in the // inner method, we need to unlock and return the result code.StackMap.ClearStack(); if (!innerMethod.ReturnType.Equals(JavaType.VoidType)) { code.StackMap.PushStack(innerMethod.ReturnType); } code.NewInstruction(0x00 /* nop */, null, null, /* label */ 2); code.StackMap.SaveFrame((ushort)2, false, CilMain.Where); code.NewInstruction(0x19 /* aload */, null, lockedObjectIndex); code.StackMap.PushStack(JavaType.ObjectType); code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, "system.threading.Monitor"), new JavaMethodRef( "Exit", JavaType.VoidType, JavaType.ObjectType)); code.NewInstruction(innerMethod.ReturnType.ReturnOpcode, null, null); // if we get here, then an exception was thrown, unlock // and rethrow the exception code.StackMap.ClearStack(); code.StackMap.PushStack(CodeExceptions.ThrowableType); code.NewInstruction(0x00 /* nop */, null, null, /* label */ 3); code.StackMap.SaveFrame((ushort)3, true, CilMain.Where); code.NewInstruction(0x19 /* aload */, null, lockedObjectIndex); code.StackMap.PushStack(JavaType.ObjectType); code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, "system.threading.Monitor"), new JavaMethodRef( "Exit", JavaType.VoidType, JavaType.ObjectType)); code.StackMap.PopStack(CilMain.Where); code.NewInstruction(0xBF /* athrow */, null, null); code.StackMap.ClearStack(); code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where); return(outerMethod); }
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", markGenericEntity: true); CilMain.JavaClasses.Add(infoClass); // Android 'R8' (ProGuard) might discard this new class, // so insert a dummy field with the type of the class. // see also ProGuard rules in IGenericEntity in baselib var infoClassField = new JavaField(); infoClassField.Name = "-generic-info-class"; infoClassField.Type = new JavaType(0, 0, infoClass.Name); infoClassField.Class = jclass; infoClassField.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_STATIC | JavaAccessFlags.ACC_FINAL | JavaAccessFlags.ACC_SYNTHETIC; if (jclass.Fields == null) { jclass.Fields = new List <JavaField>(1); } jclass.Fields.Add(infoClassField); } 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(); }
void ExceptionClauseSetup(ushort instOffset, CatchClause catchClause) { // if this is the first exception for the try block: // // the offset is recorded in the exception attribute, so we need // to generate a stack frame for it, which matches the stack frame // on entry to the try block, with a pushed java.lang.Throwable. var tryClause = catchClause.tryClause; if (catchClause == tryClause.catchClauses[0]) { stackMap.LoadFrame((ushort)tryClause.tryStart, false, CilMain.Where); stackMap.PushStack(ThrowableType); stackMap.SaveFrame((ushort)instOffset, true, CilMain.Where); if (!catchClause.finallyClause) { code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemUtilType, new JavaMethodRef("TranslateException", ThrowableType, ThrowableType)); } } // if this is a finally clause, we have nothing further to do. // but if this is a filter clause, do some additional set up. if (catchClause.finallyClause) { if (catchClause.hasNestedTry) { // see also: ScanCatchClauseForNestedTry SaveExceptionObject(tryClause, false); } if (catchClause.faultClause) { // the 'fault' clause should start with a check whether // an exception was thrown, which is very similar to what // 'endfinally' does at the end of a normal 'finally' block, // so we can reuse the same code. Translate_Endfinally(instOffset, true); } return; } // // if this is a filter clause, we just need to initialize loals // if (catchClause.filterCondStart != 0) { // should the filter test pass, we need push the exception // object. we don't know which local the filter test uses // to store the exception, so we make our own copy. this // will be loaded by the 'endfilter' instruction, see there. SaveExceptionObject(tryClause); return; } // // for a non-filter catch clause that catches any kind of // exception, we don't need to test the exception type at all // if (catchClause.catchType.Equals(ThrowableType)) { if (catchClause.includesRethrow) { SaveExceptionObject(tryClause); } } // // otherwise, we do need to test the exception type, and // possibly branch to a secondary catch clause in the chain. // if (catchClause.catchFromType == null) { // exception type is plain type. we will use follow up // instructions 'ifeq == zero' and 'ifne != zero', // see ExceptionClauseCommon if (catchClause.catchType.Equals(ThrowableType)) { code.NewInstruction(0x04 /* iconst_1 */, null, null); stackMap.PushStack(JavaType.IntegerType); } else { code.NewInstruction(0x59 /* dup */, null, null); stackMap.PushStack(ThrowableType); code.NewInstruction(0xC1 /* instanceof */, catchClause.catchType, null); } } else { // exception type is a generic type. we will use follow up // instructions 'ifnull' and 'ifnonnnull'. // see also below in ExceptionClauseCommon code.NewInstruction(0x59 /* dup */, null, null); stackMap.PushStack(ThrowableType); GenericUtil.CastToGenericType(catchClause.catchFromType, 0, code); } stackMap.PopStack(CilMain.Where); ExceptionClauseCommon(catchClause); }
void LoadObject(Code cilOp, object data) { if (data is TypeReference typeRef) { var dataType = CilType.From(typeRef); var fromType = (CilType)code.StackMap.PopStack(CilMain.Where); if (CodeSpan.LoadStore(true, fromType, null, dataType, code)) { return; } if ((!dataType.IsReference) && cilOp == Code.Ldobj && fromType is BoxedType fromBoxedType && dataType.PrimitiveType == fromBoxedType.UnboxedType.PrimitiveType) { // 'ldobj primitive' with a corresponding boxed type on the stack. // we implement by unboxing the boxed type into a primitive value. fromBoxedType.GetValue(code); stackMap.PushStack(fromBoxedType.UnboxedType); return; } if (dataType.IsGenericParameter || (dataType.IsValueClass && dataType.Equals(fromType))) { code.StackMap.PushStack(dataType); if (SkipClone(cilInst.Next, fromType)) { // see below for the several cases where we determine // that we can safely avoid making a clone of the value code.NewInstruction(0x00 /* nop */, null, null); } else if (dataType.IsGenericParameter) { GenericUtil.ValueClone(code); } else if (cilOp == Code.Ldobj) { code.NewInstruction(0x00 /* nop */, null, null); } else { CilMethod.ValueMethod(CilMethod.ValueClone, code); } return; } if (dataType.IsReference && cilOp == Code.Box) { // 'box' is permitted on reference types, we treat it as a cast code.StackMap.PushStack(fromType); CastToClass(data); return; } if (!dataType.IsReference) { var boxedType = new BoxedType(dataType, false); code.StackMap.PushStack(boxedType); boxedType.BoxValue(code); return; } } throw new InvalidProgramException(); bool SkipClone(Mono.Cecil.Cil.Instruction next, CilType checkType) { if (checkType.IsByReference) { return(true); } if (next == null) { return(false); } var op = next.OpCode.Code; if (IsBrTrueBrFalseIsInst(op)) { // if 'ldobj' or 'box' is followed by a check for null, // we don't actually need to clone just for the test return(true); } if (op == Code.Box) { if (next.Operand is TypeReference nextTypeRef) { // 'ldobj' may be followed by 'box', to load the value // of a byref value type, and then box it into an object. // effectively we only need to clone once, in such a case. return(CilType.From(nextTypeRef).Equals(checkType)); } } var(storeType, _) = locals.GetLocalFromStoreInst(op, next.Operand); if (storeType != null) { // 'ldobj' or 'box' may be followed by a store instruction. // if storing into a variable of the same value type, the // next instruction will copy the value, so skip clone. return(storeType.Equals(checkType)); } if (op == Code.Ret && checkType.IsClonedAtTop) { // if the value on the stack was cloned/boxed at the top of // the method, then we can avoid clone and return it directly. // see also: CilType.MakeClonedAtTop and its callers. return(true); } if (op == Code.Unbox_Any) { if (next.Operand is TypeReference nextTypeRef) { // 'ldobj' or 'box' may be followed by 'unbox' and // then one of the instructions above, e.g. 'brtrue' // or 'stloc'. we still want to detect such a case // and prevent a needless clone. return(SkipClone(next.Next, CilType.From(nextTypeRef))); } } return(false); } }
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 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 bool Address(CilType fromType, CilType intoType, JavaCode code) { if (intoType.Equals(SpanType) && (!fromType.Equals(SpanType))) { // allow assignment of null to clear the pointer if (fromType.Equals(JavaStackMap.Null)) { return(true); } #if false if (fromType.JavaName == "system.Void.Pointer" && (!intoType.HasGenericParameters) && intoType.GenericParameters != null) { code.NewInstruction(0xC0 /* checkcast */, SpanType, null); return(true); } #endif // allow assignment of native int (presumably zero) bool callAssign = false; bool pushNullType = true; JavaType argType = fromType; JavaType retType = SpanType; if ((!fromType.IsReference) && fromType.PrimitiveType == TypeCode.UInt64) { callAssign = true; } else if (intoType.GenericParameters != null) { // allow assignment when the types match callAssign = intoType.GenericParameters[0].Equals(fromType) || fromType.JavaName == intoType.GenericParameters[0].JavaName; // for arbitrary value types, call a Assign(ValueType) if (fromType.IsValueClass) { argType = retType = CilType.SystemValueType; GenericUtil.LoadMaybeGeneric(fromType, code); pushNullType = false; } } if (callAssign) { if (pushNullType) { code.NewInstruction(0x01 /* aconst_null */, null, null); code.StackMap.PushStack(CilType.SystemTypeType); } code.NewInstruction(0xB8 /* invokestatic */, SpanType, new JavaMethodRef("Assign" + CilMain.EXCLAMATION, retType, argType, CilType.SystemTypeType)); code.NewInstruction(0xC0 /* checkcast */, SpanType, null); code.StackMap.PopStack(CilMain.Where); // type argument return(true); } throw new Exception($"bad assignment of '{fromType.JavaName}' into pointer of '{intoType.GenericParameters[0].JavaName}'"); } return(false); }
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 void Sizeof(object data, JavaCode code) { if (data is TypeReference operandTypeReference) { var myType = CilType.From(operandTypeReference); if (myType.IsValueClass) { // compiler generated IL typically calculates the stackalloc // total buffer size, for primitive types, and uses 'sizeof' // for values types. but it also uses 'sizeof' for generic // types, which may stand for a primitive type. GenericUtil.LoadMaybeGeneric(myType, code); code.NewInstruction(0xB8 /* invokestatic */, SpanType, new JavaMethodRef("Sizeof" + CilMain.EXCLAMATION, JavaType.IntegerType, CilType.SystemTypeType)); code.StackMap.PopStack(CilMain.Where); // generic type code.StackMap.PushStack(CilType.From(JavaType.IntegerType)); return; } else if (!myType.IsReference) { // if the compiler inserted a typeof(primitive) instruction, // then we need to replace it with a load constant int size = -1; if (myType.Category == 2) // Double, Int64, UInt64 { size = 8; } else { switch (myType.PrimitiveType) { case TypeCode.Single: case TypeCode.UInt32: case TypeCode.Int32: size = 4; break; case TypeCode.UInt16: case TypeCode.Int16: case TypeCode.Char: size = 2; break; case TypeCode.Byte: case TypeCode.SByte: case TypeCode.Boolean: size = 1; break; } } if (size != -1) { code.NewInstruction(0x12 /* ldc */, null, size); code.StackMap.PushStack(CilType.From(JavaType.IntegerType)); return; } } } throw new InvalidProgramException(); }
public void New(CilType elemType, int numDims, bool arrayTypeOnStack = false) { var elemTypeForArray = elemType.IsGenericParameter ? CilType.From(JavaType.ObjectType) : elemType; var arrayType = elemTypeForArray.AdjustRank(numDims); if (elemType.IsGenericParameter) { /*if (numDims != 1) * throw new Exception("unsupported number of dimensions in generic array");*/ if (!arrayTypeOnStack) { GenericUtil.LoadMaybeGeneric(elemType, code); } var parameters = new List <JavaFieldRef>(); for (int i = 0; i < numDims; i++) { parameters.Add(new JavaFieldRef("", JavaType.IntegerType)); } parameters.Add(new JavaFieldRef("", CilType.SystemTypeType)); code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, new JavaMethodRef("New", JavaType.ObjectType, parameters)); stackMap.PopStack(CilMain.Where); // type while (numDims-- > 0) { stackMap.PopStack(CilMain.Where); } arrayType = GenericArrayType; } else if (elemType.IsReference || numDims > 1) { if (numDims == 1) { code.NewInstruction(0xBD /* anewarray */, elemType, null); } else { code.NewInstruction(0xC5 /* multianewarray */, arrayType, numDims); } for (int i = 0; i < numDims; i++) { stackMap.PopStack(CilMain.Where); } stackMap.PushStack(arrayType); // arrayObj if (elemType.ArrayRank != 0) { if (numDims == 1) { // notify the array support methods in baselib that this // is a jagged array, i.e. a single dimension array with // an element type that is also an array. for arrays of // generic type, see system.Array.New() in baselib. stackMap.PushStack(JavaType.ObjectType); code.NewInstruction(0x59 /* dup */, null, null); code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, new JavaMethodRef("MarkJagged", JavaType.VoidType, JavaType.ObjectType)); stackMap.PopStack(CilMain.Where); } } else if (elemType.IsValueClass) { code.NewInstruction(0x59 /* dup */, null, null); stackMap.PushStack(arrayType); // arrayCopy if (elemType.HasGenericParameters) { // array of a value class ArrayElement<T>, pass this type // as the second parameter to system.Array.Initialize GenericUtil.LoadMaybeGeneric(elemType, code); // third parameter is null code.NewInstruction(0x01 /* aconst_null */, null, null); stackMap.PushStack(JavaType.ObjectType); } else { // array of a plain value class, pass a constructed object // as the third parameter to system.Array.Initialize // second parameter is null code.NewInstruction(0x01 /* aconst_null */, null, null); stackMap.PushStack(CilType.SystemTypeType); // model parameter is a new object code.NewInstruction(0xBB /* new */, elemType.AsWritableClass, null); stackMap.PushStack(elemType); code.NewInstruction(0x59 /* dup */, null, null); stackMap.PushStack(elemType); code.NewInstruction(0xB7 /* invokespecial */, elemType.AsWritableClass, new CilMethod(elemType)); stackMap.PopStack(CilMain.Where); } code.NewInstruction(0x12 /* ldc */, null, numDims); stackMap.PushStack(JavaType.IntegerType); code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, InitArrayMethod); stackMap.PopStack(CilMain.Where); // numDims stackMap.PopStack(CilMain.Where); // elemType stackMap.PopStack(CilMain.Where); // arrayType stackMap.PopStack(CilMain.Where); // arrayCopy } stackMap.PopStack(CilMain.Where); // arrayObj } else { if (numDims == 1) { var length = stackMap.PopStack(CilMain.Where); stackMap.PushStack(length); if (length.Equals(JavaType.LongType)) { CodeNumber.Conversion(code, Code.Conv_Ovf_I4, null); } } code.NewInstruction(0xBC /* newarray */, null, elemType.NewArrayType); while (numDims-- > 0) { stackMap.PopStack(CilMain.Where); } } stackMap.PushStack(arrayType); }
void Store(CilType elemType, Mono.Cecil.Cil.Instruction inst) { stackMap.PopStack(CilMain.Where); // value stackMap.PopStack(CilMain.Where); // index var arrayType = stackMap.PopStack(CilMain.Where) as CilType; // array var arrayElemType = arrayType.AdjustRank(-arrayType.ArrayRank); if (elemType == null) { elemType = arrayElemType; } /*Console.WriteLine("(STORE) ARRAY TYPE = " + arrayType + "," + arrayType.ArrayRank + " ELEM TYPE = " + elemType + "," + elemType.ArrayRank + " ELEMVAL? " + elemType.IsValueClass + " ELEMGEN? " + elemType.IsGenericParameter);*/ if (object.ReferenceEquals(arrayType, GenericArrayType)) { // stelem.any T into generic array T[] code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, StoreArrayMethod); } else if (arrayElemType.IsValueClass && elemType.IsValueClass) { // storing a value type into an array of value types. // we use ValueType.ValueCopy to write over the element. int localIndex = locals.GetTempIndex(elemType); code.NewInstruction(elemType.StoreOpcode, null, localIndex); code.NewInstruction(arrayType.LoadArrayOpcode, null, null); code.NewInstruction(elemType.LoadOpcode, null, localIndex); locals.FreeTempIndex(localIndex); // we can pass any type that is not a generic parameter GenericUtil.ValueCopy(CilType.SystemTypeType, code, true); } else if (arrayType.ArrayRank > 1) { // always 'aastore' if multidimensional array code.NewInstruction(arrayType.StoreArrayOpcode, null, null); } else { if (elemType.PrimitiveType == TypeCode.Int16 && (arrayType.PrimitiveType == TypeCode.Char || arrayType.PrimitiveType == TypeCode.UInt16)) { // stelem.i2 with a char[] array, should be 'castore' not 'sastore' elemType = arrayType.AdjustRank(-arrayType.ArrayRank); } else { // Android AOT crashes the compilation if an immediate value // is stored into a byte or short array, and the value does // not fit within the range -128..127 or -32768..32767. // simply checing if the previous instruction loaded the // constant is not enough, because due to method inlining // by the Android ART JIT, the immediate value might actually // originate in a calling method. // so we always force the value into range using i2b/i2s. // see also: CodeNumber::ConvertToInteger if (arrayType.PrimitiveType == TypeCode.Boolean || arrayType.PrimitiveType == TypeCode.SByte || arrayType.PrimitiveType == TypeCode.Byte) { code.NewInstruction(0x91 /* i2b */, null, null); } else if (arrayType.PrimitiveType == TypeCode.Int16) { code.NewInstruction(0x93 /* i2s */, null, null); } } if (arrayType.IsValueClass || elemType.IsValueClass) { CilMethod.ValueMethod(CilMethod.ValueClone, code); } code.NewInstruction(elemType.StoreArrayOpcode, null, null); } }
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 StoreFieldValue(string fldName, CilType fldType, CilType fldClass, bool isStatic, bool isVolatile) { // // we generate a sequence of instructions based on the combination of // isStatic, isGeneric, isBoxed, isCopyable // // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [BOX] -> BoxedType.SetVO // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0: // * [VAL] -> LoadST -> [VAL] [ST] -> swap -> [ST] [VAL] -> putfield // // isStatic=1 IsGeneric=0 IsBoxed=1 IsCopyable=X: // [VAL] -> getstatic -> [VAL] [BOX] -> BoxedType.SetVO // // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=1: // [VAL] -> getstatic -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=0: // [VAL] -> putstatic // // isStatic=0 IsGeneric=X IsBoxed=1 IsCopyable=X: // * [INS] [VAL] -> swap -> [VAl] [INS] -> getfield -> [VAL] [BOX] -> BoxedType.SetOV // // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=1: // [INS] [VAL] -> swap -> [VAL] [INS] -> getfield -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=0: // [INS] [VAL] -> putfield // // the combinations marked with an asterisk require swapping operands, // which is handled more effectively by first popping the value operand // var fldRef = new JavaFieldRef(fldName, fldType); bool isBoxed = fldType is BoxedType; if (isStatic) { if (fldClass.HasGenericParameters) { // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1 // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0 StoreStaticGeneric(fldClass, fldType, fldRef, isVolatile); } else { // isStatic=1 IsGeneric=0 IsBoxed=1 IsCopyable=X // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=1 // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=0 StoreStaticRegular(fldClass, fldType, fldRef, isVolatile); } } else { // isStatic=0 IsGeneric=X IsBoxed=1 IsCopyable=X // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=1 // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=0 StoreInstance(fldClass, fldType, fldRef, isVolatile); } return(true); void StoreStaticGeneric(CilType fldClass, CilType fldType, JavaFieldRef fldRef, bool isVolatile) { // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [BOX] -> BoxedType.SetVO // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0: // * [VAL] -> LoadST -> [VAL] [ST] -> swap -> [ST] [VAL] -> putfield if (fldType.IsValueClass) { fldClass = LoadStaticData(fldClass); code.NewInstruction(0xB4 /* getfield */, fldClass.AsWritableClass, fldRef); if (fldType is BoxedType boxedType && (!boxedType.IsBoxedIntPtr)) { boxedType.SetValueVO(code, isVolatile); } else { GenericUtil.ValueCopy(fldType, code); } }