|
@@ -1,9 +1,12 @@
|
|
using System;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Concurrent;
|
|
using System.Linq;
|
|
using System.Linq;
|
|
|
|
+using System.Numerics;
|
|
|
|
+using System.Runtime.CompilerServices;
|
|
using Esprima.Ast;
|
|
using Esprima.Ast;
|
|
using Jint.Extensions;
|
|
using Jint.Extensions;
|
|
using Jint.Native;
|
|
using Jint.Native;
|
|
|
|
+using Jint.Native.Number;
|
|
using Jint.Native.Object;
|
|
using Jint.Native.Object;
|
|
using Jint.Runtime.Interop;
|
|
using Jint.Runtime.Interop;
|
|
|
|
|
|
@@ -19,6 +22,8 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
|
|
|
|
private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(expression)
|
|
private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(expression)
|
|
{
|
|
{
|
|
|
|
+ // TODO check https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator
|
|
|
|
+
|
|
_left = Build(engine, expression.Left);
|
|
_left = Build(engine, expression.Left);
|
|
_right = Build(engine, expression.Right);
|
|
_right = Build(engine, expression.Right);
|
|
}
|
|
}
|
|
@@ -155,70 +160,18 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
- public static bool SameValueZero(JsValue x, JsValue y)
|
|
|
|
|
|
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
|
+ internal static bool AreNonBigIntOperands(JsValue left, JsValue right)
|
|
{
|
|
{
|
|
- return x == y || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
|
|
|
|
|
|
+ return left._type != InternalTypes.BigInt && right._type != InternalTypes.BigInt;
|
|
}
|
|
}
|
|
|
|
|
|
- public static bool StrictlyEqual(JsValue x, JsValue y)
|
|
|
|
|
|
+ internal static void AssertValidBigIntArithmeticOperands(EvaluationContext context, JsValue left, JsValue right)
|
|
{
|
|
{
|
|
- var typeX = x._type & ~InternalTypes.InternalFlags;
|
|
|
|
- var typeY = y._type & ~InternalTypes.InternalFlags;
|
|
|
|
-
|
|
|
|
- if (typeX != typeY)
|
|
|
|
- {
|
|
|
|
- if (typeX == InternalTypes.Integer)
|
|
|
|
- {
|
|
|
|
- typeX = InternalTypes.Number;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (typeY == InternalTypes.Integer)
|
|
|
|
- {
|
|
|
|
- typeY = InternalTypes.Number;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (typeX != typeY)
|
|
|
|
- {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (typeX == InternalTypes.Undefined || typeX == InternalTypes.Null)
|
|
|
|
- {
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (typeX == InternalTypes.Integer)
|
|
|
|
- {
|
|
|
|
- return x.AsInteger() == y.AsInteger();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (typeX == InternalTypes.Number)
|
|
|
|
|
|
+ if (left.Type != right.Type)
|
|
{
|
|
{
|
|
- var nx = ((JsNumber) x)._value;
|
|
|
|
- var ny = ((JsNumber) y)._value;
|
|
|
|
- return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
|
|
|
|
|
|
+ ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Cannot mix BigInt and other types, use explicit conversions");
|
|
}
|
|
}
|
|
-
|
|
|
|
- if ((typeX & InternalTypes.String) != 0)
|
|
|
|
- {
|
|
|
|
- return x.ToString() == y.ToString();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (typeX == InternalTypes.Boolean)
|
|
|
|
- {
|
|
|
|
- return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((typeX & InternalTypes.Object) != 0 && x.AsObject() is IObjectWrapper xw)
|
|
|
|
- {
|
|
|
|
- var yw = y.AsObject() as IObjectWrapper;
|
|
|
|
- if (yw == null)
|
|
|
|
- return false;
|
|
|
|
- return Equals(xw.Target, yw.Target);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return x == y;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
|
|
private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
|
|
@@ -231,7 +184,7 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
{
|
|
{
|
|
var left = _left.GetValue(context).Value;
|
|
var left = _left.GetValue(context).Value;
|
|
var right = _right.GetValue(context).Value;
|
|
var right = _right.GetValue(context).Value;
|
|
- var equal = StrictlyEqual(left, right);
|
|
|
|
|
|
+ var equal = left == right;
|
|
return NormalCompletion(equal ? JsBoolean.True : JsBoolean.False);
|
|
return NormalCompletion(equal ? JsBoolean.True : JsBoolean.False);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -246,7 +199,7 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
{
|
|
{
|
|
var left = _left.GetValue(context).Value;
|
|
var left = _left.GetValue(context).Value;
|
|
var right = _right.GetValue(context).Value;
|
|
var right = _right.GetValue(context).Value;
|
|
- return NormalCompletion(StrictlyEqual(left, right) ? JsBoolean.False : JsBoolean.True);
|
|
|
|
|
|
+ return NormalCompletion(left == right ? JsBoolean.False : JsBoolean.True);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -320,9 +273,20 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
|
|
|
|
var lprim = TypeConverter.ToPrimitive(left);
|
|
var lprim = TypeConverter.ToPrimitive(left);
|
|
var rprim = TypeConverter.ToPrimitive(right);
|
|
var rprim = TypeConverter.ToPrimitive(right);
|
|
- JsValue result = lprim.IsString() || rprim.IsString()
|
|
|
|
- ? JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
|
|
|
|
- : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
|
|
|
|
|
|
+ JsValue result;
|
|
|
|
+ if (lprim.IsString() || rprim.IsString())
|
|
|
|
+ {
|
|
|
|
+ result = JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim));
|
|
|
|
+ }
|
|
|
|
+ else if (AreNonBigIntOperands(left,right))
|
|
|
|
+ {
|
|
|
|
+ result = JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ AssertValidBigIntArithmeticOperands(context, lprim, rprim);
|
|
|
|
+ result = JsBigInt.Create(TypeConverter.ToBigInt(lprim) + TypeConverter.ToBigInt(rprim));
|
|
|
|
+ }
|
|
|
|
|
|
return NormalCompletion(result);
|
|
return NormalCompletion(result);
|
|
}
|
|
}
|
|
@@ -345,9 +309,22 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
}
|
|
}
|
|
|
|
|
|
- var number = AreIntegerOperands(left, right)
|
|
|
|
- ? JsNumber.Create(left.AsInteger() - right.AsInteger())
|
|
|
|
- : JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
|
|
|
|
|
|
+ JsValue number;
|
|
|
|
+ left = TypeConverter.ToNumeric(left);
|
|
|
|
+ right = TypeConverter.ToNumeric(right);
|
|
|
|
+
|
|
|
|
+ if (AreIntegerOperands(left, right))
|
|
|
|
+ {
|
|
|
|
+ number = JsNumber.Create(left.AsInteger() - right.AsInteger());
|
|
|
|
+ }
|
|
|
|
+ else if (AreNonBigIntOperands(left, right))
|
|
|
|
+ {
|
|
|
|
+ number = JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ number = JsBigInt.Create(TypeConverter.ToBigInt(left) - TypeConverter.ToBigInt(right));
|
|
|
|
+ }
|
|
|
|
|
|
return NormalCompletion(number);
|
|
return NormalCompletion(number);
|
|
}
|
|
}
|
|
@@ -364,23 +341,33 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
var left = _left.GetValue(context).Value;
|
|
var left = _left.GetValue(context).Value;
|
|
var right = _right.GetValue(context).Value;
|
|
var right = _right.GetValue(context).Value;
|
|
|
|
|
|
|
|
+ JsValue result;
|
|
if (context.OperatorOverloadingAllowed
|
|
if (context.OperatorOverloadingAllowed
|
|
&& TryOperatorOverloading(context, left, right, "op_Multiply", out var opResult))
|
|
&& TryOperatorOverloading(context, left, right, "op_Multiply", out var opResult))
|
|
{
|
|
{
|
|
- return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
|
|
|
|
+ result = JsValue.FromObject(context.Engine, opResult);
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (AreIntegerOperands(left, right))
|
|
|
|
|
|
+ else if (AreIntegerOperands(left, right))
|
|
{
|
|
{
|
|
- return NormalCompletion(JsNumber.Create((long) left.AsInteger() * right.AsInteger()));
|
|
|
|
|
|
+ result = JsNumber.Create((long) left.AsInteger() * right.AsInteger());
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (left.IsUndefined() || right.IsUndefined())
|
|
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- return NormalCompletion(Undefined.Instance);
|
|
|
|
|
|
+ var leftNumeric = TypeConverter.ToNumeric(left);
|
|
|
|
+ var rightNumeric = TypeConverter.ToNumeric(right);
|
|
|
|
+
|
|
|
|
+ if (leftNumeric.IsNumber())
|
|
|
|
+ {
|
|
|
|
+ result = JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ AssertValidBigIntArithmeticOperands(context, leftNumeric, rightNumeric);
|
|
|
|
+ result = JsBigInt.Create(leftNumeric.AsBigInt() * rightNumeric.AsBigInt());
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- return NormalCompletion(JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right)));
|
|
|
|
|
|
+ return NormalCompletion(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -401,7 +388,9 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
}
|
|
}
|
|
|
|
|
|
- return NormalCompletion(Divide(left, right));
|
|
|
|
|
|
+ left = TypeConverter.ToNumeric(left);
|
|
|
|
+ right = TypeConverter.ToNumeric(right);
|
|
|
|
+ return NormalCompletion(Divide(context, left, right));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -425,7 +414,12 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
}
|
|
}
|
|
|
|
|
|
- return NormalCompletion(Equal(left, right) == !_invert ? JsBoolean.True : JsBoolean.False);
|
|
|
|
|
|
+ // if types match, we can take faster strict equality
|
|
|
|
+ var equality = left.Type == right.Type
|
|
|
|
+ ? left.Equals(right)
|
|
|
|
+ : left.IsLooselyEqual(right);
|
|
|
|
+
|
|
|
|
+ return NormalCompletion(equality == !_invert ? JsBoolean.True : JsBoolean.False);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -479,10 +473,120 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
|
|
|
|
protected override ExpressionResult EvaluateInternal(EvaluationContext context)
|
|
protected override ExpressionResult EvaluateInternal(EvaluationContext context)
|
|
{
|
|
{
|
|
- var left = _left.GetValue(context).Value;
|
|
|
|
- var right = _right.GetValue(context).Value;
|
|
|
|
|
|
+ var leftReference = _left.GetValue(context).Value;
|
|
|
|
+ var rightReference = _right.GetValue(context).Value;
|
|
|
|
+
|
|
|
|
+ var left = TypeConverter.ToNumeric(leftReference);
|
|
|
|
+ var right = TypeConverter.ToNumeric(rightReference);
|
|
|
|
+
|
|
|
|
+ JsValue result;
|
|
|
|
+ if (AreNonBigIntOperands(left,right))
|
|
|
|
+ {
|
|
|
|
+ // validation
|
|
|
|
+ var baseNumber = (JsNumber) left;
|
|
|
|
+ var exponentNumber = (JsNumber) right;
|
|
|
|
+
|
|
|
|
+ if (exponentNumber.IsNaN())
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.DoubleNaN);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (exponentNumber.IsZero())
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.PositiveOne);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (baseNumber.IsNaN())
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.DoubleNaN);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var exponentValue = exponentNumber._value;
|
|
|
|
+ if (baseNumber.IsPositiveInfinity())
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(exponentValue > 0 ? JsNumber.DoublePositiveInfinity : JsNumber.PositiveZero);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static bool IsOddIntegral(double value) => TypeConverter.IsIntegralNumber(value) && value % 2 != 0;
|
|
|
|
+
|
|
|
|
+ if (baseNumber.IsNegativeInfinity())
|
|
|
|
+ {
|
|
|
|
+ if (exponentValue > 0)
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.DoubleNegativeInfinity : JsNumber.DoublePositiveInfinity);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.NegativeZero : JsNumber.PositiveZero);
|
|
|
|
+ }
|
|
|
|
|
|
- return NormalCompletion(JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right))));
|
|
|
|
|
|
+ if (baseNumber.IsPositiveZero())
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(exponentValue > 0 ? JsNumber.PositiveZero : JsNumber.DoublePositiveInfinity);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (baseNumber.IsNegativeZero())
|
|
|
|
+ {
|
|
|
|
+ if (exponentValue > 0)
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.NegativeZero : JsNumber.PositiveZero);
|
|
|
|
+ }
|
|
|
|
+ return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.DoubleNegativeInfinity : JsNumber.DoublePositiveInfinity);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var baseValue = baseNumber._value;
|
|
|
|
+ if (exponentNumber.IsPositiveInfinity())
|
|
|
|
+ {
|
|
|
|
+ if (Math.Abs(baseValue) > 1)
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.DoublePositiveInfinity);
|
|
|
|
+ }
|
|
|
|
+ if (Math.Abs(baseValue) == 1)
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.DoubleNaN);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NormalCompletion(JsNumber.PositiveZero);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (exponentNumber.IsNegativeInfinity())
|
|
|
|
+ {
|
|
|
|
+ if (Math.Abs(baseValue) > 1)
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.PositiveZero);
|
|
|
|
+ }
|
|
|
|
+ if (Math.Abs(baseValue) == 1)
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.DoubleNaN);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NormalCompletion(JsNumber.DoublePositiveInfinity);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (baseValue < 0 && !TypeConverter.IsIntegralNumber(exponentValue))
|
|
|
|
+ {
|
|
|
|
+ return NormalCompletion(JsNumber.DoubleNaN);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ result = JsNumber.Create(Math.Pow(baseNumber._value, exponentValue));
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ AssertValidBigIntArithmeticOperands(context, left, right);
|
|
|
|
+
|
|
|
|
+ var exponent = right.AsBigInt();
|
|
|
|
+ if (exponent < 0)
|
|
|
|
+ {
|
|
|
|
+ ExceptionHelper.ThrowRangeError(context.Engine.Realm, "Exponent must be positive");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (exponent > int.MaxValue || exponent < int.MinValue)
|
|
|
|
+ {
|
|
|
|
+ ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Exponent does not fit 32bit range");
|
|
|
|
+ }
|
|
|
|
+ result = JsBigInt.Create(BigInteger.Pow(left.AsBigInt(), (int) exponent));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NormalCompletion(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -524,22 +628,80 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ var result = Undefined.Instance;
|
|
|
|
+ left = TypeConverter.ToNumeric(left);
|
|
|
|
+ right = TypeConverter.ToNumeric(right);
|
|
|
|
+
|
|
if (AreIntegerOperands(left, right))
|
|
if (AreIntegerOperands(left, right))
|
|
{
|
|
{
|
|
var leftInteger = left.AsInteger();
|
|
var leftInteger = left.AsInteger();
|
|
var rightInteger = right.AsInteger();
|
|
var rightInteger = right.AsInteger();
|
|
- if (leftInteger > 0 && rightInteger != 0)
|
|
|
|
|
|
+
|
|
|
|
+ if (rightInteger == 0)
|
|
{
|
|
{
|
|
- return NormalCompletion(JsNumber.Create(leftInteger % rightInteger));
|
|
|
|
|
|
+ result = JsNumber.DoubleNaN;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ var modulo = leftInteger % rightInteger;
|
|
|
|
+ if (modulo == 0 && leftInteger < 0)
|
|
|
|
+ {
|
|
|
|
+ result = JsNumber.NegativeZero;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ result = JsNumber.Create(modulo);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ else if (AreNonBigIntOperands(left, right))
|
|
|
|
+ {
|
|
|
|
+ var n = TypeConverter.ToNumber(left);
|
|
|
|
+ var d = TypeConverter.ToNumber(right);
|
|
|
|
|
|
- if (left.IsUndefined() || right.IsUndefined())
|
|
|
|
|
|
+ if (double.IsNaN(n) || double.IsNaN(d) || double.IsInfinity(n))
|
|
|
|
+ {
|
|
|
|
+ result = JsNumber.DoubleNaN;
|
|
|
|
+ }
|
|
|
|
+ else if (double.IsInfinity(d))
|
|
|
|
+ {
|
|
|
|
+ result = n;
|
|
|
|
+ }
|
|
|
|
+ else if (NumberInstance.IsPositiveZero(d) || NumberInstance.IsNegativeZero(d))
|
|
|
|
+ {
|
|
|
|
+ result = JsNumber.DoubleNaN;
|
|
|
|
+ }
|
|
|
|
+ else if (NumberInstance.IsPositiveZero(n) || NumberInstance.IsNegativeZero(n))
|
|
|
|
+ {
|
|
|
|
+ result = n;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ result = JsNumber.Create(n % d);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- return NormalCompletion(Undefined.Instance);
|
|
|
|
|
|
+ AssertValidBigIntArithmeticOperands(context, left, right);
|
|
|
|
+
|
|
|
|
+ var n = TypeConverter.ToBigInt(left);
|
|
|
|
+ var d = TypeConverter.ToBigInt(right);
|
|
|
|
+
|
|
|
|
+ if (d == 0)
|
|
|
|
+ {
|
|
|
|
+ ExceptionHelper.ThrowRangeError(context.Engine.Realm, "Division by zero");
|
|
|
|
+ }
|
|
|
|
+ else if (n == 0)
|
|
|
|
+ {
|
|
|
|
+ result = JsBigInt.Zero;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ result = JsBigInt.Create(n % d);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- return NormalCompletion(JsNumber.Create(TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right)));
|
|
|
|
|
|
+ return NormalCompletion(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -571,21 +733,29 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
|
|
|
|
protected override ExpressionResult EvaluateInternal(EvaluationContext context)
|
|
protected override ExpressionResult EvaluateInternal(EvaluationContext context)
|
|
{
|
|
{
|
|
- var left = _left.GetValue(context).Value;
|
|
|
|
- var right = _right.GetValue(context).Value;
|
|
|
|
|
|
+ var lval = _left.GetValue(context).Value;
|
|
|
|
+ var rval = _right.GetValue(context).Value;
|
|
|
|
|
|
if (context.OperatorOverloadingAllowed
|
|
if (context.OperatorOverloadingAllowed
|
|
- && TryOperatorOverloading(context, left, right, OperatorClrName, out var opResult))
|
|
|
|
|
|
+ && TryOperatorOverloading(context, lval, rval, OperatorClrName, out var opResult))
|
|
{
|
|
{
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
|
|
}
|
|
}
|
|
|
|
|
|
- if (AreIntegerOperands(left, right))
|
|
|
|
|
|
+ var lnum = TypeConverter.ToNumeric(lval);
|
|
|
|
+ var rnum = TypeConverter.ToNumeric(rval);
|
|
|
|
+
|
|
|
|
+ if (lnum.Type != rnum.Type)
|
|
{
|
|
{
|
|
- int leftValue = left.AsInteger();
|
|
|
|
- int rightValue = right.AsInteger();
|
|
|
|
|
|
+ ExceptionHelper.ThrowTypeError(context.Engine.Realm);
|
|
|
|
+ }
|
|
|
|
|
|
- JsValue result;
|
|
|
|
|
|
+ if (AreIntegerOperands(lnum, rnum))
|
|
|
|
+ {
|
|
|
|
+ int leftValue = lnum.AsInteger();
|
|
|
|
+ int rightValue = rnum.AsInteger();
|
|
|
|
+
|
|
|
|
+ JsValue result = null;
|
|
switch (_operator)
|
|
switch (_operator)
|
|
{
|
|
{
|
|
case BinaryOperator.BitwiseAnd:
|
|
case BinaryOperator.BitwiseAnd:
|
|
@@ -608,40 +778,80 @@ namespace Jint.Runtime.Interpreter.Expressions
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
|
|
ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
|
|
- result = null;
|
|
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
return NormalCompletion(result);
|
|
return NormalCompletion(result);
|
|
}
|
|
}
|
|
|
|
|
|
- return NormalCompletion(EvaluateNonInteger(left, right));
|
|
|
|
|
|
+ return NormalCompletion(EvaluateNonInteger(context.Engine.Realm, lnum, rnum));
|
|
}
|
|
}
|
|
|
|
|
|
- private JsNumber EvaluateNonInteger(JsValue left, JsValue right)
|
|
|
|
|
|
+ private JsValue EvaluateNonInteger(Realm realm, JsValue left, JsValue right)
|
|
{
|
|
{
|
|
switch (_operator)
|
|
switch (_operator)
|
|
{
|
|
{
|
|
case BinaryOperator.BitwiseAnd:
|
|
case BinaryOperator.BitwiseAnd:
|
|
- return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
|
|
|
|
|
|
+ {
|
|
|
|
+ if (!left.IsBigInt())
|
|
|
|
+ {
|
|
|
|
+ return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return JsBigInt.Create(TypeConverter.ToBigInt(left) & TypeConverter.ToBigInt(right));
|
|
|
|
+ }
|
|
|
|
|
|
case BinaryOperator.BitwiseOr:
|
|
case BinaryOperator.BitwiseOr:
|
|
- return JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
|
|
|
|
|
|
+ {
|
|
|
|
+ if (!left.IsBigInt())
|
|
|
|
+ {
|
|
|
|
+ return JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
|
|
|
|
+ }
|
|
|
|
+ return JsBigInt.Create(TypeConverter.ToBigInt(left) | TypeConverter.ToBigInt(right));
|
|
|
|
+ }
|
|
|
|
|
|
case BinaryOperator.BitwiseXOr:
|
|
case BinaryOperator.BitwiseXOr:
|
|
- return JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
|
|
|
|
|
|
+ {
|
|
|
|
+ if (!left.IsBigInt())
|
|
|
|
+ {
|
|
|
|
+ return JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
|
|
|
|
+ }
|
|
|
|
+ return JsBigInt.Create(TypeConverter.ToBigInt(left) ^ TypeConverter.ToBigInt(right));
|
|
|
|
+ }
|
|
|
|
|
|
case BinaryOperator.LeftShift:
|
|
case BinaryOperator.LeftShift:
|
|
- return JsNumber.Create(TypeConverter.ToInt32(left) << (int) (TypeConverter.ToUint32(right) & 0x1F));
|
|
|
|
|
|
+ {
|
|
|
|
+ if (!left.IsBigInt())
|
|
|
|
+ {
|
|
|
|
+ return JsNumber.Create(TypeConverter.ToInt32(left) << (int) (TypeConverter.ToUint32(right) & 0x1F));
|
|
|
|
+ }
|
|
|
|
+ return JsBigInt.Create(TypeConverter.ToBigInt(left) << (int) TypeConverter.ToBigInt(right));
|
|
|
|
+ }
|
|
|
|
|
|
case BinaryOperator.RightShift:
|
|
case BinaryOperator.RightShift:
|
|
- return JsNumber.Create(TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
|
|
|
|
|
|
+ {
|
|
|
|
+ if (!left.IsBigInt())
|
|
|
|
+ {
|
|
|
|
+ return JsNumber.Create(TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
|
|
|
|
+ }
|
|
|
|
+ return JsBigInt.Create(TypeConverter.ToBigInt(left) >> (int) TypeConverter.ToBigInt(right));
|
|
|
|
+ }
|
|
|
|
|
|
case BinaryOperator.UnsignedRightShift:
|
|
case BinaryOperator.UnsignedRightShift:
|
|
- return JsNumber.Create((uint) TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
|
|
|
|
|
|
+ {
|
|
|
|
+ if (!left.IsBigInt())
|
|
|
|
+ {
|
|
|
|
+ return JsNumber.Create((uint) TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
|
|
|
|
+ }
|
|
|
|
+ ExceptionHelper.ThrowTypeError(realm);
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
default:
|
|
default:
|
|
|
|
+ {
|
|
ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
|
|
ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
|
|
return null;
|
|
return null;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|