/// <summary> /// Generates code that initializes the variable and function declarations. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> internal void GenerateDeclarations(ILGenerator generator, OptimizationInfo optimizationInfo) { // Parent root: Expression rootExpression = optimizationInfo.RootExpression; // Initialize the declared variables and functions. foreach (var variable in this.variables.Values) { DeclaredVariable dVariable = variable as DeclaredVariable; // When a scope is reused, i.e. with an eval(), do not reinitialize the variables. if (dVariable == null || dVariable.Initialized) { continue; } if (dVariable.ValueAtTopOfScope != null) { // Emit the initialization code. var name = new NameExpression(this, dVariable.Name); Type rType = dVariable.ValueAtTopOfScope.GetResultType(optimizationInfo); name.ApplyType(optimizationInfo, rType); name.GenerateSet(generator, optimizationInfo, false, rType, delegate(bool two) { optimizationInfo.RootExpression = dVariable.ValueAtTopOfScope; dVariable.ValueAtTopOfScope.GenerateCode(generator, optimizationInfo); if (two) { // Dupe it: generator.Duplicate(); } }, false); // Mark the variable as having been initialized. dVariable.Initialized = true; } } // Restore root: optimizationInfo.RootExpression = rootExpression; }
/// <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) { // Get the previous root - we'll be changing it: Expression prevRoot = optimizationInfo.RootExpression; // 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. It's now the root. TryBlock.SetRoot(optimizationInfo); 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(Exception)); var catchVariable = new NameExpression(this.CatchScope, this.CatchVariableName); catchVariable.ApplyType(optimizationInfo, typeof(Exception)); catchVariable.GenerateSet(generator, optimizationInfo, false, typeof(Exception), delegate(bool two){ // Note that we always do nothing here. The value is already on the stack. }, false); // Emit code for the statements within the catch block. It's now the root. CatchBlock.SetRoot(optimizationInfo); CatchBlock.GenerateCode(generator, optimizationInfo); } // 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 = delegate(ILGenerator generator2, ILLabel 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. It's now the root. FinallyBlock.SetRoot(optimizationInfo); 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.LoadField(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; // Restore root: optimizationInfo.RootExpression = prevRoot; }
/// <summary> /// Generates IL for the script. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> protected override void GenerateCode(ArgVariable[] arguments, ILGenerator generator, OptimizationInfo optimizationInfo) { // Method signature: object FunctionDelegate(object thisObj, object[] arguments) // Transfer the function name into the scope. if (!string.IsNullOrEmpty(Name) && IncludeNameInScope && !HasArgument(Name) && optimizationInfo.MethodOptimizationHints.HasVariable(Name)) { var functionName = new NameExpression(InitialScope, Name); functionName.ApplyType(optimizationInfo, typeof(object)); functionName.GenerateSet(generator, optimizationInfo, false, typeof(object), delegate(bool two) { generator.LoadInt64(MethodID); generator.Call(ReflectionHelpers.MethodLookup_Load); if (two) { // Duplicate it: generator.Duplicate(); } }, false); } // Transfer the arguments object into the scope. if (MethodOptimizationHints.HasArguments && !HasArgument("arguments")) { var argsSet = new NameExpression(this.InitialScope, "arguments"); argsSet.ApplyType(optimizationInfo, typeof(object)); argsSet.GenerateSet(generator, optimizationInfo, false, typeof(object), delegate(bool two) { // argumentValues // Build an object[] from the arg values. // Define an array: int argCount = (arguments == null)?0 : arguments.Length; generator.LoadInt32(argCount); generator.NewArray(typeof(object)); for (int a = 0; a < argCount; a++) { // One of many args: ArgVariable currentArg = arguments[a]; generator.Duplicate(); generator.LoadInt32(a); currentArg.Get(generator); EmitConversion.ToAny(generator, currentArg.Type); generator.StoreArrayElement(typeof(object)); } generator.NewObject(ReflectionHelpers.Arguments_Constructor); if (two) { generator.Duplicate(); } }, false); } // Temp cache return var/target: var retVar = optimizationInfo.ReturnVariable; var retTarg = optimizationInfo.ReturnTarget; optimizationInfo.ReturnVariable = null; optimizationInfo.ReturnTarget = null; // Initialize any declarations. (this.InitialScope as DeclarativeScope).GenerateDeclarations(generator, optimizationInfo); // Generate code for the body of the function. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Define the return target - this is where the return statement jumps to. // ReturnTarget can be null if there were no return statements. if (optimizationInfo.ReturnTarget != null) { generator.DefineLabelPosition(optimizationInfo.ReturnTarget); } // Load the return value. If the variable is null, there were no return statements. if (optimizationInfo.ReturnVariable != null) { // Return the value stored in the variable. Will be null if execution hits the end // of the function without encountering any return statements. generator.LoadVariable(optimizationInfo.ReturnVariable); } // Restore: optimizationInfo.ReturnVariable = retVar; optimizationInfo.ReturnTarget = retTarg; }