/// <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> /// 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> /// 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) { // If our host is a root then we are also a root. Expression prevRoot = optimizationInfo.RootExpression; Expression operand2 = this.GetOperand(1); Expression operand3 = this.GetOperand(2); if (StaticResult != null) { // Going one way or the other statically! // Convert it to a bool: bool result = TypeConverter.ToBoolean(StaticResult); if (result) { // if(true) if (prevRoot == this) { // Host is a root therefore we are one now: optimizationInfo.RootExpression = operand2; } // Generate code for the if clause: operand2.GenerateCode(generator, optimizationInfo); } else { // if(false) if (prevRoot == this) { // Host is a root therefore we are one now: optimizationInfo.RootExpression = operand3; } // Code for the else clause: operand3.GenerateCode(generator, optimizationInfo); } // Restore root: optimizationInfo.RootExpression = prevRoot; return; } // Emit the condition. var condition = this.GetOperand(0); condition.GenerateCode(generator, optimizationInfo); // Convert the condition to a boolean. EmitConversion.ToBool(generator, condition.GetResultType(optimizationInfo)); // Branch if the condition is false. var startOfElse = generator.CreateLabel(); generator.BranchIfFalse(startOfElse); // Calculate the result type. var outputType = this.GetResultType(optimizationInfo); if (prevRoot == this) { // Host is a root therefore we are one now: optimizationInfo.RootExpression = operand2; } // Emit the second operand and convert it to the result type. operand2.GenerateCode(generator, optimizationInfo); EmitConversion.Convert(generator, operand2.GetResultType(optimizationInfo), outputType, optimizationInfo); // Branch to the end. var end = generator.CreateLabel(); generator.Branch(end); generator.DefineLabelPosition(startOfElse); if (prevRoot == this) { // Host is a root therefore we are one now: optimizationInfo.RootExpression = operand3; } // Emit the third operand and convert it to the result type. operand3.GenerateCode(generator, optimizationInfo); EmitConversion.Convert(generator, operand3.GetResultType(optimizationInfo), outputType, optimizationInfo); // Define the end label. generator.DefineLabelPosition(end); // Restore root: optimizationInfo.RootExpression = prevRoot; }