diff --git a/Jint/Native/Set/SetPrototype.cs b/Jint/Native/Set/SetPrototype.cs index c9656201e8..db53fb4aaf 100644 --- a/Jint/Native/Set/SetPrototype.cs +++ b/Jint/Native/Set/SetPrototype.cs @@ -154,7 +154,6 @@ private JsBoolean IsDisjointFrom(JsValue thisObject, JsCallArguments arguments) var set = AssertSetInstance(thisObject); var other = arguments.At(0); var otherRec = GetSetRecord(other); - var resultSetData = new JsSet(_engine, new OrderedSet(set._set._set)); if (set.Size <= otherRec.Size) { @@ -168,7 +167,7 @@ private JsBoolean IsDisjointFrom(JsValue thisObject, JsCallArguments arguments) var args = new JsValue[1]; while (index < set.Size) { - var e = resultSetData[index]; + var e = set[index]; index++; if (e is not null) { @@ -334,7 +333,6 @@ private JsBoolean IsSubsetOf(JsValue thisObject, JsCallArguments arguments) } var otherRec = GetSetRecord(other); - var resultSetData = new JsSet(_engine, new OrderedSet(set._set._set)); var thisSize = set.Size; if (thisSize > otherRec.Size) @@ -342,26 +340,23 @@ private JsBoolean IsSubsetOf(JsValue thisObject, JsCallArguments arguments) return JsBoolean.False; } - if (thisSize <= otherRec.Size) + var index = 0; + var args = new JsValue[1]; + while (index < thisSize) { - var index = 0; - var args = new JsValue[1]; - while (index < thisSize) + var e = set[index]; + if (e is not null) { - var e = resultSetData[index]; - if (e is not null) + args[0] = e; + var inOther = TypeConverter.ToBoolean(otherRec.Has.Call(otherRec.Set, args)); + if (!inOther) { - args[0] = e; - var inOther = TypeConverter.ToBoolean(otherRec.Has.Call(otherRec.Set, args)); - if (!inOther) - { - return JsBoolean.False; - } + return JsBoolean.False; } - - thisSize = set.Size; - index++; } + + thisSize = set.Size; + index++; } return JsBoolean.True; @@ -375,8 +370,7 @@ private JsBoolean IsSupersetOf(JsValue thisObject, JsCallArguments arguments) if (other is JsSet otherSet) { // fast path - var result = new HashSet(set._set._set, SameValueZeroComparer.Instance); - return result.IsSupersetOf(otherSet._set._set) ? JsBoolean.True : JsBoolean.False; + return set._set._set.IsSupersetOf(otherSet._set._set) ? JsBoolean.True : JsBoolean.False; } var thisSize = set.Size; diff --git a/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs index 2bcaa786e7..faa58af4e0 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs @@ -855,11 +855,12 @@ protected override object EvaluateInternal(EvaluationContext context) var baseValue = baseNumber._value; if (exponentNumber.IsPositiveInfinity()) { - if (Math.Abs(baseValue) > 1) + var absBase = Math.Abs(baseValue); + if (absBase > 1) { return JsNumber.DoublePositiveInfinity; } - if (Math.Abs(baseValue) == 1) + if (absBase == 1) { return JsNumber.DoubleNaN; } @@ -869,11 +870,12 @@ protected override object EvaluateInternal(EvaluationContext context) if (exponentNumber.IsNegativeInfinity()) { - if (Math.Abs(baseValue) > 1) + var absBase = Math.Abs(baseValue); + if (absBase > 1) { return JsNumber.PositiveZero; } - if (Math.Abs(baseValue) == 1) + if (absBase == 1) { return JsNumber.DoubleNaN; } diff --git a/Jint/Runtime/TypeConverter.cs b/Jint/Runtime/TypeConverter.cs index fdf4126c1c..a75f7c249b 100644 --- a/Jint/Runtime/TypeConverter.cs +++ b/Jint/Runtime/TypeConverter.cs @@ -19,6 +19,9 @@ public static class TypeConverter private static readonly string[] intToString = new string[1024]; private static readonly string[] charToString = new string[256]; + private static readonly BigInteger s_bigInt2Pow64 = BigInteger.Pow(2, 64); + private static readonly BigInteger s_bigInt2Pow63 = BigInteger.Pow(2, 63); + static TypeConverter() { for (var i = 0; i < intToString.Length; ++i) @@ -742,10 +745,10 @@ internal static bool TryStringToBigInt(string str, out BigInteger result) /// internal static long ToBigInt64(BigInteger value) { - var int64bit = BigIntegerModulo(value, BigInteger.Pow(2, 64)); - if (int64bit >= BigInteger.Pow(2, 63)) + var int64bit = BigIntegerModulo(value, s_bigInt2Pow64); + if (int64bit >= s_bigInt2Pow63) { - return (long) (int64bit - BigInteger.Pow(2, 64)); + return (long) (int64bit - s_bigInt2Pow64); } return (long) int64bit; @@ -756,7 +759,7 @@ internal static long ToBigInt64(BigInteger value) /// internal static ulong ToBigUint64(BigInteger value) { - return (ulong) BigIntegerModulo(value, BigInteger.Pow(2, 64)); + return (ulong) BigIntegerModulo(value, s_bigInt2Pow64); } /// diff --git a/README.md b/README.md index 03eea7de71..034dd8286c 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ and many more. #### ECMAScript proposals (no version yet) - ✔ Await Dictionary (`Promise.allKeyed`, `Promise.allSettledKeyed`) +- ✔ Decorators (`@decorator` syntax for classes, methods, fields, and accessors) - ✔ `Error.isError` - ✔ Explicit Resource Management (`using` and `await using`) - ✔ Immutable Arraybuffers @@ -517,6 +518,39 @@ If `jsValue` is not a Promise it is returned immediately. If it is a rejected Pr The synchronous `UnwrapIfPromise` is still available for scenarios where blocking is acceptable (e.g., CPU-bound scripts with no I/O), but `UnwrapIfPromiseAsync` should be preferred in any `async` call chain. +### Task/ValueTask to Promise Interop (Experimental) + +When the `TaskInterop` experimental feature is enabled, .NET `Task` and `ValueTask` return values are automatically converted to JavaScript Promises. This allows JavaScript code to `await` or `.then()` the results of .NET async methods without any manual wrapping: + +```c# +var engine = new Engine(options => +{ + options.ExperimentalFeatures = ExperimentalFeature.TaskInterop; +}); + +engine.SetValue("fetchData", new Func>(async url => +{ + using var client = new HttpClient(); + return await client.GetStringAsync(url); +})); + +// .NET Task is automatically converted to a JavaScript Promise +var result = engine.Evaluate("fetchData('https://example.com/api').then(data => data)"); +result = result.UnwrapIfPromise(); +``` + +Without `TaskInterop`, .NET Tasks passed to JavaScript are exposed as opaque CLR objects. With it enabled, they become native Promises that support `await`, `.then()`, and `.catch()`. + +You can configure the timeout for promise resolution via `Options.Constraints.PromiseTimeout`: + +```c# +var engine = new Engine(options => +{ + options.ExperimentalFeatures = ExperimentalFeature.TaskInterop; + options.Constraints.PromiseTimeout = TimeSpan.FromSeconds(10); +}); +``` + ## .NET Interoperability - Manipulate CLR objects from JavaScript, including: