/// <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) { if (optimizationInfo.RootExpression == this) { // Return isn't in use. Do nothing. Left.GenerateCode(generator, optimizationInfo); Right.GenerateCode(generator, optimizationInfo); generator.Pop(); generator.Pop(); return; } // Dynamic resolve required (The 'Evaluate' method above diverts away otherwise). // Engine required: EmitHelpers.LoadEngine(generator); // Emit the object: Right.GenerateCode(generator, optimizationInfo); // Emit the left-hand side expression and convert it to a string. Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, Left.GetResultType(optimizationInfo)); // Property name is now on the stack too. // Emit the property test: generator.Call(ReflectionHelpers.Object_HasProperty); }
/// <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) { // Setup the method info. // this.context.SetupMethod(optimizationInfo); // Add the generated method to the nested function list. if (optimizationInfo.NestedFunctions == null) { optimizationInfo.NestedFunctions = new List <FunctionMethodGenerator>(); } optimizationInfo.NestedFunctions.Add(context); // Add all the nested methods to the parent list. // Note: it's used to compute which variables are being hoisted. if (this.context.Dependencies != null) { foreach (var nestedFunctionExpression in this.context.Dependencies) { optimizationInfo.NestedFunctions.Add(nestedFunctionExpression); } } if (HoistValues != null) { // Script engine: EmitHelpers.LoadEngine(generator); // Method ID: generator.LoadInt64(context.MethodID); generator.LoadInt32(HoistValues.Count); // Hoist vars array: generator.NewArray(typeof(object)); // Load each one: for (int i = 0; i < HoistValues.Count; i++) { generator.Duplicate(); generator.LoadInt32(i); // Load the variable value: Type type = HoistValues[i].Get(generator); // Box if necessary: EmitConversion.ToAny(generator, type); generator.StoreArrayElement(typeof(object)); } generator.NewObject(ReflectionHelpers.HoistFunctionReference_Constructor); } else { // body generator.LoadInt64(context.MethodID); generator.Call(ReflectionHelpers.MethodLookup_Load); } }
/// <summary> /// Emits a JavaScriptException. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="name"> The type of error to generate. </param> /// <param name="message"> The error message. </param> /// <param name="path"> The path of the javascript source file that is currently executing. </param> /// <param name="function"> The name of the currently executing function. </param> /// <param name="line"> The line number of the statement that is currently executing. </param> public static void EmitThrow(ILGenerator generator, string name, string message, string path, string function, int line) { EmitHelpers.LoadEngine(generator); generator.LoadString(name); generator.LoadString(message); generator.LoadInt32(line); generator.LoadStringOrNull(path); generator.LoadStringOrNull(function); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Emit code to throw the given value. EmitHelpers.LoadEngine(generator); this.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Value.GetResultType(optimizationInfo)); generator.LoadInt32(1); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Object); generator.Throw(); }
/// <summary> /// Pops the value on the stack, converts it to a javascript object, then pushes the result /// onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> /// <param name="path"> The path of the javascript source file that is currently executing. </param> /// <param name="function"> The name of the currently executing function. </param> public static void ToPrototype(ILGenerator generator, Type fromType, string path, string function) { if (fromType == typeof(Nitrassic.Undefined)) { // Converting from undefined always throws an exception. EmitHelpers.EmitThrow(generator, "TypeError", "Undefined cannot be converted to an object", path, function, 1); } else if (fromType == typeof(Nitrassic.Null)) { // Converting from null always throws an exception. EmitHelpers.EmitThrow(generator, "TypeError", "Null cannot be converted to an object", path, function, 1); } else { // Otherwise, fall back to calling TypeConverter.ToPrototype() ToAny(generator, fromType); EmitHelpers.LoadEngine(generator); generator.Call(ReflectionHelpers.TypeConverter_ToPrototype); } }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Get the previous root - we'll be changing it: Expression prevRoot = optimizationInfo.RootExpression; List <string> labels = optimizationInfo.Labels; optimizationInfo.Labels = null; // Construct a loop expression. // var enumerator = TypeUtilities.EnumeratePropertyNames(rhs).GetEnumerator(); // while (true) { // continue-target: // if (enumerator.MoveNext() == false) // goto break-target; // lhs = enumerator.Current; // // <body statements> // } // break-target: // Call IEnumerable<string> EnumeratePropertyNames(ScriptEngine engine, object obj) // optimizationInfo.MarkSequencePoint(generator, this.TargetObjectSourceSpan); EmitHelpers.LoadEngine(generator); this.TargetObject.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.TargetObject.GetResultType(optimizationInfo)); generator.Call(ReflectionHelpers.TypeUtilities_EnumeratePropertyNames); // Store the enumerator in a temporary variable. var enumerator = generator.CreateTemporaryVariable(typeof(IEnumerator <string>)); generator.StoreVariable(enumerator); var breakTarget = generator.CreateLabel(); var continueTarget = generator.DefineLabelPosition(); // Emit debugging information. // if (optimizationInfo.DebugDocument != null) // generator.MarkSequencePoint(optimizationInfo.DebugDocument, this.VariableSourceSpan); // if (enumerator.MoveNext() == false) // goto break-target; generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_MoveNext); generator.BranchIfFalse(breakTarget); // lhs = enumerator.Current; this.Variable.GenerateSet(generator, optimizationInfo, false, typeof(string), delegate(bool two){ generator.LoadVariable(enumerator); generator.Call(ReflectionHelpers.IEnumerator_Current); if (two) { generator.Duplicate(); } }, false); // Emit the body statement(s). optimizationInfo.PushBreakOrContinueInfo(labels, breakTarget, continueTarget, false); // Mark the body as root: Body.SetRoot(optimizationInfo); Body.GenerateCode(generator, optimizationInfo); optimizationInfo.PopBreakOrContinueInfo(); generator.Branch(continueTarget); generator.DefineLabelPosition(breakTarget); // Restore root: optimizationInfo.RootExpression = prevRoot; }
/// <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) { if (optimizationInfo.RootExpression == this) { Left.GenerateCode(generator, optimizationInfo); Right.GenerateCode(generator, optimizationInfo); generator.Pop(); generator.Pop(); return; } // Emit the left-hand side expression and convert it to an object. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Left.GetResultType(optimizationInfo)); // 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.GetResultType(optimizationInfo)); // 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.LoadEngine(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.LoadInt32(1); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(rightValue); // Emit the engine for FunctionInstance_HasInstance: EmitHelpers.LoadEngine(generator); // 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> /// Emits the arguments set. /// </summary> private void EmitArguments(ILGenerator generator, OptimizationInfo optimizationInfo) { // Get the args: IList <Expression> arguments = null; Expression argumentsOperand = null; if (OperandCount > 1) { argumentsOperand = this.GetRawOperand(1); ListExpression argList = argumentsOperand as ListExpression; if (argList != null) { // Multiple parameters were recieved. arguments = argList.Items; // Set the operand to null so it doesn't try to emit it as a single arg: argumentsOperand = null; } } int paraCount = 0; int parameterOffset = 0; bool staticMethod = false; System.Reflection.ParameterInfo[] paraSet = null; IList <ArgVariable> argsSet = null; if (UserDefined == null) { // - Is the first arg ScriptEngine? // - Does it have thisObj / does it want an instance object? paraSet = ResolvedMethod.GetParameters(); paraCount = paraSet.Length; staticMethod = ResolvedMethod.IsStatic || ResolvedMethod.IsConstructor; if (paraSet.Length > 0) { if (paraSet[0].ParameterType == typeof(ScriptEngine)) { // Emit an engine reference now: EmitHelpers.LoadEngine(generator); parameterOffset++; } if (paraSet.Length > parameterOffset && paraSet[parameterOffset].Name == "thisObj") { // It's acting like an instance method. parameterOffset++; staticMethod = false; } } if (!staticMethod) { // Generate the 'this' ref: var baseExpression = ((MemberAccessExpression)this.Target).Base; baseExpression.GenerateCode(generator, optimizationInfo); } } else { // These are always static. paraCount = UserDefined.Arguments.Count; argsSet = UserDefined.Arguments; // Skip 'this' - it's emitted separately: parameterOffset = 1; } // Next, we're matching params starting from parameterOffset with the args, // type casting if needed. for (int i = parameterOffset; i < paraCount; i++) { Expression expression = null; object defaultValue = null; Type paramType = null; if (paraSet == null) { // Get the type: paramType = argsSet[i].Type; } else { // Get the parameter info: var param = paraSet[i]; // Get the parameters type: paramType = param.ParameterType; // Get the default value: defaultValue = param.RawDefaultValue; // Is it a params array? if (Attribute.IsDefined(param, typeof(ParamArrayAttribute))) { // It's always an array - get the element type: paramType = paramType.GetElementType(); // For each of the remaining args.. int offset = i - parameterOffset; int argCount = 0; if (arguments != null) { // Get the full count: argCount = arguments.Count; } else if (argumentsOperand != null) { // Just one arg and it's still hanging around. argCount = offset + 1; } // Define an array: generator.LoadInt32(argCount); generator.NewArray(paramType); for (int a = offset; a < argCount; a++) { if (arguments != null) { // One of many args: expression = arguments[a]; } else { // Just one arg: expression = argumentsOperand; } generator.Duplicate(); generator.LoadInt32(a - offset); expression.GenerateCode(generator, optimizationInfo); Type res = expression.GetResultType(optimizationInfo); EmitConversion.Convert(generator, res, paramType); generator.StoreArrayElement(paramType); } // All done - can't be anymore. break; } } if (arguments != null && (i - parameterOffset) <= arguments.Count) { // Get one of many args: expression = arguments[i - parameterOffset]; } else if (argumentsOperand != null) { // Just the one argument. expression = argumentsOperand; // By setting it to null after, it can't get emitted again // (in the event that this method actually accepts >1 args) argumentsOperand = null; } if (expression == null) { // Emit whatever the default is for the parameters type: if (defaultValue != null) { // Emit the default value: EmitHelpers.EmitValue(generator, defaultValue); } else if (paramType.IsValueType) { // E.g. an integer 0 EmitHelpers.EmitValue(generator, Activator.CreateInstance(paramType)); } else { // Just a null (a real one): generator.LoadNull(); } } else if (expression is TemplateLiteralExpression) { // Tagged template literal. TemplateLiteralExpression templateLiteral = (TemplateLiteralExpression)expression; GenerateTemplateArgumentsArray(generator, optimizationInfo, templateLiteral); return; } else { // Output the arg: expression.GenerateCode(generator, optimizationInfo); // Convert: EmitConversion.Convert(generator, expression.GetResultType(optimizationInfo), paramType); } } }
/// <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, bool rIU, Type valueType, SetValueMethod value, bool throwIfUnresolvable) { if (ResolvedProperty == null) { // Dynamic property access // ----------------------- // xxx = object.Set(x) // Load the left-hand side: var lhs = this.GetOperand(0); // -- Begin args for Prototype.SetPropertyValue -- // Script engine (engine): EmitHelpers.LoadEngine(generator); // Put LHS object onto stack now (thisObj): lhs.GenerateCode(generator, optimizationInfo); // What type have we now got on the stack? Typically expected to be 'object'. Type lhsType = lhs.GetResultType(optimizationInfo); // Ensure it's boxed (still thisObj): EmitConversion.ToAny(generator, lhsType); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.GetResultType(optimizationInfo)); if (rIU) { // Output the value now (twice): value(true); // We now have [obj][value][value] on the stack. // Calling the set method would fail (as it'll operate on the duplicated value). // So, we have to pop one off and re-add it after. // In order for SetValue to work, we need to shove the 2nd copy into a temp variable. ILLocalVariable localVar = generator.DeclareVariable(valueType); // Store into the local: generator.StoreVariable(localVar); // Set the value: generator.Call(ReflectionHelpers.Object_SetPropertyValue); // Load from the local: generator.LoadVariable(localVar); } else { // Output the value now: value(false); // Set the value: generator.Call(ReflectionHelpers.Object_SetPropertyValue); } } else if (isArrayIndex) { // Array indexer // ------------- // object[index] = x // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); // Load the right-hand side and convert to int32/uint32/whatever the indexer function wants. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); // Convert the index: EmitConversion.Convert(generator, rhs.GetResultType(optimizationInfo), ResolvedProperty.FirstIndexType); // Call set: ResolvedProperty.Set(generator, optimizationInfo, rIU, valueType, value); } else { // Named property modification (e.g. x.property = y) // ------------------------------------------------- if (ResolvedProperty.HasEngine) { // Emit the engine ref: EmitHelpers.LoadEngine(generator); } if (ResolvedProperty.HasAccessor) { // Load the left-hand side: var lhs = GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); } // Target object is now on the stack. // Set it: ResolvedProperty.Set(generator, optimizationInfo, rIU, valueType, value); } }
/// <summary> /// Pushes the value of the reference onto the stack. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { if (ResolvedProperty == null) { // Dynamic access. // optimizationInfo.TypeError("Attempted to get a property dynamically. Currently unsupported."); // Load the left-hand side: var lhs = this.GetOperand(0); // -- Begin args for Prototype.GetPropertyValue -- // Script engine (engine): EmitHelpers.LoadEngine(generator); // Emit LHS to the stack (thisObj): lhs.GenerateCode(generator, optimizationInfo); // What type have we now got on the stack? Expected to be just 'object'. Type lhsType = lhs.GetResultType(optimizationInfo); // Ensure it's boxed (still thisObj): EmitConversion.ToAny(generator, lhsType); // Load the property name and convert to a string (property). var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.GetResultType(optimizationInfo)); // Get the value: generator.Call(ReflectionHelpers.Object_GetPropertyValue); // Either it's now on the stack, or we threw a null ref. } else if (isArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); // Load the right-hand side and convert to [theTypeHere] (typically int32). var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); // Convert the index: EmitConversion.Convert(generator, rhs.GetResultType(optimizationInfo), ResolvedProperty.FirstIndexType); // Emit a get for the indexer: ResolvedProperty.Get(generator); } else { // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); if (ResolvedProperty != null) { if (ResolvedProperty.HasEngine) { // Emit the engine ref: EmitHelpers.LoadEngine(generator); } if (ResolvedProperty.HasAccessor) { // Emit the 'this' obj: lhs.GenerateCode(generator, optimizationInfo); } // Emit a get: ResolvedProperty.Get(generator); } } }
/// <summary> /// Generates CIL for the expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Literals cannot have side-effects so if a return value is not expected then generate // nothing. if (optimizationInfo.RootExpression == this) { 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.OnConstruct(source, flags) EmitHelpers.LoadPrototypes(generator); generator.LoadField(ReflectionHelpers.PrototypeLookup_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.OnConstruct(sharedRegExp, flags) EmitHelpers.LoadPrototypes(generator); generator.LoadField(ReflectionHelpers.PrototypeLookup_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.LoadEngine(generator); // 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.GetResultType(optimizationInfo)); } // 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; // We'll generate a prototype and a custom object type for it: Library.Prototype proto = CreatedPrototype; // Create a new object. generator.NewObject(proto.TypeConstructor); foreach (var keyValuePair in properties) { string propertyName = keyValuePair.Key.ToString(); Expression propertyValue = keyValuePair.Value; // Duplicate the object ref: generator.Duplicate(); // Add a new property to the object. Type valueType = propertyValue.GetResultType(optimizationInfo); // Get the property: Library.PropertyVariable pv = proto.GetProperty(propertyName); // Set it: pv.Set(generator, optimizationInfo, false, valueType, delegate(bool two){ if (pv.IsConstant) { // Emit the constant: EmitHelpers.EmitValue(generator, pv.ConstantValue); } else { // Write the value to set now: propertyValue.GenerateCode(generator, optimizationInfo); } // Note: This one always ignores 'two' }); } } else { throw new NotImplementedException("Unknown literal type."); } }