int ImportGenericParameters(TypeReference fromType) { if (!fromType.HasGenericParameters) { return(0); } int numGeneric = fromType.GenericParameters.Count; if (numGeneric != 0) { GenericParameters = new List <CilType>(numGeneric); for (int i = 0; i < numGeneric; i++) { var genericParameter = CilType.From(fromType.GenericParameters[i]); GenericParameters.Add(genericParameter); } Flags |= HAS_GEN_PRM; } return(numGeneric); }
void CastToClass(object data) { var srcType = (CilType)code.StackMap.PopStack(CilMain.Where); if (!(srcType.IsReference && data is TypeReference)) { throw new InvalidProgramException(); } byte op; var dstType = (CilType)CilType.From((TypeReference)data); if (GenericUtil.ShouldCallGenericCast(srcType, dstType)) { code.StackMap.PushStack(srcType); // casting to a generic type is done via GenericType.TestCast GenericUtil.CastToGenericType((TypeReference)data, 1, code); code.StackMap.PopStack(CilMain.Where); // srcType op = 0xC0; // checkcast } else { // cast to a non-generic type if (dstType.Equals(srcType) || dstType.Equals(JavaType.ObjectType)) { op = 0x00; // nop } else if (dstType.IsReference) { CodeArrays.CheckCast(dstType, true, code); op = 0xC0; // checkcast } else { throw new InvalidProgramException(); } } code.NewInstruction(op, dstType, null); code.StackMap.PushStack(dstType); }
public void Length() { var stackTop = (CilType)stackMap.PopStack(CilMain.Where); if (object.ReferenceEquals(stackTop, GenericArrayType)) { // length of a generic array T[] code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, "java.lang.reflect.Array"), new JavaMethodRef("getLength", JavaType.IntegerType, JavaType.ObjectType)); } else if (stackTop.IsArray) { code.NewInstruction(0xBE /* arraylength */, null, null); } else { throw new InvalidProgramException(); } stackMap.PushStack(CilType.From(JavaType.IntegerType)); }
void LinkSuperTypes(TypeDefinition defType) { int n = defType.HasInterfaces ? defType.Interfaces.Count : 0; SuperTypes = new List <CilType>(n + 1); SuperTypes.Add(CilType.From( defType.BaseType ?? defType.Module.TypeSystem.Object)); for (int i = 0; i < n; i++) { SuperTypes.Add(CilType.From(defType.Interfaces[i].InterfaceType)); } for (int i = 0; i <= n; i++) { if (SuperTypes[i].HasGenericParameters || SuperTypes[i].HasGenericSuperType) { Flags |= HAS_GEN_SUP; } } }
public static void Conversion(JavaCode code, Code cilOp) { var oldType = (CilType)code.StackMap.PopStack(CilMain.Where); var(newType, overflow, unsigned) = ConvertOpCodeToTypeCode(cilOp); int op; if (oldType.IsReference) { ConvertReference(code, oldType, newType); return; } if (newType == TypeCode.Single || newType == TypeCode.Double) { op = ConvertToFloat(code, oldType.PrimitiveType, newType, unsigned); } else if (overflow) { op = ConvertWithOverflow(code, oldType.PrimitiveType, newType); } else if (newType == TypeCode.Int64 || newType == TypeCode.UInt64) { op = ConvertToLong(code, oldType.PrimitiveType, newType); } else { op = ConvertToInteger(code, oldType.PrimitiveType, newType); } if (op != -1) { code.NewInstruction((byte)op, null, null); } code.StackMap.PushStack(CilType.From(new JavaType(newType, 0, null))); }
public static bool SubOffset(JavaType secondType, JavaCode code) { // make sure the first operand is a pointer span, not a real Span<T> var spanType1 = (CilType)code.StackMap.PopStack(CilMain.Where); if ((!spanType1.HasGenericParameters) && spanType1.GenericParameters != null && spanType1.GenericParameters[0] is CilType span1PointerType && (!span1PointerType.IsGenericParameter)) { var spanType2 = (CilType)secondType; // check if subtracting two pointer spans if ((!spanType2.HasGenericParameters) && spanType2.GenericParameters != null && spanType2.GenericParameters[0] is CilType span2PointerType && (!span2PointerType.IsGenericParameter)) { code.NewInstruction(0xB6 /* invokevirtual */, SpanType, new JavaMethodRef("Subtract", JavaType.LongType, spanType2)); code.StackMap.PushStack(CilType.From(JavaType.LongType)); return(true); } // check if subtracting an offset from a pointer span if (spanType2.Equals(JavaType.IntegerType)) { code.NewInstruction(0xB6 /* invokevirtual */, SpanType, new JavaMethodRef("Subtract", SpanType, spanType2)); code.StackMap.PushStack(SpanType); return(true); } } code.StackMap.PushStack(spanType1); return(false); }
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(JavaType.IntegerType); return; } } throw new InvalidProgramException(); }
public static void CastToGenericType(TypeReference fromType, int throwBool, JavaCode code) { var genericMark = CilMain.GenericStack.Mark(); GenericUtil.LoadMaybeGeneric(CilMain.GenericStack.EnterType(fromType), code); CilMain.GenericStack.Release(genericMark); code.NewInstruction(0x12 /* ldc */, null, throwBool); code.StackMap.PushStack(CilType.From(JavaType.IntegerType)); var parameters = new List <JavaFieldRef>(); parameters.Add(new JavaFieldRef("", JavaType.ObjectType)); parameters.Add(new JavaFieldRef("", CilType.SystemTypeType)); parameters.Add(new JavaFieldRef("", JavaType.BooleanType)); code.NewInstruction(0xB8 /* invokestatic */, GenericUtil.SystemGenericType, new JavaMethodRef("TestCast", JavaType.ObjectType, parameters)); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); }
void StoreObject(object data) { if (data is TypeReference typeRef) { var dataType = CilType.From(typeRef); var valueType = (CilType)code.StackMap.PopStack(CilMain.Where); var intoType = (CilType)code.StackMap.PopStack(CilMain.Where); if (CodeSpan.LoadStore(false, intoType, null, dataType, code)) { return; } if (intoType is BoxedType intoBoxedType && dataType.PrimitiveType == intoBoxedType.UnboxedType.PrimitiveType) { // 'stobj primitive' with a primitive value on the stack, // or 'stobj primitive[]' with a primitive array on the stack if ((!dataType.IsReference) || dataType.IsArray) { intoBoxedType.SetValueOV(code); return; } } code.StackMap.PushStack(intoType); code.StackMap.PushStack(valueType); GenericUtil.ValueCopy(dataType, code, true); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); } else { throw new InvalidProgramException(); } }
void EnterGenericInstance(IGenericInstance instance, IGenericParameterProvider provider) { if (instance.HasGenericArguments) { foreach (var genericElement in provider.GenericParameters) { var item = new GenericStackItem(); var genericArgument = instance.GenericArguments[genericElement.Position]; if (genericArgument is ArrayType genericArgumentAsArray) { item.GenericInstance = genericArgumentAsArray.ElementType as GenericInstanceType; } else { item.GenericInstance = genericArgument as GenericInstanceType; } item.GenericParent = instance; item.Name = CilType.GenericParameterFullName(genericElement); item.Type = CilType.From(genericArgument); items.Add(item); } } }
static void BitwiseNot(JavaCode code, TypeCode typeCode) { var jtype = new JavaType(typeCode, 0, null); code.StackMap.PushStack(CilType.From(jtype)); code.StackMap.PushStack(jtype); byte op = 0x82; // ixor code.NewInstruction(0x02 /* iconst_m1 */, null, null); if (typeCode == TypeCode.Int64) { code.NewInstruction(0x85 /* i2l */, null, null); op++; // lxor } else if (typeCode != TypeCode.Int32) { throw new InvalidProgramException(); } code.NewInstruction(op, null, null); code.StackMap.PopStack(CilMain.Where); }
public CilType EnterType(TypeReference fromType) { var myType = CilType.From(fromType); if (fromType is ArrayType fromArrayType) { EnterType(fromArrayType.ElementType); } else { if (fromType.HasGenericParameters) { EnterGenericProvider(fromType, myType, -1); } if (fromType.IsGenericInstance) { var genericInstance = (GenericInstanceType)fromType; EnterGenericInstance(genericInstance, genericInstance.ElementType); } } return(myType); }
public void Load(Code op, object data, Mono.Cecil.Cil.Instruction inst) { stackMap.PopStack(CilMain.Where); // pop index CilType arrayType = (CilType)stackMap.PopStack(CilMain.Where); CilType elemType = null; TypeCode elemCode = 0; switch (op) { case Code.Ldelem_Ref: if (arrayType.IsValueClass) { throw new InvalidProgramException(); } elemType = arrayType.AdjustRank(-1); break; case Code.Ldelem_Any: //if (data is ArrayType || (! (data is TypeReference))) if (!(data is TypeReference)) { throw new InvalidProgramException(); } elemType = CilType.From((TypeReference)data); // check if array element is loaded and boxed, only to check // its type or check if null. in this case we take a shortcut // and load directly from the array, without making a copy. var next = inst.Next; if (next != null && next.OpCode.Code == Code.Box) { next = next.Next; if (next != null && CodeBuilder.IsBrTrueBrFalseIsInst(next.OpCode.Code)) { code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, "java.lang.reflect.Array"), new JavaMethodRef("get", JavaType.ObjectType, JavaType.ObjectType, JavaType.IntegerType)); stackMap.PushStack(elemType); return; } } break; case Code.Ldelem_I1: case Code.Ldelem_U1: elemCode = TypeCode.Byte; break; case Code.Ldelem_U2: elemCode = TypeCode.Char; break; case Code.Ldelem_I2: elemCode = TypeCode.Int16; break; case Code.Ldelem_I4: case Code.Ldelem_U4: elemCode = TypeCode.Int32; break; case Code.Ldelem_I8: case Code.Ldelem_I: elemCode = TypeCode.Int64; break; case Code.Ldelem_R4: elemCode = TypeCode.Single; break; case Code.Ldelem_R8: elemCode = TypeCode.Double; break; default: throw new InvalidProgramException(); } if (elemCode != 0) { elemType = CilType.From(new JavaType(elemCode, 0, null)); } Load(arrayType, elemType, inst); }
public static void Compare(JavaCode code, Code cilOp, Mono.Cecil.Cil.Instruction cilInst) { // // each cil comparison instruction checks one relation (equals, // grater than, or less than) and pushes integer 0 or 1. // in contrast, jvm compares two values and returns -1, 0, or 1. // we simulate ceq/cgt/clt as a compare and branch sequence: // // call to System.UInt{16,32}.CompareTo (for unsigned compare) // comparison like lcmp/fcmpl/dcmpl (for long/float/double) // branch like if_acmpeq/if_icmplt/ifne (depends on test) // iconst_0 // goto next instruction // branch label: // iconst_1 // if (cilInst.Next == null) { throw new InvalidProgramException(); } // // first, the comparison test. this is done in a helper method // which returns the opcode for the branch following the test. // var stackTop = code.StackMap.PopStack(CilMain.Where); var stackTop2 = code.StackMap.PopStack(CilMain.Where); byte op = (cilOp == Code.Ceq) ? TestEq(code, stackTop, stackTop2, cilInst) : TestGtLt(code, stackTop, stackTop2, /* if greater than */ (cilOp == Code.Cgt || /* (vs less than) */ cilOp == Code.Cgt_Un), /* if unsigned */ (cilOp == Code.Cgt_Un || /* or unordered */ cilOp == Code.Clt_Un)); // // as branch labels, we generally use the offset of the source // cil instruction; in this case, we need a temporary label // which does not correspond to any cil instruction, so we will // use offset+1 (knowing that ceq/cgt/clt is two bytes long) // ushort label1 = (ushort)(cilInst.Offset + 1); code.NewInstruction(op, null, label1); code.StackMap.SaveFrame(label1, true, CilMain.Where); // // if the test failed and we fall through, push 0 and branch // to the next cil instruction. otherwise push 1. // ushort label2 = (ushort)cilInst.Next.Offset; code.NewInstruction(0x03 /* iconst_0 */, null, null); code.StackMap.PushStack(CilType.From(JavaType.IntegerType)); code.NewInstruction(0xA7 /* goto */, null, label2); code.StackMap.SaveFrame(label2, true, CilMain.Where); code.NewInstruction(0x04 /* iconst_1 */, null, null, (ushort)label1); }
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 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); } }
bool ImportParameters(MethodReference fromMethod) { bool appendSuffix = false; int n = fromMethod.HasParameters ? fromMethod.Parameters.Count : 0; Parameters = new List <JavaFieldRef>(n); for (int i = 0; i < n; i++) { var fromParameterType = fromMethod.Parameters[i].ParameterType; var paramType = CilType.From(fromParameterType); if (paramType.IsPointer) { if (paramType.IsValueClass || (!paramType.IsReference)) { paramType = CilType.MakeSpanOf(paramType); } else { throw CilMain.Where.Exception("invalid pointer type in parameter"); } } if (paramType.IsGenericParameter) { appendSuffix = true; var nm = "-generic-"; if (paramType.IsArray) { nm += "array-" + paramType.ArrayRank + "-"; } if (paramType.IsByReference) { nm = "-ref" + nm.Replace("&", ""); } nm += "$" + ((GenericParameter)fromParameterType.GetElementType()).Position; paramType = CilType.WrapMethodGenericParameter(paramType, nm); Flags |= GEN_ARGS; } if (paramType.IsByReference) { if (paramType.IsValueClass) { paramType = paramType.MakeByRef(); appendSuffix = true; } else { // byref of any reference type parameter becomes system.Reference, // so add a unique method name suffix var paramBoxedType = new BoxedType(paramType, false); paramType = paramBoxedType; appendSuffix |= paramBoxedType.IsBoxedReference; } } if (!appendSuffix) { appendSuffix |= fromParameterType.IsGenericInstance; if (!paramType.IsReference) { appendSuffix |= ShouldRenamePrimitive(paramType); } } Parameters.Add(new JavaFieldRef("", paramType)); } var returnType = CilType.From(fromMethod.ReturnType); if (returnType.IsGenericParameter) { returnType = CilType.WrapMethodGenericParameter(returnType); } else if (returnType.IsByReference) { returnType = new BoxedType(returnType, false); } ReturnType = returnType; appendSuffix |= fromMethod.ReturnType.IsGenericInstance; return(appendSuffix);
void LoadConstant(Code op, Mono.Cecil.Cil.Instruction inst) { JavaType pushType; object pushValue; byte pushOpcode; if (op == Code.Ldnull) { pushValue = null; pushOpcode = 0x01; // aconst_null pushType = JavaStackMap.Null; } else { var data = inst.Operand; pushOpcode = 0x12; // ldc if (data is string stringVal) // Code.Ldstr { pushValue = stringVal; pushType = JavaType.StringType; } else if (data is double doubleVal) // Code.Ldc_R8 { pushValue = doubleVal; pushType = JavaType.DoubleType; } else if (data is float floatVal) // Code.Ldc_R4 { pushValue = floatVal; pushType = JavaType.FloatType; } else if (data is long longVal) // Code.Ldc_I8 { pushValue = longVal; pushType = JavaType.LongType; } else if (IsAndBeforeShift(inst, code)) { // jvm shift instructions mask the shift count, so // eliminate AND-ing with 31 and 63 prior to a shift code.NewInstruction(0x00 /* nop */, null, null); return; } else { pushType = JavaType.IntegerType; if (data is int intVal) // Code.Ldc_I4 { pushValue = intVal; } else if (data is sbyte sbyteVal) // Code.Ldc_I4_S { pushValue = (int)sbyteVal; } else if (op >= Code.Ldc_I4_M1 && op <= Code.Ldc_I4_8) { pushValue = (int)(op - Code.Ldc_I4_0); } else { throw new InvalidProgramException(); } } } code.NewInstruction(pushOpcode, null, pushValue); stackMap.PushStack(CilType.From(pushType)); }
static CatchClause CreateCatchClause(TryClause tryClause, ExceptionHandler cilClause) { // convert a CIL exception table entry to a CatchClause, while doing // some validity checks on the input. in particular, we expect that // there are no gaps between catch clauses, and at most one finally // (or fault) clause within a protected region. var catchClause = new CatchClause(); catchClause.catchStart = cilClause.HandlerStart.Offset; catchClause.catchEnd = cilClause.HandlerEnd?.Offset ?? 0xFFFF; int handlerStart = catchClause.catchStart; if (catchClause.catchStart >= catchClause.catchEnd || catchClause.catchStart < tryClause.tryEnd) { return(null); } if (cilClause.HandlerType == ExceptionHandlerType.Catch) { // note that we treat 'catch (System.Exception)' // as 'catch (Throwable)' so that our try/catch blocks // can really see all exceptions var myType = CilType.From(cilClause.CatchType); if (myType.JavaName == "system.Exception" && (!myType.Equals(ThrowableType))) { throw CilMain.Where.Exception( "catch of system.Exception (use System.Exception)"); } if (myType.Equals(JavaType.ObjectType)) { catchClause.catchType = ThrowableType; } else { catchClause.catchType = myType; } if (myType.HasGenericParameters) { catchClause.catchFromType = cilClause.CatchType; } } else if (cilClause.HandlerType == ExceptionHandlerType.Filter) { catchClause.catchType = ThrowableType; catchClause.filterCondStart = cilClause.FilterStart.Offset; handlerStart = catchClause.filterCondStart; if (catchClause.filterCondStart < tryClause.tryEnd) { return(null); } } else if (cilClause.HandlerType == ExceptionHandlerType.Finally || cilClause.HandlerType == ExceptionHandlerType.Fault) { catchClause.catchType = ThrowableType; catchClause.finallyClause = true; // note that we treat a 'fault' clause as a special form of a // 'finally' clause, so only one of these is allowed per 'try'. catchClause.faultClause = (cilClause.HandlerType == ExceptionHandlerType.Fault); } else { return(null); } bool consecutiveClauses = true; if (tryClause.catchClauses.Count == 0) { consecutiveClauses = (handlerStart == tryClause.tryEnd); } else { foreach (var oldCatchClause in tryClause.catchClauses) { if (handlerStart <= oldCatchClause.catchStart) { consecutiveClauses = false; } if (oldCatchClause.finallyClause) { consecutiveClauses = false; } } } if (!consecutiveClauses) { throw CilMain.Where.Exception("non-consecutive clauses in a try block"); } if (catchClause.catchEnd > tryClause.catchEnd) { tryClause.catchEnd = catchClause.catchEnd; } return(catchClause); }
static JavaType InterfaceType(CilType dlgType) => CilType.From(new JavaType(0, 0, InterfaceName(dlgType)));
static int ConvertToFloat(JavaCode code, TypeCode oldType, TypeCode newType, bool unsigned) { if (newType == TypeCode.Single) { // // convert to float // if (oldType == TypeCode.Single) { return(0x00); // nop } if (oldType == TypeCode.Double) { return(0x90); // d2f } if (oldType == TypeCode.Int64) { return(0x89); // l2f } if (oldType != TypeCode.UInt64) { return(0x86); // i2f } CallUnsignedLongToDouble(code); code.NewInstruction(0x8D /* d2f */, null, null); return(-1); // no output } else if (!unsigned) { // // convert to double // if (oldType == TypeCode.Double) { return(0x00); // nop } if (oldType == TypeCode.Single) { CilMain.MakeRoomForCategory2ValueOnStack(code); return(0x8D); // f2d } if (oldType == TypeCode.Int64) { return(0x8A); // l2d } if (oldType != TypeCode.UInt64) { return(0x87); // i2d } CallUnsignedLongToDouble(code); return(-1); // no output } else { // // convert integer, interpreted as unsigned, to a double // if (oldType == TypeCode.Single || oldType == TypeCode.Double) { throw new InvalidProgramException(); } bool fromInt32 = (oldType != TypeCode.Int64 && oldType != TypeCode.UInt64); if (fromInt32) { CilMain.MakeRoomForCategory2ValueOnStack(code); code.NewInstruction(0x85 /* i2l */, null, null); code.StackMap.PushStack(JavaType.LongType); if (oldType == TypeCode.UInt32) { code.NewInstruction(0x12 /* ldc */, null, (long)0xFFFFFFFF); code.StackMap.PushStack(JavaType.LongType); code.NewInstruction(0x7F /* land */, null, null); code.StackMap.PopStack(CilMain.Where); } } CallUnsignedLongToDouble(code); if (fromInt32) { code.StackMap.PopStack(CilMain.Where); } return(-1); // no output } void CallUnsignedLongToDouble(JavaCode code) { code.NewInstruction(0xB8 /* invokestatic */, CilType.From(JavaType.DoubleType).AsWritableClass, new JavaMethodRef("UnsignedLongToDouble", JavaType.DoubleType, JavaType.LongType)); } }
void UnboxObject(Code cilOp, object data) { if (data is TypeReference typeRef) { var dataType = CilType.From(typeRef); if (dataType.IsValueClass) { if (dataType.IsGenericParameter) { if (cilOp == Code.Unbox_Any) { // unboxing (casting) a concrete type as a generic type var stackTop = (CilType)code.StackMap.PopStack(CilMain.Where); if (stackTop.Equals(JavaType.ObjectType) || dataType.Equals(stackTop)) { code.NewInstruction(0x00 /* nop */, null, null); } else { code.NewInstruction(0xC0 /* checkcast */, dataType, null); } code.StackMap.PushStack(dataType); } else // plain Unbox { throw new InvalidProgramException(); } } else { code.StackMap.PopStack(CilMain.Where); code.NewInstruction(0xC0 /* checkcast */, dataType, null); code.StackMap.PushStack(dataType); } } else if (!dataType.IsReference) { var boxedType = new BoxedType(dataType, false); var fromType = code.StackMap.PopStack(CilMain.Where); if (!fromType.Equals(boxedType)) { code.NewInstruction(0xC0 /* checkcast */, boxedType, null); } boxedType.GetValue(code); code.StackMap.PushStack(dataType); } else { if (cilOp == Code.Unbox_Any) { NullCheck(); CastToClass(data); } else // plain Unbox { throw new InvalidProgramException(); } } } }
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 Calculation(JavaCode code, Code cilOp, Mono.Cecil.Cil.Instruction cilInst) { if (cilOp == Code.And && CodeBuilder.IsAndBeforeShift(cilInst.Previous, code)) { // jvm shift instructions mask the shift count, so // eliminate AND-ing with 31 and 63 prior to a shift code.NewInstruction(0x00 /* nop */, null, null); return; } var stackTop1 = code.StackMap.PopStack(CilMain.Where); if (cilOp == Code.Sub && CodeSpan.SubOffset(stackTop1, code)) { return; } var type1 = GetNumericTypeCode(stackTop1); if (cilOp == Code.Not) { BitwiseNot(code, type1); return; } byte op; if (cilOp == Code.Neg) { op = 0x74; // ineg } else { var stackTop2 = code.StackMap.PopStack(CilMain.Where); if ((cilOp == Code.Add || cilOp == Code.Add_Ovf_Un) && CodeSpan.AddOffset(stackTop1, stackTop2, code)) { return; } char kind; var type2 = type1; type1 = GetNumericTypeCode(stackTop2); switch (cilOp) { case Code.Add: op = 0x60; kind = 'A'; break; // iadd case Code.Sub: op = 0x64; kind = 'A'; break; // isub case Code.Mul: op = 0x68; kind = 'A'; break; // imul case Code.Div: op = 0x6C; kind = 'A'; break; // idiv case Code.Rem: op = 0x70; kind = 'A'; break; // irem case Code.And: op = 0x7E; kind = 'L'; break; // iand case Code.Or: op = 0x80; kind = 'L'; break; // ior case Code.Xor: op = 0x82; kind = 'L'; break; // ixor case Code.Shl: op = 0x78; kind = 'S'; break; // ishl case Code.Shr: op = 0x7A; kind = 'S'; break; // ishr case Code.Shr_Un: op = 0x7C; kind = 'S'; break; // iushr case Code.Div_Un: case Code.Rem_Un: op = 0x00; kind = 'U'; break; case Code.Add_Ovf: case Code.Sub_Ovf: case Code.Mul_Ovf: case Code.Add_Ovf_Un: case Code.Sub_Ovf_Un: case Code.Mul_Ovf_Un: op = 0x00; kind = 'A'; break; default: throw new InvalidProgramException(); } bool ok = true; if (kind == 'S') { // second operand must be integer shift count if (type2 != TypeCode.Int32) { ok = false; } } else { // logical and arithmetic operands must be same type if (type1 != type2) { if (kind == 'A' && type1 == TypeCode.Int64 && type2 == TypeCode.Int32) { // special case: convert the second operand to Int64 code.NewInstruction(0x85 /* i2l */, null, null); code.StackMap.PushStack(JavaType.LongType); code.StackMap.PushStack(JavaType.LongType); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); } else if (kind == 'A' && type1 == TypeCode.Int32 && type2 == TypeCode.Int64) { // special case: convert the second operand to Int32 code.NewInstruction(0x88 /* l2i */, null, null); } else { ok = false; } } if (kind == 'L') { // logical operation requires integer operands if (type1 != TypeCode.Int32 && type1 != TypeCode.Int64) { ok = false; } } } if (!ok) { throw new Exception($"unexpected opcode or operands ({type1} and {type2})"); } if (kind == 'A' && op == 0) { OverflowArithmetic(code, type1, cilOp); return; } if (kind == 'U') { UnsignedDivide(code, type1, (cilOp == Code.Rem_Un)); return; } } if (type1 == TypeCode.Int64) { op++; // ixxx -> lxxx } else if (type1 == TypeCode.Single) { op += 2; // ixxx -> fxxx } else if (type1 == TypeCode.Double) { op += 3; // ixxx -> dxxx } code.NewInstruction(op, null, null); code.StackMap.PushStack(CilType.From(new JavaType(type1, 0, null))); }
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.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 { code.NewInstruction(0xBC /* newarray */, null, elemType.NewArrayType); while (numDims-- > 0) { stackMap.PopStack(CilMain.Where); } } stackMap.PushStack(arrayType); }
public static void ImportFields(JavaClass jclass, TypeDefinition cilType, bool isRetainName) { if (cilType.HasFields) { int n = cilType.Fields.Count; if (n > 0) { if (isRetainName) { throw CilMain.Where.Exception("fields not supported in a [RetainName] type"); } jclass.Fields = new List <JavaField>(n); for (int i = 0; i < n; i++) { var cilField = cilType.Fields[i]; CilMain.Where.Push($"field '{cilField.Name}'"); if (cilField.InitialValue.Length != 0) { throw CilMain.Where.Exception("unsupported InitialValue in field"); } var myField = new JavaField(); myField.Name = CilMain.MakeValidMemberName(cilField.Name); myField.Class = jclass; myField.Flags = AttributesToAccessFlags( cilField.Attributes, (cilType.HasNestedTypes || cilType.HasGenericParameters)); if (cilType.IsEnum) { myField.Type = CilType.From(cilField.FieldType); if (cilField.Constant != null) { myField.InitConstant(cilField.Constant, CilMain.Where); } } else { myField.Type = ValueUtil.GetBoxedFieldType(null, cilField); if (((CilType)myField.Type).IsValueClass) { myField.Constant = cilField; } else { if (cilField.Constant != null) { myField.InitConstant(cilField.Constant, CilMain.Where); } if (((CilType)myField.Type).IsVolatile) { myField.Flags |= JavaAccessFlags.ACC_VOLATILE; } } } jclass.Fields.Add(myField); CilMain.Where.Pop(); } } } }
void LoadConstant(Code op, object data) { JavaType pushType; object pushValue; byte pushOpcode; if (op == Code.Ldnull) { pushValue = null; pushOpcode = 0x01; // aconst_null pushType = JavaStackMap.Null; } else { pushOpcode = 0x12; // ldc if (data is string stringVal) // Code.Ldstr { pushValue = stringVal; pushType = JavaType.StringType; } else if (data is double doubleVal) // Code.Ldc_R8 { pushValue = doubleVal; pushType = JavaType.DoubleType; } else if (data is float floatVal) // Code.Ldc_R4 { pushValue = floatVal; pushType = JavaType.FloatType; } else if (data is long longVal) // Code.Ldc_I8 { pushValue = longVal; pushType = JavaType.LongType; } else { pushType = JavaType.IntegerType; if (data is int intVal) // Code.Ldc_I4 { pushValue = intVal; } else if (data is sbyte sbyteVal) // Code.Ldc_I4_S { pushValue = (int)sbyteVal; } else if (op >= Code.Ldc_I4_M1 && op <= Code.Ldc_I4_8) { pushValue = (int)(op - Code.Ldc_I4_0); } else { throw new InvalidProgramException(); } } } code.NewInstruction(pushOpcode, null, pushValue); stackMap.PushStack(CilType.From(pushType)); }
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 CreateSuppressibleFinalize(JavaMethod innerMethod, CilType declType, JavaClass theClass) { // // if the class defines a finalizer method Finalize() then: // // - create a flag field that tracks whether finalization is suppressed // // - implement interface system.GC.FinalizeSuppressible, and its Set() // method, which sets the flag field // // - create a wrapper method that checks the flag field and possibly // invokes the original finalizer // // see also: system.GC in baselib // var flagField = new JavaField(); flagField.Name = "-finalize-suppressed"; flagField.Type = CilType.From(JavaType.BooleanType); flagField.Class = theClass; flagField.Flags = JavaAccessFlags.ACC_PRIVATE | JavaAccessFlags.ACC_VOLATILE; if (theClass.Fields == null) { theClass.Fields = new List <JavaField>(); } theClass.Fields.Add(flagField); // // implement the interface method // var ifcMethod = new JavaMethod("system-GC$SuppressibleFinalize-Set", JavaType.VoidType); ifcMethod.Class = theClass; ifcMethod.Flags = JavaAccessFlags.ACC_PUBLIC; var code = ifcMethod.Code = new JavaCode(); code.Method = ifcMethod; code.Instructions = new List <JavaCode.Instruction>(); code.MaxLocals = code.MaxStack = 2; code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0x12 /* ldc */, null, (int)1); code.NewInstruction(0xB5 /* putfield */, declType, flagField); code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null); theClass.Methods.Add(ifcMethod); theClass.AddInterface("system.GC$SuppressibleFinalize"); // // create the wrapper method // var outerMethod = new JavaMethod(theClass, innerMethod); outerMethod.Flags = JavaAccessFlags.ACC_PROTECTED; innerMethod.Flags = JavaAccessFlags.ACC_PRIVATE; innerMethod.Name += "---inner"; // prepare to generate instructions code = outerMethod.Code = new JavaCode(); code.Method = outerMethod; code.Instructions = new List <JavaCode.Instruction>(); code.StackMap = new JavaStackMap(); code.StackMap.SaveFrame((ushort)0, false, CilMain.Where); code.MaxLocals = code.MaxStack = 1; // // check the flag field to determine if suppressed // code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0xB4 /* getfield */, declType, flagField); code.NewInstruction(0x9A /* ifne != zero */, null, (ushort)0xFFFE); code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0xB7 /* invokespecial */, declType, innerMethod); code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null, /* label */ 0xFFFE); code.StackMap.SaveFrame((ushort)0xFFFE, true, CilMain.Where); theClass.Methods.Add(outerMethod); }
int InitLocalsArgs(CilMethod myMethod, List <CilType> localTypes) { var parameters = myMethod.WithGenericParameters.Parameters; int numArgs = parameters.Count; CilType thisType; if (myMethod.HasThisArg) { if (myMethod.IsConstructor) { thisType = CilType.From(JavaStackMap.UninitializedThis); } else { thisType = myMethod.DeclType; if (thisType.IsValueClass) { thisType = thisType.MakeByRef(); } } stackMap.SetLocal(0, thisType); numArgs++; } else { thisType = null; } argToLocalMap = new int[numArgs]; int nextArg = 0; int nextIndex = 0; if (thisType != null) { argToLocalMap[nextArg++] = nextIndex++; localTypes.Add(thisType); numArgs--; } for (int i = 0; i < numArgs; i++) { var argType = (CilType)parameters[i].Type; stackMap.SetLocal(nextIndex, argType); var genericType = argType.GetMethodGenericParameter(); if (genericType != null) { if (genericType.IsArray && genericType.IsGenericParameter) { // note that GenericArrayType is compared by reference // in CodeArrays, to detect an array of a generic type T[] argType = CodeArrays.GenericArrayType; } else { argType = genericType; } } argToLocalMap[nextArg++] = nextIndex; nextIndex += argType.Category; localTypes.Add(argType); while (nextIndex > localTypes.Count) { localTypes.Add(null); } } return(nextIndex); }