Beispiel #1
0
        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);
        }
Beispiel #2
0
 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));
 }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
 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);
 }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        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
        }
Beispiel #9
0
        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);
            }
        }
Beispiel #10
0
        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);
        }