/// <summary> /// Default ctor /// </summary> internal AssemblyClasses(AssemblyDefinition assembly, Action<ClassSource> jarLoaded=null) { this.assembly = assembly; foreach (var attr in assembly.GetJavaCodeAttributes()) { var resourceName = (string)attr.ConstructorArguments[0].Value; var fileName = attr.ConstructorArguments.Count > 1 ? (string)attr.ConstructorArguments[1].Value : null; JavaCode javaCode; if (!javaCodes.TryGetValue(resourceName, out javaCode)) { var resource = assembly.MainModule.Resources.FirstOrDefault(x => x.Name == resourceName) as EmbeddedResource; if (resource == null) throw new LoaderException("Cannot find resource " + resourceName); javaCode = new JavaCode(resource, fileName); javaCodes[resourceName] = javaCode; if (jarLoaded != null) jarLoaded(javaCode.ClassSource); foreach (var classFileName in javaCode.Resolve(null).ClassFileNames) { var className = classFileName; if (className.EndsWith(".class", StringComparison.OrdinalIgnoreCase)) { className = className.Substring(0, className.Length - ".class".Length); } var jClass = new JavaClass(className, javaCode); javaClasses[className] = jClass; } } } var scope = AssemblyResolver.IsFrameworkAssembly(assembly) ? AttributeConstants.Dot42Scope : assembly.FullName; var ignoreFromJavaTypes = new List<TypeDefinition>(); foreach (var type in assembly.MainModule.Types) { CollectDexImportClasses(type, className2DexImportMap, type2DexImportMap, null, scope, ignoreFromJavaTypes); } foreach (var type in ignoreFromJavaTypes) { var attr = type.GetDexOrJavaImportAttribute(); var className = (string)attr.ConstructorArguments[0].Value; DexImport dexImport; if (className2DexImportMap.TryGetValue(className, out dexImport)) { dexImport.AddType(type); type2DexImportMap[type] = dexImport; } } }
public static bool CompareGtLt(JavaType stackTop, JavaType stackTop2, JavaCode code) { if (stackTop.Equals(SpanType) && stackTop2.Equals(SpanType)) { code.NewInstruction(0x01 /* aconst_null */, null, null); code.StackMap.PushStack(CilType.SystemTypeType); code.NewInstruction(0xB8 /* invokestatic */, SpanType, new JavaMethodRef("CompareTo" + CilMain.EXCLAMATION, JavaType.IntegerType, SpanType, SpanType, CilType.SystemTypeType)); code.StackMap.PopStack(CilMain.Where); // null type return(true); } return(false); }
public virtual void BoxValue(JavaCode code) { if (UnboxedType.IsEnum) { code.NewInstruction(0x12 /* ldc */, this, null); code.StackMap.PushStack(JavaType.ClassType); code.NewInstruction(0xB8 /* invokestatic */, SystemEnumType, new JavaMethodRef("Box", JavaType.ObjectType, UnboxedTypeInMethod, JavaType.ClassType)); code.NewInstruction(0xC0 /* checkcast */, this, null); code.StackMap.PopStack(CilMain.Where); } else { code.NewInstruction(0xB8 /* invokestatic */, this, new JavaMethodRef("Box", this, UnboxedTypeInMethod)); } }
public static void Conversion(JavaCode code, Code cilOp, Mono.Cecil.Cil.Instruction cilInst) { 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 { var opNext = cilInst.Next?.OpCode.Code ?? 0; op = ConvertToInteger(code, oldType.PrimitiveType, newType, opNext); } if (op != -1) { code.NewInstruction((byte)op, null, null); } code.StackMap.PushStack(CilType.From(new JavaType(newType, 0, null))); }
public static bool AddOffset(JavaType offsetType, JavaType spanType, JavaCode code) { if (spanType.Equals(SpanType)) { code.StackMap.PushStack(spanType); if (offsetType.Equals(JavaType.IntegerType)) { code.NewInstruction(0x85 /* i2l */, null, null); offsetType = JavaType.LongType; } code.StackMap.PushStack(offsetType); bool loadedType = false; if (spanType is CilType spanType2) { if ((!spanType2.HasGenericParameters) && spanType2.GenericParameters != null && spanType2.GenericParameters[0] is CilType spanPointerType && (!spanPointerType.IsGenericParameter)) { GenericUtil.LoadMaybeGeneric(spanPointerType, code); loadedType = true; } } if (!loadedType) { code.NewInstruction(0x01 /* aconst_null */, null, null); code.StackMap.PushStack(CilType.SystemTypeType); } code.NewInstruction(0xB6 /* invokevirtual */, SpanType, new JavaMethodRef("Add", SpanType, offsetType, CilType.SystemTypeType)); code.StackMap.PopStack(CilMain.Where); // span type code.StackMap.PopStack(CilMain.Where); // offset return(true); } return(false); }
public static bool AddressArray(CilType elemType, CilType spanType, JavaCode code) { if (spanType != null && spanType.Equals(SpanType) && elemType.ArrayRank == 0 && (!elemType.Equals(SpanType))) { GenericUtil.LoadMaybeGeneric(elemType, code); code.NewInstruction(0xB8 /* invokestatic */, SpanType, new JavaMethodRef("AssignArray" + CilMain.EXCLAMATION, CilType.SystemValueType, JavaType.ObjectType, JavaType.IntegerType, CilType.SystemTypeType)); code.NewInstruction(0xC0 /* checkcast */, SpanType, null); code.StackMap.PopStack(CilMain.Where); // type argument code.StackMap.PushStack(spanType); return(true); } return(false); }
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 byte CompareEq(JavaType stackTop, JavaType stackTop2, Mono.Cecil.Cil.Instruction cilInst, JavaCode code) { if (stackTop.Equals(SpanType) && (stackTop2.PrimitiveType == TypeCode.Int64 || stackTop2.PrimitiveType == TypeCode.UInt64)) { // compare Span with long throw new InvalidProgramException(); } if (stackTop2.Equals(SpanType) && (stackTop.PrimitiveType == TypeCode.Int64 || stackTop.PrimitiveType == TypeCode.UInt64)) { if (cilInst.Previous == null || cilInst.Previous.OpCode.Code != Code.Conv_U || cilInst.Previous.Previous == null || cilInst.Previous.Previous.OpCode.Code != Code.Ldc_I4_0) { // make sure the program is comparing the span address against // a zero value, which we can convert to a null reference. // ldarg.1 (span argument) // ldc.i4.0 // conv.u // bne.un label throw new InvalidProgramException(); } // compare long with Span code.NewInstruction(0x58 /* pop2 */, null, null); code.NewInstruction(0x01 /* aconst_null */, null, null); } else if (CompareGtLt(stackTop, stackTop2, code)) { return(0x99); // ifeq == zero } return(0); }
public static void Localloc(JavaCode code) { // 'localloc' step 1 // the 'localloc' instruction does not specify the type of the memory // to allocate. in most cases it is followed by a store instruction // into a variable of a pointer type, or of type System.Span, but in // cases where the result of 'localloc' is passed to a method call, // there can be any number of intermediate instruction that push // other parameters, and it might not be practial to find the type. code.NewInstruction(0x01 /* aconst_null */, null, null); code.StackMap.PushStack(CilType.SystemTypeType); code.NewInstruction(0xB8 /* invokestatic */, SpanType, new JavaMethodRef("Localloc" + CilMain.EXCLAMATION, SpanType, JavaType.LongType, CilType.SystemTypeType)); code.StackMap.PopStack(CilMain.Where); // null type code.StackMap.PopStack(CilMain.Where); // localloc size code.StackMap.PushStack(SpanType); }
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); }
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); }
// // InitInterfaceArrayField // public static void InitInterfaceArrayField(CilType toType, int numCastableInterfaces, JavaCode code, int objectIndex) { // if a type has castable interface (as counted by CastableInterfaceCount), // then we need to initialize the helper array field, for use by the // implementation of the IGenericObject.TryCast helper method if (numCastableInterfaces == 0) { return; } // objectIndex specifies the local index for the object reference, // e.g. 0 for the 'this' object, or -1 for top of stack. if (objectIndex == -1) { code.NewInstruction(0x59 /* dup */, null, null); } else { code.NewInstruction(0x19 /* aload */, null, objectIndex); } code.StackMap.PushStack(toType); code.NewInstruction(0xBB /* new */, AtomicReferenceArrayType, null); code.StackMap.PushStack(AtomicReferenceArrayType); code.NewInstruction(0x59 /* dup */, null, null); code.StackMap.PushStack(AtomicReferenceArrayType); code.NewInstruction(0x12 /* ldc */, null, numCastableInterfaces); code.StackMap.PushStack(JavaType.IntegerType); code.NewInstruction(0xB7 /* invokespecial */, AtomicReferenceArrayType, new JavaMethodRef("<init>", JavaType.VoidType, JavaType.IntegerType)); code.NewInstruction(0xB5 /* putfield */, toType, InterfaceArrayField); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); }
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); }
static int ConvertToLong(JavaCode code, TypeCode oldType, TypeCode newType, bool overflow) { if (oldType == TypeCode.Int64 || oldType == TypeCode.UInt64) { return(0x00); // nop } if (oldType == TypeCode.Double) { return(0x8F); // d2l } if (oldType == TypeCode.Single) { return(0x8C); // f2l } if (newType == TypeCode.UInt64) { code.NewInstruction(0x85 /* i2l */, null, null); code.StackMap.PushStack(JavaType.LongType); #if true long maskValue = (oldType == TypeCode.Byte) ? 0xFF : (oldType == TypeCode.UInt16) ? 0xFFFF : (oldType == TypeCode.UInt32) ? 0xFFFFFFFF : 0; code.NewInstruction(0x12 /* ldc */, null, (long)0xFFFFFFFF); #else code.NewInstruction(0x02 /* iconst_m1 */, null, null); code.NewInstruction(0x85 /* i2l */, null, null); #endif code.StackMap.PushStack(JavaType.LongType); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); return(0x7F); // land } CilMain.MakeRoomForCategory2ValueOnStack(code); return(0x85); // i2l }
static void LoadGenericInstance_1_2(CilType loadType, int genericCount, List <JavaFieldRef> parameters, JavaCode code) { // handling for the case of a generic instance with one or two // type arguments. each argument, if it is a concrete argument, // call GetType(class, class). if it is a generic argument, // call GetType(class, type). // note that the first class argument to GetType was alredy // inserted by our caller, LoadMaybeGeneric(). for (int i = 0; i < genericCount; i++) { var genericMark = CilMain.GenericStack.Mark(); var(argType, argIndex) = CilMain.GenericStack.Resolve( loadType.GenericParameters[i].JavaName); if (argIndex == 0 && (!argType.HasGenericParameters)) { // GetType(java.lang.Class, java.lang.Class) code.NewInstruction(0x12 /* ldc */, argType.AsWritableClass, null); code.StackMap.PushStack(CilType.ClassType); // second or third parameter of type java.lang.Class parameters.Add(parameters[0]); } else { // GetType(java.lang.Class, system.Type) LoadGeneric(argType, argIndex, code); // second or third parameter of type system.Type parameters.Add(new JavaFieldRef("", CilType.SystemTypeType)); } CilMain.GenericStack.Release(genericMark); } }
public static void Opposite(JavaCode code, Code cilOp, CodeLocals locals, Mono.Cecil.Cil.Instruction cilInst) { // // an opposite branch works by generating logic/opcode for // the opposite test (e.g. clt for bge), and an opposite // branch opcode (e.g. for bge, branch if clt returns zero) // var stackTop = code.StackMap.PopStack(CilMain.Where); bool isFloat = (stackTop.PrimitiveType == TypeCode.Single || stackTop.PrimitiveType == TypeCode.Double); if (cilOp == Code.Brfalse || cilOp == Code.Brfalse_S) { cilOp = Code.Brtrue; } else if (cilOp == Code.Bne_Un || cilOp == Code.Bne_Un_S) { cilOp = Code.Beq; } else if (cilOp == Code.Ble || cilOp == Code.Ble_S) { // for integer, this is the opposite of BGT // for float, this is the opposite of BGT.UN cilOp = isFloat ? Code.Bgt_Un : Code.Bgt; } else if (cilOp == Code.Ble_Un || cilOp == Code.Ble_Un_S) { // for integer, this is the opposite of BGT.UN // for float, this is the opposite of BGT cilOp = isFloat ? Code.Bgt : Code.Bgt_Un; } else if (cilOp == Code.Bge || cilOp == Code.Bge_S) { // for integer, this is the opposite of BLT // for float, this is the opposite of BLT.UN cilOp = isFloat ? Code.Blt_Un : Code.Blt; } else if (cilOp == Code.Bge_Un || cilOp == Code.Bge_Un_S) { // for integer, this is the opposite of BLT.UN // for float, this is the opposite of BLT cilOp = isFloat ? Code.Blt : Code.Blt_Un; } else { throw new InvalidProgramException(); } // // change the branch test to the opposite result // byte op = Common(code, cilOp, cilInst, stackTop); op = NegateCondition(op); Finish(code, locals, cilInst, op); }
static byte TestGtLt(JavaCode code, JavaType stackTop, JavaType stackTop2, bool greater, bool unsigned_unordered) { byte op; // // for floating point, normal comparison returns 0 if either value // is NaN, while unordered comparison returns 1. we have fcmp/dcmp // variants which return either 1 or -1. following the comparison, // we have ifgt/iflt to check if the result is either greater than // or less than 0. we consider all this when picking the opcode: // // test normal compare unordered compare // greater than ifgt xcmpl xcmpg // less than iflt xcmpg xcmpl // if (stackTop.PrimitiveType == TypeCode.Single) { op = (byte)((greater != unsigned_unordered) ? 0x95 // fcmpl : 0x96); // fcmpg } else if (stackTop.PrimitiveType == TypeCode.Double) { op = (byte)((greater != unsigned_unordered) ? 0x97 // dcmpl : 0x98); // dcmpg } // // for unsigned integer comparison, we use library function // system.UInt{32,64}.CompareTo, followed by ifgt/iflt // else if (unsigned_unordered && stackTop.PrimitiveType != TypeCode.Char) { char typeChar; int typeBits; string typeName = null; if (stackTop.PrimitiveType == TypeCode.SByte || stackTop.PrimitiveType == TypeCode.Byte) { typeChar = 'B'; typeBits = 0; typeName = "Byte"; } else if (stackTop.PrimitiveType == TypeCode.Int16 || stackTop.PrimitiveType == TypeCode.UInt16) { typeChar = 'S'; typeBits = 16; } else if (stackTop.PrimitiveType == TypeCode.Int32 || stackTop.PrimitiveType == TypeCode.UInt32) { typeChar = 'I'; typeBits = 32; } else if (stackTop.PrimitiveType == TypeCode.Int64 || stackTop.PrimitiveType == TypeCode.UInt64) { typeChar = 'J'; typeBits = 64; } else if (greater && stackTop.Equals(JavaStackMap.Null)) { // per MS CLI Partition III table 4 note 2, 'cgt.un' // can be used to check for a non-null reference return(0xA6); // if_acmpne } else { if (CodeSpan.CompareGtLt(stackTop, stackTop2, code)) { return((byte)(greater ? 0x9D // ifgt : 0x9B)); // iflt } throw new InvalidProgramException(); } if (typeBits != 0) { typeName = "UInt" + typeBits.ToString(); } code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, $"system.{typeName}"), new JavaMethodRef("CompareTo", $"({typeChar}{typeChar})I", CilMain.Where)); /* * new JavaType(0, 0, $"java.lang.{typeName}"), * new JavaMethodRef("compareUnsigned", * $"({typeChar}{typeChar})I", CilMain.Where));*/ op = 0; } // // for signed long comparison, we use lcmp followed by ifgt/iflt // else if (stackTop.PrimitiveType == TypeCode.Int64 || stackTop.PrimitiveType == TypeCode.UInt64) { op = 0x94; // lcmp (long) } // // for signed integer comparison, we have if_icmplt/if_icmpgt // which directly compare two integers and branch // else if (stackTop.PrimitiveType == TypeCode.Int32 || stackTop.PrimitiveType == TypeCode.UInt32 || stackTop.PrimitiveType == TypeCode.Int16 || stackTop.PrimitiveType == TypeCode.UInt16 || stackTop.PrimitiveType == TypeCode.SByte || stackTop.PrimitiveType == TypeCode.Byte || stackTop.PrimitiveType == TypeCode.Char || stackTop.PrimitiveType == TypeCode.Boolean) { return((byte)(greater ? 0xA3 // if_icmpgt : 0xA1)); // if_icmplt } else { throw new InvalidProgramException(); } // // return the selected opcode // if (op != 0) { code.NewInstruction(op, null, null); } return((byte)(greater ? 0x9D // ifgt : 0x9B)); // iflt }
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 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 static void ValueClone(JavaCode code) { code.NewInstruction(0xB8 /* invokestatic */, SystemGenericType, GenericClone); }
public static void LoadMaybeGeneric(CilType loadType, JavaCode code) { if (loadType.IsGenericParameter) { LoadGeneric(loadType.JavaName, code); } else { // all GetType variants take a first parameter of type java.lang.Class List <JavaFieldRef> parameters = new List <JavaFieldRef>(); parameters.Add(new JavaFieldRef("", JavaType.ClassType)); int arrayRank = loadType.ArrayRank; if (arrayRank == 0 || (!loadType.IsGenericParameter)) { code.NewInstruction(0x12 /* ldc */, loadType.AsWritableClass, null); } else { // for a generic type T[], we want to load just the element T, // and then (see below) call MakeArrayType on the result var loadTypeElem = loadType.AdjustRank(-arrayRank); code.NewInstruction(0x12 /* ldc */, loadTypeElem.AsWritableClass, null); } code.StackMap.PushStack(JavaType.ClassType); if (loadType.HasGenericParameters) { int genericCount = loadType.GenericParameters.Count; if (genericCount <= 2) { LoadGenericInstance_1_2(loadType, genericCount, parameters, code); } else { LoadGenericInstance_3_N(loadType, genericCount, parameters, code); } } code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType, new JavaMethodRef("GetType", CilType.SystemTypeType, parameters)); for (int i = 0; i < parameters.Count; i++) { code.StackMap.PopStack(CilMain.Where); } code.StackMap.PushStack(CilType.SystemTypeType); if (arrayRank != 0 && loadType.IsGenericParameter) { code.NewInstruction(0x12 /* ldc */, null, (int)arrayRank); code.StackMap.PushStack(JavaType.IntegerType); code.NewInstruction(0xB6 /* invokevirtual */, CilType.SystemTypeType, new JavaMethodRef("MakeArrayType", CilType.SystemTypeType, JavaType.IntegerType)); code.StackMap.PopStack(CilMain.Where); } } }
public static CilType CastMaybeGeneric(CilType castType, bool valueOnly, JavaCode code) { if (!castType.IsGenericParameter) { return(castType); } if (!valueOnly) { GenericUtil.ValueLoad(code); } var genericMark = CilMain.GenericStack.Mark(); var(resolvedType, resolvedIndex) = CilMain.GenericStack.Resolve(castType.JavaName); if (resolvedIndex == 0) { if (valueOnly) { // this flag is set when called from LoadFieldAddress. we can cast // the generic field to the actual type only if it is a value type if (resolvedType.IsValueClass) { castType = resolvedType; code.NewInstruction(0xC0 /* checkcast */, castType.AsWritableClass, null); } else if (castType.IsByReference || ((!resolvedType.IsReference) && resolvedType.PrimitiveType != 0)) { // cast the generic type to an actual boxed type, when it is // returned byref (called from PushMethodReturnType), or when // the actual type is a primitive (from LoadFieldAddress) var boxedType = new BoxedType(resolvedType, false); code.NewInstruction(0xC0 /* checkcast */, boxedType, null); castType = boxedType; } } else { // this flag is clear whe called from LoadFieldValue and // PushMethodReturnType. we can cast to any known actual types. var arrayRank = castType.GetMethodGenericParameter()?.ArrayRank ?? 0; castType = (arrayRank == 0) ? resolvedType : resolvedType.AdjustRank(arrayRank); if (!castType.IsReference) { var boxedType = new BoxedType(castType, false); code.NewInstruction(0xC0 /* checkcast */, boxedType, null); boxedType.GetValue(code); } else { code.NewInstruction(0xC0 /* checkcast */, castType.AsWritableClass, null); } } } CilMain.GenericStack.Release(genericMark); return(castType); }
public override void BoxValue(JavaCode code) => throw new NotImplementedException();
private void OptimizeGeneratedCode() { var Instructions = code.Instructions; var StackMap = code.StackMap; int n = Instructions.Count; // // nop optimization: // // remove any nop instructions that are not a branch target, // and only if there are no exception tables. note that // removing nops that are a branch target, or nops in a method // with exception tables, would require updating the stack map, // branch instructions and exception tables. // var nops = new List <int>(); bool discardNops = (code.Exceptions == null || code.Exceptions.Count == 0); // constants optimization: // // find occurrences of iconst_0 or iconst_1 followed by i2l // (which must not be a branch target), and convert to lconst_xx // for (int i = 0; i < n; i++) { byte op = Instructions[i].Opcode; if (op == 0x00 /* nop */) { // collect this nop only if it is not a branch target if (!StackMap.HasBranchFrame(Instructions[i].Label)) { nops.Add(i); } } else if (JavaCode.IsBranchOpcode(op)) // jump inst { if (Instructions[i].Data is int) { // if jump instruction has an explicit byte offset, // we can't do nop elimination; see FillJumpTargets // in JavaBinary.JavaCodeWriter discardNops = false; } } else if (op == 0x85 /* i2l */ && i > 0) { var prevInst = Instructions[i - 1]; if (prevInst.Opcode == 0x12 && /* ldc */ prevInst.Data is int intValue && (intValue == 0 || intValue == 1) && (!StackMap.HasBranchFrame( Instructions[i].Label))) { // convert ldc of 0 or 1 into lconst_0 or lconst_1 prevInst.Opcode = (byte)(intValue + 0x09); // convert current instruction to nop, // and mark to discard it if discarding nops Instructions[i].Opcode = 0x00; // nop nops.Add(i); } } } if (discardNops) { for (int i = nops.Count; i-- > 0;) { Instructions.RemoveAt(nops[i]); } } }
public virtual void SetValueVO(JavaCode code, bool isVolatile = false) => code.NewInstruction(0xB8 /* invokestatic */, ThisOrEnum, new JavaMethodRef(VolatileName("Set", isVolatile), JavaType.VoidType, UnboxedTypeInMethod, ThisOrEnum));
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)); } }
public virtual void SetValueOV(JavaCode code, bool isVolatile = false) { code.NewInstruction(0xB6 /* invokevirtual */, ThisOrEnum, new JavaMethodRef(VolatileName("Set", isVolatile), JavaType.VoidType, UnboxedTypeInMethod)); }
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 CodeArrays(JavaCode _code, CodeLocals _locals) { locals = _locals; code = _code; stackMap = code.StackMap; }
public static void InitializeArray(object initialValue, JavaCode code) { // // the input array on the stack should be a primitive array // var array = code.StackMap.PopStack(CilMain.Where); int size = 0; switch (array.PrimitiveType) { case TypeCode.Boolean: case TypeCode.SByte: case TypeCode.Byte: size = 1; break; case TypeCode.Char: case TypeCode.Int16: case TypeCode.UInt16: size = 2; break; case TypeCode.Single: case TypeCode.Int32: case TypeCode.UInt32: size = 4; break; case TypeCode.Double: case TypeCode.Int64: case TypeCode.UInt64: size = 8; break; default: size = 0; break; } if (size == 0 || array.ArrayRank == 0) { throw new Exception("invalid array for InitializeArray"); } // // the initializer byte buffer should be divisible by element size // var bytes = initialValue as byte[]; int count = 0; if (bytes != null && bytes.Length != 0 && (bytes.Length % size) == 0) { count = bytes.Length / size; } if (count == 0) { throw new Exception("invalid data for InitializeArray"); } // // prepare the operand stack for as many operands as we will need // code.StackMap.PushStack(array); code.StackMap.PushStack(JavaType.IntegerType); code.StackMap.PushStack(size == 8 ? JavaType.LongType : JavaType.IntegerType); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); // // generate a sequence of instructions to initialize the array, // a reference to which was already pushed at the top of the stack // byte storeArrayOpcode = ((CilType)array).AdjustRank(-array.ArrayRank).StoreArrayOpcode; for (int index = 0; index < count; index++) { if (index > 0) { code.NewInstruction(0x59 /* dup */, null, null); } code.NewInstruction(0x12 /* ldc */, null, (int)index); code.NewInstruction(0x12 /* ldc */, null, Value(bytes, index * size, array.PrimitiveType)); code.NewInstruction(storeArrayOpcode, null, null); } object Value(byte[] bytes, int offset, TypeCode primitive) { switch (primitive) { case TypeCode.Boolean: bool boolValue = BitConverter.ToBoolean(bytes, offset); return((int)(boolValue ? 1 : 0)); case TypeCode.SByte: return((int)(((sbyte[])(object)bytes)[offset])); case TypeCode.Byte: return((int)bytes[offset]); case TypeCode.Char: return((int)BitConverter.ToChar(bytes, offset)); case TypeCode.Int16: return((int)BitConverter.ToInt16(bytes, offset)); case TypeCode.UInt16: return((int)BitConverter.ToUInt16(bytes, offset)); case TypeCode.Int32: return(BitConverter.ToInt32(bytes, offset)); case TypeCode.UInt32: return((int)BitConverter.ToUInt32(bytes, offset)); case TypeCode.Int64: return(BitConverter.ToInt64(bytes, offset)); case TypeCode.UInt64: return((long)BitConverter.ToUInt64(bytes, offset)); case TypeCode.Single: return(BitConverter.ToSingle(bytes, offset)); case TypeCode.Double: return(BitConverter.ToDouble(bytes, offset)); default: return(null); } } }
/// <summary> /// Default ctor /// </summary> public JavaClass(string className, JavaCode javaCode) { this.className = className; this.javaCode = javaCode; }