Esempio n. 1
0
        /// <summary>
        /// Emit the PhpType finalizer. The finalizer is emitted only if there is __destruct() function
        /// and there is no finalizer in any base class already. The finalizer calls this.Dispose() which
        /// calls __destruct() function directly.
        /// </summary>
        /// <param name="phpType"></param>
        private static void EmitFinalizer(PhpType /*!*/ phpType)
        {
            // only if __destruct was now defined in some base class, no need to override existing definition on Finalize
            DRoutine     basedestruct;
            DRoutineDesc destruct;

            if ((destruct = phpType.TypeDesc.GetMethod(DObject.SpecialMethodNames.Destruct)) != null && (phpType.Base == null ||
                                                                                                         phpType.Base.GetMethod(DObject.SpecialMethodNames.Destruct, phpType, out basedestruct) == GetMemberResult.NotFound))
            {
                MethodBuilder finalizer_builder = phpType.RealTypeBuilder.DefineMethod("Finalize", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Family, typeof(void), Type.EmptyTypes);

                ILEmitter dil = new ILEmitter(finalizer_builder);

                // exact Finalize() method pattern follows:

                // try
                dil.BeginExceptionBlock();

                // this.Dispose(false)
                dil.Emit(OpCodes.Ldarg_0);
                dil.Emit(OpCodes.Ldc_I4_0);
                dil.Emit(OpCodes.Callvirt, PHP.Core.Emit.Methods.DObject_Dispose);

                // finally
                dil.BeginFinallyBlock();

                // Object.Finalize()
                dil.Emit(OpCodes.Ldarg_0);
                dil.Emit(OpCodes.Call, PHP.Core.Emit.Methods.Object_Finalize);

                dil.EndExceptionBlock();

                dil.Emit(OpCodes.Ret);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Emits the try block and the catch blocks.
        /// </summary>
        /// <param name="codeGenerator">A code generator.</param>
        /// <remarks>
        /// <code>
        ///	try
        /// {
        ///   // guarded code //
        /// }
        /// catch(E1 $e1)
        /// {
        ///   // E1 //
        /// }
        /// catch(E2 $e2)
        /// {
        ///   // E2 //
        /// }
        /// </code>
        /// is translated as follows:
        /// <code>
        /// try
        /// {
        ///   // guarded code //
        /// }
        /// catch(PhpUserException _e)
        /// {
        ///   PhpObject _o = _e.UserException;
        ///   if (_o instanceOf E1)
        ///   {
        ///     $e1 = _o;
        ///     // E1 //
        ///   }
        ///   else if (_o instanceOf E2)
        ///   {
        ///     $e2 = _o;
        ///     // E2 //
        ///   }
        ///   else
        ///   {
        ///     throw;
        ///   }
        /// }
        /// </code>
        /// </remarks>
        internal override void Emit(CodeGenerator /*!*/ codeGenerator)
        {
            Statistics.AST.AddNode("TryStmt");

            // emit try block without CLR exception block if possible

            if (!HasCatches && !HasFinallyStatements)
            {
                this.Statements.Emit(codeGenerator);
                return;
            }

            // emit CLR exception block

            ILEmitter il = codeGenerator.IL;

            codeGenerator.ExceptionBlockNestingLevel++;

            // TRY
            Label end_label = il.BeginExceptionBlock();

            this.Statements.Emit(codeGenerator);

            // catches

            if (HasCatches)
            {
                // catch (PHP.Core.ScriptDiedException)
                // { throw; }

                il.BeginCatchBlock(typeof(PHP.Core.ScriptDiedException));
                il.Emit(OpCodes.Rethrow);

                // catch (System.Exception ex)

                il.BeginCatchBlock(typeof(System.Exception));

                // <exception_local> = (DObject) (STACK is PhpUserException) ? ((PhpUserException)STACK).UserException : ClrObject.WrapRealObject(STACK)

                Label        clrExceptionLabel = il.DefineLabel();
                Label        wrapEndLabel      = il.DefineLabel();
                LocalBuilder exception_local   = il.GetTemporaryLocal(typeof(DObject));

                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Isinst, typeof(PHP.Core.PhpUserException)); // <STACK> as PhpUserException
                il.Emit(OpCodes.Brfalse, clrExceptionLabel);

                // if (<STACK> as PhpUserException != null)
                {
                    il.Emit(OpCodes.Ldfld, Fields.PhpUserException_UserException);
                    il.Emit(OpCodes.Br, wrapEndLabel);
                }

                // else
                il.MarkLabel(clrExceptionLabel);
                {
                    il.Emit(OpCodes.Call, Methods.ClrObject_WrapRealObject);
                }
                il.MarkLabel(wrapEndLabel);
                il.Stloc(exception_local);

                // emits all PHP catch-blocks processing into a single CLI catch-block:
                foreach (CatchItem c in catches)
                {
                    Label next_catch_label = il.DefineLabel();

                    // IF (exception <instanceOf> <type>);
                    c.Emit(codeGenerator, exception_local, end_label, next_catch_label);

                    // ELSE
                    il.MarkLabel(next_catch_label);
                }

                il.ReturnTemporaryLocal(exception_local);

                // emits the "else" branch invoked if the exceptions is not catched:
                il.Emit(OpCodes.Rethrow);
            }

            // finally

            if (HasFinallyStatements)
            {
                finallyItem.Emit(codeGenerator);
            }

            //
            il.EndExceptionBlock();

            codeGenerator.ExceptionBlockNestingLevel--;
        }
Esempio n. 3
0
        private static void WriteFinalizers(ILEmitter il, MethodBase original, ILEmitter.Label returnLabel,
                                            Dictionary <string, VariableDefinition> variables,
                                            List <MethodInfo> finalizers)
        {
            // Finalizer layout:
            // Create __exception variable to store exception info and a skip flag
            // Wrap the whole method into a try/catch
            // Call finalizers at the end of method (simulate `finally`)
            // If __exception got set, throw it
            // Begin catch block
            // Store exception into __exception
            // If skip flag is set, skip finalizers
            // Call finalizers
            // If __exception is still set, rethrow (if new exception set, otherwise throw the new exception)
            // End catch block

            if (finalizers.Count == 0)
            {
                return;
            }

            Logger.Log(Logger.LogChannel.Info, () => "Writing finalizers");

            il.emitBefore = il.IL.Body.Instructions[il.IL.Body.Instructions.Count - 1];

            // Mark the original method return label here if it hasn't been yet
            il.MarkLabel(returnLabel);

            if (!variables.TryGetValue(RESULT_VAR, out var returnValueVar))
            {
                var retVal = AccessTools.GetReturnedType(original);
                returnValueVar = variables[RESULT_VAR] = retVal == typeof(void) ? null : il.DeclareVariable(retVal);
            }

            // Create variables to hold custom exception
            var skipFinalizersVar = il.DeclareVariable(typeof(bool));

            variables[EXCEPTION_VAR] = il.DeclareVariable(typeof(Exception));

            // Start main exception block
            var mainBlock = il.BeginExceptionBlock(il.DeclareLabelFor(il.IL.Body.Instructions[0]));

            bool WriteFinalizerCalls(bool suppressExceptions)
            {
                var canRethrow = true;

                foreach (var finalizer in finalizers)
                {
                    var start = il.DeclareLabel();
                    il.MarkLabel(start);

                    EmitCallParameter(il, original, finalizer, variables, false);
                    il.Emit(OpCodes.Call, finalizer);

                    if (finalizer.ReturnType != typeof(void))
                    {
                        il.Emit(OpCodes.Stloc, variables[EXCEPTION_VAR]);
                        canRethrow = false;
                    }

                    if (suppressExceptions)
                    {
                        var exBlock = il.BeginExceptionBlock(start);

                        il.BeginHandler(exBlock, ExceptionHandlerType.Catch, typeof(object));
                        il.Emit(OpCodes.Pop);
                        il.EndExceptionBlock(exBlock);
                    }
                }

                return(canRethrow);
            }

            // First, store potential result into a variable and empty the stack
            if (returnValueVar != null)
            {
                il.Emit(OpCodes.Stloc, returnValueVar);
            }

            // Write finalizers inside the `try`
            WriteFinalizerCalls(false);

            // Mark finalizers as skipped so they won't rerun
            il.Emit(OpCodes.Ldc_I4_1);
            il.Emit(OpCodes.Stloc, skipFinalizersVar);

            // If __exception is not null, throw
            var skipLabel = il.DeclareLabel();

            il.Emit(OpCodes.Ldloc, variables[EXCEPTION_VAR]);
            il.Emit(OpCodes.Brfalse, skipLabel);
            il.Emit(OpCodes.Ldloc, variables[EXCEPTION_VAR]);
            il.Emit(OpCodes.Throw);
            il.MarkLabel(skipLabel);

            // Begin a generic `catch(Exception o)` here and capture exception into __exception
            il.BeginHandler(mainBlock, ExceptionHandlerType.Catch, typeof(Exception));
            il.Emit(OpCodes.Stloc, variables[EXCEPTION_VAR]);

            // Call finalizers or skip them if needed
            il.Emit(OpCodes.Ldloc, skipFinalizersVar);
            var postFinalizersLabel = il.DeclareLabel();

            il.Emit(OpCodes.Brtrue, postFinalizersLabel);

            var rethrowPossible = WriteFinalizerCalls(true);

            il.MarkLabel(postFinalizersLabel);

            // Possibly rethrow if __exception is still not null (i.e. suppressed)
            skipLabel = il.DeclareLabel();
            il.Emit(OpCodes.Ldloc, variables[EXCEPTION_VAR]);
            il.Emit(OpCodes.Brfalse, skipLabel);
            if (rethrowPossible)
            {
                il.Emit(OpCodes.Rethrow);
            }
            else
            {
                il.Emit(OpCodes.Ldloc, variables[EXCEPTION_VAR]);
                il.Emit(OpCodes.Throw);
            }

            il.MarkLabel(skipLabel);
            il.EndExceptionBlock(mainBlock);

            if (returnValueVar != null)
            {
                il.Emit(OpCodes.Ldloc, returnValueVar);
            }
        }