public static bool Address(CilType fromType, CilType intoType, JavaCode code) { if (intoType.Equals(SpanType) && (!fromType.Equals(SpanType))) { // allow assignment of null to clear the pointer if (fromType.Equals(JavaStackMap.Null)) { return(true); } // allow assignment of native int (presumably zero) bool callAssign = false; bool pushNullType = true; JavaType argType = fromType; JavaType retType = SpanType; if ((!fromType.IsReference) && fromType.PrimitiveType == TypeCode.UInt64) { callAssign = true; } else if (intoType.GenericParameters != null) { // allow assignment when the types match callAssign = intoType.GenericParameters[0].Equals(fromType) || fromType.JavaName == intoType.GenericParameters[0].JavaName; // for arbitrary value types, call a Assign(ValueType) if (fromType.IsValueClass) { argType = retType = CilType.SystemValueType; GenericUtil.LoadMaybeGeneric(fromType, code); pushNullType = false; } } if (callAssign) { if (pushNullType) { code.NewInstruction(0x01 /* aconst_null */, null, null); code.StackMap.PushStack(CilType.SystemTypeType); } code.NewInstruction(0xB8 /* invokestatic */, SpanType, new JavaMethodRef("Assign" + CilMain.EXCLAMATION, retType, argType, CilType.SystemTypeType)); code.NewInstruction(0xC0 /* checkcast */, SpanType, null); code.StackMap.PopStack(CilMain.Where); // null type return(true); } throw new Exception($"bad assignment of '{fromType.JavaName}' into pointer of '{intoType.GenericParameters[0].JavaName}'"); } return(false); }
static bool EqualGenericParameter(CilType p1, CilType p2) { if (p1.IsGenericParameter) { return(p2.IsGenericParameter); } if (p1.HasGenericParameters) { if (p2.HasGenericParameters) { int n = p1.GenericParameters.Count; if (n == p2.GenericParameters.Count) { for (int i = 0; i < n; i++) { if (!EqualGenericParameter(p1.GenericParameters[i], p2.GenericParameters[i])) { return(false); } } return(true); } } return(false); } return(p1.Equals(p2)); }
public static bool LoadStore(bool isLoad, CilType stackTop, JavaType opcodeType, CilType dataType, JavaCode code) { if (stackTop.Equals(SpanType) && code.Method.Class.Name != SpanType.ClassName) { string opcodeDescr; if (opcodeType == null) { opcodeType = CilType.SystemValueType; opcodeDescr = ""; // at this point we should have been called from LoadObject or // StoreObject in CodeMisc to handle a ldobj/stobj instruction, // so make sure the pointer-span element is a value type if (dataType.IsGenericParameter || (!dataType.IsValueClass)) { throw new InvalidProgramException(); } code.NewInstruction(0x12 /* ldc */, dataType.AsWritableClass, null); // make sure the stack has room for three parameters: // 'this', value reference (in case of Store), and class code.StackMap.PushStack(JavaType.ObjectType); code.StackMap.PushStack(JavaType.ObjectType); code.StackMap.PushStack(JavaType.ObjectType); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); } else { if (opcodeType.Equals(JavaType.ShortType) && stackTop.GenericParameters != null && stackTop.GenericParameters[0].Equals(JavaType.CharacterType)) { opcodeType = JavaType.CharacterType; } opcodeDescr = opcodeType.ToDescriptor(); } var voidType = JavaType.VoidType; var spanMethod = isLoad ? (new JavaMethodRef("Load" + opcodeDescr, opcodeType)) : (new JavaMethodRef("Store" + opcodeDescr, voidType, opcodeType)); if (opcodeDescr == "") { spanMethod.Parameters.Add(new JavaFieldRef("", JavaType.ClassType)); } code.NewInstruction(0xB6 /* invokevirtual */, SpanType, spanMethod); if (isLoad) { code.StackMap.PushStack(CilType.From(opcodeType)); } return(true); } return(false); }
bool LoadFieldValue(string fldName, CilType fldType, CilType fldClass, bool isStatic, bool isVolatile) { if (isStatic && fldClass.HasGenericParameters) { fldClass = LoadStaticData(fldClass); isStatic = false; } byte op; if (!isStatic) { if (method.IsConstructor && LoadFieldInConstructor(fldName, fldType, fldClass)) { return(true); } PopObjectAndLoadFromSpan(fldClass); op = 0xB4; // getfield } else { op = 0xB2; // getstatic } code.NewInstruction(op, fldClass.AsWritableClass, new JavaFieldRef(fldName, fldType)); if (fldType is BoxedType boxedType) { boxedType.GetValue(code, isVolatile); fldType = boxedType.UnboxedType; if (fldType.IsReference) { if (fldType.IsGenericParameter && fldType.IsArray) { fldType = CodeArrays.GenericArrayType; } else if (!fldType.Equals(JavaType.ObjectType)) { code.NewInstruction(0xC0 /* checkcast */, fldType.AsWritableClass, null); } } } else if (fldType.IsGenericParameter) { var newType = GenericUtil.CastMaybeGeneric(fldType, false, code); fldType = (newType != fldType) ? newType : CilType.From(JavaType.ObjectType); } stackMap.PushStack(fldType); return(true); }
public static bool MaybeGetProxy(CilType fromType, CilType intoType, JavaCode code) { if (fromType.IsArray || object.ReferenceEquals(fromType, GenericArrayType) || fromType.Equals(JavaType.StringType)) { if (GenericUtil.ShouldCallGenericCast(fromType, intoType)) { code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, GetProxyMethod); return(true); } } return(false); }
public bool EqualParameters(CilInterfaceMethod other) { if (!ReturnType.Equals(other.ReturnType)) { return(false); } int n = Parameters.Count; if (other.Parameters.Count != n) { return(false); } for (int i = 0; i < n; i++) { if (!Parameters[i].Equals(other.Parameters[i])) { return(false); } } return(true); }
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); }
public static bool ShouldCallGenericCast(CilType fromType, CilType intoType) { if (object.ReferenceEquals(fromType, intoType)) { return(false); // no, if same type (by reference) } if (intoType.PrimitiveType != 0 || intoType.ArrayRank != 0) { return(false); // no, if casting to primitive or array } if (intoType.HasGenericParameters) { if (intoType.Equals(CodeSpan.SpanType)) { return(false); } return(true); // yes, if type is generic } if (fromType.Equals(intoType)) { return(false); // no, if same type (by value) } // if casting to one of the non-generic types of that a string // implements, and the source is either a string or an object bool fromTypeIsObjectType = fromType.Equals(JavaType.ObjectType); if ((fromTypeIsObjectType || fromType.Equals(JavaType.StringType)) && (intoType.JavaName == "system.IComparable" || intoType.JavaName == "system.IConvertible")) { return(true); } // if casting to one of the types that any array should implememt, // and the castee is an array, or 'object', or one of those types, // then always call TestCast/CallCast, because we might have to // create a helper proxy for an array object bool fromTypeMayBeArray = (fromTypeIsObjectType || fromType.ArrayRank != 0 || IsArray(fromType.JavaName)); return(fromTypeMayBeArray && IsArray(intoType.JavaName)); bool IsArray(string clsnm) => ( clsnm == "system.Array" || clsnm == "system.ICloneable" || clsnm == "system.collections.IEnumerable" || clsnm == "system.collections.ICollection" || clsnm == "system.collections.IList" || clsnm == "system.collections.IStructuralComparable" || clsnm == "system.collections.IStructuralEquatable"); #if false // if casting to an array, from System.Object, System.Array, // or one of the interface types that any array should implement, // then always call TestCast/CallCast, because we might have to // "unbox" the proxy (see below), and extract the array if (intoType.ArrayRank != 0) { if (fromType.ArrayRank != 0) { return(false); // no, if casting from array to array } if (fromType.Equals(JavaType.ObjectType) || IsArray(fromType.JavaName) || IsGenericArray(fromType.JavaName)) { return(true); // yes, if casting } } bool IsGenericArray(string clsnm) => ( clsnm == "system.collections.generic.IEnumerable$$1" || clsnm == "system.collections.generic.ICollection$$1" || clsnm == "system.collections.generic.IList$$1" || clsnm == "system.collections.generic.IReadOnlyList$$1"); #endif }
bool LoadFieldInConstructor(string fldName, CilType fldType, CilType fldClass) { // Java does not allow 'getfield' instructions on an 'uninitializedThis' // until the call to super constructor. but in .Net this is permitted, // and the F# compiler generates such code in some cases. we try to work // around this, by identifying the constructor parameter that was used in // an earlier 'putfield', and loading that, instead of doing 'getfield'. bool isUninitializedThisField = (method.IsConstructor && fldClass.Equals(method.DeclType) && stackMap.GetLocal(0).Equals(JavaStackMap.UninitializedThis)); if (!isUninitializedThisField) { return(false); } // most recent instruction before the 'getfield' // should have been 'ldarg.0', translated to 'aload_0' int i = code.Instructions.Count - 1; if (i > 0 && code.Instructions[i].Opcode == 0x19 && /* aload */ code.Instructions[i].Data is int thisIndex && thisIndex == 0 // note that we pop the stack here && code.StackMap.PopStack(CilMain.Where) .Equals(JavaStackMap.UninitializedThis)) { // try to find an earlier 'putfield' instruction for the field while (i-- > 0) { var inst = code.Instructions[i]; if (inst.Opcode == 0xB5 && /* putfield */ method.DeclType.Equals(inst.Class)) { var instField = (JavaFieldRef)inst.Data; if (fldName == instField.Name && fldType.Equals(instField.Type)) { // try to find the load instruction that was used // to load the value, for that earlier 'putfield' if (FindLoadLocal(i - 1, code.Instructions.Count - 1)) { return(true); } } } } } throw new Exception("load from uninitialized this"); bool FindLoadLocal(int prevIdx, int lastIdx) { var prevInst = code.Instructions[prevIdx]; if (prevIdx > 0 && prevInst.Opcode == 0xB8 && /* invokestatic */ (JavaMethodRef)prevInst.Data is JavaMethodRef prevMethod) { if (prevMethod.Name == "Box") { // we possibly found the sequence used in boxing - // xload value, invokestatic Value.Box(), putfield prevInst = code.Instructions[prevIdx - 1]; } else if (prevIdx > 5 && prevMethod.Name == "Copy" && code.Instructions[prevIdx - 1].Opcode == 0x5A && /* dup_x1 */ code.Instructions[prevIdx - 2].Opcode == 0xB8 && /* invokestatic */ code.Instructions[prevIdx - 3].Opcode == 0x19 && /* aload (type) */ code.Instructions[prevIdx - 4].Opcode == 0xB8 && /* invokestatic */ code.Instructions[prevIdx - 5].Opcode == 0x19 /* aload (value) */) { // we possibly found the sequence used in generics - // aload (local to use for store value) // invokestatic Generic.Load // aload (generic type parameter) // invokestatic Generic.New // dup_x1 // invokestatic Generic.Copy <=== prevIdx // putfield // (see also StoreInstance method in this module) prevInst = code.Instructions[prevIdx - 5]; } } if (prevInst.Opcode >= 0x15 && /* iload, lload, fload, */ prevInst.Opcode <= 0x19 && /* dload, aload */ prevInst.Data is int localIndex) { // if the instruction before the 'putfield' is a load // from local, and assuming this local is a parameter, // then we can just load this local again, to replace // a 'getfield' instruction that cannot access 'this' code.Instructions[lastIdx].Opcode = prevInst.Opcode; code.Instructions[lastIdx].Data = localIndex; stackMap.PushStack(code.StackMap.GetLocal(localIndex)); return(true); } return(false); } }
bool ConvertVirtualToStaticCall(CilType callClass, CilMethod callMethod) { if (callClass.ClassName == callClass.JavaName) { if (callClass.ClassName == "system.FunctionalInterfaceDelegate" && callMethod.Name == "AsInterface") { // a call to system.FunctionalInterfaceDelegate::AsInterface() // should be fixed to expect an 'object' return type, and then // cast that return type to the proper interface var tempMethod = new JavaMethodRef(callMethod.Name, JavaType.ObjectType); code.NewInstruction(0xB6 /* invokevirtual */, callClass, tempMethod); code.NewInstruction(0xC0 /* checkcast */, callMethod.ReturnType, null); stackMap.PopStack(CilMain.Where); stackMap.PushStack(callMethod.ReturnType); return(true); } if (callMethod.IsExternal && NativeMethodClasses.TryGetValue(callClass.ClassName, out var altClassName)) { // a call to an instance method System.NameSpace.Class::Method, // which is implemented only as a native method, is translated // into a static call to some other class where the method is // implemented. such implementing classes are listed in the // NativeMethodClasses dictionary the bottom of this file. var altMethodName = callMethod.Name; int altMethodNameIdx = altMethodName.IndexOf(CilMain.OPEN_PARENS); if (altMethodNameIdx != -1) { altMethodName = altMethodName.Substring(0, altMethodNameIdx); } var tempMethod = new JavaMethodRef(altMethodName, callMethod.ReturnType); var parameters = new List <JavaFieldRef>(callMethod.Parameters); parameters.Insert(0, new JavaFieldRef("this", callClass)); tempMethod.Parameters = parameters; code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, altClassName), tempMethod); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } // don't bother adjusting the call if the program explicitly // refers to the java class rather than the simulated wrapper, // e.g. to java.lang.Throwable rather than System.Exception return(false); } if (callClass.Equals(JavaType.StringType) || callClass.Equals(JavaType.ThrowableType) || (callClass.Equals(JavaType.ObjectType) && callMethod.Name == "GetType" && callMethod.ToDescriptor() == "()Lsystem/Type;")) { // we map some basic .Net types their java counterparts, so we // can't invoke .Net instance methods on them directly. instead, // we invoke a static method on our helper class. for example: // ((System.String)x).CompareTo(y) -> system.String.CompareTo(x,y) var tempMethod = new JavaMethodRef(callMethod.Name, callMethod.ReturnType); var parameters = new List <JavaFieldRef>(callMethod.Parameters); parameters.Insert(0, new JavaFieldRef("this", callClass)); tempMethod.Parameters = parameters; if (callClass.Equals(JavaType.ThrowableType) && callMethod.Name == "system-Exception-GetType") { // undo the effect of CilMethod::MethodIsShadowing upon calling // the virtual/overriding GetType() from System.Exception, and // instead call the static system.Object.GetType() tempMethod.Name = "GetType"; callClass = CilType.From(new JavaType(0, 0, "system.Object")); parameters[0].Type = JavaType.ObjectType; } else { CilMethod.FixNameForVirtualToStaticCall(tempMethod, callClass); } code.NewInstruction(0xB8 /* invokestatic */, new JavaType(0, 0, callClass.JavaName), tempMethod); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } if (callClass.JavaName == null && callClass.Equals(JavaType.ClassType) && callMethod.Name == "GetRuntimeType") { // convert virtual call to RuntimeTypeHandle.GetRuntimeType // to a static call to system.RuntimeType code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType, new JavaMethodRef( callMethod.Name, callMethod.ReturnType, JavaType.ObjectType)); ClearMethodArguments(callMethod, false); PushMethodReturnType(callMethod); return(true); } return(false); }