/// <summary> /// Generates CIL for the typeof 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> private void GenerateTypeof(ILGenerator generator, OptimizationInfo optimizationInfo) { if (this.Operand is NameExpression) { // Unresolvable references must return "undefined" rather than throw an error. ((NameExpression)this.Operand).GenerateGet(generator, optimizationInfo, false); } else { // Emit code for resolving the value of the operand. this.Operand.GenerateCode(generator, optimizationInfo); } if (optimizationInfo.RootExpression == this) { // Pop it and quit. generator.Pop(); return; } // Convert to System.Object. EmitConversion.ToAny(generator, this.Operand.GetResultType(optimizationInfo)); // Call TypeUtilities.TypeOf(operand). generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); }
/// <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 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 an array containing the argument values for a tagged template literal. /// </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="templateLiteral"> The template literal expression containing the parameter /// values. </param> internal void GenerateTemplateArgumentsArray(ILGenerator generator, OptimizationInfo optimizationInfo, TemplateLiteralExpression templateLiteral) { #warning change this. // Generate an array containing the value of each argument. generator.LoadInt32(templateLiteral.Values.Count + 1); generator.NewArray(typeof(object)); // Load the first parameter. generator.Duplicate(); generator.LoadInt32(0); // The first parameter to the tag function is an array of strings. var stringsExpression = new List <Expression>(templateLiteral.Strings.Count); foreach (var templateString in templateLiteral.Strings) { stringsExpression.Add(new LiteralExpression(templateString)); } new LiteralExpression(stringsExpression).GenerateCode(generator, optimizationInfo); generator.Duplicate(); // Now we need the name of the property. generator.LoadString("raw"); // Now generate an array of raw strings. var rawStringsExpression = new List <Expression>(templateLiteral.RawStrings.Count); foreach (var rawString in templateLiteral.RawStrings) { rawStringsExpression.Add(new LiteralExpression(rawString)); } new LiteralExpression(rawStringsExpression).GenerateCode(generator, optimizationInfo); // Freeze array by calling ObjectInstance Freeze(ObjectInstance). // generator.CallStatic(ReflectionHelpers.ObjectConstructor_Freeze); // Now store the raw strings as a property of the base strings array. // generator.LoadBoolean(optimizationInfo.StrictMode); // generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object); // Store in the array. generator.StoreArrayElement(typeof(object)); // Values are passed as subsequent parameters. for (int i = 0; i < templateLiteral.Values.Count; i++) { generator.Duplicate(); generator.LoadInt32(i + 1); templateLiteral.Values[i].GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, templateLiteral.Values[i].GetResultType(optimizationInfo)); generator.StoreArrayElement(typeof(object)); } }
/// <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) { // Note: we use GetRawOperand() so that grouping operators are not ignored. var operand = this.GetRawOperand(0); // There is only one operand, and it can be either a reference or a function call. // We need to split the operand into a function and some arguments. // If the operand is a reference, it is equivalent to a function call with no arguments. // Get as a call expr: FunctionCallExpression function = operand as FunctionCallExpression; System.Reflection.MethodBase resolvedMethod = null; if (function != null) { // Did it resolve? resolvedMethod = function.ResolvedMethod; } if (resolvedMethod != null) { // Awesome, we can pre-emit the construct call! // Set is constructor (immediately cleared by CallExpr's GenerateCode method): optimizationInfo.IsConstructCall = true; // Emit an ordinary call: function.GenerateCode(generator, optimizationInfo); return; } #warning incorrect. // Emit the function instance first. function.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, function.GetResultType(optimizationInfo)); }
/// <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 IL for the script. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> protected override void GenerateCode(ArgVariable[] arguments, ILGenerator generator, OptimizationInfo optimizationInfo) { // Method signature: object FunctionDelegate(object thisObj, object[] arguments) // Transfer the function name into the scope. if (!string.IsNullOrEmpty(Name) && IncludeNameInScope && !HasArgument(Name) && optimizationInfo.MethodOptimizationHints.HasVariable(Name)) { var functionName = new NameExpression(InitialScope, Name); functionName.ApplyType(optimizationInfo, typeof(object)); functionName.GenerateSet(generator, optimizationInfo, false, typeof(object), delegate(bool two) { generator.LoadInt64(MethodID); generator.Call(ReflectionHelpers.MethodLookup_Load); if (two) { // Duplicate it: generator.Duplicate(); } }, false); } // Transfer the arguments object into the scope. if (MethodOptimizationHints.HasArguments && !HasArgument("arguments")) { var argsSet = new NameExpression(this.InitialScope, "arguments"); argsSet.ApplyType(optimizationInfo, typeof(object)); argsSet.GenerateSet(generator, optimizationInfo, false, typeof(object), delegate(bool two) { // argumentValues // Build an object[] from the arg values. // Define an array: int argCount = (arguments == null)?0 : arguments.Length; generator.LoadInt32(argCount); generator.NewArray(typeof(object)); for (int a = 0; a < argCount; a++) { // One of many args: ArgVariable currentArg = arguments[a]; generator.Duplicate(); generator.LoadInt32(a); currentArg.Get(generator); EmitConversion.ToAny(generator, currentArg.Type); generator.StoreArrayElement(typeof(object)); } generator.NewObject(ReflectionHelpers.Arguments_Constructor); if (two) { generator.Duplicate(); } }, false); } // Temp cache return var/target: var retVar = optimizationInfo.ReturnVariable; var retTarg = optimizationInfo.ReturnTarget; optimizationInfo.ReturnVariable = null; optimizationInfo.ReturnTarget = null; // Initialize any declarations. (this.InitialScope as DeclarativeScope).GenerateDeclarations(generator, optimizationInfo); // Generate code for the body of the function. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Define the return target - this is where the return statement jumps to. // ReturnTarget can be null if there were no return statements. if (optimizationInfo.ReturnTarget != null) { generator.DefineLabelPosition(optimizationInfo.ReturnTarget); } // Load the return value. If the variable is null, there were no return statements. if (optimizationInfo.ReturnVariable != null) { // Return the value stored in the variable. Will be null if execution hits the end // of the function without encountering any return statements. generator.LoadVariable(optimizationInfo.ReturnVariable); } // Restore: optimizationInfo.ReturnVariable = retVar; optimizationInfo.ReturnTarget = retTarg; }
/// <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> /// 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 addition operation. /// </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 GenerateAdd(ILGenerator generator, OptimizationInfo optimizationInfo) { // Get the statically-determined types of the left and right operands. Type leftType = this.Left.GetResultType(optimizationInfo); Type rightType = this.Right.GetResultType(optimizationInfo); // The add operator adds two strings together if at least one of the operands // is a string, otherwise it adds two numbers. if (PrimitiveTypeUtilities.IsString(leftType) || PrimitiveTypeUtilities.IsString(rightType)) { // If at least one of the operands is a string, then the add operator concatenates. // Load the left-hand side onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Convert the operand to a concatenated string. EmitConversion.ToPrimitive(generator, leftType, PrimitiveTypeHint.None); EmitConversion.ToConcatenatedString(generator, leftType); // Load the right-hand side onto the stack. this.Right.GenerateCode(generator, optimizationInfo); if (rightType == typeof(string)) { // Concatenate the two strings. generator.Call(ReflectionHelpers.ConcatenatedString_Concatenate_String); } else if (rightType == typeof(ConcatenatedString)) { // Concatenate the two strings. generator.Call(ReflectionHelpers.ConcatenatedString_Concatenate_ConcatenatedString); } else { // Convert the operand to an object. EmitConversion.ToPrimitive(generator, rightType, PrimitiveTypeHint.None); EmitConversion.ToAny(generator, rightType); // Concatenate the two strings. generator.Call(ReflectionHelpers.ConcatenatedString_Concatenate_Object); } } else if (leftType != typeof(object) && leftType != typeof(Library.ObjectInstance) && rightType != typeof(object) && rightType != typeof(Library.ObjectInstance)) { // Neither of the operands are strings. // If the two types are numeric integers, retain the one with the most accuracy. Type numeric = TypeConverter.MostAccurateInteger(leftType, rightType); if (numeric == null) { // Load the left hand side onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Convert the operand to a number. EmitConversion.ToNumber(generator, leftType); // Load the right hand side onto the stack. this.Right.GenerateCode(generator, optimizationInfo); // Convert the operand to a number. EmitConversion.ToNumber(generator, rightType); // Add the two numbers. generator.Add(); } else { // Use them both converted to 'numeric' // Load the left hand side onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Convert the operand to a number. EmitConversion.ToNumber(generator, leftType, numeric); // Load the right hand side onto the stack. this.Right.GenerateCode(generator, optimizationInfo); // Convert the operand to a number. EmitConversion.ToNumber(generator, rightType, numeric); // Add the two numbers. generator.Add(); } } else { // It is unknown whether the operands are strings. // Load the left hand side onto the stack. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, leftType); // Load the right hand side onto the stack. this.Right.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, rightType); // Add the two objects. generator.Call(ReflectionHelpers.TypeUtilities_Add); } }
/// <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 a return value is not expected, generate only the side-effects. /*if (optimizationInfo.SuppressReturnValue == true) * { * this.GenerateSideEffects(generator, optimizationInfo); * return; * }*/ // Special case the addition operator. if (this.OperatorType == OperatorType.Add) { GenerateAdd(generator, optimizationInfo); return; } // Special case the instanceof operator. if (this.OperatorType == OperatorType.InstanceOf) { GenerateInstanceOf(generator, optimizationInfo); return; } // Special case the in operator. if (this.OperatorType == OperatorType.In) { GenerateIn(generator, optimizationInfo); return; } // Special case the relational operators. if (this.OperatorType == OperatorType.LessThan || this.OperatorType == OperatorType.LessThanOrEqual || this.OperatorType == OperatorType.GreaterThan || this.OperatorType == OperatorType.GreaterThanOrEqual) { GenerateRelational(generator, optimizationInfo); return; } // Special case the logical operators. if (this.OperatorType == OperatorType.LogicalAnd || this.OperatorType == OperatorType.LogicalOr) { GenerateLogical(generator, optimizationInfo); return; } // Load the left hand side onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Convert the left argument. switch (this.OperatorType) { // Arithmetic operations. case OperatorType.Subtract: case OperatorType.Multiply: case OperatorType.Divide: case OperatorType.Modulo: EmitConversion.ToNumber(generator, this.Left.GetResultType(optimizationInfo)); break; // Bitwise operations. case OperatorType.BitwiseAnd: case OperatorType.BitwiseOr: case OperatorType.BitwiseXor: case OperatorType.LeftShift: case OperatorType.SignedRightShift: case OperatorType.UnsignedRightShift: EmitConversion.ToInt32(generator, this.Left.GetResultType(optimizationInfo)); break; // Equality operations. case OperatorType.Equal: case OperatorType.StrictlyEqual: case OperatorType.NotEqual: case OperatorType.StrictlyNotEqual: EmitConversion.ToAny(generator, this.Left.GetResultType(optimizationInfo)); break; } // Load the right hand side onto the stack. this.Right.GenerateCode(generator, optimizationInfo); // If the return isn't in use, pop them both: if (optimizationInfo.RootExpression == this) { generator.Pop(); generator.Pop(); return; } // Convert the right argument. switch (this.OperatorType) { // Arithmetic operations. case OperatorType.Subtract: case OperatorType.Multiply: case OperatorType.Divide: case OperatorType.Modulo: EmitConversion.ToNumber(generator, this.Right.GetResultType(optimizationInfo)); break; // Bitwise operations. case OperatorType.BitwiseAnd: case OperatorType.BitwiseOr: case OperatorType.BitwiseXor: EmitConversion.ToInt32(generator, this.Right.GetResultType(optimizationInfo)); break; case OperatorType.LeftShift: case OperatorType.SignedRightShift: case OperatorType.UnsignedRightShift: EmitConversion.ToUInt32(generator, this.Right.GetResultType(optimizationInfo)); generator.LoadInt32(0x1F); generator.BitwiseAnd(); break; // Equality operations. case OperatorType.Equal: case OperatorType.StrictlyEqual: case OperatorType.NotEqual: case OperatorType.StrictlyNotEqual: EmitConversion.ToAny(generator, this.Right.GetResultType(optimizationInfo)); break; } // Apply the operator. switch (this.OperatorType) { // Arithmetic operations. case OperatorType.Subtract: generator.Subtract(); break; case OperatorType.Multiply: generator.Multiply(); break; case OperatorType.Divide: generator.Divide(); break; case OperatorType.Modulo: generator.Remainder(); break; // Bitwise operations. case OperatorType.BitwiseAnd: generator.BitwiseAnd(); break; case OperatorType.BitwiseOr: generator.BitwiseOr(); break; case OperatorType.BitwiseXor: generator.BitwiseXor(); break; // Shift operations. case OperatorType.LeftShift: generator.ShiftLeft(); break; case OperatorType.SignedRightShift: generator.ShiftRight(); break; case OperatorType.UnsignedRightShift: generator.ShiftRightUnsigned(); EmitConversion.ToNumber(generator, typeof(uint)); break; // Equality operations. case OperatorType.Equal: generator.Call(ReflectionHelpers.TypeComparer_Equals); break; case OperatorType.StrictlyEqual: generator.Call(ReflectionHelpers.TypeComparer_StrictEquals); break; case OperatorType.NotEqual: generator.Call(ReflectionHelpers.TypeComparer_Equals); generator.LoadBoolean(false); generator.CompareEqual(); break; case OperatorType.StrictlyNotEqual: generator.Call(ReflectionHelpers.TypeComparer_StrictEquals); generator.LoadBoolean(false); generator.CompareEqual(); break; default: throw new NotImplementedException(string.Format("Unsupported operator {0}", this.OperatorType)); } }
/// <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> /// 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> /// 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 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) { List <string> labels = optimizationInfo.Labels; optimizationInfo.Labels = null; // We need a label for each case clause and one for the default case. var jumpTargets = new ILLabel[this.CaseClauses.Count]; int defaultIndex = -1; ILLabel endOfSwitch = generator.CreateLabel(); // Generate code for the switch value. var startOfSwitch = generator.CreateLabel(); this.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Value.GetResultType(optimizationInfo)); // Save the switch value in a variable. var switchValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(switchValue); for (int i = 0; i < this.CaseClauses.Count; i++) { var caseClause = this.CaseClauses[i]; // Create a label for each clause. jumpTargets[i] = generator.CreateLabel(); if (caseClause.Value == null) { // This is a default clause. defaultIndex = i; continue; } // TypeComparer.StrictEquals(switchValue, caseValue) generator.LoadVariable(switchValue); caseClause.Value.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, caseClause.Value.GetResultType(optimizationInfo)); generator.Call(ReflectionHelpers.TypeComparer_StrictEquals); // if (TypeComparer.StrictEquals(switchValue, caseValue) == true) // goto case i generator.BranchIfTrue(jumpTargets[i]); } // None of the cases matched, jump to the default clause or the end of the switch. if (defaultIndex >= 0) { generator.Branch(jumpTargets[defaultIndex]); } else { generator.Branch(endOfSwitch); } // Get the previous root - we'll be changing it: Expression prevRoot = optimizationInfo.RootExpression; for (int i = 0; i < this.CaseClauses.Count; i++) { // Define a label at the start of the case clause. generator.DefineLabelPosition(jumpTargets[i]); // Set up the information needed by the break statement. optimizationInfo.PushBreakOrContinueInfo(labels, endOfSwitch, null, false); // Emit the case clause statements. foreach (var statement in this.CaseClauses[i].BodyStatements) { // Mark as root: statement.SetRoot(optimizationInfo); // Emit: statement.GenerateCode(generator, optimizationInfo); } // Revert the information needed by the break statement. optimizationInfo.PopBreakOrContinueInfo(); } // Define a label for the end of the switch statement. generator.DefineLabelPosition(endOfSwitch); // Release the switch value variable for use elsewhere. generator.ReleaseTemporaryVariable(switchValue); // Restore root: optimizationInfo.RootExpression = prevRoot; }
/// <summary> /// Generates CIL for the relational 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 GenerateRelational(ILGenerator generator, OptimizationInfo optimizationInfo) { // Get the statically-determined types of the left and right operands. Type leftType = this.Left.GetResultType(optimizationInfo); Type rightType = this.Right.GetResultType(optimizationInfo); // The relational operators compare strings if both of the operands are strings. if (leftType == typeof(string) && rightType == typeof(string)) { // Both of the operands are strings. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); if (optimizationInfo.RootExpression == this) { generator.Pop(); generator.Pop(); return; } // Compare the two strings. generator.Call(ReflectionHelpers.String_CompareOrdinal); switch (this.OperatorType) { case OperatorType.LessThan: generator.LoadInt32(0); generator.CompareLessThan(); break; case OperatorType.LessThanOrEqual: generator.LoadInt32(1); generator.CompareLessThan(); break; case OperatorType.GreaterThan: generator.LoadInt32(0); generator.CompareGreaterThan(); break; case OperatorType.GreaterThanOrEqual: generator.LoadInt32(-1); generator.CompareGreaterThan(); break; } } else if (leftType == typeof(int) && rightType == typeof(int)) { // Both of the operands are integers. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); if (optimizationInfo.RootExpression == this) { generator.Pop(); generator.Pop(); return; } // Compare the two numbers. switch (this.OperatorType) { case OperatorType.LessThan: generator.CompareLessThan(); break; case OperatorType.GreaterThan: generator.CompareGreaterThan(); break; case OperatorType.LessThanOrEqual: // a <= b <--> (a > b) == false generator.CompareGreaterThan(); generator.LoadBoolean(false); generator.CompareEqual(); break; case OperatorType.GreaterThanOrEqual: // a >= b <--> (a < b) == false generator.CompareLessThan(); generator.LoadBoolean(false); generator.CompareEqual(); break; } } else if (PrimitiveTypeUtilities.IsNumeric(leftType) || PrimitiveTypeUtilities.IsNumeric(rightType)) { // At least one of the operands is a number. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); // Convert the operand to a number. EmitConversion.ToNumber(generator, leftType); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); if (optimizationInfo.RootExpression == this) { generator.Pop(); generator.Pop(); return; } // Convert the operand to a number. EmitConversion.ToNumber(generator, rightType); // Compare the two numbers. switch (this.OperatorType) { case OperatorType.LessThan: generator.CompareLessThan(); break; case OperatorType.GreaterThan: generator.CompareGreaterThan(); break; case OperatorType.LessThanOrEqual: // a <= b <--> (a > b) == false generator.CompareGreaterThanUnsigned(); generator.LoadBoolean(false); generator.CompareEqual(); break; case OperatorType.GreaterThanOrEqual: // a >= b <--> (a < b) == false generator.CompareLessThanUnsigned(); generator.LoadBoolean(false); generator.CompareEqual(); break; } } else { // It is unknown whether one of the operands is a string. // Load the left hand side operand onto the stack. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, leftType); // Load the right hand side operand onto the stack. this.Right.GenerateCode(generator, optimizationInfo); if (optimizationInfo.RootExpression == this) { generator.Pop(); generator.Pop(); return; } EmitConversion.ToAny(generator, rightType); switch (this.OperatorType) { case OperatorType.LessThan: generator.Call(ReflectionHelpers.TypeComparer_LessThan); break; case OperatorType.LessThanOrEqual: generator.Call(ReflectionHelpers.TypeComparer_LessThanOrEqual); break; case OperatorType.GreaterThan: generator.Call(ReflectionHelpers.TypeComparer_GreaterThan); break; case OperatorType.GreaterThanOrEqual: generator.Call(ReflectionHelpers.TypeComparer_GreaterThanOrEqual); break; } } }
/// <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."); } }