/// <summary> /// Pops the value on the stack, converts it to an object, then pushes the result onto the /// stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> public static void ToAny(ILGenerator generator, Type fromType) { if (PrimitiveTypeUtilities.IsValueType(fromType)) { generator.Box(fromType); } }
/// <summary> /// Pops the value on the stack, converts it to a concatenated string, then pushes the result /// onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> public static void ToConcatenatedString(ILGenerator generator, Type fromType) { // Check that a conversion is actually necessary. if (fromType == typeof(ConcatenatedString)) { return; } if (fromType == typeof(Nitrassic.Undefined) || fromType == typeof(Nitrassic.Null) || fromType == typeof(bool) || fromType == typeof(string)) { // Convert as per ToString, then create a new ConcatenatedString instance. ToString(generator, fromType); generator.NewObject(ReflectionHelpers.ConcatenatedString_Constructor_String); } else if (fromType == typeof(int) || fromType == typeof(uint) || fromType == typeof(double) || fromType == typeof(object) || fromType == typeof(Library.ObjectInstance)) { // Otherwise, fall back to calling TypeConverter.ToConcatenatedString() if (PrimitiveTypeUtilities.IsValueType(fromType)) { generator.Box(fromType); } generator.Call(ReflectionHelpers.TypeConverter_ToConcatenatedString); } else { throw new NotImplementedException(string.Format("Unsupported primitive type: {0}", fromType)); } }
/// <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> /// Pops the value on the stack, converts it to a string, then pushes the result onto the /// stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> public static void ToString(ILGenerator generator, Type fromType) { // Check that a conversion is actually necessary. if (fromType == typeof(string)) { return; } if (fromType == typeof(Nitrassic.Undefined)) { // Converting from undefined produces "undefined". generator.Pop(); generator.LoadString("undefined"); } else if (fromType == typeof(Nitrassic.Null)) { // Converting from null produces "null". generator.Pop(); generator.LoadString("null"); } else if (fromType == typeof(bool)) { // Converting from a boolean produces "false" if the boolean is false, or "true" if the boolean is true. var elseClause = generator.CreateLabel(); var endOfIf = generator.CreateLabel(); generator.BranchIfFalse(elseClause); generator.LoadString("true"); generator.Branch(endOfIf); generator.DefineLabelPosition(elseClause); generator.LoadString("false"); generator.DefineLabelPosition(endOfIf); } else if (fromType == typeof(ConcatenatedString)) { generator.Call(ReflectionHelpers.ConcatenatedString_ToString); } else { // Otherwise, fall back to calling TypeConverter.ToString() if (PrimitiveTypeUtilities.IsValueType(fromType)) { generator.Box(fromType); } generator.Call(ReflectionHelpers.TypeConverter_ToString); } }
/// <summary> /// Pops the value on the stack, converts it to a primitive value, then pushes the result /// onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> /// <param name="preferredType"> Specifies whether toString() or valueOf() should be /// preferred when converting to a primitive. </param> public static void ToPrimitive(ILGenerator generator, Type fromType, PrimitiveTypeHint preferredType) { if (fromType == typeof(Nitrassic.Undefined) || fromType == typeof(Nitrassic.Null) || fromType == typeof(bool) || fromType == typeof(string) || fromType == typeof(ConcatenatedString) || fromType == typeof(int) || fromType == typeof(uint) || fromType == typeof(double)) { return; } else if (fromType == typeof(object) || fromType == typeof(Library.ObjectInstance)) { // Otherwise, fall back to calling TypeConverter.ToPrimitive() if (PrimitiveTypeUtilities.IsValueType(fromType)) { generator.Box(fromType); } generator.LoadInt32((int)preferredType); generator.Call(ReflectionHelpers.TypeConverter_ToPrimitive); } else { throw new NotImplementedException("Unsupported primitive type: " + fromType); } }
/// <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 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 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> /// Gets the type that results from evaluating this expression. /// </summary> public override Type GetResultType(OptimizationInfo optimizationInfo) { var type = this.OperatorType; switch (this.OperatorType) { // Add case OperatorType.Add: { var lhs = this.Left.GetResultType(optimizationInfo); var rhs = this.Right.GetResultType(optimizationInfo); if (lhs == typeof(string) || rhs == typeof(string)) { return(typeof(ConcatenatedString)); } if (lhs == typeof(ConcatenatedString) || rhs == typeof(ConcatenatedString)) { return(typeof(ConcatenatedString)); } // If the two types are numeric integers, retain the one with the most accuracy. Type numeric = TypeConverter.MostAccurateInteger(lhs, rhs); if (numeric != null) { return(numeric); } if (lhs == typeof(object) || lhs == typeof(Library.ObjectInstance) || rhs == typeof(object) || rhs == typeof(Library.ObjectInstance)) { return(typeof(object)); } return(typeof(double)); } // Arithmetic operations. case OperatorType.Subtract: case OperatorType.Multiply: case OperatorType.Divide: case OperatorType.Modulo: return(typeof(double)); // Bitwise operations. case OperatorType.BitwiseAnd: case OperatorType.BitwiseOr: case OperatorType.BitwiseXor: case OperatorType.LeftShift: case OperatorType.SignedRightShift: return(typeof(int)); case OperatorType.UnsignedRightShift: return(typeof(double)); // Relational operations. case OperatorType.LessThan: case OperatorType.LessThanOrEqual: case OperatorType.GreaterThan: case OperatorType.GreaterThanOrEqual: return(typeof(bool)); // Equality operations. case OperatorType.Equal: case OperatorType.StrictlyEqual: case OperatorType.NotEqual: case OperatorType.StrictlyNotEqual: return(typeof(bool)); // Logical operations. case OperatorType.LogicalAnd: { var lhs = this.Left.GetResultType(optimizationInfo); var rhs = this.Right.GetResultType(optimizationInfo); // If lhs is true (regardless of whatever it's type is) then we respond with rhs. // If it's false, we respond with typeof(bool). // Try and eval left statically: object leftEval = Left.Evaluate(); if (leftEval == null) { // Can't statically eval it. // If rhs is a bool then great, we know for sure we're returning a bool. if (rhs == typeof(bool)) { return(typeof(bool)); } // Either a bool or rhs. For now this is an error condition. optimizationInfo.TypeError( "Ambiguous type && statement. It returns either a boolean or '" + rhs + "'. Add ==true to the right hand side to make sure it only returns a boolean." ); return(typeof(object)); } if (leftEval != null && TypeConverter.ToBoolean(leftEval)) { // Ok! We'll be returning rhs no matter what. return(rhs); } // Left eval was false so we'll be returning false. return(typeof(bool)); } case OperatorType.LogicalOr: { // The result is either the left-hand side or the right-hand side. var lhs = this.Left.GetResultType(optimizationInfo); var rhs = this.Right.GetResultType(optimizationInfo); if (lhs == rhs) { return(lhs); } if (PrimitiveTypeUtilities.IsNumeric(lhs) && PrimitiveTypeUtilities.IsNumeric(rhs)) { return(typeof(double)); } // If either evaluates statically to 0 then we also know the correct return type. object leftEval = Left.Evaluate(); if (leftEval != null && !TypeConverter.ToBoolean(leftEval)) { return(rhs); } object rightEval = Right.Evaluate(); if (rightEval != null && !TypeConverter.ToBoolean(rightEval)) { return(lhs); } return(typeof(object)); } // Misc case OperatorType.InstanceOf: case OperatorType.In: return(typeof(bool)); } throw new NotImplementedException(); }
internal override void ResolveVariables(OptimizationInfo optimizationInfo) { if (ResolvedProperty != null || propertyName != null) { // Already resolved. return; } // Resolve kids: base.ResolveVariables(optimizationInfo); // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) { throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access", 1, optimizationInfo.Source.Path, optimizationInfo.FunctionName); } propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1); if (rhs != null) { Type rhsType = rhs.GetResultType(optimizationInfo); if (rhsType == typeof(string)) { // Try a literal: LiteralExpression literalStr = rhs as LiteralExpression; if (literalStr != null) { propertyName = TypeConverter.ToString(literalStr.Value); // Could actually be numeric, so try that: if (propertyName != null && Nitrassic.Library.Array.ParseArrayIndex(propertyName) != uint.MaxValue) { // Yep, it is! isArrayIndex = true; } } } else if (PrimitiveTypeUtilities.IsNumeric(rhsType)) { // array index (a[0]) isArrayIndex = true; } } } if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); // Get the type of the LHS (the object being read from): Type lhsType = lhs.GetResultType(optimizationInfo); // Get the proto for it: Nitrassic.Library.Prototype proto = optimizationInfo.Engine.Prototypes.Get(lhsType); // Does that type have an indexer method on it? (this[uint]) ResolvedProperty = proto.Indexer(typeof(uint)); if (ResolvedProperty == null) { // Try [int] instead: ResolvedProperty = proto.Indexer(typeof(int)); } } if (ResolvedProperty == null && propertyName != null) { // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); Type lhsType = lhs.GetResultType(optimizationInfo); // Get the prototype: Nitrassic.Library.Prototype proto = optimizationInfo.Engine.Prototypes.Get(lhsType); // Get the property: ResolvedProperty = proto.GetProperty(propertyName); if (ResolvedProperty == null) { // Add it now (as undefined): ResolvedProperty = proto.AddProperty(propertyName, null, Nitrassic.Library.PropertyAttributes.FullAccess); } } }