/// <summary> /// Pops the value on the stack, converts it from one type to another, then pushes the /// result onto the stack. Undefined is converted to the given default value. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> /// <param name="targetParameter"> The type to convert to and the default value, if there is one. </param> private static void EmitTypeConversion(ILGenerator generator, Type fromType, BinderArgument argument) { // Emit either the default value if there is one, otherwise emit "undefined". if (argument.HasDefaultValue) { // Check if the input value is undefined. var elseClause = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNull(elseClause); generator.Duplicate(); generator.LoadField(ReflectionHelpers.Undefined_Value); generator.CompareEqual(); generator.BranchIfTrue(elseClause); // Convert as per normal. EmitTypeConversion(generator, fromType, argument.Type); // Jump to the end. var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); generator.DefineLabelPosition(elseClause); // Pop the existing value and emit the default value. generator.Pop(); EmitUndefined(generator, argument); // Define the end of the block. generator.DefineLabelPosition(endOfIf); } else { // Convert as per normal. EmitTypeConversion(generator, fromType, argument.Type); } }
/// <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> /// 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_Indexer_UInt); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property access (e.g. x = y.property) // ------------------------------------------- // Use a PropertyReference to speed up access if we are inside a loop. if (optimizationInfo.InsideLoop) { // C# // if (propertyReference != null) // propertyReference = new PropertyReference("property"); // value = object.GetPropertyValue(propertyReference) ILLocalVariable propertyReference = optimizationInfo.GetPropertyReferenceVariable(generator, propertyName); generator.LoadVariable(propertyReference); generator.Duplicate(); var afterIf = generator.CreateLabel(); generator.BranchIfNotNull(afterIf); generator.Pop(); generator.LoadString(propertyName); generator.NewObject(ReflectionHelpers.PropertyName_Constructor); generator.Duplicate(); generator.StoreVariable(propertyReference); generator.DefineLabelPosition(afterIf); generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_PropertyReference); } else { // C# // value = object.GetPropertyValue("property") generator.LoadString(propertyName); generator.Call(ReflectionHelpers.ObjectInstance_Indexer_Object); } } else { // Dynamic property access // ----------------------- // x = y.GetPropertyValue("property") generator.Call(ReflectionHelpers.ObjectInstance_Indexer_Object); } }
/// <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 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 a compound assignment 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> private void GenerateCompoundAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Evaluate the left hand side only once. target.GenerateReference(generator, optimizationInfo); target.DuplicateReference(generator, optimizationInfo); // For the GenerateSet, later on. // Load the value to assign. var compoundOperator = new BinaryExpression(GetCompoundBaseOperator(this.OperatorType), new ReferenceGetExpression(target), this.GetOperand(1)); compoundOperator.GenerateCode(generator, optimizationInfo); ILLocalVariable result = null; if (optimizationInfo.IgnoreReturnValue != this) { // Store the resulting value so we can return it as the result of the expression. result = generator.CreateTemporaryVariable(compoundOperator.ResultType); generator.Duplicate(); generator.StoreVariable(result); } // Store the value. target.GenerateSet(generator, optimizationInfo, compoundOperator.ResultType); if (optimizationInfo.IgnoreReturnValue != this) { // Restore the expression result. generator.LoadVariable(result); generator.ReleaseTemporaryVariable(result); } else { optimizationInfo.ReturnValueWasNotGenerated = true; } }
/// <summary> /// Generates CIL for an assignment 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> private void GenerateAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Evaluate the left hand side first! target.GenerateReference(generator, optimizationInfo); // Load the value to assign. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); ILLocalVariable result = null; if (optimizationInfo.IgnoreReturnValue != this) { // Store the RHS value so we can return it as the result of the expression. result = generator.CreateTemporaryVariable(rhs.ResultType); generator.Duplicate(); generator.StoreVariable(result); } // Store the value. target.GenerateSet(generator, optimizationInfo, rhs.ResultType); if (optimizationInfo.IgnoreReturnValue != this) { // Restore the RHS value. generator.LoadVariable(result); generator.ReleaseTemporaryVariable(result); } else { optimizationInfo.ReturnValueWasNotGenerated = true; } }
/// <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> /// 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) { // If we have allocated an IL variable, use it. var variableInfo = Scope.FindStaticVariable(Name); if (variableInfo != null && variableInfo.Store != null) { generator.LoadVariable(variableInfo.Store); if (variableInfo.Keyword != KeywordToken.Var) { var afterIf = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(afterIf); EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, $"Cannot access '{Name}' before initialization."); generator.DefineLabelPosition(afterIf); } return; } // Fallback: call RuntimeScope.GetValue() or RuntimeScope.GetValueNoThrow(). Scope.GenerateReference(generator, optimizationInfo); generator.LoadString(Name); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.Call(throwIfUnresolvable ? ReflectionHelpers.RuntimeScope_GetValue : ReflectionHelpers.RuntimeScope_GetValueNoThrow); }
/// <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> /// Generates CIL for an assignment 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> private void GenerateAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Evaluate the left hand side first! target.GenerateReference(generator, optimizationInfo); // Load the value to assign. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (rhs is FunctionExpression) { ((FunctionExpression)rhs).GenerateDisplayName(generator, optimizationInfo, target.ToString(), false); } // Store the RHS value so we can return it as the result of the expression. var result = generator.CreateTemporaryVariable(rhs.ResultType); generator.Duplicate(); generator.StoreVariable(result); // Store the value. target.GenerateSet(generator, optimizationInfo, rhs.ResultType, optimizationInfo.StrictMode); // Restore the RHS value. generator.LoadVariable(result); generator.ReleaseTemporaryVariable(result); }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Generate code for the start of the statement. var statementLocals = new StatementLocals(); GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Create the scope instance. WithScope.GenerateScopeCreation(generator, optimizationInfo); WithScope.GenerateHoistedDeclarations(generator, optimizationInfo); // Bind the scope to the with() object. WithScope.GenerateReference(generator, optimizationInfo); WithObject.GenerateCode(generator, optimizationInfo); ILLocalVariable withObject = generator.CreateTemporaryVariable(typeof(object)); generator.Duplicate(); generator.StoreVariable(withObject); generator.Call(ReflectionHelpers.RuntimeScope_With); // Generate code for the body statements. this.Body.GenerateCode(generator, optimizationInfo); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <summary> /// Generates CIL for a compound assignment 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> private void GenerateCompoundAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Load the value to assign. var compoundOperator = new BinaryExpression(GetCompoundBaseOperator(this.OperatorType), this.GetOperand(0), this.GetOperand(1)); compoundOperator.GenerateCode(generator, optimizationInfo); // Duplicate the value so it remains on the stack afterwards. //if (optimizationInfo.SuppressReturnValue == false) generator.Duplicate(); // Store the value. target.GenerateSet(generator, optimizationInfo, compoundOperator.ResultType, optimizationInfo.StrictMode); }
/// <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 an assignment 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> private void GenerateAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Load the value to assign. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (rhs is FunctionExpression) { ((FunctionExpression)rhs).GenerateDisplayName(generator, optimizationInfo, target.ToString(), false); } // Duplicate the value so it remains on the stack afterwards. //if (optimizationInfo.SuppressReturnValue == false) generator.Duplicate(); // Store the value. target.GenerateSet(generator, optimizationInfo, rhs.ResultType, optimizationInfo.StrictMode); }
/// <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) { // Allocate storage for each variable if the declarative scope object has been optimized away. if (optimizationInfo.OptimizeDeclarativeScopes == false) { // Create a new declarative scope. // parentScope EmitHelpers.LoadScope(generator); // declaredVariableNames generator.LoadInt32(this.DeclaredVariableCount); generator.NewArray(typeof(string)); int i = 0; foreach (string variableName in this.DeclaredVariableNames) { generator.Duplicate(); generator.LoadInt32(i++); generator.LoadString(variableName); generator.StoreArrayElement(typeof(string)); } // DeclarativeScope.CreateRuntimeScope(parentScope, declaredVariableNames) generator.Call(ReflectionHelpers.DeclarativeScope_CreateRuntimeScope); // Save the new scope. EmitHelpers.StoreScope(generator); } else { // The declarative scope can be optimized away entirely. foreach (var variable in this.DeclaredVariables) { variable.Store = null; variable.Type = PrimitiveType.Any; } // Indicate the scope was not created. this.ExistsAtRuntime = false; } }
/// <summary> /// Generates CIL to set the display name of the function. The function should be on top of 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="displayName"> The display name of the function. </param> /// <param name="force"> <c>true</c> to set the displayName property, even if the function has a name already. </param> public void GenerateDisplayName(ILGenerator generator, OptimizationInfo optimizationInfo, string displayName, bool force) { if (displayName == null) { throw new ArgumentNullException(nameof(displayName)); } // We only infer names for functions if the function doesn't have a name. if (force == true || string.IsNullOrEmpty(this.FunctionName)) { // Statically set the display name. this.context.DisplayName = displayName; // Generate code to set the display name at runtime. generator.Duplicate(); generator.LoadString("displayName"); generator.LoadString(displayName); generator.LoadBoolean(false); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); } }
/// <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 DuplicateReference(ILGenerator generator, OptimizationInfo optimizationInfo) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer var arg1 = generator.CreateTemporaryVariable(typeof(object)); var arg2 = generator.CreateTemporaryVariable(typeof(uint)); generator.StoreVariable(arg2); generator.StoreVariable(arg1); generator.LoadVariable(arg1); generator.LoadVariable(arg2); generator.LoadVariable(arg1); generator.LoadVariable(arg2); generator.ReleaseTemporaryVariable(arg1); generator.ReleaseTemporaryVariable(arg2); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property access generator.Duplicate(); } else { // Dynamic property access var arg1 = generator.CreateTemporaryVariable(typeof(object)); var arg2 = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(arg2); generator.StoreVariable(arg1); generator.LoadVariable(arg1); generator.LoadVariable(arg2); generator.LoadVariable(arg1); generator.LoadVariable(arg2); generator.ReleaseTemporaryVariable(arg1); generator.ReleaseTemporaryVariable(arg2); } }
/// <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.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> /// Generates code to push the "this" value for a function call. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> public void GenerateThis(ILGenerator generator) { // Optimization: if there are no with scopes, simply emit undefined. bool scopeChainHasWithScope = false; var scope = this.Scope; do { if (scope is ObjectScope && ((ObjectScope)scope).ProvidesImplicitThisValue == true) { scopeChainHasWithScope = true; break; } scope = scope.ParentScope; } while (scope != null); if (scopeChainHasWithScope == false) { // No with scopes in the scope chain, use undefined as the "this" value. EmitHelpers.EmitUndefined(generator); return; } var end = generator.CreateLabel(); scope = this.Scope; ILLocalVariable scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); generator.StoreVariable(scopeVariable); do { if (scope is DeclarativeScope) { if (scope.HasDeclaredVariable(this.Name)) { // The variable exists but declarative scopes always produce undefined for // the "this" value. EmitHelpers.EmitUndefined(generator); break; } } else { var objectScope = (ObjectScope)scope; // Check if the property exists by calling scope.ScopeObject.HasProperty(propertyName) if (objectScope.ProvidesImplicitThisValue == false) { EmitHelpers.EmitUndefined(generator); } generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); if (objectScope.ProvidesImplicitThisValue == true) { generator.Duplicate(); } generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_HasProperty); generator.BranchIfTrue(end); generator.Pop(); // If the name is not defined, use undefined for the "this" value. if (scope.ParentScope == null) { EmitHelpers.EmitUndefined(generator); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { generator.LoadVariable(scopeVariable); generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Release the temporary variable. generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(end); }
/// <summary> /// Pops the value on the stack, converts it to an integer, then pushes the integer result /// onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> public static void ToInteger(ILGenerator generator, PrimitiveType fromType) { // Check that a conversion is actually necessary. if (fromType == PrimitiveType.Int32 || fromType == PrimitiveType.UInt32 || fromType == PrimitiveType.Bool) { return; } switch (fromType) { case PrimitiveType.Undefined: case PrimitiveType.Null: // Converting from undefined or null produces 0. generator.Pop(); generator.LoadInt32(0); break; case PrimitiveType.Number: // Converting from a number produces the following: // Any number between -2147483648 and +2147483647 -> itself // Any number smaller than -2147483648 -> -2147483648 // Any number larger than +2147483647 -> +2147483647 // NaN -> 0 // bool isPositiveInfinity = input > 2147483647.0 var isPositiveInfinity = generator.CreateTemporaryVariable(typeof(bool)); generator.Duplicate(); generator.LoadDouble(2147483647.0); generator.CompareGreaterThan(); generator.StoreVariable(isPositiveInfinity); // bool notNaN = input == input var notNaN = generator.CreateTemporaryVariable(typeof(bool)); generator.Duplicate(); generator.Duplicate(); generator.CompareEqual(); generator.StoreVariable(notNaN); // input = (int)input // Infinity -> -2147483648 // -Infinity -> -2147483648 // NaN -> -2147483648 generator.ConvertToInteger(); // input = input & -((int)notNaN) generator.LoadVariable(notNaN); generator.Negate(); generator.BitwiseAnd(); // input = input - (int)isPositiveInfinity generator.LoadVariable(isPositiveInfinity); generator.Subtract(); // The temporary variables are no longer needed. generator.ReleaseTemporaryVariable(notNaN); generator.ReleaseTemporaryVariable(isPositiveInfinity); break; case PrimitiveType.String: case PrimitiveType.ConcatenatedString: case PrimitiveType.Any: case PrimitiveType.Object: // Otherwise, fall back to calling TypeConverter.ToInteger() generator.Call(ReflectionHelpers.TypeConverter_ToInteger); break; default: throw new NotImplementedException(string.Format("Unsupported primitive type: {0}", fromType)); } }
/// <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 a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Here is what we are going to generate. //private static object SampleBinder(ScriptEngine engine, object thisObject, object[] arguments) //{ // // Target function signature: int (bool, int, string, object). // bool param1; // int param2; // string param3; // object param4; // param1 = arguments[0] != 0; // param2 = TypeConverter.ToInt32(arguments[1]); // param3 = TypeConverter.ToString(arguments[2]); // param4 = Undefined.Value; // return thisObject.targetMethod(param1, param2, param3, param4); //} // Find the target method. var binderMethod = this.buckets[Math.Min(argumentCount, this.buckets.Length - 1)]; // Constrain the number of apparent arguments to within the required bounds. int minArgumentCount = binderMethod.RequiredParameterCount; int maxArgumentCount = binderMethod.RequiredParameterCount + binderMethod.OptionalParameterCount; if (binderMethod.HasParamArray == true) maxArgumentCount = int.MaxValue; foreach (var argument in binderMethod.GenerateArguments(generator, Math.Min(Math.Max(argumentCount, minArgumentCount), maxArgumentCount))) { switch (argument.Source) { case BinderArgumentSource.ScriptEngine: // Load the "engine" parameter passed by the client. generator.LoadArgument(0); break; case BinderArgumentSource.ThisValue: // Load the "this" parameter passed by the client. generator.LoadArgument(1); bool inheritsFromObjectInstance = typeof(ObjectInstance).IsAssignableFrom(argument.Type); if (argument.Type.IsClass == true && inheritsFromObjectInstance == false && argument.Type != typeof(string) && argument.Type != typeof(object)) { // If the "this" object is an unsupported class, pass it through unmodified. generator.CastClass(argument.Type); } else { if (argument.Type != typeof(object)) { // If the target "this" object type is not of type object, throw an error if // the value is undefined or null. generator.Duplicate(); var temp = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(temp); generator.LoadArgument(0); generator.LoadVariable(temp); generator.LoadString(binderMethod.Name); generator.Call(ReflectionHelpers.TypeUtilities_VerifyThisObject); generator.ReleaseTemporaryVariable(temp); } // Convert to the target type. EmitTypeConversion(generator, typeof(object), argument.Type); if (argument.Type != typeof(ObjectInstance) && inheritsFromObjectInstance == true) { // EmitConversionToObjectInstance can emit null if the toType is derived from ObjectInstance. // Therefore, if the value emitted is null it means that the "thisObject" is a type derived // from ObjectInstance (e.g. FunctionInstance) and the value provided is a different type // (e.g. ArrayInstance). In this case, throw an exception explaining that the function is // not generic. var endOfThrowLabel = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(endOfThrowLabel); generator.LoadArgument(0); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("The method '{0}' is not generic", binderMethod.Name)); generator.DefineLabelPosition(endOfThrowLabel); } } break; case BinderArgumentSource.InputParameter: if (argument.InputParameterIndex < argumentCount) { // Load the argument onto the stack. generator.LoadArgument(2); generator.LoadInt32(argument.InputParameterIndex); generator.LoadArrayElement(typeof(object)); // Get some flags that apply to the parameter. var parameterFlags = JSParameterFlags.None; var parameterAttribute = argument.GetCustomAttribute<JSParameterAttribute>(); if (parameterAttribute != null) { if (argument.Type != typeof(ObjectInstance)) throw new NotImplementedException("[JSParameter] is only supported for arguments of type ObjectInstance."); parameterFlags = parameterAttribute.Flags; } if ((parameterFlags & JSParameterFlags.DoNotConvert) == 0) { // Convert the input parameter to the correct type. EmitTypeConversion(generator, typeof(object), argument); } else { // Don't do argument conversion. /*var endOfThrowLabel = generator.CreateLabel(); generator.IsInstance(typeof(ObjectInstance)); generator.Duplicate(); generator.BranchIfNotNull(endOfThrowLabel); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("Parameter {1} parameter of '{0}' must be an object", binderMethod.Name, argument.InputParameterIndex)); generator.DefineLabelPosition(endOfThrowLabel);*/ } } else { // The target method has more parameters than we have input values. EmitUndefined(generator, argument); } break; } } // Emit the call. binderMethod.GenerateCall(generator); // Convert the return value. if (binderMethod.ReturnType == typeof(void)) EmitHelpers.EmitUndefined(generator); else { EmitTypeConversion(generator, binderMethod.ReturnType, typeof(object)); // Convert a null return value to Null.Value or Undefined.Value. var endOfSpecialCaseLabel = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(endOfSpecialCaseLabel); generator.Pop(); if ((binderMethod.Flags & JSFunctionFlags.ConvertNullReturnValueToUndefined) != 0) EmitHelpers.EmitUndefined(generator); else EmitHelpers.EmitNull(generator); generator.DefineLabelPosition(endOfSpecialCaseLabel); } // End the IL. generator.Complete(); }
/// <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 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> /// Generates CIL for an assignment 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> private void GenerateAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Load the value to assign. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (rhs is FunctionExpression) ((FunctionExpression)rhs).GenerateDisplayName(generator, optimizationInfo, target.ToString(), false); // Duplicate the value so it remains on the stack afterwards. //if (optimizationInfo.SuppressReturnValue == false) generator.Duplicate(); // Store the value. target.GenerateSet(generator, optimizationInfo, rhs.ResultType, optimizationInfo.StrictMode); }
/// <summary> /// Pops the value on the stack, converts it to an integer, then pushes the integer result /// onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> public static void ToInteger(ILGenerator generator, PrimitiveType fromType) { // Check that a conversion is actually necessary. if (fromType == PrimitiveType.Int32 || fromType == PrimitiveType.UInt32 || fromType == PrimitiveType.Bool) return; switch (fromType) { case PrimitiveType.Undefined: case PrimitiveType.Null: // Converting from undefined or null produces 0. generator.Pop(); generator.LoadInt32(0); break; case PrimitiveType.Number: // Converting from a number produces the following: // Any number between -2147483648 and +2147483647 -> itself // Any number smaller than -2147483648 -> -2147483648 // Any number larger than +2147483647 -> +2147483647 // NaN -> 0 // bool isPositiveInfinity = input > 2147483647.0 var isPositiveInfinity = generator.CreateTemporaryVariable(typeof(bool)); generator.Duplicate(); generator.LoadDouble(2147483647.0); generator.CompareGreaterThan(); generator.StoreVariable(isPositiveInfinity); // bool notNaN = input == input var notNaN = generator.CreateTemporaryVariable(typeof(bool)); generator.Duplicate(); generator.Duplicate(); generator.CompareEqual(); generator.StoreVariable(notNaN); // input = (int)input // Infinity -> -2147483648 // -Infinity -> -2147483648 // NaN -> -2147483648 generator.ConvertToInteger(); // input = input & -((int)notNaN) generator.LoadVariable(notNaN); generator.Negate(); generator.BitwiseAnd(); // input = input - (int)isPositiveInfinity generator.LoadVariable(isPositiveInfinity); generator.Subtract(); // The temporary variables are no longer needed. generator.ReleaseTemporaryVariable(notNaN); generator.ReleaseTemporaryVariable(isPositiveInfinity); break; case PrimitiveType.String: case PrimitiveType.ConcatenatedString: case PrimitiveType.Any: case PrimitiveType.Object: // Otherwise, fall back to calling TypeConverter.ToInteger() generator.Call(ReflectionHelpers.TypeConverter_ToInteger); break; default: throw new NotImplementedException(string.Format("Unsupported primitive type: {0}", fromType)); } }
/// <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) { // Deleting a variable is not allowed in strict mode. if (optimizationInfo.StrictMode == true) throw new JavaScriptException(optimizationInfo.Engine, ErrorType.SyntaxError, string.Format("Cannot delete {0} because deleting a variable or argument is not allowed in strict mode", this.Name), optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); var endOfDelete = generator.CreateLabel(); var scope = this.Scope; ILLocalVariable scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); generator.StoreVariable(scopeVariable); do { if (scope is DeclarativeScope) { var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable is known at compile-time. if (variable.Deletable == false) { // The variable cannot be deleted - return false. generator.LoadBoolean(false); } else { // The variable can be deleted (it was declared inside an eval()). // Delete the variable. generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_Delete); } 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) { generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // If the variable does exist, return true. generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_Delete); generator.Branch(endOfDelete); // } generator.DefineLabelPosition(hasValueClause); } } } else { // Check if the property exists by calling scope.ScopeObject.HasProperty(propertyName) generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.Duplicate(); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_HasProperty); // Jump past the delete if the property doesn't exist. var endOfExistsCheck = generator.CreateLabel(); generator.BranchIfFalse(endOfExistsCheck); // Call scope.ScopeObject.Delete(key, false) generator.LoadString(this.Name); generator.LoadBoolean(false); generator.Call(ReflectionHelpers.ObjectInstance_Delete); generator.Branch(endOfDelete); generator.DefineLabelPosition(endOfExistsCheck); generator.Pop(); // If the name is not defined, return true. if (scope.ParentScope == null) { generator.LoadBoolean(true); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { generator.LoadVariable(scopeVariable); generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Release the temporary variable. generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(endOfDelete); // Delete obviously has side-effects so we evaluate the return value then pop it from // the stack. //if (optimizationInfo.SuppressReturnValue == true) // generator.Pop(); }
/// <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.IsInstance(typeof(Library.ObjectInstance)); 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 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); // 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 to push the "this" value for a function call. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> public void GenerateThis(ILGenerator generator) { // Optimization: if there are no with scopes, simply emit undefined. bool scopeChainHasWithScope = false; var scope = this.Scope; do { if (scope is ObjectScope && ((ObjectScope)scope).ProvidesImplicitThisValue == true) { scopeChainHasWithScope = true; break; } scope = scope.ParentScope; } while (scope != null); if (scopeChainHasWithScope == false) { // No with scopes in the scope chain, use undefined as the "this" value. EmitHelpers.EmitUndefined(generator); return; } var end = generator.CreateLabel(); scope = this.Scope; ILLocalVariable scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); generator.StoreVariable(scopeVariable); do { if (scope is DeclarativeScope) { if (scope.HasDeclaredVariable(this.Name)) { // The variable exists but declarative scopes always produce undefined for // the "this" value. EmitHelpers.EmitUndefined(generator); break; } } else { var objectScope = (ObjectScope)scope; // Check if the property exists by calling scope.ScopeObject.HasProperty(propertyName) if (objectScope.ProvidesImplicitThisValue == false) EmitHelpers.EmitUndefined(generator); generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); if (objectScope.ProvidesImplicitThisValue == true) generator.Duplicate(); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_HasProperty); generator.BranchIfTrue(end); generator.Pop(); // If the name is not defined, use undefined for the "this" value. if (scope.ParentScope == null) { EmitHelpers.EmitUndefined(generator); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { generator.LoadVariable(scopeVariable); generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Release the temporary variable. generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(end); }
/// <summary> /// Pops the value on the stack, converts it from an object to the given type, then pushes /// the result onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="toType"> The type to convert to. </param> /// <param name="convertToAddress"> <c>true</c> if the value is intended for use as an /// instance pointer; <c>false</c> otherwise. </param> internal static void EmitConversionToType(ILGenerator generator, Type toType, bool convertToAddress) { // Convert Null.Value to null if the target type is a reference type. ILLabel endOfNullCheck = null; if (toType.IsValueType == false) { var startOfElse = generator.CreateLabel(); endOfNullCheck = generator.CreateLabel(); generator.Duplicate(); EmitHelpers.EmitNull(generator); generator.BranchIfNotEqual(startOfElse); generator.Pop(); generator.LoadNull(); generator.Branch(endOfNullCheck); generator.DefineLabelPosition(startOfElse); } switch (Type.GetTypeCode(toType)) { case TypeCode.Boolean: EmitConversion.ToBool(generator, PrimitiveType.Any); break; case TypeCode.Byte: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Char: EmitConversion.ToString(generator, PrimitiveType.Any); generator.Duplicate(); generator.Call(ReflectionHelpers.String_Length); generator.LoadInt32(1); var endOfCharCheck = generator.CreateLabel(); generator.BranchIfEqual(endOfCharCheck); EmitHelpers.EmitThrow(generator, "TypeError", "Cannot convert string to char - the string must be exactly one character long"); generator.DefineLabelPosition(endOfCharCheck); generator.LoadInt32(0); generator.Call(ReflectionHelpers.String_GetChars); break; case TypeCode.DBNull: throw new NotSupportedException("DBNull is not a supported parameter type."); case TypeCode.Decimal: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.NewObject(ReflectionHelpers.Decimal_Constructor_Double); break; case TypeCode.Double: EmitConversion.ToNumber(generator, PrimitiveType.Any); break; case TypeCode.Empty: throw new NotSupportedException("Empty is not a supported return type."); case TypeCode.Int16: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Int32: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Int64: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.ConvertToInt64(); break; case TypeCode.DateTime: case TypeCode.Object: // Check if the type must be unwrapped. generator.Duplicate(); generator.IsInstance(typeof(Jurassic.Library.ClrInstanceWrapper)); var endOfUnwrapCheck = generator.CreateLabel(); generator.BranchIfFalse(endOfUnwrapCheck); // Unwrap the wrapped instance. generator.Call(ReflectionHelpers.ClrInstanceWrapper_GetWrappedInstance); generator.DefineLabelPosition(endOfUnwrapCheck); // Value types must be unboxed. if (toType.IsValueType == true) { if (convertToAddress == true) // Unbox. generator.Unbox(toType); else // Unbox and copy to the stack. generator.UnboxAny(toType); //// Calling methods on value required the address of the value type, not the value type itself. //if (argument.Source == BinderArgumentSource.ThisValue && argument.Type.IsValueType == true) //{ // var temp = generator.CreateTemporaryVariable(argument.Type); // generator.StoreVariable(temp); // generator.LoadAddressOfVariable(temp); // generator.ReleaseTemporaryVariable(temp); //} } break; case TypeCode.SByte: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Single: EmitConversion.ToNumber(generator, PrimitiveType.Any); break; case TypeCode.String: EmitConversion.ToString(generator, PrimitiveType.Any); break; case TypeCode.UInt16: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.UInt32: EmitConversion.ToUInt32(generator, PrimitiveType.Any); break; case TypeCode.UInt64: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.ConvertToUnsignedInt64(); break; } // Label the end of the null check. if (toType.IsValueType == false) generator.DefineLabelPosition(endOfNullCheck); }
/// <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> /// Pops the value on the stack, converts it to a boolean, then pushes the boolean result /// onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> public static void ToBool(ILGenerator generator, PrimitiveType fromType) { // Check that a conversion is actually necessary. if (fromType == PrimitiveType.Bool) return; switch (fromType) { case PrimitiveType.Undefined: case PrimitiveType.Null: // Converting from undefined or null produces false. generator.Pop(); generator.LoadInt32(0); break; case PrimitiveType.Int32: case PrimitiveType.UInt32: // Converting from an integer produces true if the integer is non-zero. generator.LoadInt32(0); generator.CompareGreaterThanUnsigned(); break; case PrimitiveType.Number: // Converting from a number produces true if the number is non-zero and not NaN. var temp = generator.CreateTemporaryVariable(fromType); generator.StoreVariable(temp); // input != 0 generator.LoadVariable(temp); generator.LoadDouble(0.0); generator.CompareEqual(); generator.LoadInt32(0); generator.CompareEqual(); // input == input generator.LoadVariable(temp); generator.Duplicate(); generator.CompareEqual(); // && generator.CompareEqual(); // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(temp); break; case PrimitiveType.String: // Converting from a string produces true if the string is not empty. generator.Call(ReflectionHelpers.String_Length); generator.LoadInt32(0); generator.CompareGreaterThan(); break; case PrimitiveType.ConcatenatedString: // Converting from a string produces true if the string is not empty. generator.Call(ReflectionHelpers.ConcatenatedString_Length); generator.LoadInt32(0); generator.CompareGreaterThan(); break; case PrimitiveType.Any: case PrimitiveType.Object: // Otherwise, fall back to calling TypeConverter.ToBoolean() generator.Call(ReflectionHelpers.TypeConverter_ToBoolean); break; default: throw new NotImplementedException(string.Format("Unsupported primitive type: {0}", fromType)); } }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Generate code for the start of the statement. var statementLocals = new StatementLocals() { NonDefaultBreakStatementBehavior = true, NonDefaultSourceSpanBehavior = true }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Construct a loop expression. // var iterator = TypeUtilities.GetIterator(obj); // while (true) { // continue-target: // if (enumerator.MoveNext() == false) // goto break-target; // lhs = enumerator.Current; // // <body statements> // } // break-target: // Call: ObjectInstance GetIterator(ScriptEngine engine, ObjectInstance iterable) // Then call: IEnumerable<object> Iterate(ScriptEngine engine, ObjectInstance iterator) optimizationInfo.MarkSequencePoint(generator, this.TargetObjectSourceSpan); EmitHelpers.LoadScriptEngine(generator); generator.Duplicate(); this.TargetObject.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, this.TargetObject.ResultType, optimizationInfo); generator.Call(ReflectionHelpers.TypeUtilities_GetIterator); generator.Call(ReflectionHelpers.TypeUtilities_Iterate); // Call IEnumerable<object>.GetEnumerator() generator.Call(ReflectionHelpers.IEnumerable_Object_GetEnumerator); // Store the enumerator in a temporary variable. var enumerator = generator.CreateTemporaryVariable(typeof(IEnumerator<object>)); generator.StoreVariable(enumerator); var breakTarget = generator.CreateLabel(); var continueTarget = generator.DefineLabelPosition(); // Emit debugging information. if (optimizationInfo.DebugDocument != null) generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.VariableSourceSpan); // if (enumerator.MoveNext() == false) // goto break-target; generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_MoveNext); generator.BranchIfFalse(breakTarget); // lhs = enumerator.Current; this.Variable.GenerateReference(generator, optimizationInfo); generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_Object_Current); this.Variable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); // Emit the body statement(s). optimizationInfo.PushBreakOrContinueInfo(this.Labels, breakTarget, continueTarget, labelledOnly: false); this.Body.GenerateCode(generator, optimizationInfo); optimizationInfo.PopBreakOrContinueInfo(); generator.Branch(continueTarget); generator.DefineLabelPosition(breakTarget); // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
/// <summary> /// Generates 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 { // 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 an assignment 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> private void GenerateAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Evaluate the left hand side first! target.GenerateReference(generator, optimizationInfo); // Load the value to assign. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (rhs is FunctionExpression) ((FunctionExpression)rhs).GenerateDisplayName(generator, optimizationInfo, target.ToString(), false); // Store the RHS value so we can return it as the result of the expression. var result = generator.CreateTemporaryVariable(rhs.ResultType); generator.Duplicate(); generator.StoreVariable(result); // Store the value. target.GenerateSet(generator, optimizationInfo, rhs.ResultType, optimizationInfo.StrictMode); // Restore the RHS value. generator.LoadVariable(result); generator.ReleaseTemporaryVariable(result); }
/// <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) { // Deleting a variable is not allowed in strict mode. if (optimizationInfo.StrictMode == true) { throw new JavaScriptException(optimizationInfo.Engine, ErrorType.SyntaxError, string.Format("Cannot delete {0} because deleting a variable or argument is not allowed in strict mode", this.Name), optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); } var endOfDelete = generator.CreateLabel(); var scope = this.Scope; ILLocalVariable scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); generator.StoreVariable(scopeVariable); do { if (scope is DeclarativeScope) { var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable is known at compile-time. if (variable.Deletable == false) { // The variable cannot be deleted - return false. generator.LoadBoolean(false); } else { // The variable can be deleted (it was declared inside an eval()). // Delete the variable. generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_Delete); } 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) { generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // If the variable does exist, return true. generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_Delete); generator.Branch(endOfDelete); // } generator.DefineLabelPosition(hasValueClause); } } } else { // Check if the property exists by calling scope.ScopeObject.HasProperty(propertyName) generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.Duplicate(); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_HasProperty); // Jump past the delete if the property doesn't exist. var endOfExistsCheck = generator.CreateLabel(); generator.BranchIfFalse(endOfExistsCheck); // Call scope.ScopeObject.Delete(key, false) generator.LoadString(this.Name); generator.LoadBoolean(false); generator.Call(ReflectionHelpers.ObjectInstance_Delete); generator.Branch(endOfDelete); generator.DefineLabelPosition(endOfExistsCheck); generator.Pop(); // If the name is not defined, return true. if (scope.ParentScope == null) { generator.LoadBoolean(true); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { generator.LoadVariable(scopeVariable); generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Release the temporary variable. generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(endOfDelete); // Delete obviously has side-effects so we evaluate the return value then pop it from // the stack. //if (optimizationInfo.SuppressReturnValue == true) // generator.Pop(); }
/// <summary> /// Generates CIL for a compound assignment 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> private void GenerateCompoundAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Evaluate the left hand side only once. target.GenerateReference(generator, optimizationInfo); target.DuplicateReference(generator, optimizationInfo); // For the GenerateSet, later on. // Load the value to assign. var compoundOperator = new BinaryExpression(GetCompoundBaseOperator(this.OperatorType), new ReferenceGetExpression(target), this.GetOperand(1)); compoundOperator.GenerateCode(generator, optimizationInfo); // Store the resulting value so we can return it as the result of the expression. var result = generator.CreateTemporaryVariable(compoundOperator.ResultType); generator.Duplicate(); generator.StoreVariable(result); // Store the value. target.GenerateSet(generator, optimizationInfo, compoundOperator.ResultType, optimizationInfo.StrictMode); // Restore the expression result. generator.LoadVariable(result); generator.ReleaseTemporaryVariable(result); }
/// <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 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. // Evaluate the left hand side only once. target.GenerateReference(generator, optimizationInfo); target.DuplicateReference(generator, optimizationInfo); // For the GenerateSet, later on. // 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, store the value so it can be returned later. var result = generator.CreateTemporaryVariable(target.Type == PrimitiveType.Int32 ? PrimitiveType.Int32 : PrimitiveType.Number); if (postfix == true) { generator.Duplicate(); generator.StoreVariable(result); } // 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, store the value so it can be returned later. if (postfix == false) { generator.Duplicate(); generator.StoreVariable(result); } // Store the value. target.GenerateSet(generator, optimizationInfo, target.Type == PrimitiveType.Int32 ? PrimitiveType.Int32 : PrimitiveType.Number, optimizationInfo.StrictMode); // Restore the expression result. generator.LoadVariable(result); generator.ReleaseTemporaryVariable(result); }
/// <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> /// Pops the value on the stack, converts it to an object, then pushes the result onto the /// stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> internal static void EmitConversionToObject(ILGenerator generator, Type fromType) { // If the from type is a reference type, check for null. ILLabel endOfNullCheck = null; if (fromType.IsValueType == false) { var startOfElse = generator.CreateLabel(); endOfNullCheck = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(startOfElse); generator.Pop(); EmitHelpers.EmitNull(generator); generator.Branch(endOfNullCheck); generator.DefineLabelPosition(startOfElse); } switch (Type.GetTypeCode(fromType)) { case TypeCode.Boolean: generator.Box(typeof(bool)); break; case TypeCode.Byte: generator.Box(typeof(int)); break; case TypeCode.Char: generator.LoadInt32(1); generator.NewObject(ReflectionHelpers.String_Constructor_Char_Int); break; case TypeCode.DBNull: throw new NotSupportedException("DBNull is not a supported return type."); case TypeCode.Decimal: generator.Call(ReflectionHelpers.Decimal_ToDouble); generator.Box(typeof(double)); break; case TypeCode.Double: generator.Box(typeof(double)); break; case TypeCode.Empty: throw new NotSupportedException("Empty is not a supported return type."); case TypeCode.Int16: generator.Box(typeof(int)); break; case TypeCode.Int32: generator.Box(typeof(int)); break; case TypeCode.Int64: generator.ConvertToDouble(); generator.Box(typeof(double)); break; case TypeCode.DateTime: case TypeCode.Object: // Check if the type must be wrapped with a ClrInstanceWrapper. // Note: if the type is a value type it cannot be a primitive or it would // have been handled elsewhere in the switch. ILLabel endOfWrapCheck = null; if (fromType.IsValueType == false) { generator.Duplicate(); generator.Call(ReflectionHelpers.TypeUtilities_IsPrimitiveOrObject); endOfWrapCheck = generator.CreateLabel(); generator.BranchIfTrue(endOfWrapCheck); } // The type must be wrapped. var temp = generator.CreateTemporaryVariable(fromType); generator.StoreVariable(temp); generator.LoadArgument(0); generator.LoadVariable(temp); if (fromType.IsValueType == true) generator.Box(fromType); generator.ReleaseTemporaryVariable(temp); generator.NewObject(ReflectionHelpers.ClrInstanceWrapper_Constructor); // End of wrap check. if (fromType.IsValueType == false) generator.DefineLabelPosition(endOfWrapCheck); break; case TypeCode.SByte: generator.Box(typeof(int)); break; case TypeCode.Single: generator.Box(typeof(double)); break; case TypeCode.String: break; case TypeCode.UInt16: generator.Box(typeof(int)); break; case TypeCode.UInt32: generator.Box(typeof(uint)); break; case TypeCode.UInt64: generator.ConvertUnsignedToDouble(); generator.Box(typeof(double)); break; } // Label the end of the null check. if (fromType.IsValueType == false) generator.DefineLabelPosition(endOfNullCheck); }
/// <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 CIL to set the display name of the function. The function should be on top of 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="displayName"> The display name of the function. </param> /// <param name="force"> <c>true</c> to set the displayName property, even if the function has a name already. </param> public void GenerateDisplayName(ILGenerator generator, OptimizationInfo optimizationInfo, string displayName, bool force) { if (displayName == null) throw new ArgumentNullException("displayName"); // We only infer names for functions if the function doesn't have a name. if (force == true || string.IsNullOrEmpty(this.FunctionName)) { // Statically set the display name. this.context.DisplayName = displayName; // Generate code to set the display name at runtime. generator.Duplicate(); generator.LoadString("displayName"); generator.LoadString(displayName); generator.LoadBoolean(false); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_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) { // 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 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) { // Generate a new method. this.context.GenerateCode(); // Add the generated method to the nested function list. if (optimizationInfo.NestedFunctions == null) optimizationInfo.NestedFunctions = new List<GeneratedMethod>(); optimizationInfo.NestedFunctions.Add(this.context.GeneratedMethod); // Add all the nested methods to the parent list. if (this.context.GeneratedMethod.Dependencies != null) { foreach (var nestedFunctionExpression in this.context.GeneratedMethod.Dependencies) optimizationInfo.NestedFunctions.Add(nestedFunctionExpression); } // Store the generated method in the cache. long generatedMethodID = GeneratedMethod.Save(this.context.GeneratedMethod); // Create a UserDefinedFunction. // prototype EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Function); generator.Call(ReflectionHelpers.FunctionInstance_InstancePrototype); // name generator.LoadString(this.FunctionName); // argumentNames generator.LoadInt32(this.ArgumentNames.Count); generator.NewArray(typeof(string)); for (int i = 0; i < this.ArgumentNames.Count; i++) { generator.Duplicate(); generator.LoadInt32(i); generator.LoadString(this.ArgumentNames[i]); generator.StoreArrayElement(typeof(string)); } // scope EmitHelpers.LoadScope(generator); // bodyText generator.LoadString(this.BodyText); // body generator.LoadInt64(generatedMethodID); generator.Call(ReflectionHelpers.GeneratedMethod_Load); // strictMode generator.LoadBoolean(this.context.StrictMode); // new UserDefinedFunction(ObjectInstance prototype, string name, IList<string> argumentNames, DeclarativeScope scope, Func<Scope, object, object[], object> body, bool strictMode) generator.NewObject(ReflectionHelpers.UserDefinedFunction_Constructor); }
/// <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 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) { // Note: we use GetRawOperand() so that grouping operators are not ignored. var operand = this.GetRawOperand(0); // There is only one operand, and it can be either a reference or a function call. // We need to split the operand into a function and some arguments. // If the operand is a reference, it is equivalent to a function call with no arguments. if (operand is FunctionCallExpression) { // Emit the function instance first. var function = ((FunctionCallExpression)operand).Target; function.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, function.ResultType); } else { // Emit the function instance first. operand.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, operand.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. var targetValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(targetValue); EmitHelpers.LoadScriptEngine(generator); generator.LoadString("TypeError"); generator.LoadString("The new operator requires a function, found a '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(targetValue); 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(targetValue); if (operand is FunctionCallExpression) { // Emit an array containing the function arguments. ((FunctionCallExpression)operand).GenerateArgumentsArray(generator, optimizationInfo); } else { // Emit an empty array. generator.LoadInt32(0); generator.NewArray(typeof(object)); } // Call FunctionInstance.ConstructLateBound(argumentValues) generator.Call(ReflectionHelpers.FunctionInstance_ConstructLateBound); }
/// <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 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); // Pass in the path, function name and line. generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); // 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_CallWithStackTrace); // Allow reuse of the temporary variable. if (targetBase != null) generator.ReleaseTemporaryVariable(targetBase); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Determine the methods that have the correct number of arguments. var candidateMethods = new List<BinderMethod>(); foreach (var candidateMethod in this.targetMethods) { if (candidateMethod.IsArgumentCountCompatible(argumentCount) == true) candidateMethods.Add(candidateMethod); } // Zero candidates means no overload had the correct number of arguments. if (candidateMethods.Count == 0) { EmitHelpers.EmitThrow(generator, "TypeError", string.Format("No overload for method '{0}' takes {1} arguments", this.Name, argumentCount)); EmitHelpers.EmitDefaultValue(generator, PrimitiveType.Any); generator.Complete(); return; } // Select the method to call at run time. generator.LoadInt32(candidateMethods.Count); generator.NewArray(typeof(RuntimeMethodHandle)); for (int i = 0; i < candidateMethods.Count; i ++) { generator.Duplicate(); generator.LoadInt32(i); generator.LoadToken(candidateMethods[i]); generator.StoreArrayElement(typeof(RuntimeMethodHandle)); } generator.LoadArgument(0); generator.LoadArgument(1); generator.LoadArgument(2); generator.Call(ReflectionHelpers.BinderUtilities_ResolveOverloads); var endOfMethod = generator.CreateLabel(); for (int i = 0; i < candidateMethods.Count; i++) { // Check if this is the selected method. ILLabel endOfIf = null; if (i < candidateMethods.Count - 1) { generator.Duplicate(); generator.LoadInt32(i); endOfIf = generator.CreateLabel(); generator.BranchIfNotEqual(endOfIf); } generator.Pop(); var targetMethod = candidateMethods[i]; // Convert the arguments. foreach (var argument in targetMethod.GenerateArguments(generator, argumentCount)) { // Load the input parameter value. switch (argument.Source) { case BinderArgumentSource.ScriptEngine: generator.LoadArgument(0); break; case BinderArgumentSource.ThisValue: generator.LoadArgument(1); break; case BinderArgumentSource.InputParameter: generator.LoadArgument(2); generator.LoadInt32(argument.InputParameterIndex); generator.LoadArrayElement(typeof(object)); break; } // Convert to the target type. EmitConversionToType(generator, argument.Type, convertToAddress: argument.Source == BinderArgumentSource.ThisValue); } // Call the target method. targetMethod.GenerateCall(generator); // Convert the return value. if (targetMethod.ReturnType == typeof(void)) EmitHelpers.EmitUndefined(generator); else EmitConversionToObject(generator, targetMethod.ReturnType); // Branch to the end of the method if this was the selected method. if (endOfIf != null) { generator.Branch(endOfMethod); generator.DefineLabelPosition(endOfIf); } } generator.DefineLabelPosition(endOfMethod); generator.Complete(); }
/// <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 Dictionary<string, object>) { // This is an object literal. var properties = (Dictionary<string, object>)this.Value; // Create a new object. EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Object); generator.Call(ReflectionHelpers.Object_Construct); foreach (var keyValuePair in properties) { string propertyName = keyValuePair.Key; object propertyValue = keyValuePair.Value; generator.Duplicate(); generator.LoadString(propertyName); if (propertyValue is Expression) { // Add a new property to the object. var dataPropertyValue = (Expression)propertyValue; dataPropertyValue.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. if (dataPropertyValue is FunctionExpression) ((FunctionExpression)dataPropertyValue).GenerateDisplayName(generator, optimizationInfo, propertyName, false); EmitConversion.ToAny(generator, dataPropertyValue.ResultType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); } else if (propertyValue is Parser.ObjectLiteralAccessor) { // Add a new getter/setter to the object. var accessorValue = (Parser.ObjectLiteralAccessor)propertyValue; if (accessorValue.Getter != null) { accessorValue.Getter.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. accessorValue.Getter.GenerateDisplayName(generator, optimizationInfo, "get " + propertyName, true); EmitConversion.ToAny(generator, accessorValue.Getter.ResultType); } else generator.LoadNull(); if (accessorValue.Setter != null) { accessorValue.Setter.GenerateCode(generator, optimizationInfo); // Support the inferred function displayName property. accessorValue.Setter.GenerateDisplayName(generator, optimizationInfo, "set " + propertyName, true); EmitConversion.ToAny(generator, accessorValue.Setter.ResultType); } else generator.LoadNull(); generator.LoadInt32((int)Library.PropertyAttributes.FullAccess); generator.NewObject(ReflectionHelpers.PropertyDescriptor_Constructor3); generator.LoadBoolean(false); generator.Call(ReflectionHelpers.ObjectInstance_DefineProperty); generator.Pop(); } else throw new InvalidOperationException("Invalid property value type in object literal."); } } else throw new NotImplementedException("Unknown literal type."); }
/// <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) { // Allocate storage for each variable if the declarative scope object has been optimized away. if (optimizationInfo.OptimizeDeclarativeScopes == false) { // Create a new declarative scope. // parentScope EmitHelpers.LoadScope(generator); // declaredVariableNames generator.LoadInt32(this.DeclaredVariableCount); generator.NewArray(typeof(string)); int i = 0; foreach (string variableName in this.DeclaredVariableNames) { generator.Duplicate(); generator.LoadInt32(i ++); generator.LoadString(variableName); generator.StoreArrayElement(typeof(string)); } // DeclarativeScope.CreateRuntimeScope(parentScope, declaredVariableNames) generator.Call(ReflectionHelpers.DeclarativeScope_CreateRuntimeScope); // Save the new scope. EmitHelpers.StoreScope(generator); } else { // The declarative scope can be optimized away entirely. foreach (var variable in this.DeclaredVariables) { variable.Store = null; variable.Type = PrimitiveType.Any; } // Indicate the scope was not created. this.ExistsAtRuntime = false; } }
/// <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."); }