Exemple #1
0
        void Translate_Throw(Mono.Cecil.Cil.Instruction inst)
        {
            if (inst.OpCode.Code == Code.Rethrow)
            {
                // if a catch clause contains a 'rethrow' instruction then
                // a reference to the exception object should have previously
                // been saved in a local (in SaveExceptionObject)

                CatchClause nearestClause = FindNearestCatchClause(inst.Offset);
                if (!nearestClause.includesRethrow)
                {
                    throw new InvalidProgramException();
                }
                code.NewInstruction(0x19 /* aload */, null,
                                    nearestClause.tryClause.localIndex);
            }

            // in the jvm, the stack trace is recorded when the exception
            // object is created.  in the clr, the stack trace is recorded
            // only when the exception is thrown, so we have to emulate it.
            //
            // (also note that our system.Exception replacement (in baselib)
            // discards the initial call to fillInStackTrace, which occurs
            // during the construction of the exception object.)

            code.NewInstruction(0xB6 /* invokevirtual */, ThrowableType,
                                new JavaMethodRef("fillInStackTrace", ThrowableType));
            code.NewInstruction(0xBF /* athrow */, null, null);
        }
        void ProcessOneInstruction(Mono.Cecil.Cil.Instruction cilInst)
        {
            #if DEBUGDIAG
            var(stack1, stack2) = stackMap.FrameToString();
            Console.WriteLine($"LOCALS [{stack1}] STACK [{stack2}]");
            Console.WriteLine(cilInst);
            #endif

            int instructionsCount = code.Instructions.Count;

            code.SetLabel((ushort)cilInst.Offset);

            this.cilInst = cilInst;
            this.cilOp   = cilInst.OpCode.Code;

            exceptions.CheckAndSaveFrame(cilInst);

            var genericMark = CilMain.GenericStack.Mark();
            CallInstructionTranslator();
            CilMain.GenericStack.Release(genericMark);

            while (instructionsCount < code.Instructions.Count)
            {
                var newInst = code.Instructions[instructionsCount++];
                #if DEBUGDIAG
                newInst.Line = (ushort)(lineNumber != 0 ? lineNumber : cilInst.Offset);
                #else
                newInst.Line = (ushort)lineNumber;
                #endif
            }
        }
Exemple #3
0
        public void Translate(Mono.Cecil.Cil.Instruction inst)
        {
            switch (inst.OpCode.Code)
            {
            case Code.Throw:
            case Code.Rethrow:  Translate_Throw(inst);      break;

            case Code.Leave:
            case Code.Leave_S:  Translate_Leave(inst);      break;

            case Code.Endfinally:            Translate_Endfinally(inst.Offset); break;

            case Code.Endfilter:                    Translate_Endfilter(inst);  return;

            default:                                throw new InvalidProgramException();
            }

            //
            // these instructions break the normal flow of execution and clear the
            // stack frame.  if the following instruction already has a stack frame,
            // due to some earlier forward jump, we load that frame
            //

            CilMain.LoadFrameOrClearStack(stackMap, inst);

            locals.TrackUnconditionalBranch(inst);
        }
Exemple #4
0
        public void CheckAndSaveFrame(Mono.Cecil.Cil.Instruction inst)
        {
            int offset = inst.Offset;

            if (!catchClauses.TryGetValue(offset, out var catchClause))
            {
                //
                // on every instruction, and in particular on entry to a 'try'
                // block, we want to save the stack frame.
                //
                // - for the 'try' case:  due to possibility of any exception
                // occuring at any point within the try block, exception clauses
                // always roll back to that stack frame at the top of the 'try'.
                //
                // - for any other instruction:  because it may be the target
                // of a backwards jump at the end of a loop.
                //

                stackMap.SaveFrame((ushort)offset, false, CilMain.Where);
            }
            else
            {
                //
                // otherwise this is entry to a catch/filter/finally clause.
                //
                // if this is the first exception in the chain, we would also
                // need to push the exception type into the stack frame,
                // before saving it.
                //

                ExceptionClauseSetup((ushort)offset, catchClause);
            }
        }
Exemple #5
0
        void Translate_Endfilter(Mono.Cecil.Cil.Instruction inst)
        {
            // operand stack should contain an integer
            var integer = stackMap.PopStack(CilMain.Where);

            if (!integer.Equals(JavaType.IntegerType))
            {
                throw new InvalidProgramException();
            }

            CatchClause filterClause = null;

            foreach (var tryClause in tryClauses)
            {
                foreach (var catchClause in tryClause.catchClauses)
                {
                    if (catchClause.filterCondStart != 0 &&
                        catchClause.filterCondStart < inst.Offset &&
                        catchClause.catchStart == inst.Next?.Offset)
                    {
                        filterClause = catchClause;
                        break;
                    }
                }
            }

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

            // make the stack contain [ Throwable, integer ].  the integer
            // is already on the stack as the result of the filter test code.
            // the Throwable was stored in a local in ExceptionClauseSetup.

            var localIndex = filterClause.tryClause.localIndex;

            code.NewInstruction(0x19 /* aload */, null, localIndex);
            locals.FreeTempIndex(localIndex);

            stackMap.PushStack(ThrowableType);
            code.NewInstruction(0x5F /* swap */, null, null);

            ExceptionClauseCommon(filterClause);
        }
Exemple #6
0
        void Translate_Leave(Mono.Cecil.Cil.Instruction inst)
        {
            var catchClause = RollbackStackFrame(inst.Offset);

            if (catchClause != null && catchClause.finallyClause)
            {
                // leave is only permitted in a try or catch clauses,
                // but not a finally or a fault clause
                throw new InvalidProgramException();
            }

            if (inst.Operand is Mono.Cecil.Cil.Instruction leaveTarget)
            {
                bool directJump;
                var  finallyClause1 = FindNearestFinallyClause(inst.Offset);

                if (finallyClause1 == null)
                {
                    // if there is no finally clause, we can jump directly
                    directJump = true;
                }
                else
                {
                    // check for a control transfer within the same try clause,
                    // in this case we do not need to handle the call to 'finally'
                    var finallyClause2 = FindNearestFinallyClause(leaveTarget.Offset);
                    directJump = (finallyClause1 == finallyClause2);
                }

                if (directJump)
                {
                    if (leaveTarget != inst.Next)
                    {
                        code.NewInstruction(0xA7 /* goto */, null, (ushort)leaveTarget.Offset);
                        stackMap.SaveFrame((ushort)leaveTarget.Offset, true, CilMain.Where);
                    }
                    else
                    {
                        code.NewInstruction(0x00 /* nop */, null, null);
                    }
                }
                else
                {
                    // if we have a 'finally' clause, the 'leave' target indicates
                    // where execution should resume, after executing the 'finally'
                    // handler. but the 'finally' handler does not specify where to
                    // resume.
                    //
                    // typically, normal execution resumes just past the end of the
                    // 'finally' handler, but there are exceptions to this rule;
                    // for example, the 'finally' handler may be immediately followed
                    // by a 'catch' clause from an enclosing try/catch block.
                    //
                    // in addition, the 'leave' target may specify some other offset,
                    // for example if the 'catch' contains a 'return' statement.
                    // and the try/catch block may not have any 'leave' instructions
                    // at all, if for example, all clauses end with a 'throw'.
                    //
                    // to work around all these issues, we create a 'fake' exception
                    // object that specifies the leave target.  this is identified
                    // in 'endfinally' and used to jump to the requested offset.
                    // see Translate_Endfinally.

                    ushort leaveOffset = (ushort)leaveTarget.Offset;
                    foreach (var tryClause in tryClauses)
                    {
                        var catchClauses = tryClause.catchClauses;
                        var lastClause   = catchClauses[catchClauses.Count - 1];
                        if (lastClause.finallyClause)
                        {
                            if (lastClause.leaveOffsets == null)
                            {
                                lastClause.leaveOffsets = new List <ushort>();
                            }

                            if (lastClause.leaveOffsets.IndexOf(leaveOffset) == -1)
                            {
                                lastClause.leaveOffsets.Add(leaveOffset);
                            }
                        }
                    }

                    code.NewInstruction(0x12 /* ldc */, null, (int)leaveTarget.Offset);
                    stackMap.PushStack(JavaType.IntegerType);
                    code.NewInstruction(0xB8 /* invokestatic */, LeaveTargetType,
                                        new JavaMethodRef("New", ThrowableType, JavaType.IntegerType));
                    stackMap.PopStack(CilMain.Where);

                    stackMap.PushStack(ThrowableType);

                    var finallyOffset = (ushort)finallyClause1.catchStart;
                    if (finallyOffset != inst.Next?.Offset)
                    {
                        code.NewInstruction(0xA7 /* goto */, null, (ushort)finallyOffset);
                        stackMap.SaveFrame((ushort)finallyOffset, true, CilMain.Where);
                    }

                    stackMap.PopStack(CilMain.Where); // pop null
                }
            }
            else
            {
                throw new InvalidProgramException();
            }
        }
Exemple #7
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());
            }
        }