public DeclareVariable ( PrimitiveType type, string name = null ) : |
||
type | PrimitiveType | The type of the local variable. |
name | string | The name of the local variable. Can be |
리턴 |
/// <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(ILGenerator generator, OptimizationInfo optimizationInfo) { // Declare a variable to store the eval result. optimizationInfo.EvalResult = generator.DeclareVariable(typeof(object)); if (this.StrictMode == true) { // Create a new scope. this.InitialScope.GenerateScopeCreation(generator, optimizationInfo); } // Initialize any declarations. this.InitialScope.GenerateDeclarations(generator, optimizationInfo); // Generate the main body of code. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Make the return value from the method the eval result. generator.LoadVariable(optimizationInfo.EvalResult); // If the result is null, convert it to undefined. var end = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(end); generator.Pop(); EmitHelpers.EmitUndefined(generator); generator.DefineLabelPosition(end); }
/// <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(ILGenerator generator, OptimizationInfo optimizationInfo) { // Declare a variable to store the eval result. optimizationInfo.EvalResult = generator.DeclareVariable(typeof(object)); if (this.StrictMode == true) { // Create a new scope. this.InitialScope.GenerateScopeCreation(generator, optimizationInfo); } // Verify the scope is correct. VerifyScope(generator); // Initialize any declarations. this.InitialScope.GenerateDeclarations(generator, optimizationInfo); // Generate the main body of code. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Make the return value from the method the eval result. generator.LoadVariable(optimizationInfo.EvalResult); // If the result is null, convert it to undefined. var end = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(end); generator.Pop(); EmitHelpers.EmitUndefined(generator); generator.DefineLabelPosition(end); }
/// <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); }
/// <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); }
/// <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(ILGenerator generator, OptimizationInfo optimizationInfo) { if (Context != GeneratorContext.Global) { // Declare a variable to store the eval result. optimizationInfo.EvalResult = generator.DeclareVariable(typeof(object)); } // Generate the main body of code. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); if (Context != GeneratorContext.Global) { // Make the return value from the method the eval result. generator.LoadVariable(optimizationInfo.EvalResult); } else { // Code in the global context always returns null. generator.LoadNull(); } }
/// <summary> /// Retrieves a variable that can be used to store a shared instance of a regular /// expression. /// </summary> /// <param name="generator"> The IL generator used to create the variable. </param> /// <param name="literal"> The regular expression literal. </param> /// <returns> A varaible that can be used to store a shared instance of a regular /// expression. </returns> public ILLocalVariable GetRegExpVariable(ILGenerator generator, RegularExpressionLiteral literal) { if (literal == null) { throw new ArgumentNullException(nameof(literal)); } // Create a new Dictionary if it hasn't been created before. if (this.regularExpressionVariables == null) { this.regularExpressionVariables = new Dictionary <RegularExpressionLiteral, ILLocalVariable>(); } // Check if the literal already exists in the dictionary. ILLocalVariable variable; if (this.regularExpressionVariables.TryGetValue(literal, out variable) == false) { // The literal does not exist - add it. variable = generator.DeclareVariable(typeof(Library.RegExpInstance)); this.regularExpressionVariables.Add(literal, variable); } return(variable); }
/// <summary> /// Retrieves a variable that can be used to store a property name referencing an /// object property. /// </summary> /// <param name="generator"> The IL generator used to create the variable. </param> /// <param name="name"> The name of the property. </param> /// <returns> A variable. </returns> public ILLocalVariable GetPropertyReferenceVariable(ILGenerator generator, string name) { if (name == null) { throw new ArgumentNullException(nameof(name)); } // Create a new Dictionary if it hasn't been created before. if (this.propertyVariables == null) { this.propertyVariables = new Dictionary <string, ILLocalVariable>(); } // Check if the name already exists in the dictionary. ILLocalVariable variable; if (this.propertyVariables.TryGetValue(name, out variable) == false) { // The literal does not exist - add it. variable = generator.DeclareVariable(typeof(Library.PropertyReference), name); this.propertyVariables.Add(name, variable); } return(variable); }
/// <summary> /// Retrieves a variable that can be used to store a shared instance of a regular /// expression. /// </summary> /// <param name="generator"> The IL generator used to create the variable. </param> /// <param name="literal"> The regular expression literal. </param> /// <returns> A varaible that can be used to store a shared instance of a regular /// expression. </returns> public ILLocalVariable GetRegExpVariable(ILGenerator generator, RegularExpressionLiteral literal) { if (literal == null) throw new ArgumentNullException("literal"); // Create a new Dictionary if it hasn't been created before. if (this.regularExpressionVariables == null) this.regularExpressionVariables = new Dictionary<RegularExpressionLiteral, ILLocalVariable>(); // Check if the literal already exists in the dictionary. ILLocalVariable variable; if (this.regularExpressionVariables.TryGetValue(literal, out variable) == false) { // The literal does not exist - add it. variable = generator.DeclareVariable(typeof(Library.RegExpInstance)); this.regularExpressionVariables.Add(literal, variable); } return variable; }
/// <summary> /// Stores the value on the top of the stack in the reference. /// </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="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { // The value is initially on the top of the stack but is stored in this variable // at the last possible moment. ILLocalVariable value = null; var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfSet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // Get information about the variable. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable was declared in this scope. if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) variable.Store = generator.DeclareVariable(typeof(object), variable.Name); if (value == null) { // The value to store is on the top of the stack - convert it to the // storage type of the variable. EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo); } else { // The value to store is in a temporary variable. generator.LoadVariable(value); EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo); } // Store the value in the variable. generator.StoreVariable(variable.Store); } else if (variable.Writable == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // scope.Values[index] = value if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); } else { // The variable exists, but is read-only. // Pop the value off the stack (if it is still there). if (value == null) generator.Pop(); } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Set the value of the variable. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.LoadVariable(value); generator.Call(ReflectionHelpers.Scope_SetValue); generator.Branch(endOfSet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } if (scope.ParentScope == null) { // Optimization: if this is the global scope, use hidden classes to // optimize variable access. // Global variable modification // ---------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Get a reference to the global object. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); if (throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); generator.Branch(endOfSet); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Slow route. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); if (scope.ParentScope == null && throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // The value might be still on top of the stack. if (value == null && scope == null) generator.Pop(); // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); // Release the temporary variables. if (value != null) generator.ReleaseTemporaryVariable(value); if (scopeVariable != null) generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(endOfSet); }
/// <summary> /// Pushes the value of the reference onto the stack. /// </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="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { // This method generates code to retrieve the value of a variable, given the name of // variable and scope in which the variable is being referenced. The variable was // not necessary declared in this scope - it might be declared in any of the parent // scopes (together called a scope chain). The general algorithm is to start at the // head of the chain and search backwards until the variable is found. There are // two types of scopes: declarative scopes and object scopes. Object scopes are hard - // it cannot be known at compile time whether the variable exists or not so runtime // checks have to be inserted. Declarative scopes are easier - variables have to be // declared and cannot be deleted. There is one tricky bit: new variables can be // introduced into a declarative scope at runtime by a non-strict eval() statement. // Even worse, variables that were introduced by means of an eval() *can* be deleted. var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfGet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // The variable was declared in this scope. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) variable.Store = generator.DeclareVariable(typeof(object), variable.Name); // Load the value from the variable. generator.LoadVariable(variable.Store); // Ensure that we match ResultType. EmitConversion.Convert(generator, variable.Type, this.ResultType, optimizationInfo); } else { // scope.Values[index] if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadArrayElement(typeof(object)); } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Load the value of the variable. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_GetValue); generator.Branch(endOfGet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (scope.ParentScope == null) { // Global variable access // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("variable", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Get a reference to the global object. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) generator.Pop(); } else { // Gets the value of a variable in an object scope. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Object); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) generator.Pop(); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); // Release the temporary variable. if (scopeVariable != null) generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(endOfGet); // Object scope references may have side-effects (because of getters) so if the value // is to be ignored we evaluate the value then pop the value from the stack. //if (optimizationInfo.SuppressReturnValue == true) // generator.Pop(); }
/// <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. ILLocalVariable skipFinallyBlock = null; // Begin a catch block. The exception is on the top of the stack. generator.BeginCatchBlock(typeof(Exception)); // 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(Exception)); 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); skipFinallyBlock = generator.DeclareVariable(typeof(bool), "skipFinallyBlock"); 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 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(); // If an exception was thrown that wasn't handled by the catch block, then don't // run the finally block either. This prevents user code from being run when a // ThreadAbortException is thrown. var endOfFinallyBlock = generator.CreateLabel(); generator.LoadVariable(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]); } } // 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 code that creates a new scope. /// </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 GenerateScopeCreation(ILGenerator generator, OptimizationInfo optimizationInfo) { // Make sure we don't generate the scope twice. if (GenerateScopeCreationWasCalled) { return; } GenerateScopeCreationWasCalled = true; // We can optimize this away if there are zero variables declared in the scope, // UNLESS it's a with scope (as then we need something to bind to). if (this.variables.Count == 0 && Type != ScopeType.With) { return; } // If there is no eval(), no arguments usage and no nested functions, then we can use // IL variables instead of using RuntimeScope. if ((Type == ScopeType.TopLevelFunction || Type == ScopeType.Block) && optimizationInfo.OptimizeDeclarativeScopes) { foreach (var variable in this.variables.Values) { variable.Store = generator.DeclareVariable(variable.Type, variable.Name); if (variable.Type == PrimitiveType.Any) { generator.LoadNull(); generator.StoreVariable(variable.Store); } } return; } // The fallback: use RuntimeScope. EmitHelpers.LoadExecutionContext(generator); // parentScope if (ParentScope != null) { ParentScope.GenerateReference(generator, optimizationInfo); } else { generator.LoadNull(); } var varList = new List <DeclaredVariable>(); var letList = new List <DeclaredVariable>(); var constList = new List <DeclaredVariable>(); foreach (var variable in this.variables.Values) { if (variable.Keyword == KeywordToken.Var) { varList.Add(variable); } else if (variable.Keyword == KeywordToken.Const) { constList.Add(variable); } else { letList.Add(variable); } } varList.Sort((a, b) => a.Index - b.Index); letList.Sort((a, b) => a.Index - b.Index); constList.Sort((a, b) => a.Index - b.Index); int i; // scopeType generator.LoadEnumValue(Type); // varNames if (varList.Count == 0) { generator.LoadNull(); } else { generator.LoadInt32(varList.Count); generator.NewArray(typeof(string)); i = 0; foreach (var variable in varList) { generator.Duplicate(); generator.LoadInt32(i++); generator.LoadString(variable.Name); generator.StoreArrayElement(typeof(string)); } } // letNames if (letList.Count == 0) { generator.LoadNull(); } else { generator.LoadInt32(letList.Count); generator.NewArray(typeof(string)); i = 0; foreach (var variable in letList) { generator.Duplicate(); generator.LoadInt32(i++); generator.LoadString(variable.Name); generator.StoreArrayElement(typeof(string)); } } // constNames if (constList.Count == 0) { generator.LoadNull(); } else { generator.LoadInt32(constList.Count); generator.NewArray(typeof(string)); i = 0; foreach (var variable in constList) { generator.Duplicate(); generator.LoadInt32(i++); generator.LoadString(variable.Name); generator.StoreArrayElement(typeof(string)); } } // executionContext.CreateRuntimeScope(parentScope, varNames, letNames, constNames) generator.Call(ReflectionHelpers.ExecutionContext_CreateRuntimeScope); // Store the RuntimeScope instance in a variable. GeneratedRuntimeScope = generator.DeclareVariable(typeof(RuntimeScope), "scope"); generator.StoreVariable(GeneratedRuntimeScope); }
/// <summary> /// Stores the value on the top of the stack in the reference. /// </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="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property modification (e.g. x.property = y) // ------------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Convert the value to an object and store it in a temporary variable. var value = generator.CreateTemporaryVariable(typeof(object)); EmitConversion.ToAny(generator, valueType); generator.StoreVariable(value); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(value); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); } }
/// <summary> /// Stores the value on the top of the stack in the reference. /// </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="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { // The value is initially on the top of the stack but is stored in this variable // at the last possible moment. ILLocalVariable value = null; var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfSet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // Get information about the variable. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable was declared in this scope. if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) { variable.Store = generator.DeclareVariable(typeof(object), variable.Name); } if (value == null) { // The value to store is on the top of the stack - convert it to the // storage type of the variable. EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo); } else { // The value to store is in a temporary variable. generator.LoadVariable(value); EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo); } // Store the value in the variable. generator.StoreVariable(variable.Store); } else if (variable.Writable == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // scope.Values[index] = value if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); } else { // The variable exists, but is read-only. // Pop the value off the stack (if it is still there). if (value == null) { generator.Pop(); } } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Set the value of the variable. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.LoadVariable(value); generator.Call(ReflectionHelpers.Scope_SetValue); generator.Branch(endOfSet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); if (scope.ParentScope == null && throwIfUnresolvable == false) { // Sets the value of a global variable. // JS: object.property = value // C# ==> // if (propertyName == null) // propertyName = new PropertyName("property"); // object.SetPropertyValue(propertyName, value, strictMode); ILLocalVariable propertyName = optimizationInfo.GetGlobalPropertyReferenceVariable(generator, this.Name); generator.LoadVariable(propertyName); generator.Duplicate(); var afterIf = generator.CreateLabel(); generator.BranchIfNotNull(afterIf); generator.Pop(); generator.LoadString(this.Name); generator.NewObject(ReflectionHelpers.PropertyName_Constructor); generator.Duplicate(); generator.StoreVariable(propertyName); generator.DefineLabelPosition(afterIf); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_PropertyReference); } else { // Setting a variable within a "with" scope. generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // The value might be still on top of the stack. if (value == null && scope == null) { generator.Pop(); } // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) { EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); } // Release the temporary variables. if (value != null) { generator.ReleaseTemporaryVariable(value); } if (scopeVariable != null) { generator.ReleaseTemporaryVariable(scopeVariable); } // Define a label at the end. generator.DefineLabelPosition(endOfSet); }
/// <summary> /// Pushes the value of the reference onto the stack. /// </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="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { // This method generates code to retrieve the value of a variable, given the name of // variable and scope in which the variable is being referenced. The variable was // not necessary declared in this scope - it might be declared in any of the parent // scopes (together called a scope chain). The general algorithm is to start at the // head of the chain and search backwards until the variable is found. There are // two types of scopes: declarative scopes and object scopes. Object scopes are hard - // it cannot be known at compile time whether the variable exists or not so runtime // checks have to be inserted. Declarative scopes are easier - variables have to be // declared and cannot be deleted. There is one tricky bit: new variables can be // introduced into a declarative scope at runtime by a non-strict eval() statement. // Even worse, variables that were introduced by means of an eval() *can* be deleted. var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfGet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // The variable was declared in this scope. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) { variable.Store = generator.DeclareVariable(typeof(object), variable.Name); } // Load the value from the variable. generator.LoadVariable(variable.Store); // Ensure that we match ResultType. EmitConversion.Convert(generator, variable.Type, this.ResultType, optimizationInfo); } else { // scope.Values[index] if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadArrayElement(typeof(object)); } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Load the value of the variable. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_GetValue); generator.Branch(endOfGet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (scope.ParentScope == null) { // Global variable access // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("variable", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Get a reference to the global object. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) { generator.Pop(); } } else { // Gets the value of a variable in an object scope. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Object); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) { generator.Pop(); } } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) { EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); } // Release the temporary variable. if (scopeVariable != null) { generator.ReleaseTemporaryVariable(scopeVariable); } // Define a label at the end. generator.DefineLabelPosition(endOfGet); // Object scope references may have side-effects (because of getters) so if the value // is to be ignored we evaluate the value then pop the value from the stack. //if (optimizationInfo.SuppressReturnValue == true) // generator.Pop(); }
/// <summary> /// Stores the value on the top of the stack in the reference. /// </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="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { // The value is initially on the top of the stack but is stored in this variable // at the last possible moment. ILLocalVariable value = null; var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfSet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // Get information about the variable. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable was declared in this scope. if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) { variable.Store = generator.DeclareVariable(typeof(object), variable.Name); } if (value == null) { // The value to store is on the top of the stack - convert it to the // storage type of the variable. EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo); } else { // The value to store is in a temporary variable. generator.LoadVariable(value); EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo); } // Store the value in the variable. generator.StoreVariable(variable.Store); } else if (variable.Writable == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // scope.Values[index] = value if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); } else { // The variable exists, but is read-only. // Pop the value off the stack (if it is still there). if (value == null) { generator.Pop(); } } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Set the value of the variable. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.LoadVariable(value); generator.Call(ReflectionHelpers.Scope_SetValue); generator.Branch(endOfSet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } if (scope.ParentScope == null) { // Optimization: if this is the global scope, use hidden classes to // optimize variable access. // Global variable modification // ---------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Get a reference to the global object. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); if (throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); generator.Branch(endOfSet); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Slow route. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); if (scope.ParentScope == null && throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // The value might be still on top of the stack. if (value == null && scope == null) { generator.Pop(); } // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) { EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); } // Release the temporary variables. if (value != null) { generator.ReleaseTemporaryVariable(value); } if (scopeVariable != null) { generator.ReleaseTemporaryVariable(scopeVariable); } // Define a label at the end. generator.DefineLabelPosition(endOfSet); }
/// <summary> /// Pushes the value of the reference onto the stack. /// </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="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call Get(object) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Object); } }
/// <summary> /// Stores the value on the top of the stack in the reference. /// </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="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property modification (e.g. x.property = y) // ------------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Convert the value to an object and store it in a temporary variable. var value = generator.CreateTemporaryVariable(typeof(object)); EmitConversion.ToAny(generator, valueType); generator.StoreVariable(value); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(value); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); } }
/// <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 !XBOX if (optimizationInfo.DebugDocument != null) { generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.ConditionStatement.DebugInfo); } #endif 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) { #if !XBOX if (optimizationInfo.DebugDocument != null) { generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.ConditionStatement.DebugInfo); } #endif 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> /// Pushes the value of the reference onto the stack. /// </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="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; bool isArrayIndex = false; // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) { throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access", optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); } propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1) as LiteralExpression; if (rhs != null && (PrimitiveTypeUtilities.IsNumeric(rhs.ResultType) || rhs.ResultType == PrimitiveType.String)) { propertyName = TypeConverter.ToString(rhs.Value); // Or a array index (a[0]) if (rhs.ResultType == PrimitiveType.Int32 || (propertyName != null && Library.ArrayInstance.ParseArrayIndex(propertyName) != uint.MaxValue)) { isArrayIndex = true; } } } if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the right-hand side and convert to a uint32. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToUInt32(generator, rhs.ResultType); // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (propertyName != null) { //// Load the left-hand side and convert to an object instance. //var lhs = this.GetOperand(0); //lhs.GenerateCode(generator, optimizationInfo); //EmitConversion.ToObject(generator, lhs.ResultType); //// Call Get(string) //generator.LoadString(propertyName); //generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); // Call Get(string) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); } }
/// <summary> /// Pushes the value of the reference onto the stack. /// </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="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; bool isArrayIndex = false; // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access", optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1) as LiteralExpression; if (rhs != null && (PrimitiveTypeUtilities.IsNumeric(rhs.ResultType) || rhs.ResultType == PrimitiveType.String)) { propertyName = TypeConverter.ToString(rhs.Value); // Or a array index (a[0]) if (rhs.ResultType == PrimitiveType.Int32 || (propertyName != null && Library.ArrayInstance.ParseArrayIndex(propertyName) != uint.MaxValue)) isArrayIndex = true; } } if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the right-hand side and convert to a uint32. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToUInt32(generator, rhs.ResultType); // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (propertyName != null) { //// Load the left-hand side and convert to an object instance. //var lhs = this.GetOperand(0); //lhs.GenerateCode(generator, optimizationInfo); //EmitConversion.ToObject(generator, lhs.ResultType); //// Call Get(string) //generator.LoadString(propertyName); //generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); // Call Get(string) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); } }
/// <summary> /// Pushes the value of the reference onto the stack. /// </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="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call Get(object) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); } }
/// <summary> /// Stores the value on the top of the stack in the reference. /// </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="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { string propertyName = null; bool isArrayIndex = false; //optimizationInfo = optimizationInfo.RemoveFlags(OptimizationFlags.SuppressReturnValue); // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access"); propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1) as LiteralExpression; if (rhs != null) { propertyName = TypeConverter.ToString(rhs.Value); // Or a array index (a[0]) if (rhs.ResultType == PrimitiveType.Int32 || (propertyName != null && Library.ArrayInstance.ParseArrayIndex(propertyName) != uint.MaxValue)) isArrayIndex = true; } } // Convert the value to an object and store it in a temporary variable. var value = generator.CreateTemporaryVariable(typeof(object)); EmitConversion.ToAny(generator, valueType); generator.StoreVariable(value); if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType); // Load the right-hand side and convert to a uint32. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToUInt32(generator, rhs.ResultType); // Call the indexer. generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Int); } else if (propertyName != null) { //// Load the left-hand side and convert to an object instance. //var lhs = this.GetOperand(0); //lhs.GenerateCode(generator, optimizationInfo); //EmitConversion.ToObject(generator, lhs.ResultType); //// Call the indexer. //generator.LoadString(propertyName); //generator.LoadVariable(value); //generator.LoadBoolean(optimizationInfo.StrictMode); //generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); // Named property modification (e.g. x.property = y) // ------------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); // Call the indexer. generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); } // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(value); }
/// <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); }