/// <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> /// 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 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> /// 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> /// 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 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) { // 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) { // 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."); } }