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 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); if (srcType.ArrayRank != 0 && srcType.ArrayRank == dstType.ArrayRank && srcType.PrimitiveType != 0 && dstType.PrimitiveType != 0 && srcType.AdjustRank(-srcType.ArrayRank).NewArrayType == dstType.AdjustRank(-dstType.ArrayRank).NewArrayType) { // casting to same java array type, e.g. byte[] to sbyte[] op = 0x00; // nop } else if (dstType.IsGenericParameter) { op = 0x00; // nop } else { op = 0xC0; // checkcast } } else { throw new InvalidProgramException(); } } code.NewInstruction(op, dstType, null); code.StackMap.PushStack(dstType); }
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); }
void Process(int numCastableInterfaces) { code = newMethod.Code = new JavaCode(newMethod); var oldLabel = code.SetLabel(0xFFFF); locals = new CodeLocals(method, defMethod, code); arrays = new CodeArrays(code, locals); exceptions = new CodeExceptions(defMethodBody, code, locals); InsertMethodInitCode(numCastableInterfaces); code.SetLabel(oldLabel); stackMap = code.StackMap; stackMap.SaveFrame(0, false, CilMain.Where); ProcessInstructions(); (code.MaxLocals, code.MaxStack) = locals.GetMaxLocalsAndStack(); locals.TrackUnconditionalBranch(null); }
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); }