/// <summary> /// Generates CIL for the typeof expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateTypeof(ILGenerator generator, OptimizationInfo optimizationInfo) { // If a return value is not expected, generate only the side-effects. /*if (optimizationInfo.SuppressReturnValue == true) * { * this.GenerateSideEffects(generator, optimizationInfo); * return; * }*/ if (this.Operand is NameExpression) { // Unresolvable references must return "undefined" rather than throw an error. ((NameExpression)this.Operand).GenerateGet(generator, optimizationInfo, false); } else { // Emit code for resolving the value of the operand. this.Operand.GenerateCode(generator, optimizationInfo); } // Convert to System.Object. EmitConversion.ToAny(generator, this.Operand.ResultType); // Call TypeUtilities.TypeOf(operand). generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); }
/// <summary> /// Deletes the reference and pushes <c>true</c> if the delete succeeded, or <c>false</c> /// if the delete failed. /// </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 void GenerateDelete(ILGenerator generator, OptimizationInfo optimizationInfo) { // 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); if (this.OperatorType == OperatorType.MemberAccess && rhs is NameExpression) { generator.LoadString((rhs as NameExpression).Name); } else { rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); } // Call Delete() generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_Delete); // If the return value is not wanted then pop it from the stack. //if (optimizationInfo.SuppressReturnValue == true) // generator.Pop(); }
/// <summary> /// Generates CIL for the expression. /// </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) { // This code is only used for untagged template literals. // Tagged template literals are handled by FunctionCallExpression. // Load the values array onto the stack. generator.LoadInt32(this.Strings.Count + this.Values.Count); generator.NewArray(typeof(string)); for (int i = 0; i < this.Strings.Count; i++) { // Operands for StoreArrayElement() are: an array (string[]), index (int), value (string). // Store the string. generator.Duplicate(); generator.LoadInt32(i * 2); generator.LoadString(this.Strings[i]); generator.StoreArrayElement(typeof(string)); if (i == this.Strings.Count - 1) { break; } // Store the value. generator.Duplicate(); generator.LoadInt32(i * 2 + 1); this.Values[i].GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, this.Values[i].ResultType); generator.StoreArrayElement(typeof(string)); } // Call String.Concat(string[]) generator.CallStatic(ReflectionHelpers.String_Concat); }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Generate code for the start of the statement. var statementLocals = new StatementLocals(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); if (this.ContributesToEvalResult == true && optimizationInfo.EvalResult != null) { // Emit the expression. this.Expression.GenerateCode(generator, optimizationInfo); // Store the result. EmitConversion.ToAny(generator, this.Expression.ResultType); generator.StoreVariable(optimizationInfo.EvalResult); } else { // Emit the expression. optimizationInfo.IgnoreReturnValue = this.Expression; optimizationInfo.ReturnValueWasNotGenerated = false; this.Expression.GenerateCode(generator, optimizationInfo); if (!optimizationInfo.ReturnValueWasNotGenerated) { generator.Pop(); } } // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <summary> /// Pops the value on the stack, converts it from one type to another, then pushes the /// result onto the stack. /// </summary> /// <param name="il"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> /// <param name="toType"> The type to convert to. </param> private static void EmitTypeConversion(ILGenerator il, Type fromType, Type toType) { // If the source type equals the destination type, then there is nothing to do. if (fromType == toType) { return; } // Emit for each type of argument we support. if (toType == typeof(int)) { EmitConversion.ToInteger(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType)); } else if (typeof(ObjectInstance).IsAssignableFrom(toType)) { EmitConversion.Convert(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType), PrimitiveType.Object); if (toType != typeof(ObjectInstance)) { // Convert to null if the from type isn't compatible with the to type. // For example, if the target type is FunctionInstance and the from type is ArrayInstance, then pass null. il.IsInstance(toType); } } else { EmitConversion.Convert(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType), PrimitiveTypeUtilities.ToPrimitiveType(toType)); } }
/// <summary> /// Generates CIL for the logical operators. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateLogical(ILGenerator generator, OptimizationInfo optimizationInfo) { // Get the statically-determined types of the left and right operands. PrimitiveType leftType = this.Left.ResultType; PrimitiveType rightType = this.Right.ResultType; // Load the left-hand side operand. this.Left.GenerateCode(generator, optimizationInfo); // Make sure the output type is consistant. if (leftType != rightType) { if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true) { EmitConversion.ToNumber(generator, leftType); leftType = PrimitiveType.Number; } else { EmitConversion.ToAny(generator, leftType); leftType = PrimitiveType.Any; } } // Duplicate and convert to a Boolean. generator.Duplicate(); EmitConversion.ToBool(generator, leftType); // Stack contains "left, (bool)left" var endOfIf = generator.CreateLabel(); if (this.OperatorType == OperatorType.LogicalAnd) { generator.BranchIfFalse(endOfIf); } else { generator.BranchIfTrue(endOfIf); } // Stack contains "left". Load the right-hand side operand. generator.Pop(); this.Right.GenerateCode(generator, optimizationInfo); // Make sure the output type is consistant. if (leftType != rightType) { if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true) { EmitConversion.ToNumber(generator, rightType); } else { EmitConversion.ToAny(generator, rightType); } } // Define the label used above. generator.DefineLabelPosition(endOfIf); }
/// <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> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType) { // TODO: Optimize this. // // ++ or -- // GenerateReference // DuplicateReference // GenerateGet // ToNumber // if (postfix) Dup/Store in variable // Increment/Decrement // if (prefix) Dup/Store in variable // GenerateSet // Load variable // // += // GenerateReference // DuplicateReference // GenerateGet // Dup/Store in variable // GenerateSet // Load variable // // for (in/of) // GenerateReference // LoadVariable(enumerator) // GenerateGet // GenerateSet // // = // GenerateReference // target.GenerateGet // Dup/Store in variable // GenerateSet // Load variable // If we have allocated an IL variable, use it. var variableInfo = Scope.FindStaticVariable(Name); if (variableInfo != null && variableInfo.Store != null) { EmitConversion.Convert(generator, valueType, variableInfo.Type); generator.StoreVariable(variableInfo.Store); return; } // Fallback: call RuntimeScope.SetValue() or RuntimeScope.SetValueStrict(). var temp = generator.CreateTemporaryVariable(valueType); generator.StoreVariable(temp); Scope.GenerateReference(generator, optimizationInfo); generator.LoadString(Name); generator.LoadVariable(temp); EmitConversion.ToAny(generator, valueType); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.Call(optimizationInfo.StrictMode ? ReflectionHelpers.RuntimeScope_SetValueStrict : ReflectionHelpers.RuntimeScope_SetValue); generator.ReleaseTemporaryVariable(temp); }
/// <summary> /// Generates an array containing the argument values for a tagged template literal. /// </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="templateLiteral"> The template literal expression containing the parameter /// values. </param> internal void GenerateTemplateArgumentsArray(ILGenerator generator, OptimizationInfo optimizationInfo, TemplateLiteralExpression templateLiteral) { // Generate an array containing the value of each argument. generator.LoadInt32(templateLiteral.Values.Count + 1); generator.NewArray(typeof(object)); // Load the first parameter. generator.Duplicate(); generator.LoadInt32(0); // The first parameter to the tag function is an array of strings. var stringsExpression = new List <Expression>(templateLiteral.Strings.Count); foreach (var templateString in templateLiteral.Strings) { stringsExpression.Add(new LiteralExpression(templateString)); } new LiteralExpression(stringsExpression).GenerateCode(generator, optimizationInfo); generator.Duplicate(); // Now we need the name of the property. generator.LoadString("raw"); // Now generate an array of raw strings. var rawStringsExpression = new List <Expression>(templateLiteral.RawStrings.Count); foreach (var rawString in templateLiteral.RawStrings) { rawStringsExpression.Add(new LiteralExpression(rawString)); } new LiteralExpression(rawStringsExpression).GenerateCode(generator, optimizationInfo); // Freeze array by calling ObjectInstance Freeze(ObjectInstance). generator.CallStatic(ReflectionHelpers.ObjectConstructor_Freeze); // Now store the raw strings as a property of the base strings array. generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); // Freeze array by calling ObjectInstance Freeze(ObjectInstance). generator.CallStatic(ReflectionHelpers.ObjectConstructor_Freeze); // Store in the array. generator.StoreArrayElement(typeof(object)); // Values are passed as subsequent parameters. for (int i = 0; i < templateLiteral.Values.Count; i++) { generator.Duplicate(); generator.LoadInt32(i + 1); templateLiteral.Values[i].GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, templateLiteral.Values[i].ResultType); generator.StoreArrayElement(typeof(object)); } }
/// <summary> /// Generates CIL for the in operator. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateIn(ILGenerator generator, OptimizationInfo optimizationInfo) { // Emit the left-hand side expression and convert it to a string. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, this.Left.ResultType); // Store the left-hand side expression in a temporary variable. var temp = generator.CreateTemporaryVariable(typeof(string)); generator.StoreVariable(temp); // Emit the right-hand side expression. this.Right.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Right.ResultType); // Check the right-hand side is a javascript object - if not, throw an exception. generator.Duplicate(); generator.IsInstance(typeof(ObjectInstance)); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfTrue(endOfTypeCheck); // Throw an nicely formatted exception. var rightValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(rightValue); generator.LoadEnumValue(ErrorType.TypeError); generator.LoadString("The in operator expected an object, but found '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(rightValue); generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); generator.StoreArrayElement(typeof(object)); generator.Call(ReflectionHelpers.String_Format); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(rightValue); generator.ReinterpretCast(typeof(ObjectInstance)); // Load the left-hand side expression from the temporary variable. generator.LoadVariable(temp); // Call ObjectInstance.HasProperty(object) generator.Call(ReflectionHelpers.ObjectInstance_HasProperty); // Allow the temporary variable to be reused. generator.ReleaseTemporaryVariable(temp); }
/// <summary> /// Generates code that initializes the variable and function declarations. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> internal virtual void GenerateDeclarations(ILGenerator generator, OptimizationInfo optimizationInfo) { // Initialize the declared variables and functions. foreach (var variable in this.variables.Values) { // Emit the initialization code. if (this is ObjectScope) { // Determine the property attributes. var attributes = Library.PropertyAttributes.Enumerable; if (variable.Writable == true) { attributes |= Library.PropertyAttributes.Writable; } if (variable.Deletable == true) { attributes |= Library.PropertyAttributes.Configurable; } if (variable.ValueAtTopOfScope == null) { // void InitializeMissingProperty(object key, PropertyAttributes attributes) EmitHelpers.LoadScope(generator); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(variable.Name); generator.LoadInt32((int)attributes); generator.Call(ReflectionHelpers.ObjectInstance_InitializeMissingProperty); } else { // bool DefineProperty(string propertyName, PropertyDescriptor descriptor, bool throwOnError) EmitHelpers.LoadScope(generator); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(variable.Name); variable.ValueAtTopOfScope.GenerateCode(generator, optimizationInfo); EmitConversion.Convert(generator, variable.ValueAtTopOfScope.ResultType, PrimitiveType.Any, optimizationInfo); generator.LoadInt32((int)attributes); generator.NewObject(ReflectionHelpers.PropertyDescriptor_Constructor2); generator.LoadBoolean(false); generator.Call(ReflectionHelpers.ObjectInstance_DefineProperty); generator.Pop(); } } else if (variable.ValueAtTopOfScope != null) { variable.ValueAtTopOfScope.GenerateCode(generator, optimizationInfo); var name = new NameExpression(this, variable.Name); name.GenerateSet(generator, optimizationInfo, variable.ValueAtTopOfScope.ResultType, false); } } }
/// <summary> /// Generates CIL for the instanceof operator. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateInstanceOf(ILGenerator generator, OptimizationInfo optimizationInfo) { // Emit the left-hand side expression and convert it to an object. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Left.ResultType); // Store the left-hand side expression in a temporary variable. var temp = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(temp); // Emit the right-hand side expression. this.Right.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Right.ResultType); // Check the right-hand side is a function - if not, throw an exception. generator.IsInstance(typeof(Library.FunctionInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. var rightValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(rightValue); EmitHelpers.LoadScriptEngine(generator); generator.LoadString("TypeError"); generator.LoadString("The instanceof operator expected a function, but found '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(rightValue); generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); generator.StoreArrayElement(typeof(object)); generator.Call(ReflectionHelpers.String_Format); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(rightValue); // Load the left-hand side expression from the temporary variable. generator.LoadVariable(temp); // Call FunctionInstance.HasInstance(object) generator.Call(ReflectionHelpers.FunctionInstance_HasInstance); // Allow the temporary variable to be reused. generator.ReleaseTemporaryVariable(temp); }
/// <summary> /// Generates CIL for an increment or decrement expression. /// </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="target"> The target to modify. </param> /// <param name="postfix"> <c>true</c> if this is the postfix version of the operator; /// <c>false</c> otherwise. </param> /// <param name="increment"> <c>true</c> if this is the increment operator; <c>false</c> if /// this is the decrement operator. </param> private void GenerateIncrementOrDecrement(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target, bool postfix, bool increment) { // Note: increment and decrement can produce a number that is out of range if the // target is of type Int32. The only time this should happen is for a loop variable // where the range has been carefully checked to make sure an out of range condition // cannot happen. // Get the target value. target.GenerateGet(generator, optimizationInfo, true); // Convert it to a number. if (target.Type != PrimitiveType.Int32) { EmitConversion.ToNumber(generator, target.Type); } // If this is PostIncrement or PostDecrement, duplicate the value so it can be produced as the return value. if (postfix == true) { generator.Duplicate(); } // Load the increment constant. if (target.Type == PrimitiveType.Int32) { generator.LoadInt32(1); } else { generator.LoadDouble(1.0); } // Add or subtract the constant to the target value. if (increment == true) { generator.Add(); } else { generator.Subtract(); } // If this is PreIncrement or PreDecrement, duplicate the value so it can be produced as the return value. if (postfix == false) { generator.Duplicate(); } // Store the value. target.GenerateSet(generator, optimizationInfo, target.Type == PrimitiveType.Int32 ? PrimitiveType.Int32 : PrimitiveType.Number, optimizationInfo.StrictMode); }
/// <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 an array containing the argument values. /// </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 GenerateArgumentsArray(ILGenerator generator, OptimizationInfo optimizationInfo) { // Emit the arguments. The arguments operand can be non-existant, a single expression, // or a comma-delimited list. if (this.OperandCount < 2) { // No parameters passed. Create an empty array. generator.LoadInt32(0); generator.NewArray(typeof(object)); } else { // One or more arguments. IList <Expression> arguments; var argumentsOperand = this.GetRawOperand(1); if (argumentsOperand is ListExpression) { // Multiple parameters were passed to the function. arguments = ((ListExpression)argumentsOperand).Items; } else if (argumentsOperand is TemplateLiteralExpression) { // Tagged template literal. var templateLiteral = (TemplateLiteralExpression)argumentsOperand; GenerateTemplateArgumentsArray(generator, optimizationInfo, templateLiteral); return; } else { // A single parameter was passed to the function. arguments = new List <Expression>(1) { argumentsOperand }; } // Generate an array containing the value of each argument. generator.LoadInt32(arguments.Count); generator.NewArray(typeof(object)); for (int i = 0; i < arguments.Count; i++) { generator.Duplicate(); generator.LoadInt32(i); arguments[i].GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, arguments[i].ResultType); generator.StoreArrayElement(typeof(object)); } } }
/// <summary> /// Generates CIL for the expression. /// </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) { // If a return value is not expected, generate only the side-effects. /*if (optimizationInfo.SuppressReturnValue == true) * { * this.GenerateSideEffects(generator, optimizationInfo); * return; * }*/ // Emit the condition. var condition = this.GetOperand(0); condition.GenerateCode(generator, optimizationInfo); // Convert the condition to a boolean. EmitConversion.ToBool(generator, condition.ResultType); // Branch if the condition is false. var startOfElse = generator.CreateLabel(); generator.BranchIfFalse(startOfElse); // Calculate the result type. var outputType = this.ResultType; // Emit the second operand and convert it to the result type. var operand2 = this.GetOperand(1); operand2.GenerateCode(generator, optimizationInfo); EmitConversion.Convert(generator, operand2.ResultType, outputType, optimizationInfo); // Branch to the end. var end = generator.CreateLabel(); generator.Branch(end); generator.DefineLabelPosition(startOfElse); // Emit the third operand and convert it to the result type. var operand3 = this.GetOperand(2); operand3.GenerateCode(generator, optimizationInfo); EmitConversion.Convert(generator, operand3.ResultType, outputType, optimizationInfo); // Define the end label. generator.DefineLabelPosition(end); }
/// <summary> /// Outputs the values needed to get or set this 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> public void GenerateReference(ILGenerator generator, OptimizationInfo optimizationInfo) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // 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); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property access (e.g. x = y.property or x = y['property']) // ---------------------------------------------------------------- // 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); } else { // Dynamic property access // ----------------------- // 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 value and convert it to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); } }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Generate code for the start of the statement. var statementLocals = new StatementLocals(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Emit code to throw the given value. this.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Value.ResultType); generator.LoadInt32(0); generator.LoadNull(); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Object); generator.Throw(); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <summary> /// Deletes the reference and pushes <c>true</c> if the delete succeeded, or <c>false</c> /// if the delete failed. /// </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 void GenerateDelete(ILGenerator generator, OptimizationInfo optimizationInfo) { // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); if (lhs is SuperExpression) { // Deleting a super reference is not allowed. EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, "Unsupported reference to 'super'."); generator.LoadNull(); // Extraneous, but helps with verification. return; } lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the property name and convert to a string. var rhs = this.GetOperand(1); if (this.OperatorType == OperatorType.MemberAccess) { // delete a.b if (rhs is NameExpression nameExpession) { generator.LoadString(nameExpession.Name); } else { throw new SyntaxErrorException("Invalid member access", optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); } } else { // delete a['1'] rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToPropertyKey(generator, rhs.ResultType); } // Call Delete() generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_Delete); // If the return value is not wanted then pop it 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(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Generate code for the condition and coerce to a boolean. this.Condition.GenerateCode(generator, optimizationInfo); EmitConversion.ToBool(generator, this.Condition.ResultType); // We will need a label at the end of the if statement. var endOfEverything = generator.CreateLabel(); if (this.ElseClause == null) { // Jump to the end if the condition is false. generator.BranchIfFalse(endOfEverything); // Generate code for the if clause. this.IfClause.GenerateCode(generator, optimizationInfo); } else { // Branch to the else clause if the condition is false. var startOfElseClause = generator.CreateLabel(); generator.BranchIfFalse(startOfElseClause); // Generate code for the if clause. this.IfClause.GenerateCode(generator, optimizationInfo); // Branch to the end of the if statement. generator.Branch(endOfEverything); // Generate code for the else clause. generator.DefineLabelPosition(startOfElseClause); this.ElseClause.GenerateCode(generator, optimizationInfo); } // Define the label at the end of the if statement. generator.DefineLabelPosition(endOfEverything); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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 override void GenerateScopeCreation(ILGenerator generator, OptimizationInfo optimizationInfo) { // Create a new runtime object scope. EmitHelpers.LoadScope(generator); // parent scope if (this.ScopeObjectExpression == null) { EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Global); } else { this.ScopeObjectExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, this.ScopeObjectExpression.ResultType, optimizationInfo); } generator.LoadBoolean(this.ProvidesImplicitThisValue); generator.LoadBoolean(this.CanDeclareVariables); generator.Call(ReflectionHelpers.ObjectScope_CreateRuntimeScope); // Save the new scope. EmitHelpers.StoreScope(generator); }
/// <summary> /// Generates CIL for the expression. /// </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) { // Operands for ArrayConstructor.New() are: an ArrayConstructor instance (ArrayConstructor), an array (object[]) // ArrayConstructor EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Array); // object[] generator.LoadInt32(Items.Count); generator.NewArray(typeof(object)); for (int i = 0; i < Items.Count; i++) { // Operands for StoreArrayElement() are: an array (object[]), index (int), value (object). // Array generator.Duplicate(); // Index generator.LoadInt32(i); // Value var elementExpression = Items[i]; if (elementExpression == null) { generator.LoadNull(); } else { elementExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, elementExpression.ResultType); } // Store the element value. generator.StoreArrayElement(typeof(object)); } // ArrayConstructor.New(object[]) generator.Call(ReflectionHelpers.Array_New); }
/// <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) { // Method signature: object FunctionDelegate(Compiler.Scope scope, object thisObject, Library.FunctionInstance functionObject, object[] arguments) // Initialize the scope (note: the initial scope for a function is always declarative). this.InitialScope.GenerateScopeCreation(generator, optimizationInfo); // Verify the scope is correct. VerifyScope(generator); // In ES3 the "this" value must be an object. See 10.4.3 in the spec. if (this.StrictMode == false && this.MethodOptimizationHints.HasThis == true) { // if (thisObject == null || thisObject == Null.Value || thisObject == Undefined.Value) EmitHelpers.LoadThis(generator); generator.LoadNull(); generator.CompareEqual(); EmitHelpers.LoadThis(generator); EmitHelpers.EmitNull(generator); generator.CompareEqual(); generator.BitwiseOr(); EmitHelpers.LoadThis(generator); EmitHelpers.EmitUndefined(generator); generator.CompareEqual(); generator.BitwiseOr(); // { var startOfFalse = generator.CreateLabel(); generator.BranchIfFalse(startOfFalse); // thisObject = engine.Global; EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Global); // } else { var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); generator.DefineLabelPosition(startOfFalse); // thisObject = TypeConverter.ToObject(thisObject); EmitHelpers.LoadThis(generator); EmitConversion.ToObject(generator, PrimitiveType.Any, optimizationInfo); // } generator.DefineLabelPosition(endOfIf); EmitHelpers.StoreThis(generator); } // Transfer the function name into the scope. if (string.IsNullOrEmpty(this.Name) == false && this.IncludeNameInScope == true && this.ArgumentNames.Contains(this.Name) == false && optimizationInfo.MethodOptimizationHints.HasVariable(this.Name)) { EmitHelpers.LoadFunction(generator); var functionName = new NameExpression(this.InitialScope, this.Name); functionName.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } // Transfer the arguments object into the scope. if (this.MethodOptimizationHints.HasArguments == true && this.ArgumentNames.Contains("arguments") == false) { // prototype EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Object); generator.Call(ReflectionHelpers.FunctionInstance_InstancePrototype); // callee EmitHelpers.LoadFunction(generator); generator.CastClass(typeof(Library.UserDefinedFunction)); // scope EmitHelpers.LoadScope(generator); generator.CastClass(typeof(DeclarativeScope)); // argumentValues EmitHelpers.LoadArgumentsArray(generator); generator.NewObject(ReflectionHelpers.Arguments_Constructor); var arguments = new NameExpression(this.InitialScope, "arguments"); arguments.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } // Transfer the argument values into the scope. // Note: the arguments array can be smaller than expected. if (this.ArgumentNames.Count > 0) { var endOfArguments = generator.CreateLabel(); for (int i = 0; i < this.ArgumentNames.Count; i++) { // Check if a duplicate argument name exists. bool duplicate = false; for (int j = i + 1; j < this.ArgumentNames.Count; j++) { if (this.ArgumentNames[i] == this.ArgumentNames[j]) { duplicate = true; break; } } if (duplicate == true) { continue; } // Check if an array element exists. EmitHelpers.LoadArgumentsArray(generator); generator.LoadArrayLength(); generator.LoadInt32(i); generator.BranchIfLessThanOrEqual(endOfArguments); // Store the array element in the scope. EmitHelpers.LoadArgumentsArray(generator); generator.LoadInt32(i); generator.LoadArrayElement(typeof(object)); var argument = new NameExpression(this.InitialScope, this.ArgumentNames[i]); argument.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } generator.DefineLabelPosition(endOfArguments); } // Initialize any declarations. this.InitialScope.GenerateDeclarations(generator, optimizationInfo); //EmitHelpers.LoadScope(generator); //EmitConversion.ToObject(generator, PrimitiveType.Any); //generator.Pop(); // Generate code for the body of the function. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Define the return target - this is where the return statement jumps to. // ReturnTarget can be null if there were no return statements. if (optimizationInfo.ReturnTarget != null) { generator.DefineLabelPosition(optimizationInfo.ReturnTarget); } // Load the return value. If the variable is null, there were no return statements. if (optimizationInfo.ReturnVariable != null) { // Return the value stored in the variable. Will be null if execution hits the end // of the function without encountering any return statements. generator.LoadVariable(optimizationInfo.ReturnVariable); } else { // There were no return statements - return null. generator.LoadNull(); } }
/// <summary> /// Generates CIL for the relational operators. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateRelational(ILGenerator generator, OptimizationInfo optimizationInfo) { // Get the statically-determined types of the left and right operands. PrimitiveType leftType = this.Left.ResultType; PrimitiveType rightType = this.Right.ResultType; // The relational operators compare strings if both of the operands are strings. if (leftType == PrimitiveType.String && rightType == PrimitiveType.String) { // Both of the operands are strings. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); // Compare the two strings. generator.Call(ReflectionHelpers.String_CompareOrdinal); switch (this.OperatorType) { case OperatorType.LessThan: generator.LoadInt32(0); generator.CompareLessThan(); break; case OperatorType.LessThanOrEqual: generator.LoadInt32(1); generator.CompareLessThan(); break; case OperatorType.GreaterThan: generator.LoadInt32(0); generator.CompareGreaterThan(); break; case OperatorType.GreaterThanOrEqual: generator.LoadInt32(-1); generator.CompareGreaterThan(); break; } } else if (leftType == PrimitiveType.Int32 && rightType == PrimitiveType.Int32) { // Both of the operands are integers. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); // Compare the two numbers. switch (this.OperatorType) { case OperatorType.LessThan: generator.CompareLessThan(); break; case OperatorType.GreaterThan: generator.CompareGreaterThan(); break; case OperatorType.LessThanOrEqual: // a <= b <--> (a > b) == false generator.CompareGreaterThan(); generator.LoadBoolean(false); generator.CompareEqual(); break; case OperatorType.GreaterThanOrEqual: // a >= b <--> (a < b) == false generator.CompareLessThan(); generator.LoadBoolean(false); generator.CompareEqual(); break; } } else if (PrimitiveTypeUtilities.IsNumeric(leftType) || PrimitiveTypeUtilities.IsNumeric(rightType)) { // At least one of the operands is a number. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Convert the operand to a number. EmitConversion.ToNumber(generator, leftType); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); // Convert the operand to a number. EmitConversion.ToNumber(generator, rightType); // Compare the two numbers. switch (this.OperatorType) { case OperatorType.LessThan: generator.CompareLessThan(); break; case OperatorType.GreaterThan: generator.CompareGreaterThan(); break; case OperatorType.LessThanOrEqual: // a <= b <--> (a > b) == false generator.CompareGreaterThanUnsigned(); generator.LoadBoolean(false); generator.CompareEqual(); break; case OperatorType.GreaterThanOrEqual: // a >= b <--> (a < b) == false generator.CompareLessThanUnsigned(); generator.LoadBoolean(false); generator.CompareEqual(); break; } } else { // It is unknown whether one of the operands is a string. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, leftType); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, rightType); switch (this.OperatorType) { case OperatorType.LessThan: generator.Call(ReflectionHelpers.TypeComparer_LessThan); break; case OperatorType.LessThanOrEqual: generator.Call(ReflectionHelpers.TypeComparer_LessThanOrEqual); break; case OperatorType.GreaterThan: generator.Call(ReflectionHelpers.TypeComparer_GreaterThan); break; case OperatorType.GreaterThanOrEqual: generator.Call(ReflectionHelpers.TypeComparer_GreaterThanOrEqual); break; } } }
/// <summary> /// Generates CIL for the expression. /// </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) { // Check if this is a direct call to eval(). if (this.Target is NameExpression && ((NameExpression)this.Target).Name == "eval") { GenerateEval(generator, optimizationInfo); return; } // Emit the function instance first. ILLocalVariable targetBase = null; if (this.Target is MemberAccessExpression) { // The function is a member access expression (e.g. "Math.cos()"). // Evaluate the left part of the member access expression. var baseExpression = ((MemberAccessExpression)this.Target).Base; baseExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, baseExpression.ResultType); targetBase = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(targetBase); // Evaluate the right part of the member access expression. var memberAccessExpression = new MemberAccessExpression(((MemberAccessExpression)this.Target).Operator); memberAccessExpression.Push(new TemporaryVariableExpression(targetBase)); memberAccessExpression.Push(((MemberAccessExpression)this.Target).GetOperand(1)); memberAccessExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } else { // Something else (e.g. "eval()"). this.Target.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } // Check the object really is a function - if not, throw an exception. generator.IsInstance(typeof(Library.FunctionInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. generator.Pop(); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("'{0}' is not a function", this.Target.ToString())); generator.DefineLabelPosition(endOfTypeCheck); // Generate code to produce the "this" value. There are three cases. if (this.Target is NameExpression) { // 1. The function is a name expression (e.g. "parseInt()"). // In this case this = scope.ImplicitThisValue, if there is one, otherwise undefined. ((NameExpression)this.Target).GenerateThis(generator); } else if (this.Target is MemberAccessExpression) { // 2. The function is a member access expression (e.g. "Math.cos()"). // In this case this = Math. //var baseExpression = ((MemberAccessExpression)this.Target).Base; //baseExpression.GenerateCode(generator, optimizationInfo); //EmitConversion.ToAny(generator, baseExpression.ResultType); generator.LoadVariable(targetBase); } else { // 3. Neither of the above (e.g. "(function() { return 5 })()") // In this case this = undefined. EmitHelpers.EmitUndefined(generator); } // Emit an array containing the function arguments. GenerateArgumentsArray(generator, optimizationInfo); // Call FunctionInstance.CallLateBound(thisValue, argumentValues) generator.Call(ReflectionHelpers.FunctionInstance_CallLateBound); // Allow reuse of the temporary variable. if (targetBase != null) { generator.ReleaseTemporaryVariable(targetBase); } }
/// <summary> /// Generates CIL for the expression. /// </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) { // Literals cannot have side-effects so if a return value is not expected then generate // nothing. //if (optimizationInfo.SuppressReturnValue == true) // return; if (this.Value is int) { generator.LoadInt32((int)this.Value); } else if (this.Value is double) { generator.LoadDouble((double)this.Value); } else if (this.Value is string) { generator.LoadString((string)this.Value); } else if (this.Value is bool) { generator.LoadBoolean((bool)this.Value); } else if (this.Value is RegularExpressionLiteral) { // RegExp var sharedRegExpVariable = optimizationInfo.GetRegExpVariable(generator, (RegularExpressionLiteral)this.Value); var label1 = generator.CreateLabel(); var label2 = generator.CreateLabel(); // if (sharedRegExp == null) { generator.LoadVariable(sharedRegExpVariable); generator.LoadNull(); generator.BranchIfNotEqual(label1); // sharedRegExp = Global.RegExp.Construct(source, flags) EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_RegExp); generator.LoadString(((RegularExpressionLiteral)this.Value).Pattern); generator.LoadString(((RegularExpressionLiteral)this.Value).Flags); generator.Call(ReflectionHelpers.RegExp_Construct); generator.Duplicate(); generator.StoreVariable(sharedRegExpVariable); // } else { generator.Branch(label2); generator.DefineLabelPosition(label1); // Global.RegExp.Construct(sharedRegExp, flags) EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_RegExp); generator.LoadVariable(sharedRegExpVariable); generator.LoadNull(); generator.Call(ReflectionHelpers.RegExp_Construct); // } generator.DefineLabelPosition(label2); } else if (this.Value == Null.Value) { // Null. EmitHelpers.EmitNull(generator); } else if (this.Value == Undefined.Value) { // Undefined. EmitHelpers.EmitUndefined(generator); } else if (this.Value is List <Expression> ) { // Construct an array literal. var arrayLiteral = (List <Expression>) this.Value; // Operands for ArrayConstructor.New() are: an ArrayConstructor instance (ArrayConstructor), an array (object[]) // ArrayConstructor EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Array); // object[] generator.LoadInt32(arrayLiteral.Count); generator.NewArray(typeof(object)); for (int i = 0; i < arrayLiteral.Count; i++) { // Operands for StoreArrayElement() are: an array (object[]), index (int), value (object). // Array generator.Duplicate(); // Index generator.LoadInt32(i); // Value var elementExpression = arrayLiteral[i]; if (elementExpression == null) { generator.LoadNull(); } else { elementExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, elementExpression.ResultType); } // Store the element value. generator.StoreArrayElement(typeof(object)); } // ArrayConstructor.New(object[]) generator.Call(ReflectionHelpers.Array_New); } else if (this.Value is List <KeyValuePair <Expression, Expression> > ) { // This is an object literal. var properties = (List <KeyValuePair <Expression, Expression> >) this.Value; // Create a new object. EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Object); generator.Call(ReflectionHelpers.Object_Construct); foreach (var keyValuePair in properties) { Expression propertyName = keyValuePair.Key; Expression propertyValue = keyValuePair.Value; generator.Duplicate(); // The key can be a property name or an expression that evaluates to a name. propertyName.GenerateCode(generator, optimizationInfo); EmitConversion.ToPropertyKey(generator, propertyName.ResultType); var functionValue = propertyValue as FunctionExpression; if (functionValue != null && functionValue.DeclarationType == FunctionDeclarationType.Getter) { // Add a getter to the object. functionValue.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (propertyName is LiteralExpression && ((LiteralExpression)propertyName).Value is string) { functionValue.GenerateDisplayName(generator, optimizationInfo, "get " + (string)((LiteralExpression)propertyName).Value, true); } generator.Call(ReflectionHelpers.ReflectionHelpers_SetObjectLiteralGetter); } else if (functionValue != null && functionValue.DeclarationType == FunctionDeclarationType.Setter) { // Add a setter to the object. functionValue.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (propertyName is LiteralExpression && ((LiteralExpression)propertyName).Value is string) { functionValue.GenerateDisplayName(generator, optimizationInfo, "set " + (string)((LiteralExpression)propertyName).Value, true); } generator.Call(ReflectionHelpers.ReflectionHelpers_SetObjectLiteralSetter); } else { // Add a new property to the object. propertyValue.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (propertyValue is FunctionExpression && propertyName is LiteralExpression && ((LiteralExpression)propertyName).Value is string) { ((FunctionExpression)propertyValue).GenerateDisplayName(generator, optimizationInfo, (string)((LiteralExpression)propertyName).Value, false); } EmitConversion.ToAny(generator, propertyValue.ResultType); generator.Call(ReflectionHelpers.ReflectionHelpers_SetObjectLiteralValue); } } } else { throw new NotImplementedException("Unknown literal type."); } }
/// <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) { // Method signature: object FunctionDelegate(Compiler.Scope scope, object thisObject, Library.FunctionInstance functionObject, object[] arguments) // Initialize the scope (note: the initial scope for a function is always declarative). this.BaseScope.GenerateScopeCreation(generator, optimizationInfo); // In ES3 the "this" value must be an object. See 10.4.3 in the spec. if (this.StrictMode == false && this.MethodOptimizationHints.HasThis == true) { // context.ConvertThisToObject(); EmitHelpers.LoadExecutionContext(generator); generator.Call(ReflectionHelpers.ExecutionContext_ConvertThisToObject); } // Transfer the function name into the scope. if (Name.HasStaticName && !Name.IsGetter && !Name.IsSetter && this.Arguments.Any(a => a.Name == Name.StaticName) == false && optimizationInfo.MethodOptimizationHints.HasVariable(Name.StaticName)) { EmitHelpers.LoadFunction(generator); var functionName = new NameExpression(this.BaseScope, Name.StaticName); functionName.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); } // Transfer the arguments object into the scope. if (this.MethodOptimizationHints.HasArguments == true && this.Arguments.Any(a => a.Name == "arguments") == false) { // executionContext.CreateArgumentsInstance(object[] arguments) EmitHelpers.LoadExecutionContext(generator); this.BaseScope.GenerateReference(generator, optimizationInfo); EmitHelpers.LoadArgumentsArray(generator); generator.Call(ReflectionHelpers.ExecutionContext_CreateArgumentsInstance); var arguments = new NameExpression(this.BaseScope, "arguments"); arguments.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); } // Transfer the argument values into the scope. // Note: the arguments array can be smaller than expected. if (this.Arguments.Count > 0) { for (int i = 0; i < this.Arguments.Count; i++) { // Check if a duplicate argument name exists. bool duplicate = false; for (int j = i + 1; j < this.Arguments.Count; j++) { if (this.Arguments[i].Name == this.Arguments[j].Name) { duplicate = true; break; } } if (duplicate == true) { continue; } var loadDefaultValue = generator.CreateLabel(); var storeValue = generator.CreateLabel(); // Check if an array element exists. EmitHelpers.LoadArgumentsArray(generator); generator.LoadArrayLength(); generator.LoadInt32(i); generator.BranchIfLessThanOrEqual(loadDefaultValue); // Load the parameter value from the parameters array. EmitHelpers.LoadArgumentsArray(generator); generator.LoadInt32(i); generator.LoadArrayElement(typeof(object)); if (this.Arguments[i].DefaultValue == null) { // Branch to the part where it stores the value. generator.Branch(storeValue); // Load undefined. generator.DefineLabelPosition(loadDefaultValue); EmitHelpers.EmitUndefined(generator); generator.ReinterpretCast(typeof(object)); } else { // Check if it's undefined. generator.Duplicate(); EmitHelpers.EmitUndefined(generator); generator.ReinterpretCast(typeof(object)); generator.BranchIfNotEqual(storeValue); generator.Pop(); // Load the default value. generator.DefineLabelPosition(loadDefaultValue); this.Arguments[i].DefaultValue.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Arguments[i].DefaultValue.ResultType); } // Store the value in the scope. generator.DefineLabelPosition(storeValue); var argument = new NameExpression(this.BaseScope, this.Arguments[i].Name); argument.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); } } // Initialize any declarations. this.BaseScope.GenerateHoistedDeclarations(generator, optimizationInfo); // Generate code for the body of the function. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Define the return target - this is where the return statement jumps to. // ReturnTarget can be null if there were no return statements. if (optimizationInfo.ReturnTarget != null) { generator.DefineLabelPosition(optimizationInfo.ReturnTarget); } // Load the return value. If the variable is null, there were no return statements. if (optimizationInfo.ReturnVariable != null) { // Return the value stored in the variable. Will be null if execution hits the end // of the function without encountering any return statements. generator.LoadVariable(optimizationInfo.ReturnVariable); } else { // There were no return statements - return null. generator.LoadNull(); } }
/// <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> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Generate code for the start of the statement. var statementLocals = new StatementLocals() { NonDefaultBreakStatementBehavior = true }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // We need a label for each case clause and one for the default case. var jumpTargets = new ILLabel[this.CaseClauses.Count]; int defaultIndex = -1; ILLabel endOfSwitch = generator.CreateLabel(); // Generate code for the switch value. var startOfSwitch = generator.CreateLabel(); this.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Value.ResultType); // Save the switch value in a variable. var switchValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(switchValue); for (int i = 0; i < this.CaseClauses.Count; i++) { var caseClause = this.CaseClauses[i]; // Create a label for each clause. jumpTargets[i] = generator.CreateLabel(); if (caseClause.Value == null) { // This is a default clause. defaultIndex = i; continue; } // TypeComparer.StrictEquals(switchValue, caseValue) generator.LoadVariable(switchValue); caseClause.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, caseClause.Value.ResultType); generator.Call(ReflectionHelpers.TypeComparer_StrictEquals); // if (TypeComparer.StrictEquals(switchValue, caseValue) == true) // goto case i generator.BranchIfTrue(jumpTargets[i]); } // None of the cases matched, jump to the default clause or the end of the switch. if (defaultIndex >= 0) { generator.Branch(jumpTargets[defaultIndex]); } else { generator.Branch(endOfSwitch); } for (int i = 0; i < this.CaseClauses.Count; i++) { // Define a label at the start of the case clause. generator.DefineLabelPosition(jumpTargets[i]); // Set up the information needed by the break statement. optimizationInfo.PushBreakOrContinueInfo(this.Labels, endOfSwitch, null, labelledOnly: false); // Emit the case clause statements. foreach (var statement in this.CaseClauses[i].BodyStatements) { statement.GenerateCode(generator, optimizationInfo); } // Revert the information needed by the break statement. optimizationInfo.PopBreakOrContinueInfo(); } // Define a label for the end of the switch statement. generator.DefineLabelPosition(endOfSwitch); // Release the switch value variable for use elsewhere. generator.ReleaseTemporaryVariable(switchValue); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <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); }