public static void Instance(JavaCode code, CodeLocals locals, Mono.Cecil.Cil.Instruction cilInst) { if (cilInst.Operand is TypeReference cilType && cilInst.Next != null) { var stackTop = (CilType)code.StackMap.PopStack(CilMain.Where); if (!stackTop.IsReference) { throw new InvalidProgramException(); // top of stack is a primitive type } var castType = (CilType)CilType.From(cilType); JavaType castClass = CilType.From(cilType).AsWritableClass; if (GenericUtil.ShouldCallGenericCast(stackTop, castType) || castType.IsGenericParameter) { code.StackMap.PushStack(stackTop); // casting to a generic type is done via GenericType.TestCast GenericUtil.CastToGenericType(cilType, 0, code); code.StackMap.PopStack(CilMain.Where); // stackTop if (!castType.IsGenericParameter) { code.NewInstruction(0xC0 /* checkcast */, castClass, null); } code.StackMap.PushStack(castClass); } else if (CodeArrays.CheckCast(castType, false, code)) { // if casting to Object[], ValueType[], to an array of // interface type, or to an array of a generic parameter, // then CodeArrays.CheckCast already generated a call to // system.Array.CheckCast in baselib, and we are done here if (!castType.IsGenericParameter) { // avoid cast since T[] might be a primitive array code.NewInstruction(0xC0 /* checkcast */, castClass, null); } code.StackMap.PushStack(castClass); } // // the cil 'isinst' casts the operand to the requested class, // but the jvm 'instanceof' only returns zero or one. so we // also use 'checkcast' to get the jvm to acknowledge the cast // // however, if the cil 'isinst' is immediately followed by // 'brtrue' or 'brfalse' then we don't have to actually cast // else if (!TestForBranch(code, castClass, cilInst.Next)) { ushort nextLabel = (ushort)cilInst.Next.Offset; int localIndex = locals.GetTempIndex(stackTop); TestAndCast(code, castClass, stackTop, nextLabel, localIndex); locals.FreeTempIndex(localIndex); } }
void Store(CilType elemType) { 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); } if (arrayType.IsValueClass || elemType.IsValueClass) { CilMethod.ValueMethod(CilMethod.ValueClone, code); } code.NewInstruction(elemType.StoreArrayOpcode, null, null); } }
void Translate_Endfinally(int instOffset, bool setupFaultClause = false) { var thisClause = RollbackStackFrame(instOffset); if (thisClause == null || (!thisClause.finallyClause)) { throw new InvalidProgramException(); } int localIndex = -1; if (thisClause.hasNestedTry) { localIndex = thisClause.tryClause.localIndex; stackMap.SetLocal(localIndex, ThrowableType); } if (thisClause.faultClause && (!setupFaultClause)) { // if this instruction appears in a 'fault' clause, // then it should be interpreted as 'endfault' rather // than 'endfilter', and just rethrow the exception if (thisClause.hasNestedTry) { code.NewInstruction(0x19 /* aload */, null, localIndex); } code.NewInstruction(0xBF /* athrow */, null, null); return; } // // typical variant of a non-nested 'endfinally' instruction. // proceed to the specified leave offset, or rethrow the exception. // if (!thisClause.hasNestedTry) { localIndex = locals.GetTempIndex(ThrowableType); stackMap.SetLocal(localIndex, JavaStackMap.Top); stackMap.PushStack(ThrowableType); code.NewInstruction(0x3A /* astore */, null, localIndex); stackMap.PopStack(CilMain.Where); } // if a try/catch exits due to non-exceptional control flow, then // the exception object will be a 'fake' exception object created // in Translate_Leave, see there. we have to identify this, and // branch accordingly. if (thisClause.leaveOffsets != null) { code.NewInstruction(0x19 /* aload */, null, localIndex); code.NewInstruction(0xB8 /* invokestatic */, LeaveTargetType, new JavaMethodRef("Get", JavaType.IntegerType, ThrowableType)); int leaveIndex = -1; if (thisClause.leaveOffsets.Count != 1) { leaveIndex = locals.GetTempIndex(JavaType.IntegerType); code.NewInstruction(0x36 /* istore */, null, leaveIndex); } foreach (var leaveOffset in thisClause.leaveOffsets) { if (leaveIndex != -1) { code.NewInstruction(0x15 /* iload */, null, leaveIndex); } stackMap.PushStack(JavaType.IntegerType); code.NewInstruction(0x12 /* ldc */, null, (int)leaveOffset); stackMap.PushStack(JavaType.IntegerType); code.NewInstruction(0x9F /* if_icmpeq */, null, leaveOffset); stackMap.PopStack(CilMain.Where); stackMap.PopStack(CilMain.Where); stackMap.SaveFrame(leaveOffset, true, CilMain.Where); } if (leaveIndex != -1) { locals.FreeTempIndex(localIndex); } } // if the try/catch did exit due to an exception cause, then we // just need to rethrow the exception. code.NewInstruction(0x19 /* aload */, null, localIndex); if (setupFaultClause) { // if called from ExceptionClauseSetup to setup a 'fault' clause, // then we just want to put the exception back on the stack stackMap.PushStack(ThrowableType); } else { // if called to end a 'finally' clause, then we do want to throw code.NewInstruction(0xBF /* athrow */, null, null); } if (!thisClause.hasNestedTry) { // if this 'endfinally' instruction is the last in the clause, // then we can release the local index that holds the exception if (instOffset + 1 == thisClause.catchEnd) { locals.FreeTempIndex(localIndex); } } }
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); } }