void SaveExceptionObject(TryClause tryClause, bool keepOnStack = true) { if (keepOnStack) { stackMap.PushStack(ThrowableType); code.NewInstruction(0x59 /* dup */, null, null); } tryClause.localIndex = locals.GetTempIndex(ThrowableType); code.NewInstruction(0x3A /* astore */, null, tryClause.localIndex); stackMap.PopStack(CilMain.Where); }
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 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); } }