/// <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 an assignment expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="target"> The target to modify. </param> private void GenerateAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Load the value to assign. var rhs = GetOperand(1); // Potentially transferring a constant here. // Note that all of the others affect the source value in some way // so this is the only one which can potentially transfer a constant like this. object constValue = target.GetConstantValue(); // Store the value. target.GenerateSet(generator, optimizationInfo, optimizationInfo.RootExpression != this, rhs.GetResultType(optimizationInfo), delegate(bool two) { // Generate the code: if (constValue != null) { // Straight emit the constant value (Note that it could be a function - it handles that for us): EmitHelpers.EmitValue(generator, constValue); } else { rhs.GenerateCode(generator, optimizationInfo); } if (two) { // Duplicate the value so it remains on the stack afterwards. generator.Duplicate(); } }, optimizationInfo.StrictMode); }
/// <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> /// Generates code to push the "this" value for a function call. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> public void GenerateThis(ILGenerator generator) { // Optimization: if there are no with scopes, simply emit undefined. bool scopeChainHasWithScope = false; var scope = this.Scope; do { if (scope is ObjectScope && ((ObjectScope)scope).ProvidesImplicitThisValue == true) { scopeChainHasWithScope = true; break; } scope = scope.ParentScope; } while (scope != null); if (scopeChainHasWithScope == false) { // No with scopes in the scope chain, use undefined as the "this" value. EmitHelpers.EmitUndefined(generator); return; } scope = this.Scope; do { DeclarativeScope dScope = scope as DeclarativeScope; if (dScope != null) { if (dScope.HasDeclaredVariable(this.Name)) { // The variable exists but declarative scopes always produce undefined for // the "this" value. EmitHelpers.EmitUndefined(generator); break; } } else { // Object scope - what object type is it for? Prototype proto = (scope as ObjectScope).ScopePrototype; _Variable = proto.GetProperty(Name); if (_Variable != null) { // Output a get now: _Variable.Get(generator); break; } } scope = scope.ParentScope; } while (scope != null); }
/// <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> /// 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) { // Parent root: Expression rootExpression = optimizationInfo.RootExpression; // Get an array of comma-delimited expressions. var items = this.Items; int max = items.Count; if (max == 0) { if (rootExpression != this) { // Some other expression is using '()' - emit an undefined: EmitHelpers.EmitUndefined(generator); } return; } if (rootExpression != this) { // Some other expression is the root. Return the last one. max--; } for (int i = 0; i < max; i++) { // Generate the code for the item: Expression expr = items[i]; // Update root - we don't want return values except for the last one: optimizationInfo.RootExpression = expr; // Generate the code: expr.GenerateCode(generator, optimizationInfo); } // Restore root: optimizationInfo.RootExpression = rootExpression; if (rootExpression != this) { // Return the last one // Generate code for the last item and return the value. items[items.Count - 1].GenerateCode(generator, optimizationInfo); } }
/// <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 (Variable == null) { // It's undefined: EmitHelpers.EmitUndefined(generator); return; } if (Variable.IsHoisted(Scope)) { // Hoist the variable: Variable = (Scope as DeclarativeScope).Function.Hoist(Variable); } // Output a get now: Variable.Get(generator); }
/// <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> /// Pops the value on the stack, converts it to the given type, 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="toType"> The type to convert to. </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 Convert(ILGenerator generator, Type fromType, Type toType, string path, string function) { // Check that a conversion is actually necessary. if (fromType == toType) { return; } if (toType == typeof(object)) { ToAny(generator, fromType); } else if (toType == typeof(Nitrassic.Undefined)) { generator.Pop(); EmitHelpers.EmitUndefined(generator); } else if (toType == typeof(Nitrassic.Null)) { generator.Pop(); EmitHelpers.EmitNull(generator); } else if (toType == typeof(bool)) { ToBool(generator, fromType); } else if (toType == typeof(long)) { generator.ConvertToInt64(); } else if (toType == typeof(ulong)) { generator.ConvertToUnsignedInt64(); } else if (toType == typeof(int)) { generator.ConvertToInt32(); } else if (toType == typeof(uint)) { generator.ConvertToUnsignedInt32(); } else if (toType == typeof(short)) { generator.ConvertToInt16(); } else if (toType == typeof(ushort)) { generator.ConvertToUnsignedInt16(); } else if (toType == typeof(sbyte)) { generator.ConvertToInt8(); } else if (toType == typeof(byte)) { generator.ConvertToUnsignedInt8(); } else if (toType == typeof(double)) { ToNumber(generator, fromType); } else if (toType == typeof(string)) { ToString(generator, fromType); } else if (toType == typeof(ConcatenatedString)) { ToConcatenatedString(generator, fromType); } else { throw new NotImplementedException(string.Format("Unsupported primitive type: {0}", toType)); } }
/// <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> /// 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> /// Emits the given value. Only possible for certain types. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="value"> The value to emit. </param> public static void EmitValue(ILGenerator generator, object value) { if (value == null) { generator.LoadNull(); } else if (value is Nitrassic.Undefined) { EmitHelpers.EmitUndefined(generator); } else if (value is System.Reflection.MethodBase) { generator.LoadToken(value as System.Reflection.MethodBase); generator.Call(ReflectionHelpers.MethodBase_GetMethod); } else if (value is MethodGroup) { throw new NotImplementedException("Can't emit methodGroups here"); } else if (value is FunctionMethodGenerator) { // body generator.LoadInt64((value as FunctionMethodGenerator).MethodID); generator.Call(ReflectionHelpers.MethodLookup_Load); } else { switch (Type.GetTypeCode(value.GetType())) { case TypeCode.Boolean: generator.LoadBoolean((bool)value); break; case TypeCode.Byte: generator.LoadInt32((byte)value); break; case TypeCode.Char: generator.LoadInt32((char)value); break; case TypeCode.Double: generator.LoadDouble((double)value); break; case TypeCode.Int16: generator.LoadInt32((short)value); break; case TypeCode.Int32: generator.LoadInt32((int)value); break; case TypeCode.Int64: generator.LoadInt64((long)value); break; case TypeCode.SByte: generator.LoadInt32((sbyte)value); break; case TypeCode.Single: generator.LoadDouble((float)value); break; case TypeCode.String: generator.LoadString((string)value); break; case TypeCode.UInt16: generator.LoadInt32((ushort)value); break; case TypeCode.UInt32: generator.LoadInt32((uint)value); break; case TypeCode.UInt64: generator.LoadInt64((ulong)value); break; case TypeCode.Object: case TypeCode.Empty: case TypeCode.DateTime: case TypeCode.DBNull: case TypeCode.Decimal: throw new NotImplementedException(string.Format("Cannot emit the value '{0}' (a " + value.GetType() + ")", value)); } } }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { if (optimizationInfo.IsConstructor) { return; } // Emit the return value. Type returnType = ReturnType; // Determine if this is the last statement in the function. BlockStatement hostBlock = optimizationInfo.AbstractSyntaxTree as BlockStatement; bool lastStatement; if (hostBlock != null && hostBlock.Statements.Count > 0 && hostBlock.Statements[hostBlock.Statements.Count - 1] == this) { lastStatement = true; } else { lastStatement = false; } // Current return var: var returnVar = optimizationInfo.ReturnVariable; if (Value != null) { // Emit the returned value: Value.GenerateCode(generator, optimizationInfo); // The first return statement initializes the variable that holds the return value. if (returnVar == null) { if (!lastStatement) { returnVar = generator.DeclareVariable(returnType, "returnValue"); optimizationInfo.ReturnVariable = returnVar; } } } else if (returnVar != null) { // We need to return undefined here, which will require changing the return type to object. // No value being set: EmitHelpers.EmitUndefined(generator); } if (returnVar != null) { // the return type here must be a child type of the return type. // If it isn't then this function returns different things. // Store the return value in a variable. generator.StoreVariable(returnVar); } // There is no need to jump to the end of the function if this is the last statement. if (!lastStatement) { // The first return statement that needs to branch creates the return label. This is // defined in FunctionmethodGenerator.GenerateCode at the end of the function. if (optimizationInfo.ReturnTarget == null) { optimizationInfo.ReturnTarget = generator.CreateLabel(); } // Branch to the end of the function. Note: the return statement might be branching // from inside a try { } or finally { } block to outside. EmitLongJump() handles this. optimizationInfo.EmitLongJump(generator, optimizationInfo.ReturnTarget); } }
/// <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 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) { // Get the target as a name expression: NameExpression nameExpr = Target as NameExpression; // Grab if this is a 'new' call: bool isConstructor = optimizationInfo.IsConstructCall; optimizationInfo.IsConstructCall = false; if (ResolvedMethod != null) { // We have a known method! if (UserDefined != null) { if (isConstructor) { // Generate code to produce the "this" value. Library.Prototype proto = UserDefined.GetInstancePrototype(optimizationInfo.Engine); // Create the object now: generator.NewObject(proto.TypeConstructor); // Duplicate (which will act as our return value): if (optimizationInfo.RootExpression != this) { generator.Duplicate(); } } else { // There are three cases for non-constructor calls. if (this.Target is NameExpression) { // 1. The function is a name expression (e.g. "parseInt()"). // In this case this = scope.ImplicitThisValue, if there is one, otherwise undefined. ((NameExpression)this.Target).GenerateThis(generator); } else if (this.Target is MemberAccessExpression) { // 2. The function is a member access expression (e.g. "Math.cos()"). // In this case this = Math. var baseExpression = ((MemberAccessExpression)this.Target).Base; baseExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, baseExpression.GetResultType(optimizationInfo)); } else { // 3. Neither of the above (e.g. "(function() { return 5 })()") // In this case this = undefined. EmitHelpers.EmitUndefined(generator); } } } // Emit the rest of the args: EmitArguments(generator, optimizationInfo); // Got a return type? Type returnType = GetResultType(optimizationInfo); // Then the call! if (typeof(System.Reflection.ConstructorInfo).IsAssignableFrom(ResolvedMethod.GetType())) { // Actual constructor call: generator.NewObject(ResolvedMethod as System.Reflection.ConstructorInfo); } else { // Ordinary method: generator.Call(ResolvedMethod); } if (isConstructor) { // Always a returned value here. Needed? if (optimizationInfo.RootExpression == this) { // Remove the return value: generator.Pop(); } } else { if (returnType == typeof(Nitrassic.Undefined)) { if (optimizationInfo.RootExpression != this) { // Put undef on the stack: EmitHelpers.EmitUndefined(generator); } } else if (optimizationInfo.RootExpression == this) { // Remove the return value: generator.Pop(); } } } else { // Either runtime resolve it or it's not actually a callable function throw new NotImplementedException("A function was called which was not supported (" + ToString() + ")"); } }
/// <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) { // Special-case the delete operator. if (this.OperatorType == OperatorType.Delete) { GenerateDelete(generator, optimizationInfo); return; } // Special-case the typeof operator. if (this.OperatorType == OperatorType.Typeof) { GenerateTypeof(generator, optimizationInfo); return; } // Load the operand onto the stack. this.Operand.GenerateCode(generator, optimizationInfo); // If we're not using the return value, pop and quit. if (optimizationInfo.RootExpression == this) { // Pop it and quit. generator.Pop(); return; } // Convert the operand to the correct type. switch (this.OperatorType) { case OperatorType.Plus: case OperatorType.Minus: EmitConversion.ToNumber(generator, this.Operand.GetResultType(optimizationInfo)); break; case OperatorType.BitwiseNot: EmitConversion.ToInt32(generator, this.Operand.GetResultType(optimizationInfo)); break; case OperatorType.LogicalNot: EmitConversion.ToBool(generator, this.Operand.GetResultType(optimizationInfo)); break; } // Apply the operator. switch (this.OperatorType) { case OperatorType.Plus: break; case OperatorType.Minus: generator.Negate(); break; case OperatorType.BitwiseNot: generator.BitwiseNot(); break; case OperatorType.LogicalNot: generator.LoadBoolean(false); generator.CompareEqual(); break; case OperatorType.Void: generator.Pop(); EmitHelpers.EmitUndefined(generator); break; default: throw new NotImplementedException(string.Format("Unsupported operator {0}", this.OperatorType)); } }
/// <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."); } }
/// <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> /// Generates CIL for the logical operators. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateLogical(ILGenerator generator, OptimizationInfo optimizationInfo) { // If either evaluates statically to 0 then we also know the correct return type. object leftEval = Left.Evaluate(); if (leftEval != null) { // RHS only. bool leftTrue = TypeConverter.ToBoolean(leftEval); // a && b // If a is false, don't do anything with b. // If a is true, emit b. // a || b // If a is false, emit b. If it's true, emit a. if (OperatorType == OperatorType.LogicalAnd && !leftTrue) { // Don't evaluate the RHS. Just emit a 'false' if one is needed. if (optimizationInfo.RootExpression != this) { // Emit the false: generator.LoadBoolean(false); } } else if (OperatorType == OperatorType.LogicalOr && leftTrue) { // Emit the left object only. if (optimizationInfo.RootExpression != this) { // Load it: EmitHelpers.EmitValue(generator, leftEval); } } else if (optimizationInfo.RootExpression == this) { // Emitting b (no return type required). // Right will be the root instead. optimizationInfo.RootExpression = Right; Right.GenerateCode(generator, optimizationInfo); optimizationInfo.RootExpression = this; } else { // Emitting b (return type required). // Output required. Right.GenerateCode(generator, optimizationInfo); } return; } // Evaluate B, just in case we're doing RHS only: object rightEval = Right.Evaluate(); // Get the statically-determined types of the left and right operands. Type leftType = this.Left.GetResultType(optimizationInfo); Type rightType = rightEval == null?this.Right.GetResultType(optimizationInfo) : rightEval.GetType(); // Load the left-hand side operand. this.Left.GenerateCode(generator, optimizationInfo); // Make sure the output type is consistant. if (leftType != rightType) { if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true) { EmitConversion.ToNumber(generator, leftType); leftType = typeof(double); } else { EmitConversion.ToAny(generator, leftType); leftType = typeof(object); } } // If this is an OR, we might be using the value currently on the stack if it's true. // So, duplicate: if (OperatorType == OperatorType.LogicalOr && optimizationInfo.RootExpression != this) { generator.Duplicate(); } // Convert to a boolean: EmitConversion.ToBool(generator, leftType); // If this is an AND, we might be using the value currently on the stack if it's false. // So, duplicate: if (OperatorType == OperatorType.LogicalAnd && optimizationInfo.RootExpression != this) { generator.Duplicate(); } // Stack contains: // OR: "left, (bool)left" // AND: "(bool)left, (bool)left" var endOfIf = generator.CreateLabel(); if (this.OperatorType == OperatorType.LogicalAnd) { generator.BranchIfFalse(endOfIf); } else { generator.BranchIfTrue(endOfIf); } if (optimizationInfo.RootExpression == this) { // Right hand side will now be the root (output is not in use). optimizationInfo.RootExpression = Right; Right.GenerateCode(generator, optimizationInfo); // Restore: optimizationInfo.RootExpression = this; } else { // Stack contains "left" which we don't need if we fall through here. Pop it off: generator.Pop(); // Output is in use. Right.GenerateCode(generator, optimizationInfo); // Make sure the output type is consistant. if (leftType != rightType) { if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true) { EmitConversion.ToNumber(generator, rightType); } else { EmitConversion.ToAny(generator, rightType); } } } // Define the label used above. generator.DefineLabelPosition(endOfIf); }
/// <summary> /// Generates CIL for 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); }