/// <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); }
/// <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); }
/// <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; // When we have a finally block, use a temporary variable that stores if the // finally block should be skipped. This will be set to true when an exception was // caught but ScriptEngine.CanCatchException() returns false. // returns true. ILLocalVariable skipFinallyBlock = null; if (this.FinallyBlock != null) { // Finally requires two exception nested blocks. generator.BeginExceptionBlock(); skipFinallyBlock = generator.CreateTemporaryVariable(typeof(bool)); generator.LoadBoolean(false); generator.StoreVariable(skipFinallyBlock); } // Begin the exception block. generator.BeginExceptionBlock(); // Generate code for the try block. this.TryBlock.GenerateCode(generator, optimizationInfo); // Generate code for the catch block. // Begin a catch block. The exception is on the top of the stack. generator.BeginCatchBlock(typeof(object)); // Check the exception is catchable by calling CanCatchException(ex). // We need to handle the case where JS code calls into .NET code which then throws // a JavaScriptException from a different ScriptEngine. // If CatchBlock is null, we need to rethrow the exception in every case. var endOfIfLabel = generator.CreateLabel(); generator.Duplicate(); // ex var exceptionTemporary = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(exceptionTemporary); EmitHelpers.LoadScriptEngine(generator); generator.LoadVariable(exceptionTemporary); generator.ReleaseTemporaryVariable(exceptionTemporary); generator.Call(ReflectionHelpers.ScriptEngine_CanCatchException); generator.BranchIfTrue(endOfIfLabel); if (this.FinallyBlock != null) { generator.LoadBoolean(true); generator.StoreVariable(skipFinallyBlock); } if (this.CatchBlock == null) { generator.DefineLabelPosition(endOfIfLabel); } generator.Rethrow(); if (this.CatchBlock != null) { generator.DefineLabelPosition(endOfIfLabel); } if (this.CatchBlock != null) { // Create a RuntimeScope instance. CatchBlock.Scope.GenerateScopeCreation(generator, optimizationInfo); if (this.CatchVariableName != null) { // Store the error object in the variable provided. generator.ReinterpretCast(typeof(JavaScriptException)); EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.JavaScriptException_GetErrorObject); var catchVariable = new NameExpression(CatchBlock.Scope, this.CatchVariableName); catchVariable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); } else { // Remove the exception object from the stack. generator.Pop(); } // Emit code for the statements within the catch block. this.CatchBlock.GenerateCode(generator, optimizationInfo); } // Generate code for the finally block. if (this.FinallyBlock != null) { generator.BeginFinallyBlock(); // If an exception was thrown that isn't determined as catchable by the ScriptEngine, // then don't run the finally block either. This prevents user code from being run // when a non-JavaScriptException is thrown (e.g. to cancel script execution). var endOfFinallyBlock = generator.CreateLabel(); generator.LoadVariable(skipFinallyBlock); generator.ReleaseTemporaryVariable(skipFinallyBlock); generator.BranchIfTrue(endOfFinallyBlock); 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); // Define the position at the end of the finally block. generator.DefineLabelPosition(endOfFinallyBlock); // 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]); } } else { generator.Pop(); } // 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); }
/// <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); }
/// <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); // <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) { if (optimizationInfo.DebugDocument != null) { generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.ConditionStatement.DebugInfo); } 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; 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(); } } // 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) { if (optimizationInfo.DebugDocument != null) { generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.ConditionStatement.DebugInfo); } 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 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); }