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