From c06d368c9d7ef9e009c894bf3a85c49dd7ff097a Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Sat, 15 Jun 2024 20:51:22 +0300 Subject: [PATCH] Fix BigInt arithmetic assignments (#1891) --- Jint.Tests/Runtime/BigIntTests.cs | 29 +++++++++++++++++++ Jint/Native/JsBigInt.cs | 6 ++-- .../Expressions/JintAssignmentExpression.cs | 15 ++++++---- .../Expressions/JintBinaryExpression.cs | 1 + 4 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 Jint.Tests/Runtime/BigIntTests.cs diff --git a/Jint.Tests/Runtime/BigIntTests.cs b/Jint.Tests/Runtime/BigIntTests.cs new file mode 100644 index 0000000000..8953b048ba --- /dev/null +++ b/Jint.Tests/Runtime/BigIntTests.cs @@ -0,0 +1,29 @@ +using Jint.Native; + +namespace Jint.Tests.Runtime; + +public class BigIntTests +{ + [Theory] + [InlineData("a = a + b;", "146")] + [InlineData("a = a - b;", "100")] + [InlineData("a = a * b;", "2829")] + [InlineData("a = a / b;", "5")] + [InlineData("a += b;", "146")] + [InlineData("a -= b;", "100")] + [InlineData("a *= b;", "2829")] + [InlineData("a /= b;", "5")] + public void BasicOperations(string statement, string expected) + { + var outputValues = new List(); + var engine = new Engine() + .SetValue("log", outputValues.Add); + engine.Evaluate("let a = 123n; let b = 23n;"); + + engine.Evaluate(statement); + + engine.Evaluate("log(a)"); + Assert.True(outputValues[0].IsBigInt(), "The type of the value is expected to stay BigInt"); + Assert.Equal(expected, outputValues[0].ToString()); + } +} diff --git a/Jint/Native/JsBigInt.cs b/Jint/Native/JsBigInt.cs index 042784db10..6b6f887521 100644 --- a/Jint/Native/JsBigInt.cs +++ b/Jint/Native/JsBigInt.cs @@ -19,6 +19,7 @@ static JsBigInt() { bigIntegers[i] = new JsBigInt(i); } + _bigIntegerToJsValue = bigIntegers; } @@ -40,12 +41,9 @@ internal static JsBigInt Create(BigInteger bigInt) internal static JsBigInt Create(JsValue value) { - return value is JsBigInt jsBigInt - ? jsBigInt - : Create(TypeConverter.ToBigInt(value)); + return value as JsBigInt ?? Create(TypeConverter.ToBigInt(value)); } - public override object ToObject() => _value; internal override bool ToBoolean() => _value != 0; diff --git a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs index 6b89c60f2f..e9e3f3f630 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs @@ -101,13 +101,14 @@ protected override object EvaluateInternal(EvaluationContext context) newLeftValue = jsString.Append(rprim); } - else if (!AreIntegerOperands(originalLeftValue, rval)) + else if (JintBinaryExpression.AreNonBigIntOperands(originalLeftValue, rval)) { newLeftValue = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim); } else { - newLeftValue = TypeConverter.ToBigInt(lprim) + TypeConverter.ToBigInt(rprim); + JintBinaryExpression.AssertValidBigIntArithmeticOperands(lprim, rprim); + newLeftValue = JsBigInt.Create(TypeConverter.ToBigInt(lprim) + TypeConverter.ToBigInt(rprim)); } } @@ -121,13 +122,14 @@ protected override object EvaluateInternal(EvaluationContext context) { newLeftValue = JsNumber.Create(originalLeftValue.AsInteger() - rval.AsInteger()); } - else if (!AreIntegerOperands(originalLeftValue, rval)) + else if (JintBinaryExpression.AreNonBigIntOperands(originalLeftValue, rval)) { newLeftValue = JsNumber.Create(TypeConverter.ToNumber(originalLeftValue) - TypeConverter.ToNumber(rval)); } else { - newLeftValue = JsNumber.Create(TypeConverter.ToBigInt(originalLeftValue) - TypeConverter.ToBigInt(rval)); + JintBinaryExpression.AssertValidBigIntArithmeticOperands(originalLeftValue, rval); + newLeftValue = JsBigInt.Create(TypeConverter.ToBigInt(originalLeftValue) - TypeConverter.ToBigInt(rval)); } break; @@ -144,13 +146,14 @@ protected override object EvaluateInternal(EvaluationContext context) { newLeftValue = JsValue.Undefined; } - else if (!AreIntegerOperands(originalLeftValue, rval)) + else if (JintBinaryExpression.AreNonBigIntOperands(originalLeftValue, rval)) { newLeftValue = TypeConverter.ToNumber(originalLeftValue) * TypeConverter.ToNumber(rval); } else { - newLeftValue = TypeConverter.ToBigInt(originalLeftValue) * TypeConverter.ToBigInt(rval); + JintBinaryExpression.AssertValidBigIntArithmeticOperands(originalLeftValue, rval); + newLeftValue = JsBigInt.Create(TypeConverter.ToBigInt(originalLeftValue) * TypeConverter.ToBigInt(rval)); } break; diff --git a/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs index a28a045985..e239d2d47e 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs @@ -357,6 +357,7 @@ protected override object EvaluateInternal(EvaluationContext context) } else { + JintBinaryExpression.AssertValidBigIntArithmeticOperands(left, right); number = JsBigInt.Create(TypeConverter.ToBigInt(left) - TypeConverter.ToBigInt(right)); }