Example #1
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultBreakStatementBehavior = true, NonDefaultDebugInfoBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Construct a loop expression.
            // var enumerator = TypeUtilities.EnumeratePropertyNames(rhs).GetEnumerator();
            // while (true) {
            //   continue-target:
            //   if (enumerator.MoveNext() == false)
            //     goto break-target;
            //   lhs = enumerator.Current;
            //
            //   <body statements>
            // }
            // break-target:

            // Call IEnumerable<string> EnumeratePropertyNames(ScriptEngine engine, object obj)
            EmitHelpers.LoadScriptEngine(generator);
            this.TargetObject.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToAny(generator, this.TargetObject.ResultType);
            generator.Call(ReflectionHelpers.TypeUtilities_EnumeratePropertyNames);

            // Call IEnumerable<string>.GetEnumerator()
            generator.Call(ReflectionHelpers.IEnumerable_GetEnumerator);

            // Store the enumerator in a temporary variable.
            var enumerator = generator.CreateTemporaryVariable(typeof(IEnumerator<string>));
            generator.StoreVariable(enumerator);

            var breakTarget = generator.CreateLabel();
            var continueTarget = generator.DefineLabelPosition();

            // Emit debugging information.
            if (optimizationInfo.DebugDocument != null)
                generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.VariableDebugInfo);

            //   if (enumerator.MoveNext() == false)
            //     goto break-target;
            generator.LoadVariable(enumerator);
            generator.Call(ReflectionHelpers.IEnumerator_MoveNext);
            generator.BranchIfFalse(breakTarget);

            // lhs = enumerator.Current;
            generator.LoadVariable(enumerator);
            generator.Call(ReflectionHelpers.IEnumerator_Current);
            this.Variable.GenerateSet(generator, optimizationInfo, PrimitiveType.String, false);

            // Emit the body statement(s).
            optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget, continueTarget, labelledOnly: false);
            this.Body.GenerateCode(generator, optimizationInfo);
            optimizationInfo.PopBreakOrContinueInfo();

            generator.Branch(continueTarget);
            generator.DefineLabelPosition(breakTarget);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            if (this.ContributesToEvalResult == true && optimizationInfo.EvalResult != null)
            {
                // Emit the expression.
                this.Expression.GenerateCode(generator, optimizationInfo);

                // Store the result.
                EmitConversion.ToAny(generator, this.Expression.ResultType);
                generator.StoreVariable(optimizationInfo.EvalResult);
            }
            else
            {
                // Emit the expression.
                this.Expression.GenerateCode(generator, optimizationInfo);
                generator.Pop();
            }

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Create the scope.
            this.Scope.GenerateScopeCreation(generator, optimizationInfo);

            // Make sure the scope is reverted even if an exception is thrown.
            generator.BeginExceptionBlock();

            // Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE
            // instructions so that the finally block is executed correctly.
            var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally;
            optimizationInfo.InsideTryCatchOrFinally = true;

            // Generate code for the body statements.
            this.Body.GenerateCode(generator, optimizationInfo);

            // Reset the InsideTryCatchOrFinally flag.
            optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally;

            // Revert the scope.
            generator.BeginFinallyBlock();
            this.Scope.GenerateScopeDestruction(generator, optimizationInfo);
            generator.EndExceptionBlock();

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #4
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #5
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultSourceSpanBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Emit an unconditional branch.
            // Note: the continue statement might be branching from inside a try { } or finally { }
            // block to outside.  EmitLongJump() handles this.
            optimizationInfo.EmitLongJump(generator, optimizationInfo.GetContinueTarget(this.Label));

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #7
0
        /// <summary>
        /// Generates CIL for the end of every statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        /// <param name="locals"> Variables common to both GenerateStartOfStatement() and GenerateEndOfStatement(). </param>
        public void GenerateEndOfStatement(ILGenerator generator, OptimizationInfo optimizationInfo, StatementLocals locals)
        {
            if (locals.NonDefaultBreakStatementBehavior == false && this.HasLabels == true)
            {
                // Revert the information needed by the break statement.
                generator.DefineLabelPosition(locals.EndOfStatement);
                optimizationInfo.PopBreakOrContinueInfo();
            }

            #if DEBUG && !SILVERLIGHT && !XBOX
            // Check that the stack count is zero.
            if (generator is DynamicILGenerator && ((DynamicILGenerator)generator).StackSize != locals.OriginalStackSize)
                throw new InvalidOperationException("Encountered unexpected stack imbalance.");
            #endif
        }
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Inserts a breakpoint into the IL.
            generator.Breakpoint();

            // When the debugger stops, it stops at the first instruction after the breakpoint.  By
            // inserting a no-op operation the debugger will highlight the "debugger" statement
            // instead of the statement after the "debugger" statement.
            generator.NoOperation();

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #9
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Emit code to throw the given value.
            this.Value.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToAny(generator, this.Value.ResultType);
            generator.LoadInt32(0);
            generator.LoadNull();
            generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Object);
            generator.Throw();

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Emit the return value.
            if (this.Value == null)
                EmitHelpers.EmitUndefined(generator);
            else
            {
                this.Value.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, this.Value.ResultType);
            }

            // Determine if this is the last statement in the function.
            bool lastStatement = optimizationInfo.AbstractSyntaxTree is BlockStatement &&
                ((BlockStatement)optimizationInfo.AbstractSyntaxTree).Statements.Count > 0 &&
                ((BlockStatement)optimizationInfo.AbstractSyntaxTree).Statements[((BlockStatement)optimizationInfo.AbstractSyntaxTree).Statements.Count - 1] == this;

            // The first return statement initializes the variable that holds the return value.
            if (optimizationInfo.ReturnVariable == null)
                optimizationInfo.ReturnVariable = generator.DeclareVariable(typeof(object), "returnValue");

            // Store the return value in a variable.
            generator.StoreVariable(optimizationInfo.ReturnVariable);

            // There is no need to jump to the end of the function if this is the last statement.
            if (lastStatement == false)
            {
                
                // The first return statement that needs to branch creates the return label.  This is
                // defined in FunctionmethodGenerator.GenerateCode() at the end of the function.
                if (optimizationInfo.ReturnTarget == null)
                    optimizationInfo.ReturnTarget = generator.CreateLabel();

                // Branch to the end of the function.  Note: the return statement might be branching
                // from inside a try { } or finally { } block to outside.  EmitLongJump() handles this.
                optimizationInfo.EmitLongJump(generator, optimizationInfo.ReturnTarget);

            }

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #11
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Generate code for the condition and coerce to a boolean.
            this.Condition.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToBool(generator, this.Condition.ResultType);

            // We will need a label at the end of the if statement.
            var endOfEverything = generator.CreateLabel();

            if (this.ElseClause == null)
            {
                // Jump to the end if the condition is false.
                generator.BranchIfFalse(endOfEverything);

                // Generate code for the if clause.
                this.IfClause.GenerateCode(generator, optimizationInfo);
            }
            else
            {
                // Branch to the else clause if the condition is false.
                var startOfElseClause = generator.CreateLabel();
                generator.BranchIfFalse(startOfElseClause);

                // Generate code for the if clause.
                this.IfClause.GenerateCode(generator, optimizationInfo);

                // Branch to the end of the if statement.
                generator.Branch(endOfEverything);

                // Generate code for the else clause.
                generator.DefineLabelPosition(startOfElseClause);
                this.ElseClause.GenerateCode(generator, optimizationInfo);
                
            }

            // Define the label at the end of the if statement.
            generator.DefineLabelPosition(endOfEverything);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #12
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultSourceSpanBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            foreach (var declaration in this.Declarations)
            {
                if (declaration.InitExpression != null)
                {
                    // Create a new assignment expression and generate code for it.
                    var initializationStatement = new ExpressionStatement(
                        new AssignmentExpression(this.Scope, declaration.VariableName, declaration.InitExpression));
                    initializationStatement.SourceSpan = declaration.SourceSpan;
                    initializationStatement.GenerateCode(generator, optimizationInfo);
                }
            }

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #13
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            foreach (var declaration in this.Declarations)
            {
                if (declaration.InitExpression != null)
                {
                    // Create a new assignment expression and generate code for it.
                    if (optimizationInfo.DebugDocument != null)
                        generator.MarkSequencePoint(optimizationInfo.DebugDocument, declaration.DebugInfo);
                    var initializationStatement = new ExpressionStatement(
                        new AssignmentExpression(this.Scope, declaration.VariableName, declaration.InitExpression));
                    initializationStatement.GenerateCode(generator, optimizationInfo);
                }
            }

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #14
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultBreakStatementBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // We need a label for each case clause and one for the default case.
            var jumpTargets = new ILLabel[this.CaseClauses.Count];
            int defaultIndex = -1;
            ILLabel endOfSwitch = generator.CreateLabel();

            // Generate code for the switch value.
            var startOfSwitch = generator.CreateLabel();
            this.Value.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToAny(generator, this.Value.ResultType);

            // Save the switch value in a variable.
            var switchValue = generator.CreateTemporaryVariable(typeof(object));
            generator.StoreVariable(switchValue);

            for (int i = 0; i < this.CaseClauses.Count; i ++)
            {
                var caseClause = this.CaseClauses[i];

                // Create a label for each clause.
                jumpTargets[i] = generator.CreateLabel();

                if (caseClause.Value == null)
                {
                    // This is a default clause.
                    defaultIndex = i;
                    continue;
                }

                // TypeComparer.StrictEquals(switchValue, caseValue)
                generator.LoadVariable(switchValue);
                caseClause.Value.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, caseClause.Value.ResultType);
                generator.Call(ReflectionHelpers.TypeComparer_StrictEquals);

                // if (TypeComparer.StrictEquals(switchValue, caseValue) == true)
                //     goto case i
                generator.BranchIfTrue(jumpTargets[i]);
            }
            
            // None of the cases matched, jump to the default clause or the end of the switch.
            if (defaultIndex >= 0)
                generator.Branch(jumpTargets[defaultIndex]);
            else
                generator.Branch(endOfSwitch);

            for (int i = 0; i < this.CaseClauses.Count; i++)
            {
                // Define a label at the start of the case clause.
                generator.DefineLabelPosition(jumpTargets[i]);

                // Set up the information needed by the break statement.
                optimizationInfo.PushBreakOrContinueInfo(this.Labels, endOfSwitch, null, labelledOnly: false);

                // Emit the case clause statements.
                foreach (var statement in this.CaseClauses[i].BodyStatements)
                    statement.GenerateCode(generator, optimizationInfo);

                // Revert the information needed by the break statement.
                optimizationInfo.PopBreakOrContinueInfo();
            }

            // Define a label for the end of the switch statement.
            generator.DefineLabelPosition(endOfSwitch);

            // Release the switch value variable for use elsewhere.
            generator.ReleaseTemporaryVariable(switchValue);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #15
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals()
            {
                NonDefaultBreakStatementBehavior = true, NonDefaultSourceSpanBehavior = true
            };

            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Construct a loop expression.
            // var iterator = TypeUtilities.GetIterator(obj);
            // while (true) {
            //   continue-target:
            //   if (enumerator.MoveNext() == false)
            //     goto break-target;
            //   lhs = enumerator.Current;
            //
            //   <body statements>
            // }
            // break-target:

            // Call: ObjectInstance GetIterator(ScriptEngine engine, ObjectInstance iterable)
            // Then call: IEnumerable<object> Iterate(ScriptEngine engine, ObjectInstance iterator)
            optimizationInfo.MarkSequencePoint(generator, this.TargetObjectSourceSpan);
            EmitHelpers.LoadScriptEngine(generator);
            this.TargetObject.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToAny(generator, this.TargetObject.ResultType);
            generator.Call(ReflectionHelpers.TypeUtilities_ForOf);

            // Call IEnumerable<object>.GetEnumerator()
            generator.Call(ReflectionHelpers.IEnumerable_Object_GetEnumerator);

            // Store the enumerator in a temporary variable.
            var enumerator = generator.CreateTemporaryVariable(typeof(IEnumerator <object>));

            generator.StoreVariable(enumerator);

            var breakTarget    = generator.CreateLabel();
            var continueTarget = generator.DefineLabelPosition();

            // Generate the scope variable if necessary.
            this.Scope.GenerateScopeCreation(generator, optimizationInfo);

            // Emit debugging information.
            if (optimizationInfo.DebugDocument != null)
            {
                generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.VariableSourceSpan);
            }

            //   if (enumerator.MoveNext() == false)
            //     goto break-target;
            generator.LoadVariable(enumerator);
            generator.Call(ReflectionHelpers.IEnumerator_MoveNext);
            generator.BranchIfFalse(breakTarget);

            // lhs = enumerator.Current;
            this.Variable.GenerateReference(generator, optimizationInfo);
            generator.LoadVariable(enumerator);
            generator.Call(ReflectionHelpers.IEnumerator_Object_Current);
            this.Variable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any);

            // Emit the body statement(s).
            optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget, continueTarget, labelledOnly: false);
            this.Body.GenerateCode(generator, optimizationInfo);
            optimizationInfo.PopBreakOrContinueInfo();

            generator.Branch(continueTarget);
            generator.DefineLabelPosition(breakTarget);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #16
0
        /// <summary>
        /// Generates CIL for the start of every statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        /// <param name="locals"> Variables common to both GenerateStartOfStatement() and GenerateEndOfStatement(). </param>
        public void GenerateStartOfStatement(ILGenerator generator, OptimizationInfo optimizationInfo, StatementLocals locals)
        {
#if DEBUG && !SILVERLIGHT
            // Statements must not produce or consume any values on the stack.
            if (generator is DynamicILGenerator)
                locals.OriginalStackSize = ((DynamicILGenerator)generator).StackSize;
#endif

            if (locals.NonDefaultBreakStatementBehavior == false && this.HasLabels == true)
            {
                // Set up the information needed by the break statement.
                locals.EndOfStatement = generator.CreateLabel();
                optimizationInfo.PushBreakOrContinueInfo(this.Labels, locals.EndOfStatement, null, labelledOnly: true);
            }

            // Emit debugging information.
            if (locals.NonDefaultDebugInfoBehavior == false && optimizationInfo.DebugDocument != null && this.DebugInfo != null)
                generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.DebugInfo);
        }
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultSourceSpanBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Unlike in .NET, in javascript there are no restrictions on what can appear inside
            // try, catch and finally blocks.  The one restriction which causes problems is the
            // inability to jump out of .NET finally blocks.  This is required when break, continue
            // or return statements appear inside of a finally block.  To work around this, when
            // inside a finally block these instructions throw an exception instead.

            // Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE
            // instructions so that the finally block is executed correctly.
            var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally;
            optimizationInfo.InsideTryCatchOrFinally = true;

            // Finally requires two exception nested blocks.
            if (this.FinallyBlock != null)
                generator.BeginExceptionBlock();

            // Begin the exception block.
            generator.BeginExceptionBlock();

            // Generate code for the try block.
            this.TryBlock.GenerateCode(generator, optimizationInfo);

            // Generate code for the catch block.
            if (this.CatchBlock != null)
            {
                // Begin a catch block.  The exception is on the top of the stack.
                generator.BeginCatchBlock(typeof(JavaScriptException));

                // Create a new DeclarativeScope.
                this.CatchScope.GenerateScopeCreation(generator, optimizationInfo);

                // Store the error object in the variable provided.
                generator.Call(ReflectionHelpers.JavaScriptException_ErrorObject);
                var catchVariable = new NameExpression(this.CatchScope, this.CatchVariableName);
                catchVariable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false);

                // Make sure the scope is reverted even if an exception is thrown.
                generator.BeginExceptionBlock();

                // Emit code for the statements within the catch block.
                this.CatchBlock.GenerateCode(generator, optimizationInfo);

                // Revert the scope.
                generator.BeginFinallyBlock();
                this.CatchScope.GenerateScopeDestruction(generator, optimizationInfo);
                generator.EndExceptionBlock();
            }

            // Generate code for the finally block.
            if (this.FinallyBlock != null)
            {
                generator.BeginFinallyBlock();

                var branches = new List<ILLabel>();
                var previousStackSize = optimizationInfo.LongJumpStackSizeThreshold;
                optimizationInfo.LongJumpStackSizeThreshold = optimizationInfo.BreakOrContinueStackSize;
                var previousCallback = optimizationInfo.LongJumpCallback;
                optimizationInfo.LongJumpCallback = (generator2, label) =>
                    {
                        // It is not possible to branch out of a finally block - therefore instead of
                        // generating LEAVE instructions we throw an exception then catch it to transfer
                        // control out of the finally block.
                        generator2.LoadInt32(branches.Count);
                        generator2.NewObject(ReflectionHelpers.LongJumpException_Constructor);
                        generator2.Throw();

                        // Record any branches that are made within the finally code.
                        branches.Add(label);
                    };

                // Emit code for the finally block.
                this.FinallyBlock.GenerateCode(generator, optimizationInfo);

                // End the main exception block.
                generator.EndExceptionBlock();

                // Begin a catch block to catch any LongJumpExceptions. The exception object is on
                // the top of the stack.
                generator.BeginCatchBlock(typeof(LongJumpException));

                if (branches.Count > 0)
                {
                    // switch (exception.RouteID)
                    // {
                    //    case 0: goto label1;
                    //    case 1: goto label2;
                    // }
                    ILLabel[] switchLabels = new ILLabel[branches.Count];
                    for (int i = 0; i < branches.Count; i++)
                        switchLabels[i] = generator.CreateLabel();
                    generator.Call(ReflectionHelpers.LongJumpException_RouteID);
                    generator.Switch(switchLabels);
                    for (int i = 0; i < branches.Count; i++)
                    {
                        generator.DefineLabelPosition(switchLabels[i]);
                        generator.Leave(branches[i]);
                    }
                }

                // Reset the state we clobbered.
                optimizationInfo.LongJumpStackSizeThreshold = previousStackSize;
                optimizationInfo.LongJumpCallback = previousCallback;
            }

            // End the exception block.
            generator.EndExceptionBlock();

            // Reset the InsideTryCatchOrFinally flag.
            optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally;

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #18
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultBreakStatementBehavior = true, NonDefaultSourceSpanBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // <initializer>
            // if (<condition>)
            // {
            // 	 <loop body>
            //   <increment>
            //   while (true) {
            //     if (<condition> == false)
            //       break;
            //
            //     <body statements>
            //
            //     continue-target:
            //     <increment>
            //   }
            // }
            // break-target:

            // Set up some labels.
            var continueTarget = generator.CreateLabel();
            var breakTarget1 = generator.CreateLabel();
            var breakTarget2 = generator.CreateLabel();

            // Emit the initialization statement.
            if (this.InitStatement != null)
                this.InitStatement.GenerateCode(generator, optimizationInfo);

            // Check the condition and jump to the end if it is false.
            if (this.CheckConditionAtEnd == false && this.ConditionStatement != null)
            {
                optimizationInfo.MarkSequencePoint(generator, this.ConditionStatement.SourceSpan);
                this.Condition.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToBool(generator, this.Condition.ResultType);
                generator.BranchIfFalse(breakTarget1);
            }

            // Emit the loop body.
            optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget1, continueTarget, false);
            this.Body.GenerateCode(generator, optimizationInfo);
            optimizationInfo.PopBreakOrContinueInfo();

            // Increment the loop variable.
            if (this.IncrementStatement != null)
                this.IncrementStatement.GenerateCode(generator, optimizationInfo);

            // Strengthen the variable types.
            List<KeyValuePair<Scope.DeclaredVariable, RevertInfo>> previousVariableTypes = null;
            var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally;
            if (optimizationInfo.OptimizeInferredTypes == true)
            {
                // Keep a record of the variable types before strengthening.
                previousVariableTypes = new List<KeyValuePair<Scope.DeclaredVariable, RevertInfo>>();

                var typedVariables = FindTypedVariables();
                foreach (var variableAndType in typedVariables)
                {
                    var variable = variableAndType.Key;
                    var variableInfo = variableAndType.Value;
                    if (variableInfo.Conditional == false && variableInfo.Type != variable.Type)
                    {
                        // Save the previous type so we can restore it later.
                        var previousType = variable.Type;
                        previousVariableTypes.Add(new KeyValuePair<Scope.DeclaredVariable, RevertInfo>(variable, new RevertInfo() { Type = previousType, Variable = variable.Store }));

                        // Load the existing value.
                        var nameExpression = new NameExpression(variable.Scope, variable.Name);
                        nameExpression.GenerateGet(generator, optimizationInfo, false);

                        // Store the typed value.
                        variable.Store = generator.DeclareVariable(variableInfo.Type);
                        variable.Type = variableInfo.Type;
                        nameExpression.GenerateSet(generator, optimizationInfo, previousType, false);
                    }
                }

                // The variables must be reverted even in the presence of exceptions.
                if (previousVariableTypes.Count > 0)
                {
                    generator.BeginExceptionBlock();

                    // Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE
                    // instructions so that the finally block is executed correctly.
                    optimizationInfo.InsideTryCatchOrFinally = true;
                }
            }

            // The inner loop starts here.
            var startOfLoop = generator.DefineLabelPosition();

            // Check the condition and jump to the end if it is false.
            if (this.ConditionStatement != null)
            {
                optimizationInfo.MarkSequencePoint(generator, this.ConditionStatement.SourceSpan);
                this.Condition.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToBool(generator, this.Condition.ResultType);
                generator.BranchIfFalse(breakTarget2);
            }

            // Emit the loop body.
            optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget2, continueTarget, labelledOnly: false);
            this.Body.GenerateCode(generator, optimizationInfo);
            optimizationInfo.PopBreakOrContinueInfo();

            // The continue statement jumps here.
            generator.DefineLabelPosition(continueTarget);

            // Increment the loop variable.
            if (this.IncrementStatement != null)
                this.IncrementStatement.GenerateCode(generator, optimizationInfo);

            // Unconditionally branch back to the start of the loop.
            generator.Branch(startOfLoop);

            // Define the end of the loop (actually just after).
            generator.DefineLabelPosition(breakTarget2);

            // Revert the variable types.
            if (previousVariableTypes != null && previousVariableTypes.Count > 0)
            {
                // Revert the InsideTryCatchOrFinally flag.
                optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally;

                // Revert the variable types within a finally block.
                generator.BeginFinallyBlock();

                foreach (var previousVariableAndType in previousVariableTypes)
                {
                    var variable = previousVariableAndType.Key;
                    var variableRevertInfo = previousVariableAndType.Value;

                    // Load the existing value.
                    var nameExpression = new NameExpression(variable.Scope, variable.Name);
                    nameExpression.GenerateGet(generator, optimizationInfo, false);

                    // Store the typed value.
                    var previousType = variable.Type;
                    variable.Store = variableRevertInfo.Variable;
                    variable.Type = variableRevertInfo.Type;
                    nameExpression.GenerateSet(generator, optimizationInfo, previousType, false);
                }

                // End the exception block.
                generator.EndExceptionBlock();
            }

            // Define the end of the loop (actually just after).
            generator.DefineLabelPosition(breakTarget1);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }