Пример #1
0
        void InitLocalsRefs2(CilType plainType, BoxedType boxedType, bool arg, int index)
        {
            if (arg)
            {
                code.NewInstruction(plainType.LoadOpcode, null, index);
            }
            else
            {
                code.NewInstruction(plainType.InitOpcode, null, null);
            }

            stackMap.PushStack(plainType);

            if (boxedType != null)
            {
                boxedType.BoxValue(code);
            }
            else if (plainType.IsGenericParameter)
            {
                GenericUtil.ValueClone(code);
            }
            else
            {
                CilMethod.ValueMethod(CilMethod.ValueClone, code);
                code.NewInstruction(0xC0 /* checkcast */, plainType, null);
            }

            code.NewInstruction(0x3A /* astore */, null, index);
            stackMap.PopStack(CilMain.Where);
        }
Пример #2
0
        public static string GenericParameterFullName(GenericParameter genericParameter)
        {
            string name = genericParameter.Name;
            string prefix;

            if (genericParameter.Type == GenericParameterType.Method)
            {
                prefix = "M!" + genericParameter.DeclaringMethod.FullName;
                if (name.StartsWith("!!"))
                {
                    name = CilMethod.AsDefinition(genericParameter.DeclaringMethod)
                           .GenericParameters[genericParameter.Position].Name;
                }
                else if (name.StartsWith("!"))
                {
                    throw new ArgumentException();
                }
            }
            else
            {
                prefix = "T!" + genericParameter.DeclaringType.FullName;
                if (name.StartsWith("!"))
                {
                    name = CilType.AsDefinition(genericParameter.DeclaringType)
                           .GenericParameters[genericParameter.Position].Name;
                }
            }
            return(prefix + "<" + name + ">");
        }
Пример #3
0
        public CodeLocals(CilMethod myMethod, MethodDefinition defMethod, JavaCode code)
        {
            this.code = code;
            stackMap  = code.StackMap = new JavaStackMap();

            InitLocals(myMethod, defMethod);
            WriteDebugData(myMethod.DeclType);
        }
Пример #4
0
        internal static void BuildJavaCode(JavaMethod newMethod, CilMethod myMethod,
                                           MethodDefinition defMethod, int numCastableInterfaces)
        {
            var body = defMethod.Body;

            if (body.Instructions.Count == 0)
            {
                throw CilMain.Where.Exception("input method is empty");
            }

            if (body.Instructions[body.Instructions.Count - 1].Offset > 0xFFF0)
            {
                throw CilMain.Where.Exception("input method is too large");
            }

            CodeBuilder o = null;

            try
            {
                o = new CodeBuilder();

                o.defMethod             = defMethod;
                o.defMethodBody         = defMethod.Body;
                o.newMethod             = newMethod;
                o.method                = myMethod;
                o.numCastableInterfaces = numCastableInterfaces;

                o.Process();
            }
            catch (Exception e)
            {
                if (e is JavaException)
                {
                    throw;
                }
                #if DEBUGDIAG
                Console.WriteLine(e);
                #endif
                if (o != null && o.cilInst != null)
                {
                    if (o.lineNumber != 0)
                    {
                        CilMain.Where.Push($"line {o.lineNumber}");
                    }
                    CilMain.Where.Push(
                        $"'{o.cilInst.OpCode.Name}' instruction at offset {o.cilInst.Offset,0:X4}");
                }
                string msg = e.Message;
                if (e is InvalidProgramException)
                {
                    msg = "unexpected opcode or operands";
                }
                throw CilMain.Where.Exception(msg);
            }
        }
Пример #5
0
        //
        // create default parameterless constructor.  (it will be called at the
        // top of any method which allocates a value type local.)  and note that
        // if this is a generic value type, this new constructor will be further
        // modified in GenericUtil.MakeGenericClass.FixConstructorsInFrom
        //

        static void CreateDefaultConstructor(JavaClass valueClass, CilType fromType,
                                             int numCastableInterfaces, bool initFields)
        {
            foreach (var oldMethod in valueClass.Methods)
            {
                if (oldMethod.Name == "<init>")
                {
                    return;
                }
            }

            var code = CilMethod.CreateConstructor(valueClass, fromType.GenericParametersCount, true);

            if (fromType.HasGenericParameters)
            {
                code.StackMap = new JavaStackMap();
                var genericMark = CilMain.GenericStack.Mark();
                CilMain.GenericStack.EnterMethod(fromType, code.Method, true);

                // initialize the generic type field
                GenericUtil.InitializeTypeField(fromType, code);

                CilMain.GenericStack.Release(genericMark);
                code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where);
            }

            // init the array of generic interfaces
            InterfaceBuilder.InitInterfaceArrayField(
                fromType, numCastableInterfaces, code, 0);

            if (initFields)
            {
                var oldLabel = code.SetLabel(0xFFFF);

                InitializeInstanceFields(valueClass, fromType, null, code);

                code.SetLabel(oldLabel);
            }

            code.NewInstruction(0x19 /* aload */, null, (int)0);
            code.NewInstruction(0xB7 /* invokespecial */, new JavaType(0, 0, valueClass.Super),
                                new JavaMethodRef("<init>", JavaType.VoidType));

            code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where);
            if (code.MaxStack < 1)
            {
                code.MaxStack = 1;
            }

            code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null);
        }
Пример #6
0
        public static void BuildGenericProxy(CilInterfaceMethod ifcMethod, string methodSuffix,
                                             CilType intoType, List <CilInterfaceMethod> classMethods,
                                             JavaClass ifcClass)
        {
            CilMethod targetMethod = null;

            //Console.WriteLine("\n***** LOOKING FOR INTERFACE METHOD " + ifcMethod + " (SUFFIX " + methodSuffix + ") AKA " + ifcMethod.Method);

            foreach (var clsMethod in classMethods)
            {
                /*Console.WriteLine("> LOOKING AT "
                 + (clsMethod.Method.IsExplicitImpl ? "EXPLICIT " : "")
                 + "CLASS METHOD " + clsMethod + " AKA " + clsMethod.Method);*/

                if (ifcMethod.GenericCompare(clsMethod))
                {
                    if (clsMethod.Method.IsExplicitImpl)
                    {
                        // a matching explicit override method is the best match,
                        // and we can immediately stop searching
                        targetMethod = clsMethod.Method;
                        break;
                    }
                    else
                    {
                        // more than one method may match, if a derived type overrides
                        // or hides a method that also exists in a base type.  but the
                        // derived (primary) type methods always come first.
                        if (targetMethod == null)
                        {
                            targetMethod = clsMethod.Method;
                        }
                    }
                }
            }

            if (targetMethod == null)
            {
                throw CilMain.Where.Exception(
                          $"missing method '{ifcMethod.Method}' "
                          + $"(for interface '{ifcMethod.Method.DeclType}')");
            }

            // Console.WriteLine("INTERFACE METHOD " + ifcMethod.Method + " TARGET " + targetMethod);

            BuildGenericProxy2(ifcMethod, targetMethod, true, intoType, ifcClass);
        }
Пример #7
0
        public static void ValueCopy(CilType valueType, JavaCode code, bool swap = false)
        {
            // if 'from' value is pushed before 'into' object, call with swap == false
            // if 'into' object is pushed before 'from' value, call with swap == true
            if (valueType.IsGenericParameter)
            {
                if (swap)
                {
                    code.NewInstruction(0x5F /* swap */, null, null);
                }
                else
                {
                    // if storing a primitive value into a generic type,
                    // and the generic type can be resolved to a primitive type,
                    // then use a boxed-set method call

                    var stackArray    = code.StackMap.StackArray();
                    int stackArrayLen = stackArray.Length;
                    if (stackArrayLen > 0 && (!stackArray[stackArrayLen - 1].IsReference))
                    {
                        var genericMark = CilMain.GenericStack.Mark();
                        var(primitiveType, _) = CilMain.GenericStack.Resolve(valueType.JavaName);
                        CilMain.GenericStack.Release(genericMark);

                        if (!primitiveType.IsReference)
                        {
                            var boxedType = new BoxedType(primitiveType, false);
                            code.NewInstruction(0xC0 /* checkcast */, boxedType, null);
                            boxedType.SetValueVO(code);
                            return;
                        }
                    }
                }

                code.NewInstruction(0xB8 /* invokestatic */, SystemGenericType,
                                    new JavaMethod("Copy", JavaType.VoidType,
                                                   JavaType.ObjectType, JavaType.ObjectType));
            }
            else
            {
                if (swap)
                {
                    code.NewInstruction(0x5F /* swap */, null, null);
                }
                CilMethod.ValueMethod(CilMethod.ValueCopyTo, code);
            }
        }
Пример #8
0
        public void Call(CilMethod method, Mono.Cecil.Cil.Instruction inst)
        {
            int numDims  = method.Parameters.Count;
            var elemType = method.DeclType;

            if (method.Name == "Get")
            {
                Deref(numDims);

                stackMap.PopStack(CilMain.Where);   // pop index
                stackMap.PopStack(CilMain.Where);   // pop array
                Load(null, elemType, inst);
            }

            else if (method.Name == "Set")
            {
                numDims--;          // last parameter is value
                if (numDims > 1)
                {
                    var valueType  = stackMap.PopStack(CilMain.Where);
                    int localIndex = locals.GetTempIndex(valueType);
                    code.NewInstruction(valueType.StoreOpcode, null, localIndex);

                    Deref(numDims);

                    code.NewInstruction(valueType.LoadOpcode, null, localIndex);
                    stackMap.PushStack(valueType);
                }

                Store(elemType);
            }

            else if (method.Name == "Address")
            {
                Deref(numDims);

                Address(elemType);
            }

            else
            {
                throw new InvalidProgramException();
            }
        }
Пример #9
0
        bool Translate_Constrained(CilMethod callMethod, object data)
        {
            var typeRef = data as TypeReference;

            if (typeRef == null)
            {
                throw new InvalidProgramException();
            }

            var constrainType = CilType.From(typeRef);

            int localIndex = SaveMethodArguments(callMethod);

            var calledObjectType = (CilType)stackMap.PopStack(CilMain.Where);

            if (calledObjectType is BoxedType boxedType && boxedType.IsBoxedReference)
            {
                boxedType.GetValue(code);
            }
Пример #10
0
        bool Translate_Callvirt(CilMethod callMethod)
        {
            var callClass = callMethod.DeclType;

            CheckAndBoxArguments(callMethod, true);

            byte op;

            if (callClass.IsInterface)
            {
                if (callClass.ClassName == "system.ValueMethod")
                {
                    // convert calls on interface method from system.ValueMethod
                    // to calls on virtual method from system.ValueType
                    op        = 0xB6; // invokevirtual
                    callClass = CilType.From(CilType.SystemValueType);
                }
                else
                {
                    op = 0xB9; // invokeinterface
                }
            }
            else
            {
                if (ConvertVirtualToStaticCall(callClass, callMethod))
                {
                    return(true);
                }

                op = 0xB6; // invokevirtual
            }

            PushGenericArguments(callMethod);

            code.NewInstruction(op, callClass.AsWritableClass,
                                callMethod.WithGenericParameters);

            ClearMethodArguments(callMethod, false);
            PushMethodReturnType(callMethod);

            return(true);
        }
Пример #11
0
        void Load(CilType arrayType, CilType elemType, Mono.Cecil.Cil.Instruction inst)
        {
            if (arrayType == null)
            {
                arrayType = elemType.AdjustRank(1);
            }

            /*Console.WriteLine("(LOAD) ARRAY TYPE = " + arrayType + "," + arrayType.ArrayRank
             + " ELEM TYPE = " + elemType + "," + elemType.ArrayRank
             + " ELEMVAL? " + elemType.IsValueClass
             + " ELEMGEN? " + elemType.IsGenericParameter);*/

            if (object.ReferenceEquals(arrayType, GenericArrayType) ||
                elemType.IsGenericParameter || arrayType.IsGenericParameter)
            {
                code.NewInstruction(0xB8 /* invokestatic */,
                                    SystemArrayType, LoadArrayMethod);
                if (elemType.ArrayRank != 0)
                {
                    elemType = GenericArrayType;
                }
            }
            else
            {
                if (elemType.PrimitiveType == TypeCode.Int16 &&
                    (arrayType.PrimitiveType == TypeCode.Char ||
                     arrayType.PrimitiveType == TypeCode.UInt16))
                {
                    // ldelem.i2 with a char[] array, should be 'caload' not 'saload'
                    elemType = arrayType.AdjustRank(-arrayType.ArrayRank);
                }

                code.NewInstruction(elemType.LoadArrayOpcode, null, null);

                if (arrayType.IsValueClass || elemType.IsValueClass)
                {
                    CilMethod.ValueMethod(CilMethod.ValueClone, code);
                }
            }

            stackMap.PushStack(elemType);
        }
Пример #12
0
        public static CilMethod From(MethodReference fromMethod)
        {
            if (!_Methods.TryGetValue(fromMethod, out var converted))
            {
                CilMain.Where.Push($"method '{fromMethod.FullName}'");

                MethodDefinition defMethod;
                bool             isGenericInstance = fromMethod.IsGenericInstance ||
                                                     fromMethod.DeclaringType.IsGenericInstance;

                if (fromMethod.DeclaringType is ArrayType fromArrayType)
                {
                    defMethod = null;
                    converted = new CilMethod(fromMethod, fromArrayType);

                    if (!isGenericInstance)
                    {
                        _Methods.Add(fromMethod, converted);
                    }
                }
                else
                {
                    defMethod = AsDefinition(fromMethod);
                    converted = new CilMethod(fromMethod, defMethod);

                    if (!isGenericInstance)
                    {
                        _Methods.Add(fromMethod, converted);

                        if (defMethod != fromMethod && defMethod != null &&
                            (!_Methods.TryGetValue(defMethod, out var converted2)))
                        {
                            _Methods.Add(defMethod, converted);
                        }
                    }
                }

                CilMain.Where.Pop();
            }
            return(converted);
        }
Пример #13
0
        void InitLocals(CilMethod myMethod, MethodDefinition defMethod)
        {
            var _localTypes = new List <CilType>();

            int nextIndex = InitLocalsArgs(myMethod, _localTypes);

            //indexLocalVars = nextIndex;
            nextIndex = InitLocalsVars(defMethod.Body, _localTypes, nextIndex);
            //countLocalVars = nextIndex - indexLocalVars;

            maxTempIndex  = nextIndex;
            nextTempIndex = nextIndex;
            nextTempLabel = 0xFFFE;

            InitLocalsRefs(defMethod, _localTypes);

            localTypes = _localTypes.ToArray();

            if (defMethod.Body.HasExceptionHandlers)
            {
                // jvm resets the stackmap on each exception clause, to the state it
                // was on entry to the try block.  this means any locals initialized
                // within a try block are 'lost' outside the try, and cause stackmap
                // conflicts.  two examples:
                //
                // (1) filter condition code (in a filter clause) sets a local to the
                // exception object, then uses that local in a separate catch clause.
                // (2) returning a value from a 'try' can initialize a local with the
                // return value, but the 'return' instruction is outside the 'try'.
                //
                // to avoid stackmap conflicts, we have to initialize the variables
                // in advance.  ideally, on entry to each 'try' block, and only for
                // those variables actually referenced in the block.  but this causes
                // several complications, so instead we go for a simpler approach,
                // and always initialize all variables in advance.

                InitializeUnusedVariables();
            }

            instCountAfterInitCode = code.Instructions.Count;
        }
Пример #14
0
        public CilMethod EnterMethod(MethodReference fromMethod)
        {
            var myMethod      = CilMethod.From(fromMethod);
            var declaringType = fromMethod.DeclaringType;

            int parameterIndex = (myMethod.HasThisArg ? 1 : 0);

            foreach (var p in myMethod.Parameters)
            {
                parameterIndex += p.Type.Category;
            }

            if (myMethod.IsConstructor || myMethod.IsStatic)
            {
                if (declaringType.HasGenericParameters)
                {
                    parameterIndex += EnterGenericProvider(declaringType, null, parameterIndex);
                }
            }

            if (declaringType.IsGenericInstance)
            {
                var genericInstance = (GenericInstanceType)declaringType;
                EnterGenericInstance(genericInstance, genericInstance.ElementType);
            }

            if (fromMethod.HasGenericParameters)
            {
                EnterGenericProvider(fromMethod, null, parameterIndex);
            }

            if (fromMethod.IsGenericInstance)
            {
                var genericInstance = (GenericInstanceMethod)fromMethod;
                EnterGenericInstance(genericInstance, genericInstance.ElementMethod);
            }

            return(myMethod);
        }
Пример #15
0
        public static void BuildOverloadProxy(TypeDefinition fromType, MethodDefinition fromMethod,
                                              CilMethod targetMethod, JavaClass intoClass)
        {
            // create a proxy bridge to forward invocations of a virtual method from
            // the base class, to an implementation in a derived class.  this is needed
            // where the base virtual method has generic parameters or return type,
            // for example:  virtual T SomeMethod(T arg)   will be translated as:
            //      java.lang.Object SomeMethod(-generic-$)(java.lang.Object arg)
            // and in a derived class that specializes T as String:
            //      java.lang.String SomeMethod(java.lang.String arg)
            // so the derived class must also include a proxy bridge method with the
            // same name as in the base class, and forward the call.

            //Console.WriteLine($"CHECK OVERRIDE {fromMethod}");
            int targetMethodCount = targetMethod.Parameters.Count;

            for (;;)
            {
                var baseType = fromType.BaseType;
                if (baseType == null)
                {
                    break;
                }
                fromType = CilType.AsDefinition(baseType);
                if (!baseType.IsGenericInstance)
                {
                    continue;
                }

                foreach (var fromMethod2 in fromType.Methods)
                {
                    //Console.WriteLine($"\tCOMPARE {fromMethod2} {fromMethod2.IsVirtual} {fromMethod2.Name == fromMethod.Name} {targetMethodCount == fromMethod2.Parameters.Count}");
                    if (fromMethod2.IsVirtual && fromMethod2.Name == fromMethod.Name &&
                        targetMethodCount == fromMethod2.Parameters.Count)
                    {
                        if (CilMethod.CompareMethods(fromMethod, fromMethod2))
                        {
                            //Console.WriteLine(">>>>>>>>>>>>> WARNING SAME: " + fromMethod.ToString() + " AND " + fromType);
                            continue;
                        }

                        var genericMark = CilMain.GenericStack.Mark();
                        CilMain.GenericStack.EnterType(baseType);
                        var baseMethod = new CilInterfaceMethod(
                            CilMain.GenericStack.EnterMethod(fromMethod2));
                        CilMain.GenericStack.Release(genericMark);

                        //Console.WriteLine($"\twould compare with {fromMethod2} in class {baseType}");
                        //Console.WriteLine($"\t\t{baseMethod}");

                        if (targetMethodCount != baseMethod.Parameters.Count)
                        {
                            continue;
                        }
                        if (!IsGenericOrEqual(targetMethod.ReturnType, baseMethod.ReturnType))
                        {
                            continue;
                        }
                        bool sameParameters = true;
                        for (int i = 0; i < targetMethodCount; i++)
                        {
                            if (!IsGenericOrEqual(targetMethod.Parameters[i].Type,
                                                  baseMethod.Parameters[i]))
                            {
                                bool equalsAfterUnboxing = (
                                    targetMethod.Parameters[i].Type is BoxedType boxedType &&
                                    boxedType.UnboxedType.Equals(baseMethod.Parameters[i]));

                                if (!equalsAfterUnboxing)
                                {
                                    //Console.WriteLine($"\tMISMATCH {targetMethod.Parameters[i].Type} vs {baseMethod.Parameters[i]}/{baseMethod.Parameters[i].IsGenericParameter}");
                                    sameParameters = false;
                                    break;
                                }
                            }
                        }
                        if (sameParameters)
                        {
                            //Console.WriteLine($"proxying {targetMethod} in class {targetMethod.DeclType}");
                            BuildGenericProxy2(baseMethod, targetMethod,
                                               false, targetMethod.DeclType, intoClass);
                            return;
                        }
                    }
                }
            }
Пример #16
0
        public static JavaClass MakeGenericClass(JavaClass fromClass, CilType fromType)
        {
            // if the generic class has static fields or a static initializer
            // then we need to move those into a separate class that can be
            // instantiated multiple times for multiple separate instances,
            // one for each concrete implementation of the generic type.

            int numGeneric = fromType.GenericParameters.Count;

            var dataClass = MoveStaticFields(fromClass, null);

            dataClass = MoveStaticInit(fromClass, dataClass);

            if (dataClass != null)
            {
                FixConstructorInData(dataClass, numGeneric);
            }

            // a generic class implements the IGenericObject interface,
            // and has a generic-type field for the concrete implementation
            // of the generic type and generic arguments

            CreateGenericTypeFields(fromClass, numGeneric);
            BuildGetTypeMethod(fromClass, fromType);

            return(dataClass);

            //
            // move any static fields from the generic class,
            // as instance fields in a new class
            //

            JavaClass MoveStaticFields(JavaClass fromClass, JavaClass dataClass)
            {
                var fields = fromClass.Fields;

                if (fields == null)
                {
                    return(dataClass);
                }

                int n = fields.Count;

                for (int i = 0; i < n;)
                {
                    var fld = fields[i];
                    if ((fld.Flags & JavaAccessFlags.ACC_STATIC) == 0)
                    {
                        i++;
                        continue;
                    }
                    if (((CilType)fld.Type).IsLiteral)
                    {
                        i++;
                        continue;
                    }

                    if (dataClass == null)
                    {
                        dataClass = CreateClass(fromClass);
                    }

                    if (fld.Constant != null)
                    {
                        throw CilMain.Where.Exception($"initializer in static field '{fld.Name}' in generic class");
                    }

                    fields.RemoveAt(i);
                    n--;

                    fld.Flags &= ~JavaAccessFlags.ACC_STATIC;
                    dataClass.Fields.Add(fld);
                }

                return(dataClass);
            }

            //
            // move the static constructor/initializer
            // from the generic class to the new data class
            //

            JavaClass MoveStaticInit(JavaClass fromClass, JavaClass dataClass)
            {
                var methods = fromClass.Methods;
                int n       = methods.Count;

                for (int i = 0; i < n;)
                {
                    var mth = methods[i];
                    if (mth.Name != "<clinit>")
                    {
                        i++;
                        continue;
                    }

                    if (dataClass == null)
                    {
                        dataClass = CreateClass(fromClass);
                    }

                    methods.RemoveAt(i);
                    n--;

                    mth.Name  = "<init>";
                    mth.Class = dataClass;
                    mth.Flags = JavaAccessFlags.ACC_PUBLIC;
                    dataClass.Methods.Add(mth);
                }

                return(dataClass);
            }

            //
            // create a constructor if there was no static initializer,
            // or inject a call to super class constructor
            //

            void FixConstructorInData(JavaClass dataClass, int numGeneric)
            {
                JavaCode code;
                bool     insertReturn;

                if (dataClass.Methods.Count == 0)
                {
                    code         = CilMethod.CreateConstructor(dataClass, numGeneric, true);
                    insertReturn = true;

                    code.MaxStack  = 1;
                    code.MaxLocals = 1 + numGeneric;
                }
                else
                {
                    code = dataClass.Methods[0].Code;
                    if (code.MaxStack < 1)
                    {
                        code.MaxStack = 1;
                    }

                    // we are injecting a call to super constructor at the very top,
                    // so local 0 should have the proper type, not uninitializedThis
                    code.StackMap.SetLocalInAllFrames(
                        0, CilType.From(new JavaType(0, 0, dataClass.Name)), null);

                    insertReturn = false;
                }

                code.Instructions.Insert(0, new Instruction(
                                             0x19 /* aload */, null, (int)0, 0xFFFF));

                code.Instructions.Insert(1, new Instruction(
                                             0xB7 /* invokespecial */, JavaType.ObjectType,
                                             new JavaMethodRef("<init>", JavaType.VoidType), 0xFFFF));

                // the static initializer can call static methods on its own type,
                // and those methods can invoke system.RuntimeType.GetType() to get
                // a reference to the generic type that is still being initialized.
                // and more importantly, a reference to the the static-generic data
                // object that is constructed by this method.  to make the object
                // available to such access, we call system.RuntimeType.SetStatic().
                // see also system.RuntimeType.MakeGenericType/MakeGenericType().

                code.Instructions.Insert(2, new Instruction(
                                             0x19 /* aload */, null, (int)0, 0xFFFF));

                code.Instructions.Insert(3, new Instruction(
                                             0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType,
                                             new JavaMethodRef("SetStatic",
                                                               JavaType.VoidType, JavaType.ObjectType), 0xFFFF));

                if (insertReturn)
                {
                    code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null, 0xFFFF);
                }
            }

            //
            // create the new data class
            //

            JavaClass CreateClass(JavaClass fromClass) =>
            CilMain.CreateInnerClass(fromClass, fromClass.Name + "$$static", 0,
                                     markGenericEntity: true);

            //
            // create a private instance field to hold the runtime type
            // for a particular combination of generic type and arguments
            //

            void CreateGenericTypeFields(JavaClass fromClass, int numGeneric)
            {
                var fld = new JavaField();

                fld.Name  = ConcreteTypeField.Name;
                fld.Type  = ConcreteTypeField.Type;
                fld.Class = fromClass;
                fld.Flags = JavaAccessFlags.ACC_PRIVATE;

                if (fromClass.Fields == null)
                {
                    fromClass.Fields = new List <JavaField>();
                }

                fromClass.Fields.Add(fld);
            }
        }
Пример #17
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);
        }
Пример #18
0
        int InitLocalsArgs(CilMethod myMethod, List <CilType> localTypes)
        {
            var     parameters = myMethod.WithGenericParameters.Parameters;
            int     numArgs    = parameters.Count;
            CilType thisType;

            if (myMethod.HasThisArg)
            {
                if (myMethod.IsConstructor)
                {
                    thisType = CilType.From(JavaStackMap.UninitializedThis);
                }
                else
                {
                    thisType = myMethod.DeclType;
                    if (thisType.IsValueClass)
                    {
                        thisType = thisType.MakeByRef();
                    }
                }
                stackMap.SetLocal(0, thisType);
                numArgs++;
            }
            else
            {
                thisType = null;
            }

            argToLocalMap = new int[numArgs];
            int nextArg   = 0;
            int nextIndex = 0;

            if (thisType != null)
            {
                argToLocalMap[nextArg++] = nextIndex++;
                localTypes.Add(thisType);
                numArgs--;
            }

            for (int i = 0; i < numArgs; i++)
            {
                var argType = (CilType)parameters[i].Type;
                stackMap.SetLocal(nextIndex, argType);

                var genericType = argType.GetMethodGenericParameter();
                if (genericType != null)
                {
                    if (genericType.IsArray && genericType.IsGenericParameter)
                    {
                        // note that GenericArrayType is compared by reference
                        // in CodeArrays, to detect an array of a generic type T[]
                        argType = CodeArrays.GenericArrayType;
                    }
                    else
                    {
                        argType = genericType;
                    }
                }

                argToLocalMap[nextArg++] = nextIndex;
                nextIndex += argType.Category;

                localTypes.Add(argType);
                while (nextIndex > localTypes.Count)
                {
                    localTypes.Add(null);
                }
            }

            return(nextIndex);
        }
Пример #19
0
        public static void LoadFunction(JavaCode code, Mono.Cecil.Cil.Instruction cilInst)
        {
            if (cilInst.Operand is MethodReference implMethodRef)
            {
                var implMethod = CilMethod.From(implMethodRef);

                var(declType, declMethod, dlgMethodName) = FindInterfaceType(cilInst);

                if (dlgMethodName != null)
                {
                    // if this is an artificial delegate for a functional interface,
                    // then we can't actually instantiate this particular delegate,
                    // because its definition only exists in the DLL created by
                    // DotNetImporter;  see BuildDelegate there.  we fix the Newobj
                    // instruction to instantiate system.FunctionalInterfaceDelegate.

                    cilInst.Next.Operand = DelegateConstructor(cilInst.Next.Operand);

                    declMethod = new JavaMethodRef(dlgMethodName,
                                                   implMethod.ReturnType,
                                                   implMethod.Parameters);
                }

                if (IsPrimitive(declMethod.ReturnType))
                {
                    // primitive return value is translated to java.lang.Object,
                    // because the delegate target may return a generic type that
                    // is specialized for the primitive type in the delegate.
                    // see also MakeInterface and GenerateInvoke
                    declMethod = new JavaMethodRef(
                        declMethod.Name, JavaType.ObjectType, declMethod.Parameters);
                }

                // select the method handle kind for the dynamic call site.
                // for a non-static invocation, we need to push an object reference.

                JavaMethodHandle.HandleKind callKind;
                if (implMethod.DeclType.IsInterface)
                {
                    callKind = JavaMethodHandle.HandleKind.InvokeInterface;
                }
                else
                {
                    callKind = JavaMethodHandle.HandleKind.InvokeVirtual;
                }

                if (cilInst.OpCode.Code == Code.Ldftn)
                {
                    if (implMethod.IsStatic)
                    {
                        callKind = JavaMethodHandle.HandleKind.InvokeStatic;
                    }

                    else
                    {
                        var  implMethodDef = CilMethod.AsDefinition(implMethodRef);
                        bool isVirtual     = implMethodDef.IsVirtual;
                        if (isVirtual && implMethodDef.DeclaringType.IsSealed)
                        {
                            isVirtual = false;
                        }

                        if (!isVirtual)
                        {
                            // non-virtual instance method
                            code.NewInstruction(0x59 /* dup */, null, null);
                            code.StackMap.PushStack(JavaType.ObjectType);
                        }
                        else
                        {
                            // virtual method should only be specified in 'ldvirtftn'
                            throw new Exception("virtual method referenced");
                        }
                    }
                }

                //
                // the method may take generic type parameters, i.e. the System.Type
                // parameters that are added at the end of the parameter list, by
                // CilMethod::ImportGenericParameters.  but when the delegate is
                // called, these parameters will not be pushed.  therefore we do
                // the following when assigning such a method to a delegate:
                //
                // (1) we select a different implementing method, i.e. the bridge
                // method generated by CreateCapturingBridgeMethod (see below),
                // which moves the type arguments to the head of the parameter list.
                // (2) we generate and push the type arguments at this time, via
                // calls to GenericUtil.LoadGeneric.
                // (3) we specify the type arguments as capture parameters of the
                // call site.  (see also JavaCallSite.)  this means the generated
                // proxy will inject these parameters when it calls the bridge
                // method from step (1), and that bridge method will push these
                // parameters at the end of the parameter list, when it invokes
                // the actual target method.
                //

                List <JavaFieldRef> TypeArgs    = null;
                JavaMethodRef       implMethod2 = implMethod;

                var implGenericMethod = implMethod.WithGenericParameters;
                if (implGenericMethod != implMethod)
                {
                    implMethod2 = new JavaMethodRef(
                        "delegate-bridge-" + implGenericMethod.Name,
                        implMethod.ReturnType, implMethod.Parameters);

                    var count1 = implMethod.Parameters.Count;
                    var count2 = implGenericMethod.Parameters.Count;
                    TypeArgs = implGenericMethod.Parameters.GetRange(count1, count2 - count1);

                    var callMethod = CilMain.GenericStack.EnterMethod(implMethodRef);
                    for (int i = count1; i < count2; i++)
                    {
                        GenericUtil.LoadGeneric(implGenericMethod.Parameters[i].Name, code);
                    }
                    for (int i = count1; i < count2; i++)
                    {
                        code.StackMap.PopStack(CilMain.Where);
                    }
                }

                // create a CallSite that implements the method signature
                // declMethod, in the functional interface declType, in order
                // to proxy-invoke the method implMethod.DeclType::implMethod.

                var callSite = new JavaCallSite(declType, declMethod,
                                                implMethod.DeclType, implMethod2,
                                                TypeArgs, callKind);

                code.NewInstruction(0xBA /* invokedynamic */, null, callSite);

                if (callKind != JavaMethodHandle.HandleKind.InvokeStatic)
                {
                    code.StackMap.PopStack(CilMain.Where);
                }

                code.StackMap.PushStack(CilType.From(declType));
            }
            else
            {
                throw new InvalidProgramException();
            }

            //
            // ldftn or ldvirtftn should be followed by newobj, which we can use
            // to identify the delegate, and therefore, the interface
            //

            (JavaType, JavaMethodRef, string) FindInterfaceType(
                Mono.Cecil.Cil.Instruction cilInst)
            {
                cilInst = cilInst.Next;
                if (cilInst != null && cilInst.OpCode.Code == Code.Newobj &&
                    cilInst.Operand is MethodReference constructorRef)
                {
                    var dlgType = CilType.From(constructorRef.DeclaringType);
                    if (dlgType.IsDelegate)
                    {
                        //
                        // check for an artificial delegate, generated to represent a
                        // java functional interface: (BuildDelegate in DotNetImporter)
                        //
                        // delegate type is marked [java.lang.attr.AsInterface],
                        // and is child of an interface type.
                        // interface type is marked [java.lang.attr.RetainName],
                        // and has one method.
                        //

                        var dlgType0 = CilType.AsDefinition(constructorRef.DeclaringType);
                        if (dlgType0.HasCustomAttribute("AsInterface"))
                        {
                            var ifcType0 = dlgType0.DeclaringType;
                            var ifcType  = CilType.From(ifcType0);
                            if (ifcType.IsInterface && ifcType.IsRetainName &&
                                ifcType0.HasMethods && ifcType0.Methods.Count == 1)
                            {
                                return(ifcType, null, ifcType0.Methods[0].Name);
                            }
                        }

                        //
                        // otherwise, a normal delegate, which may be generic, or plain.
                        // interface name is DelegateType$interface.
                        // we look for a method named Invoke.
                        //

                        foreach (var method in dlgType0.Methods)
                        {
                            if (method.Name == "Invoke")
                            {
                                return(InterfaceType(dlgType), CilMethod.From(method), null);
                            }
                        }
                    }
                }
                throw new InvalidProgramException();
            }

            //
            // create a method reference to delegate constructor from baselib:
            // system.MulticastDelegate::.ctor(object, object)
            //

            CilMethod DelegateConstructor(object Operand)
            {
                var baseType = CilType.AsDefinition(((MethodReference)Operand)
                                                    .DeclaringType).BaseType;

                if (baseType.Namespace != "System" || baseType.Name != "MulticastDelegate")
                {
                    throw CilMain.Where.Exception(
                              $"delegate base type is '{baseType.Name}', "
                              + "but expected 'System.MulticastDelegate'");
                }

                return(CilMethod.CreateDelegateConstructor());
            }
        }
Пример #20
0
        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);
            }
        }
Пример #21
0
        public CilInterfaceMethod(CilMethod fromMethod)
        {
            var name = fromMethod.Name;
            var idx  = name.IndexOf(CilMain.OPEN_PARENS);

            if (idx != -1)
            {
                name = name.Substring(0, idx);
            }
            idx = name.IndexOf("--");
            if (idx != -1)
            {
                name = name.Substring(0, idx);
            }
            idx = name.LastIndexOf('-');
            if (idx != -1)
            {
                name = name.Substring(idx + 1);
            }

            ResolvedGenericTypes = "";

            var returnType = (CilType)fromMethod.ReturnType;

            if (returnType.IsGenericParameter)
            {
                var genericMark = CilMain.GenericStack.Mark();
                var(type, index)     = CilMain.GenericStack.Resolve(returnType.JavaName);
                ResolvedGenericTypes = $"{type},{index};";
                if (index == 0)
                {
                    returnType = type;
                }

                CilMain.GenericStack.Release(genericMark);
            }

            int n          = fromMethod.Parameters.Count;
            var parameters = new List <CilType>(n);

            for (int i = 0; i < n; i++)
            {
                var parameter = (CilType)fromMethod.Parameters[i].Type;
                if (parameter.IsGenericParameter)
                {
                    parameter = parameter.GenericParameters[0];

                    var genericMark = CilMain.GenericStack.Mark();
                    var(type, index)      = CilMain.GenericStack.Resolve(parameter.JavaName);
                    ResolvedGenericTypes += $"{type},{index};";
                    if (index == 0)
                    {
                        if (parameter.ArrayRank != 0)
                        {
                            type = type.AdjustRank(parameter.ArrayRank);
                        }
                        parameter = type;
                    }

                    CilMain.GenericStack.Release(genericMark);
                }
                parameters.Add(parameter);
            }

            Method     = fromMethod;
            SimpleName = name;
            Parameters = parameters;
            ReturnType = returnType;
        }
Пример #22
0
        public static List <JavaClass> BuildProxyMethods(List <CilInterface> allInterfaces,
                                                         TypeDefinition fromType, CilType intoType,
                                                         JavaClass theClass)
        {
            //
            // process only if the class (or interface) has any methods or super interfaces
            //

            var classMethods = theClass.Methods;

            if (classMethods.Count == 0)
            {
                return(null);
            }

            bool isInterface = intoType.IsInterface;

            if ((!isInterface) && theClass.Interfaces == null)
            {
                return(null);
            }

            var theMethods = CilInterfaceMethod.CollectAll(fromType);

            //
            // if any interfaces are marked [RetainName], make sure that
            // all corresponding methods are also marked [RetainName]
            //

            CheckRetainNameMethods(theMethods, allInterfaces, intoType);

            //
            // if this is an abstract class but forced to an interface via [AddInterface]
            // decoration, then we need to remove all constructors generated for the class
            //

            if (intoType.IsInterface)
            {
                if (!fromType.IsInterface)
                {
                    for (int i = classMethods.Count; i-- > 0;)
                    {
                        if (classMethods[i].Name == "<init>")
                        {
                            classMethods.RemoveAt(i);
                        }
                    }
                }

                if (intoType.HasGenericParameters)
                {
                    // the RuntimeType constructor in baselib uses IGenericEntity
                    // marker interface to identify generic classes.  note that
                    // real generic types implement IGenericObject -> IGenericEntity.

                    theClass.AddInterface("system.IGenericEntity");
                }

                return(null);
            }

            //
            // for each implemented interface, build proxy methods
            //

            List <JavaClass> output = null;

            int ifcNumber = 0;

            foreach (var ifc in allInterfaces)
            {
                if ((!ifc.DirectReference) && ifc.SuperImplements)
                {
                    // we don't have to build proxy for an interface if it is
                    // implemented by a super type and not by our primary type
                    continue;
                }
                if (ifc.GenericTypes == null)
                {
                    foreach (var ifcMethod in ifc.Methods)
                    {
                        // build proxy methods:  interface$method -> method
                        var newMethod = BuildPlainProxy(ifcMethod, intoType, theMethods);
                        if (newMethod != null)
                        {
                            newMethod.Class = theClass;
                            theClass.Methods.Add(newMethod);
                        }
                    }
                }
                else
                {
                    var ifcClass = CreateInnerClass(theClass, intoType, ++ifcNumber);
                    ifcClass.AddInterface(ifc.InterfaceType.JavaName);

                    if (output == null)
                    {
                        output = new List <JavaClass>();
                        CreateInterfaceArrayField(theClass);
                    }
                    output.Add(ifcClass);

                    // if the class implements a generic interface for multiple types,
                    // then we need a method suffix to differentiate between the methods.
                    // see also:  CilMethod::InsertMethodNamePrefix
                    string methodSuffix = "";
                    foreach (var genericType in ifc.GenericTypes)
                    {
                        methodSuffix += "--" + CilMethod.GenericParameterSuffixName(genericType);
                    }

                    foreach (var ifcMethod in ifc.Methods)
                    {
                        // build proxy classes:  proxy sub-class -> this class
                        BuildGenericProxy(ifcMethod, methodSuffix, intoType, theMethods, ifcClass);
                    }
                }
            }

            return(output);

            JavaClass CreateInnerClass(JavaClass parentClass, CilType parentType, int ifcNumber)
            {
                // generic interfaces are implemented as proxy sub-classes which
                // call methods on the parent class object.  we need to define
                // an inner class.  this class has one instance field which is a
                // reference to the parent class.  the constructor takes this
                // reference as a parameter and initializes the instance field.

                var newClass = CilMain.CreateInnerClass(parentClass,
                                                        parentClass.Name + "$$generic" + ifcNumber.ToString());

                var fld = new JavaField();

                fld.Name  = ParentFieldName;
                fld.Type  = parentType;
                fld.Class = newClass;
                fld.Flags = JavaAccessFlags.ACC_PRIVATE;
                newClass.Fields.Add(fld);

                var code = CilMain.CreateHelperMethod(newClass,
                                                      new JavaMethodRef("<init>", JavaType.VoidType, JavaType.ObjectType),
                                                      2, 2);

                code.Method.Flags &= ~JavaAccessFlags.ACC_BRIDGE;   // invalid for constructor

                code.NewInstruction(0x19 /* aload */, null, (int)0);
                code.NewInstruction(0xB7 /* invokespecial */, JavaType.ObjectType,
                                    new JavaMethodRef("<init>", JavaType.VoidType));
                code.NewInstruction(0x19 /* aload */, null, (int)0);
                code.NewInstruction(0x19 /* aload */, null, (int)1);
                code.NewInstruction(0xC0 /* checkcast */, parentType, null);
                code.NewInstruction(0xB5 /* putfield */, new JavaType(0, 0, newClass.Name),
                                    new JavaFieldRef(ParentFieldName, parentType));
                code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null);

                return(newClass);
            }

            void CreateInterfaceArrayField(JavaClass parentClass)
            {
                // the parent class has a helper array field that is used to track
                // the proxy objects generated for implemented generic interfaces.
                // see also: InitInterfaceArrayField, below.

                var fld = new JavaField();

                fld.Name  = InterfaceArrayField.Name;
                fld.Type  = InterfaceArrayField.Type;
                fld.Class = parentClass;
                fld.Flags = JavaAccessFlags.ACC_PRIVATE;

                if (parentClass.Fields == null)
                {
                    parentClass.Fields = new List <JavaField>(1);
                }
                parentClass.Fields.Add(fld);
            }
        }
Пример #23
0
        public static void BuildGenericProxy(CilInterfaceMethod ifcMethod, /*string methodSuffix,*/
                                             CilType intoType, List <CilInterfaceMethod> classMethods,
                                             JavaClass ifcClass)
        {
            CilMethod targetMethod = null;

            //Console.WriteLine("\n***** LOOKING FOR INTERFACE METHOD " + ifcMethod + " (SUFFIX " + methodSuffix + ") AKA " + ifcMethod.Method);

            foreach (var clsMethod in classMethods)
            {
                /*Console.WriteLine("> LOOKING AT "
                 + (clsMethod.Method.IsExplicitImpl ? "EXPLICIT " : "")
                 + "CLASS METHOD " + clsMethod + " AKA " + clsMethod.Method);*/

                if (ifcMethod.GenericCompare(clsMethod))
                {
                    if (clsMethod.Method.IsExplicitImpl)
                    {
                        // a matching explicit override method is the best match,
                        // and we can immediately stop searching
                        targetMethod = clsMethod.Method;
                        break;
                    }
                    else
                    {
                        // more than one method may match, if a derived type overrides
                        // or hides a method that also exists in a base type.  but the
                        // derived (primary) type methods always come first
                        if (targetMethod == null)
                        {
                            targetMethod = clsMethod.Method;
                        }

                        // if a second method matches, and the set of generic types
                        // in its signature exactly matches the interface method we
                        // are looking for, then prefer this method.  when a class
                        // implements same-name methods from multiple interfaces,
                        // this is needed to pick the right method.
                        // see also ResolvedGenericTypes in CilInterfaceMethod.

                        else if (clsMethod.ResolvedGenericTypes.Length != 0 &&
                                 clsMethod.ResolvedGenericTypes
                                 == ifcMethod.ResolvedGenericTypes)
                        {
                            targetMethod = clsMethod.Method;
                        }
                    }
                }
            }

            if (targetMethod == null)
            {
                throw CilMain.Where.Exception(
                          $"missing method '{ifcMethod.Method}' "
                          + $"(for interface '{ifcMethod.Method.DeclType}')");
            }

            // Console.WriteLine("INTERFACE METHOD " + ifcMethod.Method + " TARGET " + targetMethod);

            BuildGenericProxy2(ifcMethod, targetMethod, true, intoType, ifcClass);
        }
Пример #24
0
        bool Translate_Newobj(CilMethod callMethod)
        {
            if (callMethod.IsStatic)
            {
                return(false);
            }

            var newClass = callMethod.DeclType;

            if (callMethod.IsArrayMethod)
            {
                arrays.New(newClass, callMethod.Parameters.Count);
                return(true);
            }

            if (newClass.IsValueClass)
            {
                if (!callMethod.IsValueInit)
                {
                    return(false);
                }
            }
            else
            {
                if (!callMethod.IsConstructor)
                {
                    return(false);
                }

                if (newClass.Equals(JavaType.StringType) &&
                    code.Method.Class.Name != newClass.JavaName)
                {
                    // redirect the System.String constructor to system.String.New
                    var newMethod = new JavaMethodRef("New", newClass);
                    newMethod.Parameters = callMethod.Parameters;

                    code.NewInstruction(0xB8 /* invokestatic */,
                                        new JavaType(0, 0, newClass.JavaName), newMethod);

                    int n = callMethod.Parameters.Count;
                    while (n-- > 0)
                    {
                        stackMap.PopStack(CilMain.Where);
                    }
                    stackMap.PushStack(newClass);

                    return(true);
                }

                if (newClass.Equals(JavaType.ThrowableType))
                {
                    // generally we translate System.Exception to java.lang.Throwable,
                    // but not when allocating a new object
                    newClass = CilType.From(new JavaType(0, 0, newClass.JavaName));
                }
            }

            //
            // save any parameters pushed for the call to the constructor
            //

            int localIndex = SaveMethodArguments(callMethod);

            code.NewInstruction(0xBB /* new */, newClass.AsWritableClass, null);
            stackMap.PushStack(newClass);
            code.NewInstruction(0x59 /* dup */, null, null);
            stackMap.PushStack(newClass);

            if (newClass.IsValueClass)
            {
                // new value type object.  first call the default constructor,
                // to allocate the object.  then call the constructor provided
                // in the Newobj instruction, to initialize the new object

                var defaultConstructor = new CilMethod(newClass);
                PushGenericArguments(defaultConstructor);
                code.NewInstruction(0xB7 /* invokespecial */, newClass,
                                    defaultConstructor.WithGenericParameters);

                // the defaul constructor accepted generic parameters, if any,
                // and they should be discarded now

                int numGeneric =
                    defaultConstructor.WithGenericParameters.Parameters.Count;

                while (numGeneric-- > 0)
                {
                    stackMap.PopStack(CilMain.Where);
                }

                // if Newobj specifies a non-default constructor, then call it

                int numNonGeneric = callMethod.Parameters.Count;
                if (numNonGeneric != 0)
                {
                    code.NewInstruction(0x59 /* dup */, null, null);

                    LoadMethodArguments(callMethod, localIndex);

                    code.NewInstruction(0xB6 /* invokevirtual */, newClass, callMethod);

                    while (numNonGeneric-- > 0)
                    {
                        stackMap.PopStack(CilMain.Where);
                    }
                }

                stackMap.PopStack(CilMain.Where);
            }
            else
            {
                //
                // new reference type object
                //

                LoadMethodArguments(callMethod, localIndex);
                PushGenericArguments(callMethod);

                code.NewInstruction(0xB7 /* invokespecial */, newClass.AsWritableClass,
                                    callMethod.WithGenericParameters);

                ClearMethodArguments(callMethod, false);
            }

            return(true);
        }
Пример #25
0
        public static JavaMethod BuildPlainProxy(CilInterfaceMethod ifcMethod, CilType intoType,
                                                 List <CilInterfaceMethod> classMethods)
        {
            CilMethod targetMethod = null;

            foreach (var clsMethod in classMethods)
            {
                if (clsMethod.Method.IsExplicitImpl)
                {
                    // no need for a proxy if we already have an override method,
                    // which has the same name as the proxy:  interface$method
                    if (ifcMethod.Method.Name == clsMethod.Method.Name)
                    {
                        return(null);
                    }
                }
                else if (ifcMethod.PlainCompare(clsMethod))
                {
                    // more than one method may match, if a derived type overrides
                    // or hides a method that also exists in a base type.  but the
                    // derived (primary) type methods always come first.
                    if (targetMethod == null)
                    {
                        targetMethod = clsMethod.Method;
                    }
                }
            }

            if (targetMethod == null)
            {
                throw CilMain.Where.Exception(
                          $"missing method '{ifcMethod.Method}' "
                          + $"(for interface '{ifcMethod.Method.DeclType}')");
            }

            if (targetMethod.IsRetainName)
            {
                // method retains is name, so it doesn't require a proxy bridge
                return(null);
            }

            //
            // create proxy method
            //

            var newMethod = new JavaMethod(null, targetMethod);

            newMethod.Name  = ifcMethod.Method.Name;
            newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_BRIDGE;

            var code = newMethod.Code = new JavaCode();

            code.Method       = newMethod;
            code.Instructions = new List <JavaCode.Instruction>();

            //
            // push 'this' and all other parameters
            //

            code.NewInstruction(0x19 /* aload */, null, (int)0);
            int numArgs = newMethod.Parameters.Count;
            int index   = 1;

            for (int i = 0; i < numArgs; i++)
            {
                var arg = targetMethod.Parameters[i].Type;
                code.NewInstruction(arg.LoadOpcode, null, (int)index);
                index += arg.Category;
            }

            //
            // invoke proxy target method and return
            //

            code.NewInstruction(0xB6 /* invokevirtual */, intoType, targetMethod);
            code.NewInstruction(targetMethod.ReturnType.ReturnOpcode, null, null);

            code.MaxLocals = code.MaxStack = index;

            return(newMethod);
        }
Пример #26
0
        void Load(CilType arrayType, CilType elemType, Mono.Cecil.Cil.Instruction inst)
        {
            if (arrayType == null)
            {
                arrayType = elemType.AdjustRank(1);
            }

            /*Console.WriteLine("(LOAD) ARRAY TYPE = " + arrayType + "," + arrayType.ArrayRank
             + " ELEM TYPE = " + elemType + "," + elemType.ArrayRank
             + " ELEMVAL? " + elemType.IsValueClass
             + " ELEMGEN? " + elemType.IsGenericParameter);*/

            if (object.ReferenceEquals(arrayType, GenericArrayType) ||
                elemType.IsGenericParameter || arrayType.IsGenericParameter)
            {
                code.NewInstruction(0xB8 /* invokestatic */,
                                    SystemArrayType, LoadArrayMethod);
                if (elemType.ArrayRank != 0)
                {
                    elemType = GenericArrayType;
                }
            }
            else
            {
                if (elemType.PrimitiveType == TypeCode.Int16 &&
                    (arrayType.PrimitiveType == TypeCode.Char ||
                     arrayType.PrimitiveType == TypeCode.UInt16))
                {
                    // ldelem.i2 with a char[] array, should be 'caload' not 'saload'
                    elemType = arrayType.AdjustRank(-arrayType.ArrayRank);
                }

                code.NewInstruction(elemType.LoadArrayOpcode, null, null);

                if (elemType.PrimitiveType == TypeCode.Byte)
                {
                    // unsigned byte result should be truncated to 8-bits
                    // (unless already followed by "ldc.i4 255 ; and")
                    bool followedByAndWith255 =
                        CodeBuilder.IsLoadConstant(inst.Next) == 0xFF &&
                        inst.Next.Next?.OpCode.Code == Code.And;

                    if (!followedByAndWith255)
                    {
                        stackMap.PushStack(JavaType.IntegerType);
                        code.NewInstruction(0x12 /* ldc */, null, (int)0xFF);
                        code.NewInstruction(0x7E /* iand */, null, null);
                        stackMap.PopStack(CilMain.Where);
                    }
                }

                if (arrayType.IsValueClass || elemType.IsValueClass)
                {
                    CilMethod.ValueMethod(CilMethod.ValueClone, code);
                    if (elemType.IsValueClass)
                    {
                        code.NewInstruction(0xC0 /* checkcast */, elemType, null);
                    }
                }
            }

            stackMap.PushStack(elemType);
        }
Пример #27
0
        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);
            }
        }
Пример #28
0
        public static void BuildGenericProxy2(CilInterfaceMethod ifcMethod, CilMethod targetMethod,
                                              bool parentField, CilType intoType, JavaClass ifcClass)
        {
            //
            // create proxy method
            //

            var targetMethod2 = targetMethod.WithGenericParameters;
            var ifcMethod2    = ifcMethod.Method.WithGenericParameters;

            var newMethod = new JavaMethod(ifcClass, targetMethod2);

            newMethod.Name  = ifcMethod2.Name;
            newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_BRIDGE;

            var code = newMethod.Code = new JavaCode();

            code.Method       = newMethod;
            code.Instructions = new List <JavaCode.Instruction>();

            //
            // push a reference to the parent object
            //

            code.NewInstruction(0x19 /* aload */, null, (int)0);
            if (parentField)
            {
                code.NewInstruction(0xB4 /* getfield */, new JavaType(0, 0, ifcClass.Name),
                                    new JavaFieldRef(ParentFieldName, intoType));
            }

            //
            // push all other parameters
            //

            int numArgs  = newMethod.Parameters.Count;
            int index    = 1;
            int maxStack = 1;

            for (int i = 0; i < numArgs; i++)
            {
                var ifcArg = ifcMethod2.Parameters[i].Type;
                code.NewInstruction(ifcArg.LoadOpcode, null, (int)index);
                index += ifcArg.Category;

                var clsArg = (CilType)targetMethod2.Parameters[i].Type;
                if (JavaType.ObjectType.Equals(ifcArg))
                {
                    if (!clsArg.IsReference)
                    {
                        var boxedArg = new BoxedType(clsArg, false);
                        code.NewInstruction(0xC0 /* checkcast */, boxedArg, null);
                        boxedArg.GetValue(code);
                    }
                    else if (!JavaType.ObjectType.Equals(clsArg))
                    {
                        code.NewInstruction(0xC0 /* checkcast */, clsArg, null);
                    }
                    // a parameter in the target method may be a concrete type,
                    // but if it is a generic java.lang.Object in the interface,
                    // then it must be a generic java.lang.Object in the proxy
                    newMethod.Parameters[i] = new JavaFieldRef("", ifcArg);
                }
                maxStack += clsArg.Category;
            }

            //
            // invoke proxy target method
            //

            code.NewInstruction(0xB6 /* invokevirtual */, intoType, targetMethod2);

            //
            // return value from method
            //

            var clsRet = (CilType)targetMethod2.ReturnType;
            var ifcRet = ifcMethod2.ReturnType;

            if (JavaType.ObjectType.Equals(ifcRet))
            {
                if (!clsRet.IsReference)
                {
                    var boxedArg = new BoxedType(clsRet, false);
                    boxedArg.BoxValue(code);
                }
                // the return value in the target method may be a concrete type,
                // but if it is a generic java.lang.Object in the interface,
                // then it must also be a generic java.lang.Object in the proxy
                newMethod.ReturnType = ifcRet;
                code.NewInstruction(ifcRet.ReturnOpcode, null, null);
            }
            else
            {
                code.NewInstruction(clsRet.ReturnOpcode, null, null);
            }

            code.MaxLocals = index;
            code.MaxStack  = maxStack;

            ifcClass.Methods.Add(newMethod);
        }
Пример #29
0
        void LoadObject(Code cilOp, object data)
        {
            if (data is TypeReference typeRef)
            {
                var dataType = CilType.From(typeRef);
                var fromType = (CilType)code.StackMap.PopStack(CilMain.Where);

                if (CodeSpan.LoadStore(true, fromType, null, dataType, code))
                {
                    return;
                }

                if ((!dataType.IsReference) && cilOp == Code.Ldobj &&
                    fromType is BoxedType fromBoxedType &&
                    dataType.PrimitiveType == fromBoxedType.UnboxedType.PrimitiveType)
                {
                    // 'ldobj primitive' with a corresponding boxed type on the stack.
                    // we implement by unboxing the boxed type into a primitive value.
                    fromBoxedType.GetValue(code);
                    stackMap.PushStack(fromBoxedType.UnboxedType);
                    return;
                }

                if (dataType.IsGenericParameter ||
                    (dataType.IsValueClass && dataType.Equals(fromType)))
                {
                    code.StackMap.PushStack(dataType);

                    if (SkipClone(cilInst.Next, fromType))
                    {
                        // see below for the several cases where we determine
                        // that we can safely avoid making a clone of the value
                        code.NewInstruction(0x00 /* nop */, null, null);
                    }
                    else if (dataType.IsGenericParameter)
                    {
                        GenericUtil.ValueClone(code);
                    }
                    else if (cilOp == Code.Ldobj)
                    {
                        code.NewInstruction(0x00 /* nop */, null, null);
                    }
                    else
                    {
                        CilMethod.ValueMethod(CilMethod.ValueClone, code);
                    }
                    return;
                }

                if (dataType.IsReference && cilOp == Code.Box)
                {
                    // 'box' is permitted on reference types, we treat it as a cast
                    code.StackMap.PushStack(fromType);
                    CastToClass(data);
                    return;
                }

                if (!dataType.IsReference)
                {
                    var boxedType = new BoxedType(dataType, false);
                    code.StackMap.PushStack(boxedType);
                    boxedType.BoxValue(code);
                    return;
                }
            }
            throw new InvalidProgramException();


            bool SkipClone(Mono.Cecil.Cil.Instruction next, CilType checkType)
            {
                if (checkType.IsByReference)
                {
                    return(true);
                }

                if (next == null)
                {
                    return(false);
                }
                var op = next.OpCode.Code;

                if (IsBrTrueBrFalseIsInst(op))
                {
                    // if 'ldobj' or 'box' is followed by a check for null,
                    // we don't actually need to clone just for the test
                    return(true);
                }

                if (op == Code.Box)
                {
                    if (next.Operand is TypeReference nextTypeRef)
                    {
                        // 'ldobj' may be followed by 'box', to load the value
                        // of a byref value type, and then box it into an object.
                        // effectively we only need to clone once, in such a case.
                        return(CilType.From(nextTypeRef).Equals(checkType));
                    }
                }

                var(storeType, _) = locals.GetLocalFromStoreInst(op, next.Operand);
                if (storeType != null)
                {
                    // 'ldobj' or 'box' may be followed by a store instruction.
                    // if storing into a variable of the same value type, the
                    // next instruction will copy the value, so skip clone.
                    return(storeType.Equals(checkType));
                }

                if (op == Code.Ret && checkType.IsClonedAtTop)
                {
                    // if the value on the stack was cloned/boxed at the top of
                    // the method, then we can avoid clone and return it directly.
                    // see also:  CilType.MakeClonedAtTop and its callers.
                    return(true);
                }

                if (op == Code.Unbox_Any)
                {
                    if (next.Operand is TypeReference nextTypeRef)
                    {
                        // 'ldobj' or 'box' may be followed by 'unbox' and
                        // then one of the instructions above, e.g. 'brtrue'
                        // or 'stloc'.  we still want to detect such a case
                        // and prevent a needless clone.
                        return(SkipClone(next.Next, CilType.From(nextTypeRef)));
                    }
                }

                return(false);
            }
        }
Пример #30
0
        bool Translate_Call(CilMethod callMethod)
        {
            var currentClass = method.DeclType;
            var callClass    = callMethod.DeclType;

            byte op;

            if (callMethod.IsStatic)
            {
                if (ConvertCallToNop())
                {
                    return(true);
                }

                if (callClass.Equals(JavaType.ObjectType) ||
                    callClass.Equals(JavaType.StringType))
                {
                    // generally we translate System.Object to java.lang.Object,
                    //                    and System.String to java.lang.String,
                    // but not in the case of a static method call
                    callClass = CilType.From(new JavaType(0, 0, callClass.JavaName));
                }

                else if (IsSystemTypeOperatorOrIsCallToIsInterface(callClass, callMethod))
                {
                    // rename System.Type operators == and != to system.RuntimeType, and
                    // rename call to RuntimeTypeHandle.IsInterface to system.RuntimeType
                    callClass = CilType.SystemRuntimeTypeType;
                }

                else if (callMethod.IsExternal &&
                         NativeMethodClasses.TryGetValue(callClass.ClassName, out var altClassName))
                {
                    // when the call target is a native methods, redirect it
                    callClass = CilType.From(new JavaType(0, 0, altClassName));
                }

                op = 0xB8; // invokestatic
            }
            else if (callMethod.IsConstructor || currentClass.IsDerivedFrom(callClass))
            {
                if (callClass.Equals(JavaType.ThrowableType))
                {
                    // generally we translate System.Exception to java.lang.Throwable,
                    // but not in the case of a super constructor call
                    callClass = CilType.From(new JavaType(0, 0, callClass.JavaName));
                }

                if (ConvertVirtualToStaticCall(callClass, callMethod))
                {
                    return(true);
                }

                if (callMethod.IsVirtual && currentClass.Equals(callClass))
                {
                    // Android 'D8' does not support 'invokespecial' on a method
                    // on the same class, unless the method is marked final.
                    // if we know the target method is a virtual method, then
                    // it surely is not marked final, so use 'invokevirtual'.

                    op = 0xB6; // invokevirtual
                }
                else
                {
                    op = 0xB7; // invokespecial
                }

                if (callMethod.Name == "clone" && callMethod.Parameters.Count == 0)
                {
                    // if calling clone on the super object, implement Cloneable
                    code.Method.Class.AddInterface("java.lang.Cloneable");
                }
            }
            else
            {
                if (ConvertVirtualToStaticCall(callClass, callMethod))
                {
                    return(true);
                }

                op = 0xB6; // invokevirtual
            }

            if (callMethod.IsArrayMethod)
            {
                arrays.Call(callMethod, cilInst);
            }
            else
            {
                CheckAndBoxArguments(callMethod, (op == 0xB6));
                PushGenericArguments(callMethod);
                code.NewInstruction(op, callClass.AsWritableClass,
                                    callMethod.WithGenericParameters);

                ClearMethodArguments(callMethod, (op == 0xB7));

                PushMethodReturnType(callMethod);
            }

            return(true);

            bool IsSystemTypeOperatorOrIsCallToIsInterface(CilType callClass, CilMethod callMethod)
            {
                if (callClass.Equals(CilType.SystemTypeType) && (
                        callMethod.Name == "op_Equality" || callMethod.Name == "op_Inequality"))
                {
                    return(true);
                }
                if (callClass.Equals(JavaType.ClassType) && callMethod.Name == "IsInterface")
                {
                    return(true);
                }
                return(false);
            }
        }