/// <summary> /// Generates CIL for the expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // This code is only used for untagged template literals. // Tagged template literals are handled by FunctionCallExpression. // Load the values array onto the stack. generator.LoadInt32(this.Strings.Count + this.Values.Count); generator.NewArray(typeof(string)); for (int i = 0; i < this.Strings.Count; i++) { // Operands for StoreArrayElement() are: an array (string[]), index (int), value (string). // Store the string. generator.Duplicate(); generator.LoadInt32(i * 2); generator.LoadString(this.Strings[i]); generator.StoreArrayElement(typeof(string)); if (i == this.Strings.Count - 1) { break; } // Store the value. generator.Duplicate(); generator.LoadInt32(i * 2 + 1); this.Values[i].GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, this.Values[i].ResultType); generator.StoreArrayElement(typeof(string)); } // Call String.Concat(string[]) generator.CallStatic(ReflectionHelpers.String_Concat); }
/// <summary> /// Generates 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 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 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) { // 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> /// Stores the value on the top of the stack in the reference. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { string propertyName = null; bool isArrayIndex = false; //optimizationInfo = optimizationInfo.RemoveFlags(OptimizationFlags.SuppressReturnValue); // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access", optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1) as LiteralExpression; if (rhs != null) { propertyName = TypeConverter.ToString(rhs.Value); // Or a array index (a[0]) if (rhs.ResultType == PrimitiveType.Int32 || (propertyName != null && Library.ArrayInstance.ParseArrayIndex(propertyName) != uint.MaxValue)) isArrayIndex = true; } } // Convert the value to an object and store it in a temporary variable. var value = generator.CreateTemporaryVariable(typeof(object)); EmitConversion.ToAny(generator, valueType); generator.StoreVariable(value); if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the right-hand side and convert to a uint32. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToUInt32(generator, rhs.ResultType); // Call the indexer. generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Int); } else if (propertyName != null) { //// Load the left-hand side and convert to an object instance. //var lhs = this.GetOperand(0); //lhs.GenerateCode(generator, optimizationInfo); //EmitConversion.ToObject(generator, lhs.ResultType); //// Call the indexer. //generator.LoadString(propertyName); //generator.LoadVariable(value); //generator.LoadBoolean(optimizationInfo.StrictMode); //generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); // Named property modification (e.g. x.property = y) // ------------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); // Call the indexer. generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); } // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(value); }
/// <summary> /// Generates CIL for the 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 if (this.context.DeclarationType == FunctionDeclarationType.Getter) { generator.LoadString("get " + this.FunctionName); } else if (this.context.DeclarationType == FunctionDeclarationType.Setter) { generator.LoadString("set " + this.FunctionName); } else { generator.LoadString(this.FunctionName); } // argumentNames generator.LoadInt32(this.Arguments.Count); generator.NewArray(typeof(string)); for (int i = 0; i < this.Arguments.Count; i++) { generator.Duplicate(); generator.LoadInt32(i); generator.LoadString(this.Arguments[i].Name); 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 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> /// Gets an enumerable list of argument objects, equal in size to /// <paramref name="argumentCount"/> while generating code to prepare those arguments for /// a method call. /// </summary> /// <param name="argumentCount"> The number of arguments to return. </param> /// <param name="generator"> The IL generator used to create an array if the method has a /// ParamArray parameter. </param> /// <returns> An enumerable list of argument objects. </returns> public IEnumerable <BinderArgument> GenerateArguments(ILGenerator generator, int argumentCount) { if (generator == null) { throw new ArgumentNullException(nameof(generator)); } int paramArrayIndex = 0; foreach (var argument in this.GetArguments(argumentCount)) { if (argument.IsParamArrayArgument == true) { if (paramArrayIndex == 0) { // This is the start of the ParamArray arguments. // Create an array. int paramArraySize = Math.Max(0, argumentCount - this.OptionalParameterCount - this.RequiredParameterCount); generator.LoadInt32(paramArraySize); generator.NewArray(argument.Type); } // Load the array and index. generator.Duplicate(); generator.LoadInt32(paramArrayIndex++); } // Yield will have the side effect of generating a value. yield return(argument); if (argument.IsParamArrayArgument == true) { // Store the value in the ParamArray array. generator.StoreArrayElement(argument.Type); } } // Populate any missing optional arguments with the default value. if (this.RequiredParameterCount + this.OptionalParameterCount - argumentCount > 0) { var parameters = this.GetParameters(); for (int i = 0; i < this.RequiredParameterCount + this.OptionalParameterCount - argumentCount; i++) { var optionalParameter = parameters[argumentCount + i]; if ((optionalParameter.Attributes & ParameterAttributes.HasDefault) == ParameterAttributes.HasDefault) { // Emit the default value. EmitHelpers.EmitValue(generator, new BinderArgument(optionalParameter, 0).DefaultValue); } else { // Emit default(T). EmitHelpers.EmitDefaultValue(generator, optionalParameter.ParameterType); } } } // Create an empty array if a ParamArray argument exists but no arguments were provided. if (this.HasParamArray == true && paramArrayIndex == 0) { // Create an array. generator.LoadInt32(0); generator.NewArray(this.ParamArrayElementType); } }
/// <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 string prefix = null; if (Name.IsGetter) { prefix = "get "; } else if (Name.IsSetter) { prefix = "set "; } if (Name.HasStaticName) { generator.LoadString(prefix + Name.StaticName); } else { // Compute the name at runtime. if (prefix != null) { generator.LoadString(prefix); } Name.ComputedName.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, Name.ComputedName.ResultType); if (prefix != null) { generator.CallStatic(ReflectionHelpers.String_Concat_String_String); } } // argumentNames generator.LoadInt32(this.Arguments.Count); generator.NewArray(typeof(string)); for (int i = 0; i < this.Arguments.Count; i++) { generator.Duplicate(); generator.LoadInt32(i); generator.LoadString(this.Arguments[i].Name); generator.StoreArrayElement(typeof(string)); } // scope Scope.GenerateReference(generator, optimizationInfo); // bodyText generator.LoadString(this.BodyText); // body generator.LoadInt64(generatedMethodID); generator.Call(ReflectionHelpers.GeneratedMethod_Load); // strictMode generator.LoadBoolean(this.context.StrictMode); // container if (ContainerVariable != null) { generator.LoadVariable(ContainerVariable); } else { generator.LoadNull(); } // CreateFunction(ObjectInstance prototype, string name, IList<string> argumentNames, // RuntimeScope scope, Func<Scope, object, object[], object> body, // bool strictMode, FunctionInstance container) generator.CallStatic(ReflectionHelpers.ReflectionHelpers_CreateFunction); }
/// <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 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> /// Stores the value on the top of the stack in the reference. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { // The value is initially on the top of the stack but is stored in this variable // at the last possible moment. ILLocalVariable value = null; var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfSet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // Get information about the variable. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable was declared in this scope. if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) { variable.Store = generator.DeclareVariable(typeof(object), variable.Name); } if (value == null) { // The value to store is on the top of the stack - convert it to the // storage type of the variable. EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo); } else { // The value to store is in a temporary variable. generator.LoadVariable(value); EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo); } // Store the value in the variable. generator.StoreVariable(variable.Store); } else if (variable.Writable == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // scope.Values[index] = value if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); } else { // The variable exists, but is read-only. // Pop the value off the stack (if it is still there). if (value == null) { generator.Pop(); } } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Set the value of the variable. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.LoadVariable(value); generator.Call(ReflectionHelpers.Scope_SetValue); generator.Branch(endOfSet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } if (scope.ParentScope == null) { // Optimization: if this is the global scope, use hidden classes to // optimize variable access. // Global variable modification // ---------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Get a reference to the global object. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); if (throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); generator.Branch(endOfSet); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Slow route. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); if (scope.ParentScope == null && throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // The value might be still on top of the stack. if (value == null && scope == null) { generator.Pop(); } // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) { EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); } // Release the temporary variables. if (value != null) { generator.ReleaseTemporaryVariable(value); } if (scopeVariable != null) { generator.ReleaseTemporaryVariable(scopeVariable); } // Define a label at the end. generator.DefineLabelPosition(endOfSet); }
/// <summary> /// 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.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. /// </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> /// Stores the value on the top of the stack in the reference. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { // The value is initially on the top of the stack but is stored in this variable // at the last possible moment. ILLocalVariable value = null; var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfSet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // Get information about the variable. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable was declared in this scope. if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) { variable.Store = generator.DeclareVariable(typeof(object), variable.Name); } if (value == null) { // The value to store is on the top of the stack - convert it to the // storage type of the variable. EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo); } else { // The value to store is in a temporary variable. generator.LoadVariable(value); EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo); } // Store the value in the variable. generator.StoreVariable(variable.Store); } else if (variable.Writable == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // scope.Values[index] = value if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); } else { // The variable exists, but is read-only. // Pop the value off the stack (if it is still there). if (value == null) { generator.Pop(); } } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Set the value of the variable. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.LoadVariable(value); generator.Call(ReflectionHelpers.Scope_SetValue); generator.Branch(endOfSet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); if (scope.ParentScope == null && throwIfUnresolvable == false) { // Sets the value of a global variable. // JS: object.property = value // C# ==> // if (propertyName == null) // propertyName = new PropertyName("property"); // object.SetPropertyValue(propertyName, value, strictMode); ILLocalVariable propertyName = optimizationInfo.GetGlobalPropertyReferenceVariable(generator, this.Name); generator.LoadVariable(propertyName); generator.Duplicate(); var afterIf = generator.CreateLabel(); generator.BranchIfNotNull(afterIf); generator.Pop(); generator.LoadString(this.Name); generator.NewObject(ReflectionHelpers.PropertyName_Constructor); generator.Duplicate(); generator.StoreVariable(propertyName); generator.DefineLabelPosition(afterIf); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_PropertyReference); } else { // Setting a variable within a "with" scope. generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // The value might be still on top of the stack. if (value == null && scope == null) { generator.Pop(); } // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) { EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); } // Release the temporary variables. if (value != null) { generator.ReleaseTemporaryVariable(value); } if (scopeVariable != null) { generator.ReleaseTemporaryVariable(scopeVariable); } // Define a label at the end. generator.DefineLabelPosition(endOfSet); }
/// <summary> /// 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) { // This code is only used for untagged template literals. // Tagged template literals are handled by FunctionCallExpression. // Load the values array onto the stack. generator.LoadInt32(this.Strings.Count + this.Values.Count); generator.NewArray(typeof(string)); for (int i = 0; i < this.Strings.Count; i++) { // Operands for StoreArrayElement() are: an array (string[]), index (int), value (string). // Store the string. generator.Duplicate(); generator.LoadInt32(i * 2); generator.LoadString(this.Strings[i]); generator.StoreArrayElement(typeof(string)); if (i == this.Strings.Count - 1) break; // Store the value. generator.Duplicate(); generator.LoadInt32(i * 2 + 1); this.Values[i].GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, this.Values[i].ResultType); generator.StoreArrayElement(typeof(string)); } // Call String.Concat(string[]) generator.CallStatic(ReflectionHelpers.String_Concat); }
/// <summary> /// Generates 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, ErrorType.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) { // 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 code that creates a new scope. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> internal void GenerateScopeCreation(ILGenerator generator, OptimizationInfo optimizationInfo) { // Make sure we don't generate the scope twice. if (GenerateScopeCreationWasCalled) { return; } GenerateScopeCreationWasCalled = true; // We can optimize this away if there are zero variables declared in the scope, // UNLESS it's a with scope (as then we need something to bind to). if (this.variables.Count == 0 && Type != ScopeType.With) { return; } // If there is no eval(), no arguments usage and no nested functions, then we can use // IL variables instead of using RuntimeScope. if ((Type == ScopeType.TopLevelFunction || Type == ScopeType.Block) && optimizationInfo.OptimizeDeclarativeScopes) { foreach (var variable in this.variables.Values) { variable.Store = generator.DeclareVariable(variable.Type, variable.Name); if (variable.Type == PrimitiveType.Any) { generator.LoadNull(); generator.StoreVariable(variable.Store); } } return; } // The fallback: use RuntimeScope. EmitHelpers.LoadExecutionContext(generator); // parentScope if (ParentScope != null) { ParentScope.GenerateReference(generator, optimizationInfo); } else { generator.LoadNull(); } var varList = new List <DeclaredVariable>(); var letList = new List <DeclaredVariable>(); var constList = new List <DeclaredVariable>(); foreach (var variable in this.variables.Values) { if (variable.Keyword == KeywordToken.Var) { varList.Add(variable); } else if (variable.Keyword == KeywordToken.Const) { constList.Add(variable); } else { letList.Add(variable); } } varList.Sort((a, b) => a.Index - b.Index); letList.Sort((a, b) => a.Index - b.Index); constList.Sort((a, b) => a.Index - b.Index); int i; // scopeType generator.LoadEnumValue(Type); // varNames if (varList.Count == 0) { generator.LoadNull(); } else { generator.LoadInt32(varList.Count); generator.NewArray(typeof(string)); i = 0; foreach (var variable in varList) { generator.Duplicate(); generator.LoadInt32(i++); generator.LoadString(variable.Name); generator.StoreArrayElement(typeof(string)); } } // letNames if (letList.Count == 0) { generator.LoadNull(); } else { generator.LoadInt32(letList.Count); generator.NewArray(typeof(string)); i = 0; foreach (var variable in letList) { generator.Duplicate(); generator.LoadInt32(i++); generator.LoadString(variable.Name); generator.StoreArrayElement(typeof(string)); } } // constNames if (constList.Count == 0) { generator.LoadNull(); } else { generator.LoadInt32(constList.Count); generator.NewArray(typeof(string)); i = 0; foreach (var variable in constList) { generator.Duplicate(); generator.LoadInt32(i++); generator.LoadString(variable.Name); generator.StoreArrayElement(typeof(string)); } } // executionContext.CreateRuntimeScope(parentScope, varNames, letNames, constNames) generator.Call(ReflectionHelpers.ExecutionContext_CreateRuntimeScope); // Store the RuntimeScope instance in a variable. GeneratedRuntimeScope = generator.DeclareVariable(typeof(RuntimeScope), "scope"); generator.StoreVariable(GeneratedRuntimeScope); }
/// <summary> /// Stores the value on the top of the stack in the reference. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { string propertyName = null; bool isArrayIndex = false; //optimizationInfo = optimizationInfo.RemoveFlags(OptimizationFlags.SuppressReturnValue); // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) { throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access", optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); } propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1) as LiteralExpression; if (rhs != null) { propertyName = TypeConverter.ToString(rhs.Value); // Or a array index (a[0]) if (rhs.ResultType == PrimitiveType.Int32 || (propertyName != null && Library.ArrayInstance.ParseArrayIndex(propertyName) != uint.MaxValue)) { isArrayIndex = true; } } } // Convert the value to an object and store it in a temporary variable. var value = generator.CreateTemporaryVariable(typeof(object)); EmitConversion.ToAny(generator, valueType); generator.StoreVariable(value); if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the right-hand side and convert to a uint32. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToUInt32(generator, rhs.ResultType); // Call the indexer. generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Int); } else if (propertyName != null) { //// Load the left-hand side and convert to an object instance. //var lhs = this.GetOperand(0); //lhs.GenerateCode(generator, optimizationInfo); //EmitConversion.ToObject(generator, lhs.ResultType); //// Call the indexer. //generator.LoadString(propertyName); //generator.LoadVariable(value); //generator.LoadBoolean(optimizationInfo.StrictMode); //generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); // Named property modification (e.g. x.property = y) // ------------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); // Call the indexer. generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); } // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(value); }
/// <summary> /// Generates CIL for the 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> /// Stores the value on the top of the stack in the reference. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property modification (e.g. x.property = y) // ------------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Convert the value to an object and store it in a temporary variable. var value = generator.CreateTemporaryVariable(typeof(object)); EmitConversion.ToAny(generator, valueType); generator.StoreVariable(value); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(value); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); } }
/// <summary> /// Generates 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> /// Stores the value on the top of the stack in the reference. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property modification (e.g. x.property = y) // ------------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Convert the value to an object and store it in a temporary variable. var value = generator.CreateTemporaryVariable(typeof(object)); EmitConversion.ToAny(generator, valueType); generator.StoreVariable(value); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValue("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // The temporary variable is no longer needed. generator.ReleaseTemporaryVariable(value); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call the indexer. EmitConversion.ToAny(generator, valueType); generator.LoadBoolean(optimizationInfo.StrictMode); generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_String); } }
/// <summary> /// Generates 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); // Generate a unique integer that is used as the cache key. int callSiteId = System.Threading.Interlocked.Increment(ref templateCallSiteId); // Call GetCachedTemplateStringsArray(ScriptEngine engine, int callSiteId) EmitHelpers.LoadScriptEngine(generator); generator.LoadInt32(callSiteId); generator.CallStatic(ReflectionHelpers.ReflectionHelpers_GetCachedTemplateStringsArray); // If that's null, do it the long way. var afterCreateTemplateStringsArray = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(afterCreateTemplateStringsArray); generator.Pop(); // Start emitting arguments for CreateTemplateStringsArray. EmitHelpers.LoadScriptEngine(generator); // int callSiteId generator.LoadInt32(callSiteId); // string[] strings generator.LoadInt32(templateLiteral.Strings.Count); generator.NewArray(typeof(string)); for (int i = 0; i < templateLiteral.Strings.Count; i++) { // strings[i] = templateLiteral.Strings[i] generator.Duplicate(); generator.LoadInt32(i); generator.LoadString(templateLiteral.Strings[i]); generator.StoreArrayElement(typeof(string)); } // string[] raw generator.LoadInt32(templateLiteral.RawStrings.Count); generator.NewArray(typeof(string)); for (int i = 0; i < templateLiteral.RawStrings.Count; i++) { // raw[i] = templateLiteral.RawStrings[i] generator.Duplicate(); generator.LoadInt32(i); generator.LoadString(templateLiteral.RawStrings[i]); generator.StoreArrayElement(typeof(string)); } // CreateTemplateStringsArray(ScriptEngine engine, int callSiteId, object[] strings, object[] rawStrings) generator.CallStatic(ReflectionHelpers.ReflectionHelpers_CreateTemplateStringsArray); generator.DefineLabelPosition(afterCreateTemplateStringsArray); // 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> /// Stores the value on the top of the stack in the reference. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to create a new property instead. </param> public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable) { // The value is initially on the top of the stack but is stored in this variable // at the last possible moment. ILLocalVariable value = null; var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfSet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // Get information about the variable. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { // The variable was declared in this scope. if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) variable.Store = generator.DeclareVariable(typeof(object), variable.Name); if (value == null) { // The value to store is on the top of the stack - convert it to the // storage type of the variable. EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo); } else { // The value to store is in a temporary variable. generator.LoadVariable(value); EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo); } // Store the value in the variable. generator.StoreVariable(variable.Store); } else if (variable.Writable == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // scope.Values[index] = value if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); } else { // The variable exists, but is read-only. // Pop the value off the stack (if it is still there). if (value == null) generator.Pop(); } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Set the value of the variable. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.LoadVariable(value); generator.Call(ReflectionHelpers.Scope_SetValue); generator.Branch(endOfSet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (value == null) { // The value to store is on the top of the stack - convert it to an // object and store it in a temporary variable. EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo); value = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(value); } if (scope.ParentScope == null) { // Optimization: if this is the global scope, use hidden classes to // optimize variable access. // Global variable modification // ---------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) // else // object.InlinePropertyValues[__object_property_cachedIndex] = value; // Get a reference to the global object. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // xxx = object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); if (throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // object.InlinePropertyValues[__object_property_cachedIndex] = value; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadVariable(value); generator.StoreArrayElement(typeof(object)); generator.Branch(endOfSet); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Slow route. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.LoadVariable(value); generator.LoadBoolean(optimizationInfo.StrictMode); if (scope.ParentScope == null && throwIfUnresolvable == false) { // Set the property value unconditionally. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); } else { // Set the property value if the property exists. generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists); // The return value is true if the property was defined, and false if it wasn't. generator.BranchIfTrue(endOfSet); } } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // The value might be still on top of the stack. if (value == null && scope == null) generator.Pop(); // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); // Release the temporary variables. if (value != null) generator.ReleaseTemporaryVariable(value); if (scopeVariable != null) generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(endOfSet); }
/// <summary> /// 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."); }