예제 #1
0
        public static JavaMethod CreateSyncWrapper(JavaMethod innerMethod, CilType declType)
        {
            //
            // if method is decorated with [MethodImplOptions.Synchronized],
            // create a wrapper method that locks the object (for instance methods)
            // or the type (for static methods), and then calls the original method,
            // which we make private and rename to have a unique suffix
            //

            if (innerMethod.Name == "<init>")
            {
                throw CilMain.Where.Exception("[Synchronized] is not supported on constructors");
            }

            var outerMethod = new JavaMethod(innerMethod.Class, innerMethod);

            outerMethod.Flags  = innerMethod.Flags;
            innerMethod.Flags &= ~(JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_PROTECTED);
            innerMethod.Flags |= JavaAccessFlags.ACC_PRIVATE;

            innerMethod.Name += "---inner";

            // count the size of locals in the parameters, plus one.  we have to
            // add one for an instance method, to account for the 'this' argument.
            // and if a static method, we have to add one for the lock object.

            var numLocals = 1;

            for (int i = outerMethod.Parameters.Count; i-- > 0;)
            {
                numLocals += outerMethod.Parameters[i].Type.Category;
            }

            // prepare to generate instructions

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

            code.Method       = outerMethod;
            code.Instructions = new List <JavaCode.Instruction>();
            code.MaxLocals    = numLocals + 1;

            var exception = new JavaAttribute.Code.Exception();

            exception.start     = /* label */ 1;
            exception.endPlus1  = /* label */ 2;
            exception.handler   = /* label */ 3;
            exception.catchType = CodeExceptions.ThrowableType.ClassName;

            code.Exceptions = new List <JavaAttribute.Code.Exception>();
            code.Exceptions.Add(exception);

            code.StackMap = new JavaStackMap();
            code.StackMap.SaveFrame((ushort)0, false, CilMain.Where);

            // get a reference to 'this' (for instance methods)
            // or to the type object (for static methods),
            // then lock on the reference pushed on the stack

            int lockedObjectIndex;

            if ((outerMethod.Flags & JavaAccessFlags.ACC_STATIC) == 0)
            {
                code.NewInstruction(0x19 /* aload */, null, (int)0);
                code.StackMap.PushStack(JavaType.ObjectType);
                code.StackMap.SetLocal(0, JavaType.ObjectType);

                lockedObjectIndex = 0;
            }
            else
            {
                GenericUtil.LoadMaybeGeneric(declType, code);
                code.NewInstruction(0x59 /* dup */, null, null);
                code.StackMap.PushStack(JavaType.ObjectType);
                code.StackMap.PopStack(CilMain.Where);
                code.NewInstruction(0x3A /* astore */, null, (int)numLocals);
                code.StackMap.SetLocal((int)numLocals, JavaType.ObjectType);

                lockedObjectIndex = (int)numLocals;
            }

            code.NewInstruction(0xB8 /* invokestatic */,
                                new JavaType(0, 0, "system.threading.Monitor"),
                                new JavaMethodRef(
                                    "Enter", JavaType.VoidType, JavaType.ObjectType));
            code.StackMap.PopStack(CilMain.Where);

            code.NewInstruction(0x00 /* nop */, null, null, /* label */ 1);
            code.StackMap.SaveFrame((ushort)1, false, CilMain.Where);

            // push all arguments for the call to the inner method

            byte callOpcode;
            int  localIndex = 0;

            if ((outerMethod.Flags & JavaAccessFlags.ACC_STATIC) == 0)
            {
                code.StackMap.PushStack(JavaType.ObjectType);
                code.NewInstruction(0x19 /* aload */, null, (int)0);
                localIndex++;
                callOpcode = 0xB7; // invokespecial
            }
            else
            {
                callOpcode = 0xB8; // invokestatic
            }
            for (int i = 0; i < outerMethod.Parameters.Count; i++)
            {
                var paramType = outerMethod.Parameters[i].Type;
                code.StackMap.PushStack(paramType);
                code.NewInstruction(paramType.LoadOpcode, null, (int)localIndex);
                localIndex += paramType.Category;
            }

            code.NewInstruction(callOpcode, declType, innerMethod);

            // if we get here, then no exception was thrown in the
            // inner method, we need to unlock and return the result

            code.StackMap.ClearStack();
            if (!innerMethod.ReturnType.Equals(JavaType.VoidType))
            {
                code.StackMap.PushStack(innerMethod.ReturnType);
            }

            code.NewInstruction(0x00 /* nop */, null, null, /* label */ 2);
            code.StackMap.SaveFrame((ushort)2, false, CilMain.Where);

            code.NewInstruction(0x19 /* aload */, null, lockedObjectIndex);
            code.StackMap.PushStack(JavaType.ObjectType);

            code.NewInstruction(0xB8 /* invokestatic */,
                                new JavaType(0, 0, "system.threading.Monitor"),
                                new JavaMethodRef(
                                    "Exit", JavaType.VoidType, JavaType.ObjectType));

            code.NewInstruction(innerMethod.ReturnType.ReturnOpcode, null, null);

            // if we get here, then an exception was thrown, unlock
            // and rethrow the exception

            code.StackMap.ClearStack();
            code.StackMap.PushStack(CodeExceptions.ThrowableType);

            code.NewInstruction(0x00 /* nop */, null, null, /* label */ 3);
            code.StackMap.SaveFrame((ushort)3, true, CilMain.Where);

            code.NewInstruction(0x19 /* aload */, null, lockedObjectIndex);
            code.StackMap.PushStack(JavaType.ObjectType);

            code.NewInstruction(0xB8 /* invokestatic */,
                                new JavaType(0, 0, "system.threading.Monitor"),
                                new JavaMethodRef(
                                    "Exit", JavaType.VoidType, JavaType.ObjectType));

            code.StackMap.PopStack(CilMain.Where);
            code.NewInstruction(0xBF /* athrow */, null, null);

            code.StackMap.ClearStack();
            code.MaxStack = code.StackMap.GetMaxStackSize(CilMain.Where);

            return(outerMethod);
        }
예제 #2
0
        internal static void BuildJavaClass(TypeDefinition cilType, JavaClass parentClass)
        {
            CilMain.Where.Push($"class '{cilType.FullName}'");

            var genericMark = CilMain.GenericStack.Mark();
            var myType      = CilMain.GenericStack.EnterType(cilType);

            var jclass = new JavaClass();

            jclass.Name  = myType.JavaName;
            jclass.Flags = AttributesToAccessFlags(cilType.Attributes, myType.IsInterface);

            if (myType.IsInterface)
            {
                jclass.Super = JavaType.ObjectType.ClassName; // java.lang.Object
            }
            else if (cilType.BaseType != null)
            {
                var myBaseType = CilType.From(cilType.BaseType);
                jclass.Super = myBaseType.Equals(JavaType.ObjectType)
                             ? JavaType.ObjectType.ClassName // java.lang.Object
                             : myBaseType.JavaName;
            }
            else
            {
                throw CilMain.Where.Exception("missing base class");
            }

            var myInterfaces = ImportInterfaces(jclass, myType, cilType);

            int numCastableInterfaces = myType.IsGenericThisOrSuper
                                      ? InterfaceBuilder.CastableInterfaceCount(myInterfaces)
                                      : 0;

            ImportFields(jclass, cilType, myType.IsRetainName);

            ImportMethods(jclass, cilType, numCastableInterfaces);

            if (myType.JavaName == "system.Convert")
            {
                DiscardBase64MethodsInConvertClass(jclass);
            }

            ValueUtil.InitializeStaticFields(jclass, myType);

            if (myType.IsValueClass)
            {
                ValueUtil.MakeValueClass(jclass, myType, numCastableInterfaces);
            }

            else if (myType.IsEnum)
            {
                ValueUtil.MakeEnumClass(jclass, myType,
                                        cilType.HasCustomAttribute("System.FlagsAttribute", true));
            }

            else if (myType.IsDelegate)
            {
                var delegateInterface = Delegate.FixClass(jclass, myType);
                CilMain.JavaClasses.Add(delegateInterface);
            }

            // if derives directly from object, and does not implement ToString
            CodeBuilder.CreateToStringMethod(jclass);

            ResetFieldReferences(jclass);

            LinkClasses(jclass, parentClass, cilType);

            var interfaceClasses = InterfaceBuilder.BuildProxyMethods(
                myInterfaces, cilType, myType, jclass);

            if (interfaceClasses != null)
            {
                foreach (var childClass in interfaceClasses)
                {
                    CilMain.JavaClasses.Add(childClass);
                }
            }

            if (myType.HasGenericParameters)
            {
                JavaClass dataClass;

                if (!myType.IsInterface)
                {
                    dataClass = GenericUtil.MakeGenericClass(jclass, myType);
                    if (dataClass != null)
                    {
                        CilMain.JavaClasses.Add(dataClass);
                    }
                }
                else
                {
                    dataClass = null;
                }

                JavaClass infoClass = jclass;
                if (myType.IsInterface)
                {
                    // Android 'D8' desugars static methods on an interface by
                    // moving into a separate class, so we do it ourselves.
                    // see also system.RuntimeType.CreateGeneric() in baselib
                    infoClass = CilMain.CreateInnerClass(jclass, jclass.Name + "$$info",
                                                         markGenericEntity: true);
                    CilMain.JavaClasses.Add(infoClass);

                    // Android 'R8' (ProGuard) might discard this new class,
                    // so insert a dummy field with the type of the class.
                    // see also ProGuard rules in IGenericEntity in baselib
                    var infoClassField = new JavaField();
                    infoClassField.Name  = "-generic-info-class";
                    infoClassField.Type  = new JavaType(0, 0, infoClass.Name);
                    infoClassField.Class = jclass;
                    infoClassField.Flags = JavaAccessFlags.ACC_PUBLIC
                                           | JavaAccessFlags.ACC_STATIC
                                           | JavaAccessFlags.ACC_FINAL
                                           | JavaAccessFlags.ACC_SYNTHETIC;
                    if (jclass.Fields == null)
                    {
                        jclass.Fields = new List <JavaField>(1);
                    }
                    jclass.Fields.Add(infoClassField);
                }

                GenericUtil.CreateGenericInfoMethod(infoClass, dataClass, myType);

                GenericUtil.CreateGenericVarianceField(infoClass, myType, cilType);
            }

            if (myType.IsGenericThisOrSuper)
            {
                jclass.Signature = GenericUtil.MakeGenericSignature(cilType, jclass.Super);

                if (!myInterfaces.Exists(x => x.InterfaceType.JavaName == "system.IGenericObject"))
                {
                    if (!myType.IsInterface)
                    {
                        // create IGenericObject methods GetType and TryCast
                        // only if class did not already implement IGenericObject
                        if (!myType.HasGenericParameters)
                        {
                            GenericUtil.BuildGetTypeMethod(jclass, myType);
                        }

                        InterfaceBuilder.BuildTryCastMethod(
                            myInterfaces, myType, numCastableInterfaces, jclass);
                    }
                }
            }

            CilMain.GenericStack.Release(genericMark);
            CilMain.Where.Pop();
        }
예제 #3
0
        void ExceptionClauseSetup(ushort instOffset, CatchClause catchClause)
        {
            // if this is the first exception for the try block:
            //
            // the offset is recorded in the exception attribute, so we need
            // to generate a stack frame for it, which matches the stack frame
            // on entry to the try block, with a pushed java.lang.Throwable.

            var tryClause = catchClause.tryClause;

            if (catchClause == tryClause.catchClauses[0])
            {
                stackMap.LoadFrame((ushort)tryClause.tryStart, false, CilMain.Where);
                stackMap.PushStack(ThrowableType);
                stackMap.SaveFrame((ushort)instOffset, true, CilMain.Where);

                if (!catchClause.finallyClause)
                {
                    code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemUtilType,
                                        new JavaMethodRef("TranslateException",
                                                          ThrowableType, ThrowableType));
                }
            }

            // if this is a finally clause, we have nothing further to do.
            // but if this is a filter clause, do some additional set up.

            if (catchClause.finallyClause)
            {
                if (catchClause.hasNestedTry)
                {
                    // see also:  ScanCatchClauseForNestedTry
                    SaveExceptionObject(tryClause, false);
                }

                if (catchClause.faultClause)
                {
                    // the 'fault' clause should start with a check whether
                    // an exception was thrown, which is very similar to what
                    // 'endfinally' does at the end of a normal 'finally' block,
                    // so we can reuse the same code.
                    Translate_Endfinally(instOffset, true);
                }

                return;
            }

            //
            // if this is a filter clause, we just need to initialize loals
            //

            if (catchClause.filterCondStart != 0)
            {
                // should the filter test pass, we need push the exception
                // object.  we don't know which local the filter test uses
                // to store the exception, so we make our own copy.  this
                // will be loaded by the 'endfilter' instruction, see there.

                SaveExceptionObject(tryClause);

                return;
            }

            //
            // for a non-filter catch clause that catches any kind of
            // exception, we don't need to test the exception type at all
            //

            if (catchClause.catchType.Equals(ThrowableType))
            {
                if (catchClause.includesRethrow)
                {
                    SaveExceptionObject(tryClause);
                }
            }

            //
            // otherwise, we do need to test the exception type, and
            // possibly branch to a secondary catch clause in the chain.
            //

            if (catchClause.catchFromType == null)
            {
                // exception type is plain type.  we will use follow up
                // instructions 'ifeq == zero' and 'ifne != zero',
                // see ExceptionClauseCommon

                if (catchClause.catchType.Equals(ThrowableType))
                {
                    code.NewInstruction(0x04 /* iconst_1 */, null, null);
                    stackMap.PushStack(JavaType.IntegerType);
                }
                else
                {
                    code.NewInstruction(0x59 /* dup */, null, null);
                    stackMap.PushStack(ThrowableType);
                    code.NewInstruction(0xC1 /* instanceof */, catchClause.catchType, null);
                }
            }
            else
            {
                // exception type is a generic type.  we will use follow up
                // instructions 'ifnull' and 'ifnonnnull'.
                // see also below in ExceptionClauseCommon
                code.NewInstruction(0x59 /* dup */, null, null);
                stackMap.PushStack(ThrowableType);
                GenericUtil.CastToGenericType(catchClause.catchFromType, 0, code);
            }

            stackMap.PopStack(CilMain.Where);

            ExceptionClauseCommon(catchClause);
        }
예제 #4
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);
            }
        }
예제 #5
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());
            }
        }
예제 #6
0
        public static void Indirection(JavaCode code, Code cilOp)
        {
            var(name, opcodeType) = IndirectOpCodeToNameAndType(cilOp);
            bool isRef = opcodeType.Equals(JavaType.ObjectType);

            bool isLoad;

            if (cilOp >= Code.Ldind_I1 && cilOp <= Code.Ldind_Ref)
            {
                isLoad = true;
            }
            else
            {
                isLoad = false;
                var valueType = code.StackMap.PopStack(CilMain.Where);
                if (valueType.IsReference != isRef)
                {
                    throw new InvalidProgramException();
                }
            }

            var stackTop = (CilType)code.StackMap.PopStack(CilMain.Where);

            if (stackTop.IsGenericParameter)
            {
                if (isLoad)
                {
                    var resultType = GenericUtil.CastMaybeGeneric(stackTop, false, code);
                    if (resultType == stackTop && (!stackTop.Equals(JavaType.ObjectType)))
                    {
                        code.NewInstruction(0xC0 /* checkcast */, stackTop.AsWritableClass, null);
                        resultType = stackTop;
                    }
                    code.StackMap.PushStack(resultType);
                }
                else
                {
                    code.NewInstruction(0x5F /* swap */, null, null);
                    GenericUtil.ValueCopy(stackTop, code);
                }
                return;
            }

            //
            // non-generic object reference
            //

            var boxedType = stackTop as BoxedType;

            if (boxedType == null || boxedType.IsBoxedReference != isRef)
            {
                if (CodeSpan.LoadStore(isLoad, stackTop, opcodeType, null, code))
                {
                    return;
                }

                if (object.ReferenceEquals(stackTop, CodeArrays.GenericArrayType))
                {
                    // a byref parameter T[] gets translated to java.lang.Object,
                    // so we have to explicitly cast it to system.Reference
                    boxedType = new BoxedType(stackTop, false);
                    code.NewInstruction(0x5F /* swap */, null, null);
                    code.NewInstruction(0xC0 /* checkcast */,
                                        boxedType.AsWritableClass, null);
                    code.NewInstruction(0x5F /* swap */, null, null);
                }
                else
                {
                    throw new ArgumentException($"incompatible type '{stackTop}'");
                }
            }

            var unboxedType     = boxedType.UnboxedType;
            var unboxedTypeCode = unboxedType.IsReference ? 0 : unboxedType.PrimitiveType;

            JavaMethodRef method;

            if (CompareIndirectTypes(unboxedTypeCode, opcodeType.PrimitiveType))
            {
                // indirect access to a primitive or reference type, with a
                // reference type that represents the boxed form of the same type.

                if (isLoad)
                {
                    boxedType.GetValue(code);
                    if (unboxedType.IsReference)
                    {
                        // if we know the type of indirected value, cast to it
                        if (!unboxedType.Equals(JavaType.ObjectType))
                        {
                            code.NewInstruction(0xC0 /* checkcast */,
                                                unboxedType.AsWritableClass, null);
                        }
                    }
                    else
                    {
                        unboxedType = CilType.From(opcodeType);
                    }
                    code.StackMap.PushStack(unboxedType);
                }
                else
                {
                    // if we are storing a real array into a boxed reference of
                    // e.g., system.Array, then we have to create an array proxy
                    CodeArrays.MaybeGetProxy(CodeArrays.GenericArrayType, unboxedType, code);

                    boxedType.SetValueOV(code);
                }
                return;
            }

            // indirect access to a primitive value from a reference type that
            // represents some other a primitive value;  for example ldind.r4
            // from a system.Int32.  we call the "CodeNumber.Indirection methods"
            // helpers, defined in all baselib primitives, to assist.

            if (opcodeType.IsIntLike)
            {
                opcodeType = JavaType.IntegerType;
            }
            if (isLoad)
            {
                method = new JavaMethodRef("Get_" + name, opcodeType);
                code.StackMap.PushStack(CilType.From(opcodeType));
            }
            else
            {
                method = new JavaMethodRef("Set_" + name, JavaType.VoidType, opcodeType);
            }
            code.NewInstruction(0xB6 /* invokevirtual */, boxedType, method);
        }
예제 #7
0
        public static bool Address(CilType fromType, CilType intoType, JavaCode code)
        {
            if (intoType.Equals(SpanType) && (!fromType.Equals(SpanType)))
            {
                // allow assignment of null to clear the pointer
                if (fromType.Equals(JavaStackMap.Null))
                {
                    return(true);
                }

                    #if false
                if (fromType.JavaName == "system.Void.Pointer" &&
                    (!intoType.HasGenericParameters) &&
                    intoType.GenericParameters != null)
                {
                    code.NewInstruction(0xC0 /* checkcast */, SpanType, null);
                    return(true);
                }
                    #endif

                // allow assignment of native int (presumably zero)
                bool     callAssign   = false;
                bool     pushNullType = true;
                JavaType argType      = fromType;
                JavaType retType      = SpanType;

                if ((!fromType.IsReference) && fromType.PrimitiveType == TypeCode.UInt64)
                {
                    callAssign = true;
                }
                else if (intoType.GenericParameters != null)
                {
                    // allow assignment when the types match
                    callAssign = intoType.GenericParameters[0].Equals(fromType) ||
                                 fromType.JavaName == intoType.GenericParameters[0].JavaName;

                    // for arbitrary value types, call a Assign(ValueType)
                    if (fromType.IsValueClass)
                    {
                        argType = retType = CilType.SystemValueType;
                        GenericUtil.LoadMaybeGeneric(fromType, code);
                        pushNullType = false;
                    }
                }

                if (callAssign)
                {
                    if (pushNullType)
                    {
                        code.NewInstruction(0x01 /* aconst_null */, null, null);
                        code.StackMap.PushStack(CilType.SystemTypeType);
                    }

                    code.NewInstruction(0xB8 /* invokestatic */, SpanType,
                                        new JavaMethodRef("Assign" + CilMain.EXCLAMATION,
                                                          retType, argType, CilType.SystemTypeType));

                    code.NewInstruction(0xC0 /* checkcast */, SpanType, null);

                    code.StackMap.PopStack(CilMain.Where);  // type argument
                    return(true);
                }

                throw new Exception($"bad assignment of '{fromType.JavaName}' into pointer of '{intoType.GenericParameters[0].JavaName}'");
            }
            return(false);
        }
예제 #8
0
        public static JavaClass FixClass(JavaClass fromClass, CilType fromType)
        {
            JavaType  interfaceType  = InterfaceType(fromType);
            JavaClass interfaceClass = null;

            var fromMethods = fromClass.Methods;

            for (int i = fromMethods.Count; i-- > 0;)
            {
                var nm = fromMethods[i].Name;
                if (nm == "BeginInvoke" || nm == "EndInvoke")
                {
                    fromClass.Methods.RemoveAt(i);
                }
            }

            foreach (var method in fromMethods)
            {
                if ((method.Flags & JavaAccessFlags.ACC_STATIC) == 0 && method.Code == null)
                {
                    if (method.Name == "<init>")
                    {
                        GenerateConstructor(method, fromType);
                    }
                    else if (method.Name == "Invoke")
                    {
                        if (interfaceClass != null)
                        {
                            break;
                        }

                        interfaceClass = MakeInterface(fromClass, method, interfaceType.ClassName);
                        GenerateInvoke(method, fromType, interfaceType);
                    }

                    /*else if (method.Name == "BeginInvoke")
                     * {
                     *  if (interfaceClass == null)
                     *      break;
                     *
                     *  GenerateBeginInvoke();
                     *  continue;
                     * }
                     * else if (method.Name == "EndInvoke")
                     * {
                     *  GenerateEndInvoke();
                     *  continue;
                     * }*/
                    else
                    {
                        break;
                    }

                    method.Flags &= ~JavaAccessFlags.ACC_ABSTRACT;
                }
            }

            if (interfaceClass == null)
            {
                throw CilMain.Where.Exception("invalid delegate class");
            }

            return(interfaceClass);

            //
            //
            //

            JavaClass MakeInterface(JavaClass fromClass, JavaMethod fromMethod, string newName)
            {
                var newClass = CilMain.CreateInnerClass(fromClass, newName,
                                                        JavaAccessFlags.ACC_PUBLIC
                                                        | JavaAccessFlags.ACC_ABSTRACT
                                                        | JavaAccessFlags.ACC_INTERFACE);

                var newMethod = new JavaMethod(newClass, fromMethod);

                newMethod.Flags = JavaAccessFlags.ACC_PUBLIC
                                  | JavaAccessFlags.ACC_ABSTRACT;
                if (IsPrimitive(newMethod.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 GenerateInvoke and LoadFunction
                    newMethod.ReturnType = JavaType.ObjectType;
                }
                newClass.Methods.Add(newMethod);

                return(newClass);
            }

            //
            //
            //

            void GenerateConstructor(JavaMethod method, CilType dlgType)
            {
                var code = method.Code = new JavaCode();

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

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

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

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

                code.NewInstruction(0x19 /* aload_0 */, null, (int)0);
                code.NewInstruction(0x19 /* aload_1 */, null, (int)1);
                code.NewInstruction(0x19 /* aload_2 */, null, (int)2);

                code.NewInstruction(0xB7 /* invokespecial */,
                                    new JavaType(0, 0, method.Class.Super),
                                    new JavaMethodRef("<init>", JavaType.VoidType,
                                                      JavaType.ObjectType, JavaType.ObjectType));

                code.NewInstruction(0xB1 /* return (void) */, null, null);

                if (code.MaxStack < 3)
                {
                    code.MaxStack = 3;
                }
                code.MaxLocals = 3 + dlgType.GenericParametersCount;
            }

            //
            //
            //

            void GenerateInvoke(JavaMethod method, CilType dlgType, JavaType ifcType)
            {
                var delegateType = new JavaType(0, 0, "system.Delegate");

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

                code.Method       = method;
                code.Instructions = new List <Instruction>();
                code.StackMap     = new JavaStackMap();

                code.StackMap.SetLocal(0, dlgType);
                int index = 1;

                for (int i = 0; i < method.Parameters.Count; i++)
                {
                    var paramType = (CilType)method.Parameters[i].Type;
                    code.StackMap.SetLocal(index, paramType);
                    index += paramType.Category;
                }
                code.StackMap.SaveFrame((ushort)0, false, CilMain.Where);

                //
                // step 1, call the target method through the interface reference
                // stored in the 'invokable' field of the system.Delegate class
                //

                code.NewInstruction(0x19 /* aload_0 */, null, (int)0);
                code.NewInstruction(0xB4 /* getfield */, delegateType,
                                    new JavaFieldRef("invokable", JavaType.ObjectType));

                code.NewInstruction(0xC0 /* checkcast */, ifcType, null);
                code.StackMap.PushStack(JavaType.ObjectType);

                // push method parameters 'invokeinterface'

                index = 1;
                for (int i = 0; i < method.Parameters.Count; i++)
                {
                    var paramType = (CilType)method.Parameters[i].Type;
                    code.NewInstruction(paramType.LoadOpcode, null, index);
                    code.StackMap.PushStack(paramType);

                    if (paramType.IsGenericParameter && (!paramType.IsByReference))
                    {
                        // invoke the helper method which identifies our boxed
                        // primitives and re-boxes them as java boxed values.
                        // see also: GenericType::DelegateParameterin baselib.
                        GenericUtil.LoadMaybeGeneric(
                            paramType.GetMethodGenericParameter(), code);
                        code.NewInstruction(0xB8 /* invokestatic */,
                                            SystemDelegateUtilType, DelegateParameterMethod);
                        code.StackMap.PopStack(CilMain.Where);
                    }
                    index += paramType.Category;
                }

                var returnType = (CilType)method.ReturnType;

                if (IsPrimitive(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 GenerateInvoke and LoadFunction
                    var adjustedMethod = new JavaMethodRef(
                        method.Name, JavaType.ObjectType, method.Parameters);
                    code.NewInstruction(0xB9 /* invokeinterface */, ifcType, adjustedMethod);
                }
                else
                {
                    code.NewInstruction(0xB9 /* invokeinterface */, ifcType, method);
                }

                code.StackMap.ClearStack();
                code.StackMap.PushStack(returnType);

                // check if this delegate has a 'following' delegate,
                // attached via system.MulticastDelegate.CombineImpl

                code.NewInstruction(0x19 /* aload_0 */, null, (int)0);
                code.NewInstruction(0xB4 /* getfield */, delegateType,
                                    new JavaFieldRef("following", delegateType));
                code.NewInstruction(0xC7 /* ifnonnull */, null, (ushort)1);

                if (returnType.IsGenericParameter && (!returnType.IsByReference))
                {
                    GenericUtil.LoadMaybeGeneric(
                        returnType.GetMethodGenericParameter(), code);
                    code.NewInstruction(0xB8 /* invokestatic */,
                                        SystemDelegateUtilType, DelegateReturnValueMethod);
                    code.StackMap.PopStack(CilMain.Where);
                }
                else if (IsPrimitive(returnType))
                {
                    // if the delegate returns a primitive type, we need to unbox
                    // the object returned by the method we just called.  it will be
                    // a java boxed primitive (e.g. java.lang.Integer) if the called
                    // method actually returns a primitive, due to the boxing done
                    // by the invokedynamic mechanism.  if the called method returns
                    // a generic type, it will be our boxed type, e.g. system.Int32.
                    //
                    // see also DelegateReturnValueX helper methods in baselib,
                    // and MakeInterface and LoadFunction in this file.
                    var helperMethod = new JavaMethodRef(
                        DelegateReturnValueMethod.Name + returnType.ToDescriptor(),
                        returnType, JavaType.ObjectType);
                    code.NewInstruction(0xB8 /* invokestatic */,
                                        SystemDelegateUtilType, helperMethod);
                }

                code.NewInstruction(returnType.ReturnOpcode, null, index);
                code.StackMap.PopStack(CilMain.Where);

                //
                // step 2
                //
                // if this delegate has a 'following' delegate, then we need to
                // call it, but first get rid of the return value on the stack
                //

                byte popOpcode;

                if (returnType.Equals(JavaType.VoidType))
                {
                    popOpcode = 0x00; // nop
                }
                else // select 0x57 pop, or 0x58 pop2
                {
                    var adjustedReturnType =
                        IsPrimitive(returnType) ? JavaType.ObjectType : returnType;
                    code.StackMap.PushStack(adjustedReturnType);
                    popOpcode = (byte)(0x56 + returnType.Category);
                }
                code.NewInstruction(popOpcode, null, null, (ushort)1);
                code.StackMap.SaveFrame((ushort)1, true, CilMain.Where);

                // now call the Invoke method on the 'following' delegate

                code.NewInstruction(0x19 /* aload_0 */, null, (int)0);
                code.NewInstruction(0xB4 /* getfield */, delegateType,
                                    new JavaFieldRef("following", delegateType));
                code.NewInstruction(0xC0 /* checkcast */, dlgType, null);

                // push all method parameters for 'invokevirtual'

                index = 1;
                for (int i = 0; i < method.Parameters.Count; i++)
                {
                    var paramType = (CilType)method.Parameters[i].Type;
                    code.NewInstruction(paramType.LoadOpcode, null, index);

                    if (paramType.IsGenericParameter && (!paramType.IsByReference))
                    {
                        // invoke the helper method which identifies our boxed
                        // primitives and re-boxes them as java boxed values.
                        // see also: GenericType::DelegateParameterin baselib.
                        GenericUtil.LoadMaybeGeneric(
                            paramType.GetMethodGenericParameter(), code);
                        code.NewInstruction(0xB8 /* invokestatic */,
                                            SystemDelegateUtilType, DelegateParameterMethod);
                    }
                    index += paramType.Category;
                }

                code.NewInstruction(0xB6 /* invokevirtual */, dlgType, method);

                code.NewInstruction(returnType.ReturnOpcode, null, index);
                code.StackMap.ClearStack();

                code.MaxStack  = code.StackMap.GetMaxStackSize(CilMain.Where);
                code.MaxLocals = index;
            }

            //
            //
            //

            /*void GenerateBeginInvoke()
             * {
             * }*/

            //
            //
            //

            /*void GenerateEndInvoke()
             * {
             * }*/
        }
예제 #9
0
        public static void Sizeof(object data, JavaCode code)
        {
            if (data is TypeReference operandTypeReference)
            {
                var myType = CilType.From(operandTypeReference);
                if (myType.IsValueClass)
                {
                    // compiler generated IL typically calculates the stackalloc
                    // total buffer size, for primitive types, and uses 'sizeof'
                    // for values types.  but it also uses 'sizeof' for generic
                    // types, which may stand for a primitive type.
                    GenericUtil.LoadMaybeGeneric(myType, code);
                    code.NewInstruction(0xB8 /* invokestatic */, SpanType,
                                        new JavaMethodRef("Sizeof" + CilMain.EXCLAMATION,
                                                          JavaType.IntegerType, CilType.SystemTypeType));
                    code.StackMap.PopStack(CilMain.Where);  // generic type
                    code.StackMap.PushStack(CilType.From(JavaType.IntegerType));
                    return;
                }
                else if (!myType.IsReference)
                {
                    // if the compiler inserted a typeof(primitive) instruction,
                    // then we need to replace it with a load constant
                    int size = -1;
                    if (myType.Category == 2)   // Double, Int64, UInt64
                    {
                        size = 8;
                    }
                    else
                    {
                        switch (myType.PrimitiveType)
                        {
                        case TypeCode.Single:
                        case TypeCode.UInt32:
                        case TypeCode.Int32:
                            size = 4;
                            break;

                        case TypeCode.UInt16:
                        case TypeCode.Int16:
                        case TypeCode.Char:
                            size = 2;
                            break;

                        case TypeCode.Byte:
                        case TypeCode.SByte:
                        case TypeCode.Boolean:
                            size = 1;
                            break;
                        }
                    }
                    if (size != -1)
                    {
                        code.NewInstruction(0x12 /* ldc */, null, size);
                        code.StackMap.PushStack(CilType.From(JavaType.IntegerType));
                        return;
                    }
                }
            }
            throw new InvalidProgramException();
        }
예제 #10
0
        public void New(CilType elemType, int numDims, bool arrayTypeOnStack = false)
        {
            var elemTypeForArray = elemType.IsGenericParameter
                                 ? CilType.From(JavaType.ObjectType) : elemType;
            var arrayType = elemTypeForArray.AdjustRank(numDims);

            if (elemType.IsGenericParameter)
            {
                /*if (numDims != 1)
                 *  throw new Exception("unsupported number of dimensions in generic array");*/

                if (!arrayTypeOnStack)
                {
                    GenericUtil.LoadMaybeGeneric(elemType, code);
                }

                var parameters = new List <JavaFieldRef>();
                for (int i = 0; i < numDims; i++)
                {
                    parameters.Add(new JavaFieldRef("", JavaType.IntegerType));
                }
                parameters.Add(new JavaFieldRef("", CilType.SystemTypeType));

                code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType,
                                    new JavaMethodRef("New", JavaType.ObjectType, parameters));

                stackMap.PopStack(CilMain.Where);   // type

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

                arrayType = GenericArrayType;
            }

            else if (elemType.IsReference || numDims > 1)
            {
                if (numDims == 1)
                {
                    code.NewInstruction(0xBD /* anewarray */, elemType, null);
                }
                else
                {
                    code.NewInstruction(0xC5 /* multianewarray */, arrayType, numDims);
                }

                for (int i = 0; i < numDims; i++)
                {
                    stackMap.PopStack(CilMain.Where);
                }

                stackMap.PushStack(arrayType);  // arrayObj

                if (elemType.ArrayRank != 0)
                {
                    if (numDims == 1)
                    {
                        // notify the array support methods in baselib that this
                        // is a jagged array, i.e. a single dimension array with
                        // an element type that is also an array.  for arrays of
                        // generic type, see system.Array.New() in baselib.

                        stackMap.PushStack(JavaType.ObjectType);
                        code.NewInstruction(0x59 /* dup */, null, null);
                        code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType,
                                            new JavaMethodRef("MarkJagged",
                                                              JavaType.VoidType, JavaType.ObjectType));
                        stackMap.PopStack(CilMain.Where);
                    }
                }

                else if (elemType.IsValueClass)
                {
                    code.NewInstruction(0x59 /* dup */, null, null);
                    stackMap.PushStack(arrayType);      // arrayCopy

                    if (elemType.HasGenericParameters)
                    {
                        // array of a value class ArrayElement<T>, pass this type
                        // as the second parameter to system.Array.Initialize

                        GenericUtil.LoadMaybeGeneric(elemType, code);

                        // third parameter is null
                        code.NewInstruction(0x01 /* aconst_null */, null, null);
                        stackMap.PushStack(JavaType.ObjectType);
                    }
                    else
                    {
                        // array of a plain value class, pass a constructed object
                        // as the third parameter to system.Array.Initialize

                        // second parameter is null
                        code.NewInstruction(0x01 /* aconst_null */, null, null);
                        stackMap.PushStack(CilType.SystemTypeType);

                        // model parameter is a new object
                        code.NewInstruction(0xBB /* new */, elemType.AsWritableClass, null);
                        stackMap.PushStack(elemType);
                        code.NewInstruction(0x59 /* dup */, null, null);
                        stackMap.PushStack(elemType);
                        code.NewInstruction(0xB7 /* invokespecial */, elemType.AsWritableClass,
                                            new CilMethod(elemType));
                        stackMap.PopStack(CilMain.Where);
                    }

                    code.NewInstruction(0x12 /* ldc */, null, numDims);
                    stackMap.PushStack(JavaType.IntegerType);

                    code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, InitArrayMethod);

                    stackMap.PopStack(CilMain.Where);   // numDims
                    stackMap.PopStack(CilMain.Where);   // elemType
                    stackMap.PopStack(CilMain.Where);   // arrayType
                    stackMap.PopStack(CilMain.Where);   // arrayCopy
                }

                stackMap.PopStack(CilMain.Where);   // arrayObj
            }
            else
            {
                if (numDims == 1)
                {
                    var length = stackMap.PopStack(CilMain.Where);
                    stackMap.PushStack(length);
                    if (length.Equals(JavaType.LongType))
                    {
                        CodeNumber.Conversion(code, Code.Conv_Ovf_I4, null);
                    }
                }

                code.NewInstruction(0xBC /* newarray */, null, elemType.NewArrayType);

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

            stackMap.PushStack(arrayType);
        }
예제 #11
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);
            }
        }
예제 #12
0
        public static JavaClass FixClass(JavaClass fromClass, CilType fromType)
        {
            JavaType  interfaceType  = InterfaceType(fromType);
            JavaClass interfaceClass = null;

            foreach (var method in fromClass.Methods)
            {
                if ((method.Flags & JavaAccessFlags.ACC_STATIC) == 0 && method.Code == null)
                {
                    if (method.Name == "<init>")
                    {
                        GenerateConstructor(method, fromType);
                    }
                    else if (method.Name == "Invoke")
                    {
                        if (interfaceClass != null)
                        {
                            break;
                        }

                        interfaceClass = MakeInterface(fromClass, method, interfaceType.ClassName);
                        GenerateInvoke(method, fromType, interfaceType);
                    }
                    else if (method.Name == "BeginInvoke")
                    {
                        if (interfaceClass == null)
                        {
                            break;
                        }

                        GenerateBeginInvoke();
                        continue;
                    }
                    else if (method.Name == "EndInvoke")
                    {
                        GenerateEndInvoke();
                        continue;
                    }
                    else
                    {
                        break;
                    }

                    method.Flags &= ~JavaAccessFlags.ACC_ABSTRACT;
                }
            }

            if (interfaceClass == null)
            {
                throw CilMain.Where.Exception("invalid delegate class");
            }

            return(interfaceClass);

            //
            //
            //

            JavaClass MakeInterface(JavaClass fromClass, JavaMethod fromMethod, string newName)
            {
                var newClass = CilMain.CreateInnerClass(fromClass, newName,
                                                        JavaAccessFlags.ACC_PUBLIC
                                                        | JavaAccessFlags.ACC_ABSTRACT
                                                        | JavaAccessFlags.ACC_INTERFACE);

                var newMethod = new JavaMethod(newClass, fromMethod);

                newMethod.Flags = JavaAccessFlags.ACC_PUBLIC
                                  | JavaAccessFlags.ACC_ABSTRACT;
                if (IsPrimitive(newMethod.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 GenerateInvoke and LoadFunction
                    newMethod.ReturnType = JavaType.ObjectType;
                }
                newClass.Methods.Add(newMethod);

                return(newClass);
            }

            //
            //
            //

            void GenerateConstructor(JavaMethod method, CilType dlgType)
            {
                var code = method.Code = new JavaCode();

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

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

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

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

                code.NewInstruction(0x19 /* aload_0 */, null, (int)0);
                code.NewInstruction(0x19 /* aload_1 */, null, (int)1);
                code.NewInstruction(0x19 /* aload_2 */, null, (int)2);

                code.NewInstruction(0xB7 /* invokespecial */,
                                    new JavaType(0, 0, method.Class.Super),
                                    new JavaMethodRef("<init>", JavaType.VoidType,
                                                      JavaType.ObjectType, JavaType.ObjectType));

                code.NewInstruction(0xB1 /* return (void) */, null, null);

                if (code.MaxStack < 3)
                {
                    code.MaxStack = 3;
                }
                code.MaxLocals = 3 + dlgType.GenericParametersCount;
            }

            //
            //
            //

            void GenerateInvoke(JavaMethod method, CilType dlgType, JavaType ifcType)
            {
                var code = method.Code = new JavaCode();

                code.Method       = method;
                code.Instructions = new List <Instruction>();
                code.StackMap     = new JavaStackMap();

                code.NewInstruction(0x19 /* aload_0 */, null, (int)0);
                code.NewInstruction(0xB4 /* getfield */,
                                    new JavaType(0, 0, "system.Delegate"),
                                    new JavaFieldRef("invokable", JavaType.ObjectType));

                code.NewInstruction(0xC0 /* checkcast */, ifcType, null);
                code.StackMap.PushStack(JavaType.ObjectType);

                int index = 1;

                for (int i = 0; i < method.Parameters.Count; i++)
                {
                    var paramType = (CilType)method.Parameters[i].Type;
                    code.NewInstruction(paramType.LoadOpcode, null, index);
                    code.StackMap.PushStack(paramType);

                    if (paramType.IsGenericParameter && (!paramType.IsByReference))
                    {
                        // invoke the helper method which identifies our boxed
                        // primitives and re-boxes them as java boxed values.
                        // see also: GenericType::DelegateParameterin baselib.
                        GenericUtil.LoadMaybeGeneric(
                            paramType.GetMethodGenericParameter(), code);
                        code.NewInstruction(0xB8 /* invokestatic */,
                                            SystemDelegateUtilType, DelegateParameterMethod);
                        code.StackMap.PopStack(CilMain.Where);
                    }
                    index += paramType.Category;
                }

                var returnType = (CilType)method.ReturnType;

                if (IsPrimitive(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 GenerateInvoke and LoadFunction
                    var adjustedMethod = new JavaMethodRef(
                        method.Name, JavaType.ObjectType, method.Parameters);
                    code.NewInstruction(0xB9 /* invokeinterface */, ifcType, adjustedMethod);
                }
                else
                {
                    code.NewInstruction(0xB9 /* invokeinterface */, ifcType, method);
                }

                code.StackMap.ClearStack();
                code.StackMap.PushStack(returnType);

                if (returnType.IsGenericParameter && (!returnType.IsByReference))
                {
                    GenericUtil.LoadMaybeGeneric(
                        returnType.GetMethodGenericParameter(), code);
                    code.NewInstruction(0xB8 /* invokestatic */,
                                        SystemDelegateUtilType, DelegateReturnValueMethod);
                    code.StackMap.PopStack(CilMain.Where);
                }
                else if (IsPrimitive(returnType))
                {
                    // if the delegate returns a primitive type, we need to unbox
                    // the object returned by the method we just called.  it will be
                    // a java boxed primitive (e.g. java.lang.Integer) if the called
                    // method actually returns a primitive, due to the boxing done
                    // by the invokedynamic mechanism.  if the called method returns
                    // a generic type, it will be our boxed type, e.g. system.Int32.
                    //
                    // see also DelegateReturnValueX helper methods in baselib,
                    // and MakeInterface and LoadFunction in this file.
                    var helperMethod = new JavaMethodRef(
                        DelegateReturnValueMethod.Name + returnType.ToDescriptor(),
                        returnType, JavaType.ObjectType);
                    code.NewInstruction(0xB8 /* invokestatic */,
                                        SystemDelegateUtilType, helperMethod);
                }

                code.NewInstruction(returnType.ReturnOpcode, null, index);
                code.StackMap.PopStack(CilMain.Where);

                code.MaxStack  = code.StackMap.GetMaxStackSize(CilMain.Where);
                code.MaxLocals = index;
            }

            //
            //
            //

            void GenerateBeginInvoke()
            {
            }

            //
            //
            //

            void GenerateEndInvoke()
            {
            }
        }
예제 #13
0
        bool StoreFieldValue(string fldName, CilType fldType, CilType fldClass,
                             bool isStatic, bool isVolatile)
        {
            //
            // we generate a sequence of instructions based on the combination of
            //      isStatic, isGeneric, isBoxed, isCopyable
            //
            // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X:
            //   [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [BOX] -> BoxedType.SetVO
            //
            // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1:
            //   [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [OBJ] -> ValueType.Copy
            //
            // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0:
            // * [VAL] -> LoadST -> [VAL] [ST] -> swap -> [ST] [VAL] -> putfield
            //
            // isStatic=1 IsGeneric=0 IsBoxed=1 IsCopyable=X:
            //   [VAL] -> getstatic -> [VAL] [BOX] -> BoxedType.SetVO
            //
            // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=1:
            //   [VAL] -> getstatic -> [VAL] [OBJ] -> ValueType.Copy
            //
            // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=0:
            //   [VAL] -> putstatic
            //
            // isStatic=0 IsGeneric=X IsBoxed=1 IsCopyable=X:
            // * [INS] [VAL] -> swap -> [VAl] [INS] -> getfield -> [VAL] [BOX] -> BoxedType.SetOV
            //
            // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=1:
            //   [INS] [VAL] -> swap -> [VAL] [INS] -> getfield -> [VAL] [OBJ] -> ValueType.Copy
            //
            // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=0:
            //   [INS] [VAL] -> putfield
            //
            // the combinations marked with an asterisk require swapping operands,
            // which is handled more effectively by first popping the value operand
            //

            var  fldRef  = new JavaFieldRef(fldName, fldType);
            bool isBoxed = fldType is BoxedType;

            if (isStatic)
            {
                if (fldClass.HasGenericParameters)
                {
                    // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X
                    // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1
                    // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0
                    StoreStaticGeneric(fldClass, fldType, fldRef, isVolatile);
                }
                else
                {
                    // isStatic=1 IsGeneric=0 IsBoxed=1 IsCopyable=X
                    // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=1
                    // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=0
                    StoreStaticRegular(fldClass, fldType, fldRef, isVolatile);
                }
            }
            else
            {
                // isStatic=0 IsGeneric=X IsBoxed=1 IsCopyable=X
                // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=1
                // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=0
                StoreInstance(fldClass, fldType, fldRef, isVolatile);
            }

            return(true);



            void StoreStaticGeneric(CilType fldClass, CilType fldType, JavaFieldRef fldRef,
                                    bool isVolatile)
            {
                // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X:
                //   [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [BOX] -> BoxedType.SetVO
                //
                // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1:
                //   [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [OBJ] -> ValueType.Copy
                //
                // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0:
                // * [VAL] -> LoadST -> [VAL] [ST] -> swap -> [ST] [VAL] -> putfield

                if (fldType.IsValueClass)
                {
                    fldClass = LoadStaticData(fldClass);

                    code.NewInstruction(0xB4 /* getfield */, fldClass.AsWritableClass, fldRef);

                    if (fldType is BoxedType boxedType && (!boxedType.IsBoxedIntPtr))
                    {
                        boxedType.SetValueVO(code, isVolatile);
                    }
                    else
                    {
                        GenericUtil.ValueCopy(fldType, code);
                    }
                }