/// <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> /// Gets the type that results from evaluating this expression. /// </summary> public override Type GetResultType(OptimizationInfo optimizationInfo) { // The result is either the type of the second operand or the third operand. var a = this.GetOperand(1).GetResultType(optimizationInfo); var b = this.GetOperand(2).GetResultType(optimizationInfo); if (a == b) { return(a); } if (PrimitiveTypeUtilities.IsNumeric(a) == true && PrimitiveTypeUtilities.IsNumeric(b) == true) { return(typeof(double)); } if (StaticResult != null) { // Convert it to a bool: bool result = TypeConverter.ToBoolean(StaticResult); if (result) { return(a); } return(b); } // Don't know if it'll be a or b, and they're distinctive types. return(typeof(object)); }
/// <summary> /// Generates CIL for the in operator. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateIn(ILGenerator generator, OptimizationInfo optimizationInfo) { if (optimizationInfo.RootExpression == this) { // Return isn't in use. Do nothing. Left.GenerateCode(generator, optimizationInfo); Right.GenerateCode(generator, optimizationInfo); generator.Pop(); generator.Pop(); return; } // Dynamic resolve required (The 'Evaluate' method above diverts away otherwise). // Engine required: EmitHelpers.LoadEngine(generator); // Emit the object: Right.GenerateCode(generator, optimizationInfo); // Emit the left-hand side expression and convert it to a string. Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, Left.GetResultType(optimizationInfo)); // Property name is now on the stack too. // Emit the property test: generator.Call(ReflectionHelpers.Object_HasProperty); }
public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Get the previous root - we'll be changing it: Expression prevRoot = optimizationInfo.RootExpression; // Labelled is now the root: Labelled.SetRoot(optimizationInfo); if (Labelled.DefaultBreakStatementBehaviour) { // Set up the information needed by the break statement. ILLabel endOfStatement = generator.CreateLabel(); optimizationInfo.PushBreakOrContinueInfo(labels, endOfStatement, null, true); Labelled.GenerateCode(generator, optimizationInfo); // Revert the information needed by the break statement. generator.DefineLabelPosition(endOfStatement); optimizationInfo.PopBreakOrContinueInfo(); } else { // Loop, For-In, For-Of or Switch only. optimizationInfo.Labels = labels; Labelled.GenerateCode(generator, optimizationInfo); } // Restore root: optimizationInfo.RootExpression = prevRoot; }
/// <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 (Variable == null) { // Create a new variable: Variable = Scope.AddVariable(Name, valueType, null); } else if (Variable.IsHoisted(Scope)) { Wrench.Log.Add("Attempted to set a hoisted variable. This is slow and is usually unnecessary. Ignoring it by treating it as a local."); // This happens in this situation: // function a(){ // var b="hello!"; // var test=function(){ // b="overwriting a hoisted variable here (Nitrassic treats it as a new local in test's scope)"; // }; // // Nitrassic is wrong if this happens: // test(); // console.log(b); // *should* be the value set from inside test // } // Create a new variable: Variable = Scope.AddVariable(Name, valueType, null); } // Output a set now: Variable.Set(generator, optimizationInfo, rIU, valueType, value); }
/// <summary> /// Generates CIL for an assignment expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="target"> The target to modify. </param> private void GenerateAssignment(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target) { // Load the value to assign. var rhs = GetOperand(1); // Potentially transferring a constant here. // Note that all of the others affect the source value in some way // so this is the only one which can potentially transfer a constant like this. object constValue = target.GetConstantValue(); // Store the value. target.GenerateSet(generator, optimizationInfo, optimizationInfo.RootExpression != this, rhs.GetResultType(optimizationInfo), delegate(bool two) { // Generate the code: if (constValue != null) { // Straight emit the constant value (Note that it could be a function - it handles that for us): EmitHelpers.EmitValue(generator, constValue); } else { rhs.GenerateCode(generator, optimizationInfo); } if (two) { // Duplicate the value so it remains on the stack afterwards. generator.Duplicate(); } }, optimizationInfo.StrictMode); }
/// <summary> /// Generates CIL for the 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 an unconditional branch. // Note: the continue statement might be branching from inside a try { } or finally { } // block to outside. EmitLongJump() handles this. optimizationInfo.EmitLongJump(generator, optimizationInfo.GetContinueTarget(this.Label)); }
/// <summary> /// Gets the type that results from evaluating this expression. /// </summary> public override Type GetResultType(OptimizationInfo optimizationInfo) { switch (this.OperatorType) { case OperatorType.Plus: case OperatorType.Minus: return(typeof(double)); case OperatorType.BitwiseNot: return(typeof(int)); case OperatorType.LogicalNot: return(typeof(bool)); case OperatorType.Void: return(typeof(Nitrassic.Undefined)); case OperatorType.Typeof: return(typeof(string)); case OperatorType.Delete: return(typeof(bool)); default: throw new NotImplementedException(string.Format("Unsupported operator {0}", this.OperatorType)); } }
/// <summary> /// Gets the type that results from evaluating this expression. /// </summary> public override Type GetResultType(OptimizationInfo optimizationInfo) { // Get the operand: var operand = this.GetRawOperand(0); // Get as a call expr: FunctionCallExpression function = operand as FunctionCallExpression; if (function != null) { // Set IsConstructCall so a potential resolve runs correctly: optimizationInfo.IsConstructCall = true; } Type fType = operand.GetResultType(optimizationInfo); if (function != null) { // Clear ICC: optimizationInfo.IsConstructCall = false; } if (function.IsUserDefined) { // It's user defined which means the return type is actually the instance type: fType = function.InstanceType(optimizationInfo.Engine); } return(fType); }
internal override void ResolveVariables(OptimizationInfo optimizationInfo) { // Apply var: Variable.ApplyType(optimizationInfo, typeof(string)); base.ResolveVariables(optimizationInfo); }
internal override void Set(ILGenerator generator, OptimizationInfo optimizationInfo, bool rIU, Type valueType, SetValueMethod value) { if (_Type == null) { _Type = valueType; } else if (_Type != valueType) { // Essentially declaring a new variable. _Type = valueType; Store = null; } // Declare an IL local variable if no storage location has been allocated yet. if (Store == null) { Store = generator.DeclareVariable(valueType, Name); } // Load the value: value(rIU); // Store the value in the variable. generator.StoreVariable(Store); }
/// <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) { // This code is only used for untagged template literals. // Tagged template literals are handled by FunctionCallExpression. // Load the values array onto the stack. generator.LoadInt32(this.Strings.Count + this.Values.Count); generator.NewArray(typeof(string)); for (int i = 0; i < this.Strings.Count; i++) { // Operands for StoreArrayElement() are: an array (string[]), index (int), value (string). // Store the string. generator.Duplicate(); generator.LoadInt32(i * 2); generator.LoadString(this.Strings[i]); generator.StoreArrayElement(typeof(string)); if (i == this.Strings.Count - 1) { break; } // Store the value. generator.Duplicate(); generator.LoadInt32(i * 2 + 1); Values[i].GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, Values[i].GetResultType(optimizationInfo)); generator.StoreArrayElement(typeof(string)); } // Call String.Concat(string[]) generator.CallStatic(ReflectionHelpers.String_Concat); }
/// <summary>Resolves the type of variable this is referencing.</summary> internal override void ResolveVariables(OptimizationInfo optimizationInfo) { if (_Variable != null) { return; } // Resolve kids: base.ResolveVariables(optimizationInfo); var scope = this.Scope; while (scope != null) { // Is the variable in this scope? _Variable = scope.GetVariable(Name); if (_Variable != null) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // The variable was found - no need to search any more parent scopes. return; } scope = scope.ParentScope; } }
/// <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) { // The left hand side needs to be a variable reference or member access. var target = this.GetOperand(0) as IReferenceExpression; switch (this.OperatorType) { case OperatorType.Assignment: // Standard assignment operator. GenerateAssignment(generator, optimizationInfo, target); break; case OperatorType.PostIncrement: GenerateIncrementOrDecrement(generator, optimizationInfo, target, true, true); break; case OperatorType.PostDecrement: GenerateIncrementOrDecrement(generator, optimizationInfo, target, true, false); break; case OperatorType.PreIncrement: GenerateIncrementOrDecrement(generator, optimizationInfo, target, false, true); break; case OperatorType.PreDecrement: GenerateIncrementOrDecrement(generator, optimizationInfo, target, false, false); break; default: // All other compound operators. GenerateCompoundAssignment(generator, optimizationInfo, target); break; } }
/// <summary> /// Gets the type that results from evaluating this expression. /// </summary> public override Type GetResultType(OptimizationInfo optimizationInfo) { var type = this.OperatorType; if (type == OperatorType.PostIncrement || type == OperatorType.PostDecrement || type == OperatorType.PreIncrement || type == OperatorType.PreDecrement) { // If the input var is an int, the out is an int too: Type input = GetOperand(0).GetResultType(optimizationInfo); if (input == typeof(int) || input == typeof(uint) || input == typeof(short) || input == typeof(ushort)) { return(input); } else if (input == typeof(double)) { return(typeof(double)); } else { throw new Exception("Unable to increment/ decrement that (not a suitable type). " + input); } } if (type == OperatorType.Assignment) { return(this.GetOperand(1).GetResultType(optimizationInfo)); } var compoundOperator = new BinaryExpression(GetCompoundBaseOperator(type), this.GetOperand(0), this.GetOperand(1)); return(compoundOperator.GetResultType(optimizationInfo)); }
/// <summary>Gets the compiled function for the given args set. Note that this args set /// is the actual types and always includes the 'this' keywords type.</summary> public Library.UserDefinedFunction GetCompiled(Type[] args, ScriptEngine engine, bool isConstructor) { int genCount = GeneratedMethods.Count; for (int i = 0; i < genCount; i++) { if (GeneratedMethods[i].ArgsEqual(args)) { // Got a match! Already compiled it. return(GeneratedMethods[i]); } } // Need to compile it now with our given arg types. int argCount = args == null?0:args.Length; // The source contains a fixed number of args; it goes up to this: int maxArg = Arguments.Count; // First, map args to a block of ArgVariable objects: ArgVariable[] argsSet = new ArgVariable[argCount]; for (int i = 0; i < argCount; i++) { // Setup the arg variable: ArgVariable curArg; if (i < maxArg) { // Use the existing args object so it correctly updates in the scope: curArg = Arguments[i]; } else { // Need to create a fake one: curArg = new ArgVariable("@unused-arg-" + i); } // Apply type (a little different here as we have to account for null too): curArg.RawType = args[i]; // Apply to set: argsSet[i] = curArg; } // If we're passing less args than the source supports, update the types of those unused args to being 'Undefined': for (int i = argCount; i < maxArg; i++) { Arguments[i].Type = typeof(Nitrassic.Undefined); } // Create info: OptimizationInfo info = new OptimizationInfo(engine); info.IsConstructor = isConstructor; // Generate it! return(GenerateCode(argsSet, info)); }
/// <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> /// Resolves variables and property references. /// </summary> internal virtual void ResolveVariables(OptimizationInfo optimizationInfo) { // Resolve all child nodes: foreach (AstNode child in ChildNodes) { // Resolve now: child.ResolveVariables(optimizationInfo); } }
public void TryApplyConstant(OptimizationInfo optimizationInfo, bool isConst) { Variable v = Variable; if (v != null) { v.IsConstant = isConst; } }
/// <summary> /// Gets the type that results from evaluating this expression. /// </summary> public override Type GetResultType(OptimizationInfo optimizationInfo) { if (HoistValues != null) { return(typeof(HoistFunctionReference)); } return(typeof(FunctionMethodGenerator)); }
private FunctionCallExpression AttemptConversion(OptimizationInfo optimizationInfo) { var operand = this.GetRawOperand(0); // Try a NameExpression which refs a constructor instead. NameExpression nameExpr = operand as NameExpression; if (nameExpr == null) { return(null); } // Get the type: Type type = nameExpr.Variable.Type; // Get the prototype; Library.Prototype proto = optimizationInfo.Engine.Prototypes.Get(type); // Get ctr object, which may be a set: object onCtr = proto.OnConstruct; if (onCtr == null) { throw new Exception("Didn't recognise that as a suitable constructable object"); } // Ok! It could be a set - let's check: MethodGroup group = onCtr as MethodGroup; System.Reflection.MethodBase resolvedMethod = null; if (group == null) { // It must be MethodBase - it can't be anything else: resolvedMethod = onCtr as System.Reflection.MethodBase; } else { // We have a group! Find the overload that we're after (no args were passed here): resolvedMethod = group.Match(null); } // Create a FunctionCallExpr. FunctionCallExpression function = new FunctionCallExpression(Operator.FunctionCall); // Apply resolved: function.ResolvedMethod = resolvedMethod; // Apply target: function.Push(operand); // Overwrite local operand: SetRawOperand(0, function); return(function); }
/// <summary> /// Parses the source text into an abstract syntax tree. /// </summary> public override void Parse(OptimizationInfo optimizationInfo) { using (var lexer = new Lexer(this.Engine, this.Source)) { var parser = new Parser(this.Engine, lexer, this.InitialScope, optimizationInfo, this.Options, CodeContext.Global); this.AbstractSyntaxTree = parser.Parse(); this.StrictMode = parser.StrictMode; this.MethodOptimizationHints = parser.MethodOptimizationHints; } }
/// <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) { // Inserts a breakpoint into the IL. generator.Breakpoint(); // When the debugger stops, it stops at the first instruction after the breakpoint. By // inserting a no-op operation the debugger will highlight the "debugger" statement // instead of the statement after the "debugger" statement. generator.NoOperation(); }
public void TryApplyConstant(OptimizationInfo optimizationInfo, bool isConst) { // Try loading a constant now: Variable v = ResolvedProperty; if (v != null) { v.IsConstant = isConst; } }
/// <summary> /// Deletes the reference and pushes <c>true</c> if the delete succeeded, or <c>false</c> /// if the delete failed. /// </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 void GenerateDelete(ILGenerator generator, OptimizationInfo optimizationInfo) { // Deleting a variable is not allowed in strict mode. if (optimizationInfo.StrictMode == true) { throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", string.Format("Cannot delete {0} because deleting a variable or argument is not allowed in strict mode", this.Name), 1, optimizationInfo.Source.Path, optimizationInfo.FunctionName); } // Always just false: generator.LoadBoolean(false); }
/// <summary>Updates the type of the property variable. Note that this does nothing if the property /// is fixed; i.e. it's built in.</summary> public void ApplyType(OptimizationInfo optimizationInfo, Type type) { if (ResolvedProperty == null) { // Unable to set it. return; } // Set the type: ResolvedProperty.Type = type; }
public object TryApplyConstant(OptimizationInfo optimizationInfo, Expression expr) { // Try loading a constant now: Variable v = Variable; if (v != null && v.TryLoadConstant(expr)) { return(v.ConstantValue); } return(null); }
/// <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 an increment or decrement 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> /// <param name="postfix"> <c>true</c> if this is the postfix version of the operator; /// <c>false</c> otherwise. </param> /// <param name="increment"> <c>true</c> if this is the increment operator; <c>false</c> if /// this is the decrement operator. </param> private void GenerateIncrementOrDecrement(ILGenerator generator, OptimizationInfo optimizationInfo, IReferenceExpression target, bool postfix, bool increment) { // Note: increment and decrement can produce a number that is out of range if the // target is of type Int32. The only time this should happen is for a loop variable // where the range has been carefully checked to make sure an out of range condition // cannot happen. // Store the value. target.GenerateSet(generator, optimizationInfo, optimizationInfo.RootExpression != this, target.GetResultType(optimizationInfo) == typeof(int) ? typeof(int) : typeof(double), delegate(bool two) { // Get the target value. target.GenerateGet(generator, optimizationInfo, true); // Convert it to a number. if (target.GetResultType(optimizationInfo) != typeof(int)) { EmitConversion.ToNumber(generator, target.GetResultType(optimizationInfo)); } // If this is PostIncrement or PostDecrement, duplicate the value so it can be produced as the return value. if (postfix && two) { generator.Duplicate(); } // Load the increment constant. if (target.GetResultType(optimizationInfo) == typeof(int)) { generator.LoadInt32(1); } else { generator.LoadDouble(1.0); } // Add or subtract the constant to the target value. if (increment == true) { generator.Add(); } else { generator.Subtract(); } // If this is PreIncrement or PreDecrement, duplicate the value so it can be produced as the return value. if (!postfix && two) { generator.Duplicate(); } }, optimizationInfo.StrictMode); }