From 2171041e584f7062e59fd928f5576e58b3d2e093 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 2 Feb 2024 14:41:17 -0800 Subject: [PATCH 01/13] Implement JSValue.As and JSValue.AsUnchecked --- src/NodeApi/IJSValue.cs | 41 ++++++++++++++++++++++++++++ src/NodeApi/Interop/JSAbortSignal.cs | 21 ++++++++++++-- src/NodeApi/JSArray.cs | 20 ++++++++++++-- src/NodeApi/JSAsyncIterable.cs | 21 ++++++++++++-- src/NodeApi/JSBigInt.cs | 21 +++++++++++--- src/NodeApi/JSDate.cs | 20 ++++++++++++-- src/NodeApi/JSFunction.cs | 19 +++++++++++-- src/NodeApi/JSIterable.cs | 23 ++++++++++++---- src/NodeApi/JSMap.cs | 20 ++++++++++++-- src/NodeApi/JSObject.cs | 19 +++++++++++-- src/NodeApi/JSPromise.cs | 19 +++++++++++-- src/NodeApi/JSProxy.cs | 20 ++++++++++++-- src/NodeApi/JSSet.cs | 20 ++++++++++++-- src/NodeApi/JSSymbol.cs | 19 +++++++++++-- src/NodeApi/JSTypedArray.cs | 25 +++++++++++++++-- src/NodeApi/JSValue.cs | 23 ++++++++++++++++ 16 files changed, 317 insertions(+), 34 deletions(-) create mode 100644 src/NodeApi/IJSValue.cs diff --git a/src/NodeApi/IJSValue.cs b/src/NodeApi/IJSValue.cs new file mode 100644 index 00000000..3059bd4f --- /dev/null +++ b/src/NodeApi/IJSValue.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if !NET7_0_OR_GREATER +using System; +using System.Reflection; +#endif + +namespace Microsoft.JavaScript.NodeApi; + +#if NET7_0_OR_GREATER +// A static interface that helps with the conversion of JSValue to a specific type. +public interface IJSValue where TSelf : struct, IJSValue +{ + public static abstract bool CanBeConvertedFrom(JSValue value); + + public static abstract TSelf CreateUnchecked(JSValue value); +} +#else +// A static class that helps with the conversion of JSValue to a specific type. +public static class IJSValueShim where T : struct +{ + private static readonly Func s_canBeConvertedFrom = + (Func)Delegate.CreateDelegate( + typeof(Func), + typeof(T).GetMethod( + nameof(JSObject.CanBeConvertedFrom), + BindingFlags.Static | BindingFlags.Public)!); + + private static readonly Funcs_createUnchecked = + (Func)Delegate.CreateDelegate( + typeof(Func), + typeof(T).GetMethod( + nameof(JSObject.CreateUnchecked), + BindingFlags.Static | BindingFlags.Public)!); + + public static bool CanBeConvertedFrom(JSValue value) => s_canBeConvertedFrom(value); + + public static T CreateUnchecked(JSValue value) => s_createUnchecked(value); +} +#endif diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index c072b51d..66ba1a2a 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -16,11 +16,17 @@ namespace Microsoft.JavaScript.NodeApi.Interop; /// https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal /// public readonly struct JSAbortSignal : IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSAbortSignal(JSValue value) => new(value); - public static implicit operator JSValue(JSAbortSignal promise) => promise._value; + public static implicit operator JSValue(JSAbortSignal value) => value.AsJSValue(); + public static explicit operator JSAbortSignal?(JSValue value) => value.As(); + public static explicit operator JSAbortSignal(JSValue value) + => value.As() + ?? throw new InvalidCastException("JSValue is not an AbortSignal."); public static explicit operator JSAbortSignal(JSObject obj) => (JSAbortSignal)(JSValue)obj; public static implicit operator JSObject(JSAbortSignal promise) => (JSObject)promise._value; @@ -38,6 +44,17 @@ public static explicit operator JSAbortSignal(CancellationToken cancellation) public static explicit operator JSAbortSignal(CancellationToken? cancellation) => cancellation.HasValue ? FromCancellationToken(cancellation.Value) : default; + #region IJSValue implementation + + // TODO: (vmoroz) Implement + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSAbortSignal CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + private CancellationToken ToCancellationToken() { if (!_value.IsObject()) diff --git a/src/NodeApi/JSArray.cs b/src/NodeApi/JSArray.cs index 603d5343..c5f605b4 100644 --- a/src/NodeApi/JSArray.cs +++ b/src/NodeApi/JSArray.cs @@ -5,15 +5,21 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using static Microsoft.JavaScript.NodeApi.Runtime.JSRuntime; namespace Microsoft.JavaScript.NodeApi; public readonly partial struct JSArray : IList, IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSArray(JSValue value) => new(value); - public static implicit operator JSValue(JSArray arr) => arr._value; + public static implicit operator JSValue(JSArray arr) => arr.AsJSValue(); + public static explicit operator JSArray?(JSValue value) => value.As(); + public static explicit operator JSArray(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not an Array"); public static explicit operator JSArray(JSObject obj) => (JSArray)(JSValue)obj; public static implicit operator JSObject(JSArray arr) => (JSObject)arr._value; @@ -45,6 +51,16 @@ public JSArray(JSValue[] array) } } + #region IJSValue implementation + + public static bool CanBeConvertedFrom(JSValue value) => value.IsArray(); + + public static JSArray CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + /// public int Length => _value.GetArrayLength(); diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index e01b1289..988f896c 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -9,11 +9,17 @@ namespace Microsoft.JavaScript.NodeApi; public readonly partial struct JSAsyncIterable : IAsyncEnumerable, IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSAsyncIterable(JSValue value) => new(value); - public static implicit operator JSValue(JSAsyncIterable iterable) => iterable._value; + public static implicit operator JSValue(JSAsyncIterable value) => value.AsJSValue(); + public static explicit operator JSAsyncIterable?(JSValue value) => value.As(); + public static explicit operator JSAsyncIterable(JSValue value) + => value.As() + ?? throw new InvalidCastException("JSValue is not an AsyncIterable."); public static explicit operator JSAsyncIterable(JSObject obj) => (JSAsyncIterable)(JSValue)obj; public static implicit operator JSObject(JSAsyncIterable iterable) => (JSObject)iterable._value; @@ -23,6 +29,17 @@ private JSAsyncIterable(JSValue value) _value = value; } + #region IJSValue implementation + + //TODO: (vmoroz) implement proper check using Symbol.asyncIterator + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSAsyncIterable CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + #pragma warning disable IDE0060 // Unused parameter public Enumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => new(_value); diff --git a/src/NodeApi/JSBigInt.cs b/src/NodeApi/JSBigInt.cs index c2a5642e..54bf0080 100644 --- a/src/NodeApi/JSBigInt.cs +++ b/src/NodeApi/JSBigInt.cs @@ -8,11 +8,16 @@ namespace Microsoft.JavaScript.NodeApi; public readonly struct JSBigInt : IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSBigInt(JSValue value) => new(value); - public static implicit operator JSValue(JSBigInt value) => value._value; + public static implicit operator JSValue(JSBigInt value) => value.AsJSValue(); + public static explicit operator JSBigInt?(JSValue value) => value.As(); + public static explicit operator JSBigInt(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a BigInt"); public static implicit operator JSBigInt(BigInteger value) => new(value); public static explicit operator BigInteger(JSBigInt value) => value.ToBigInteger(); @@ -39,13 +44,21 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) { } + #region IJSValue implementation + + public static bool CanBeConvertedFrom(JSValue value) => value.IsBigInt(); + + public static JSBigInt CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + public int GetWordCount() => _value.GetBigIntWordCount(); public void CopyTo(Span destination, out int sign, out int wordCount) => _value.GetBigIntWords(destination, out sign, out wordCount); - public JSValue AsJSValue() => _value; - public BigInteger ToBigInteger() => _value.ToBigInteger(); public long ToInt64(out bool isLossless) => _value.ToInt64BigInt(out isLossless); diff --git a/src/NodeApi/JSDate.cs b/src/NodeApi/JSDate.cs index 91006c61..8e0a6ab4 100644 --- a/src/NodeApi/JSDate.cs +++ b/src/NodeApi/JSDate.cs @@ -4,15 +4,21 @@ using System; using System.Diagnostics.CodeAnalysis; using Microsoft.JavaScript.NodeApi.Interop; +using static Microsoft.JavaScript.NodeApi.Runtime.JSRuntime; namespace Microsoft.JavaScript.NodeApi; public readonly struct JSDate : IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSDate(JSValue value) => new(value); - public static implicit operator JSValue(JSDate date) => date._value; + public static implicit operator JSValue(JSDate value) => value.AsJSValue(); + public static explicit operator JSDate?(JSValue value) => value.As(); + public static explicit operator JSDate(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a Date"); private JSDate(JSValue value) { @@ -36,6 +42,16 @@ public JSDate(string dateString) public long DateValue => (long)_value.CallMethod("valueOf"); + #region IJSValue implementation + + public static bool CanBeConvertedFrom(JSValue value) => value.IsDate(); + + public static JSDate CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + public static JSDate FromDateTime(DateTime value) { DateTimeKind kind = value.Kind; diff --git a/src/NodeApi/JSFunction.cs b/src/NodeApi/JSFunction.cs index 9fd7f61b..7bd8ca54 100644 --- a/src/NodeApi/JSFunction.cs +++ b/src/NodeApi/JSFunction.cs @@ -10,11 +10,16 @@ namespace Microsoft.JavaScript.NodeApi; /// Represents a JavaScript Function value. /// public readonly struct JSFunction : IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSFunction(JSValue value) => new(value); - public static implicit operator JSValue(JSFunction function) => function._value; + public static implicit operator JSValue(JSFunction value) => value.AsJSValue(); + public static explicit operator JSFunction?(JSValue value) => value.As(); + public static explicit operator JSFunction(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a Function"); private JSFunction(JSValue value) { @@ -267,6 +272,16 @@ public JSFunction( { } + #region IJSValue implementation + + public static bool CanBeConvertedFrom(JSValue value) => value.IsFunction(); + + public static JSFunction CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + /// /// Gets the name of the function, or an empty string if the function is unnamed. /// diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index 5b5d6641..7e2e4fa8 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -9,14 +9,16 @@ namespace Microsoft.JavaScript.NodeApi; public readonly partial struct JSIterable : IEnumerable, IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSIterable(JSValue value) => new(value); - public static implicit operator JSValue(JSIterable iterable) => iterable._value; - - public static explicit operator JSArray(JSIterable iterable) => (JSArray)iterable._value; - public static implicit operator JSIterable(JSArray array) => (JSIterable)(JSValue)array; + public static implicit operator JSValue(JSIterable value) => value.AsJSValue(); + public static explicit operator JSIterable?(JSValue value) => value.As(); + public static explicit operator JSIterable(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not an Iterable."); public static explicit operator JSIterable(JSObject obj) => (JSIterable)(JSValue)obj; public static implicit operator JSObject(JSIterable iterable) => (JSObject)iterable._value; @@ -26,6 +28,17 @@ private JSIterable(JSValue value) _value = value; } + #region IJSValue implementation + + //TODO: (vmoroz) implement proper check using Symbol.iterator + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSIterable CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + public Enumerator GetEnumerator() => new(_value); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/NodeApi/JSMap.cs b/src/NodeApi/JSMap.cs index 66c39b44..0ca6489e 100644 --- a/src/NodeApi/JSMap.cs +++ b/src/NodeApi/JSMap.cs @@ -9,11 +9,16 @@ namespace Microsoft.JavaScript.NodeApi; public readonly partial struct JSMap : IDictionary, IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSMap(JSValue value) => new(value); - public static implicit operator JSValue(JSMap map) => map._value; + public static implicit operator JSValue(JSMap value) => value.AsJSValue(); + public static explicit operator JSMap?(JSValue value) => value.As(); + public static explicit operator JSMap(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a Map."); public static explicit operator JSMap(JSObject obj) => (JSMap)(JSValue)obj; public static implicit operator JSObject(JSMap map) => (JSObject)map._value; @@ -40,6 +45,17 @@ public JSMap(JSIterable iterable) _value = JSRuntimeContext.Current.Import(null, "Map").CallAsConstructor(iterable); } + #region IJSValue implementation + + // TODO: (vmoroz) Implement using instanceof + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSMap CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + public int Count => (int)_value["size"]; public Enumerator GetEnumerator() => new(_value); diff --git a/src/NodeApi/JSObject.cs b/src/NodeApi/JSObject.cs index 891fc8dd..33a0296f 100644 --- a/src/NodeApi/JSObject.cs +++ b/src/NodeApi/JSObject.cs @@ -9,11 +9,16 @@ namespace Microsoft.JavaScript.NodeApi; public readonly partial struct JSObject : IDictionary, IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSObject(JSValue value) => new(value); - public static implicit operator JSValue(JSObject obj) => obj._value; + public static implicit operator JSValue(JSObject value) => value.AsJSValue(); + public static explicit operator JSObject?(JSValue value) => value.As(); + public static explicit operator JSObject(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not an Object."); private JSObject(JSValue value) { @@ -24,6 +29,16 @@ public JSObject() : this(JSValue.CreateObject()) { } + #region IJSValue implementation + + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSObject CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + public JSObject(IEnumerable> properties) : this() { foreach (KeyValuePair property in properties) diff --git a/src/NodeApi/JSPromise.cs b/src/NodeApi/JSPromise.cs index d670a40b..1db14c45 100644 --- a/src/NodeApi/JSPromise.cs +++ b/src/NodeApi/JSPromise.cs @@ -15,11 +15,16 @@ namespace Microsoft.JavaScript.NodeApi; /// /// public readonly struct JSPromise : IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSPromise(JSValue value) => new(value); - public static implicit operator JSValue(JSPromise promise) => promise._value; + public static implicit operator JSValue(JSPromise value) => value.AsJSValue(); + public static explicit operator JSPromise?(JSValue value) => value.As(); + public static explicit operator JSPromise(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a Promise."); public static explicit operator JSPromise(JSObject obj) => (JSPromise)(JSValue)obj; public static implicit operator JSObject(JSPromise promise) => (JSObject)promise._value; @@ -143,6 +148,16 @@ private static async void ResolveDeferred( } } + #region IJSValue implementation + + public static bool CanBeConvertedFrom(JSValue value) => value.IsPromise(); + + public static JSPromise CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + /// /// Registers callbacks that are invoked when a promise is fulfilled and/or rejected, /// and returns a new chained promise. diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index 71de1298..d562a135 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -13,12 +13,17 @@ namespace Microsoft.JavaScript.NodeApi; /// Enables creation of JS Proxy objects with C# handler callbacks. /// public readonly partial struct JSProxy : IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; private readonly JSValue _revoke = default; - public static explicit operator JSProxy(JSValue value) => new(value); - public static implicit operator JSValue(JSProxy proxy) => proxy._value; + public static implicit operator JSValue(JSProxy value) => value.AsJSValue(); + public static explicit operator JSProxy?(JSValue value) => value.As(); + public static explicit operator JSProxy(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a Proxy."); private JSProxy(JSValue value) { @@ -65,6 +70,17 @@ public JSProxy( } } + #region IJSValue implementation + + // TODO: (vmoroz) Implement using instanceof + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSProxy CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + /// /// Revokes the proxy, so that further access to the target is no longer trapped by /// the proxy handler. diff --git a/src/NodeApi/JSSet.cs b/src/NodeApi/JSSet.cs index b07b2a7f..ba8ec122 100644 --- a/src/NodeApi/JSSet.cs +++ b/src/NodeApi/JSSet.cs @@ -10,11 +10,16 @@ namespace Microsoft.JavaScript.NodeApi; public readonly partial struct JSSet : ISet, IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; - public static explicit operator JSSet(JSValue value) => new(value); - public static implicit operator JSValue(JSSet set) => set._value; + public static implicit operator JSValue(JSSet value) => value.AsJSValue(); + public static explicit operator JSSet?(JSValue value) => value.As(); + public static explicit operator JSSet(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a Set."); public static explicit operator JSSet(JSObject obj) => (JSSet)(JSValue)obj; public static implicit operator JSObject(JSSet set) => (JSObject)set._value; @@ -44,6 +49,17 @@ public JSSet(JSIterable iterable) _value = JSRuntimeContext.Current.Import(null, "Set").CallAsConstructor(iterable); } + #region IJSValue implementation + + // TODO: (vmoroz) Implement using instanceof + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSSet CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + public int Count => (int)_value["size"]; bool ICollection.IsReadOnly => false; diff --git a/src/NodeApi/JSSymbol.cs b/src/NodeApi/JSSymbol.cs index 2b5e4049..17640ce5 100644 --- a/src/NodeApi/JSSymbol.cs +++ b/src/NodeApi/JSSymbol.cs @@ -7,6 +7,9 @@ namespace Microsoft.JavaScript.NodeApi; public readonly struct JSSymbol : IEquatable +#if NET7_0_OR_GREATER + , IJSValue +#endif { private readonly JSValue _value; @@ -16,8 +19,10 @@ namespace Microsoft.JavaScript.NodeApi; private static readonly Lazy s_asyncIteratorSymbol = new(() => new JSReference(JSValue.Global["Symbol"]["asyncIterator"])); - public static explicit operator JSSymbol(JSValue value) => new(value); - public static implicit operator JSValue(JSSymbol symbol) => symbol._value; + public static implicit operator JSValue(JSSymbol value) => value.AsJSValue(); + public static explicit operator JSSymbol?(JSValue value) => value.As(); + public static explicit operator JSSymbol(JSValue value) + => value.As() ?? throw new InvalidCastException("JSValue is not a Symbol."); private JSSymbol(JSValue value) { @@ -29,6 +34,16 @@ public JSSymbol(string? description = null) _value = JSValue.CreateSymbol(description ?? JSValue.Undefined); } + #region IJSValue implementation + + public static bool CanBeConvertedFrom(JSValue value) => value.IsSymbol(); + + public static JSSymbol CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + /// /// Gets the symbol's description, or null if it does not have one. /// diff --git a/src/NodeApi/JSTypedArray.cs b/src/NodeApi/JSTypedArray.cs index 16324186..e8282055 100644 --- a/src/NodeApi/JSTypedArray.cs +++ b/src/NodeApi/JSTypedArray.cs @@ -9,12 +9,20 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly struct JSTypedArray : IEquatable where T : struct +public readonly struct JSTypedArray : IEquatable +#if NET7_0_OR_GREATER + , IJSValue> +#endif + where T : struct { private readonly JSValue _value; - public static explicit operator JSTypedArray(JSValue value) => new(value); - public static implicit operator JSValue(JSTypedArray arr) => arr._value; + public static implicit operator JSValue(JSTypedArray value) => value.AsJSValue(); + public static explicit operator JSTypedArray?(JSValue value) => value.As>(); + public static explicit operator JSTypedArray(JSValue value) + => value.As>() + ?? throw new InvalidCastException("JSValue is not a TypedArray."); + private static int ElementSize { get; } = default(T) switch { @@ -101,6 +109,17 @@ public unsafe JSTypedArray(ReadOnlyMemory data) } } + #region IJSValue> implementation + + //TODO: (vmoroz) Implement correctly + public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + + public static JSTypedArray CreateUnchecked(JSValue value) => new(value); + + #endregion + + public JSValue AsJSValue() => _value; + /// /// Checks if this Memory is already owned by a JS TypedArray value, and if so /// returns that JS value. diff --git a/src/NodeApi/JSValue.cs b/src/NodeApi/JSValue.cs index 07289740..3329191d 100644 --- a/src/NodeApi/JSValue.cs +++ b/src/NodeApi/JSValue.cs @@ -391,6 +391,29 @@ public JSValueType TypeOf() => _handle.IsNull public bool IsBigInt() => TypeOf() == JSValueType.BigInt; + public bool Is() where TValue : struct +#if NET7_0_OR_GREATER + , IJSValue => TValue.CanBeConvertedFrom(this); +#else + => IJSValueShim.CanBeConvertedFrom(this); +#endif + + public TValue? As() where TValue : struct +#if NET7_0_OR_GREATER + , IJSValue + => TValue.CanBeConvertedFrom(this) ? TValue.CreateUnchecked(this) : default; +#else + => IJSValueShim.CanBeConvertedFrom(this) + ? IJSValueShim.CreateUnchecked(this) : default; +#endif + + public TValue AsUnchecked() where TValue : struct +#if NET7_0_OR_GREATER + , IJSValue => TValue.CreateUnchecked(this); +#else + => IJSValueShim.CreateUnchecked(this); +#endif + public double GetValueDouble() => GetRuntime(out napi_env env, out napi_value handle) .GetValueDouble(env, handle, out double result).ThrowIfFailed(result); From 5c4e176c8ff8c3af3278f606f87e50395b3e7c8c Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 9 Feb 2024 17:58:24 -0800 Subject: [PATCH 02/13] Address PR feedback --- src/NodeApi/IJSValue.cs | 27 ++++++++++++++++----------- src/NodeApi/Interop/JSAbortSignal.cs | 23 +++++++++++------------ src/NodeApi/JSArray.cs | 21 ++++++++++----------- src/NodeApi/JSAsyncIterable.cs | 21 +++++++++++---------- src/NodeApi/JSBigInt.cs | 20 ++++++++++---------- src/NodeApi/JSDate.cs | 20 ++++++++++---------- src/NodeApi/JSFunction.cs | 20 ++++++++++---------- src/NodeApi/JSIterable.cs | 20 ++++++++++---------- src/NodeApi/JSMap.cs | 20 ++++++++++---------- src/NodeApi/JSObject.cs | 20 ++++++++++---------- src/NodeApi/JSPromise.cs | 20 ++++++++++---------- src/NodeApi/JSProxy.cs | 20 ++++++++++---------- src/NodeApi/JSSet.cs | 20 ++++++++++---------- src/NodeApi/JSSymbol.cs | 20 ++++++++++---------- src/NodeApi/JSTypedArray.cs | 21 ++++++++++----------- src/NodeApi/JSValue.cs | 22 +++++++++++++--------- 16 files changed, 171 insertions(+), 164 deletions(-) diff --git a/src/NodeApi/IJSValue.cs b/src/NodeApi/IJSValue.cs index 3059bd4f..08d1a8e4 100644 --- a/src/NodeApi/IJSValue.cs +++ b/src/NodeApi/IJSValue.cs @@ -1,40 +1,45 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#if !NET7_0_OR_GREATER using System; + +#if !NET7_0_OR_GREATER using System.Reflection; #endif namespace Microsoft.JavaScript.NodeApi; -#if NET7_0_OR_GREATER // A static interface that helps with the conversion of JSValue to a specific type. -public interface IJSValue where TSelf : struct, IJSValue +public interface IJSValue : IEquatable where TSelf : struct, IJSValue { - public static abstract bool CanBeConvertedFrom(JSValue value); + public JSValue AsJSValue(); + +#if NET7_0_OR_GREATER + public static abstract bool CanCreateFrom(JSValue value); public static abstract TSelf CreateUnchecked(JSValue value); +#endif } -#else + +#if !NET7_0_OR_GREATER // A static class that helps with the conversion of JSValue to a specific type. -public static class IJSValueShim where T : struct +internal static class IJSValueShim where T : struct { - private static readonly Func s_canBeConvertedFrom = + private static readonly Func s_canBeCreatedFrom = (Func)Delegate.CreateDelegate( typeof(Func), typeof(T).GetMethod( - nameof(JSObject.CanBeConvertedFrom), + "CanCreateFrom", BindingFlags.Static | BindingFlags.Public)!); private static readonly Funcs_createUnchecked = (Func)Delegate.CreateDelegate( typeof(Func), typeof(T).GetMethod( - nameof(JSObject.CreateUnchecked), - BindingFlags.Static | BindingFlags.Public)!); + "CreateUnchecked", + BindingFlags.Static | BindingFlags.NonPublic)!); - public static bool CanBeConvertedFrom(JSValue value) => s_canBeConvertedFrom(value); + public static bool CanCreateFrom(JSValue value) => s_canBeCreatedFrom(value); public static T CreateUnchecked(JSValue value) => s_createUnchecked(value); } diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index 66ba1a2a..8ef84893 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -15,18 +15,13 @@ namespace Microsoft.JavaScript.NodeApi.Interop; /// https://nodejs.org/api/globals.html#class-abortsignal /// https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal /// -public readonly struct JSAbortSignal : IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly struct JSAbortSignal : IJSValue { private readonly JSValue _value; public static implicit operator JSValue(JSAbortSignal value) => value.AsJSValue(); public static explicit operator JSAbortSignal?(JSValue value) => value.As(); - public static explicit operator JSAbortSignal(JSValue value) - => value.As() - ?? throw new InvalidCastException("JSValue is not an AbortSignal."); + public static explicit operator JSAbortSignal(JSValue value) => value.CastTo(); public static explicit operator JSAbortSignal(JSObject obj) => (JSAbortSignal)(JSValue)obj; public static implicit operator JSObject(JSAbortSignal promise) => (JSObject)promise._value; @@ -46,15 +41,19 @@ public static explicit operator JSAbortSignal(CancellationToken? cancellation) #region IJSValue implementation - // TODO: (vmoroz) Implement - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); - - public static JSAbortSignal CreateUnchecked(JSValue value) => new(value); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - #endregion +#if NET7_0_OR_GREATER + // TODO: (vmoroz) Implement + static JSAbortSignal IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSAbortSignal CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; +#endregion + private CancellationToken ToCancellationToken() { if (!_value.IsObject()) diff --git a/src/NodeApi/JSArray.cs b/src/NodeApi/JSArray.cs index c5f605b4..7e7bbddf 100644 --- a/src/NodeApi/JSArray.cs +++ b/src/NodeApi/JSArray.cs @@ -5,21 +5,16 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using static Microsoft.JavaScript.NodeApi.Runtime.JSRuntime; namespace Microsoft.JavaScript.NodeApi; -public readonly partial struct JSArray : IList, IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly partial struct JSArray : IJSValue, IList { private readonly JSValue _value; public static implicit operator JSValue(JSArray arr) => arr.AsJSValue(); public static explicit operator JSArray?(JSValue value) => value.As(); - public static explicit operator JSArray(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not an Array"); + public static explicit operator JSArray(JSValue value) => value.CastTo(); public static explicit operator JSArray(JSObject obj) => (JSArray)(JSValue)obj; public static implicit operator JSObject(JSArray arr) => (JSObject)arr._value; @@ -53,14 +48,18 @@ public JSArray(JSValue[] array) #region IJSValue implementation - public static bool CanBeConvertedFrom(JSValue value) => value.IsArray(); + public static bool CanCreateFrom(JSValue value) => value.IsArray(); - public static JSArray CreateUnchecked(JSValue value) => new(value); - - #endregion +#if NET7_0_OR_GREATER + static JSArray IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSArray CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; + #endregion + /// public int Length => _value.GetArrayLength(); diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index 988f896c..b0bda8d4 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -8,18 +8,15 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly partial struct JSAsyncIterable : IAsyncEnumerable, IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly partial struct JSAsyncIterable : + IJSValue, IAsyncEnumerable { private readonly JSValue _value; public static implicit operator JSValue(JSAsyncIterable value) => value.AsJSValue(); public static explicit operator JSAsyncIterable?(JSValue value) => value.As(); public static explicit operator JSAsyncIterable(JSValue value) - => value.As() - ?? throw new InvalidCastException("JSValue is not an AsyncIterable."); + => value.CastTo(); public static explicit operator JSAsyncIterable(JSObject obj) => (JSAsyncIterable)(JSValue)obj; public static implicit operator JSObject(JSAsyncIterable iterable) => (JSObject)iterable._value; @@ -32,14 +29,18 @@ private JSAsyncIterable(JSValue value) #region IJSValue implementation //TODO: (vmoroz) implement proper check using Symbol.asyncIterator - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - public static JSAsyncIterable CreateUnchecked(JSValue value) => new(value); - - #endregion +#if NET7_0_OR_GREATER + static JSAsyncIterable IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSAsyncIterable CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; + #endregion + #pragma warning disable IDE0060 // Unused parameter public Enumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => new(_value); diff --git a/src/NodeApi/JSBigInt.cs b/src/NodeApi/JSBigInt.cs index 54bf0080..819ce180 100644 --- a/src/NodeApi/JSBigInt.cs +++ b/src/NodeApi/JSBigInt.cs @@ -7,17 +7,13 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly struct JSBigInt : IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly struct JSBigInt : IJSValue { private readonly JSValue _value; public static implicit operator JSValue(JSBigInt value) => value.AsJSValue(); public static explicit operator JSBigInt?(JSValue value) => value.As(); - public static explicit operator JSBigInt(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a BigInt"); + public static explicit operator JSBigInt(JSValue value) => value.CastTo(); public static implicit operator JSBigInt(BigInteger value) => new(value); public static explicit operator BigInteger(JSBigInt value) => value.ToBigInteger(); @@ -46,14 +42,18 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) #region IJSValue implementation - public static bool CanBeConvertedFrom(JSValue value) => value.IsBigInt(); + public static bool CanCreateFrom(JSValue value) => value.IsBigInt(); - public static JSBigInt CreateUnchecked(JSValue value) => new(value); - - #endregion +#if NET7_0_OR_GREATER + static JSBigInt IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSBigInt CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; + #endregion + public int GetWordCount() => _value.GetBigIntWordCount(); public void CopyTo(Span destination, out int sign, out int wordCount) diff --git a/src/NodeApi/JSDate.cs b/src/NodeApi/JSDate.cs index 8e0a6ab4..ab06c5f7 100644 --- a/src/NodeApi/JSDate.cs +++ b/src/NodeApi/JSDate.cs @@ -4,21 +4,16 @@ using System; using System.Diagnostics.CodeAnalysis; using Microsoft.JavaScript.NodeApi.Interop; -using static Microsoft.JavaScript.NodeApi.Runtime.JSRuntime; namespace Microsoft.JavaScript.NodeApi; -public readonly struct JSDate : IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly struct JSDate : IJSValue { private readonly JSValue _value; public static implicit operator JSValue(JSDate value) => value.AsJSValue(); public static explicit operator JSDate?(JSValue value) => value.As(); - public static explicit operator JSDate(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a Date"); + public static explicit operator JSDate(JSValue value) => value.CastTo(); private JSDate(JSValue value) { @@ -44,13 +39,18 @@ public JSDate(string dateString) #region IJSValue implementation - public static bool CanBeConvertedFrom(JSValue value) => value.IsDate(); + public static bool CanCreateFrom(JSValue value) => value.IsDate(); - public static JSDate CreateUnchecked(JSValue value) => new(value); +#if NET7_0_OR_GREATER + static JSDate IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSDate CreateUnchecked(JSValue value) => new(value); +#endif + + public JSValue AsJSValue() => _value; #endregion - public JSValue AsJSValue() => _value; public static JSDate FromDateTime(DateTime value) { diff --git a/src/NodeApi/JSFunction.cs b/src/NodeApi/JSFunction.cs index 7bd8ca54..bf22e92c 100644 --- a/src/NodeApi/JSFunction.cs +++ b/src/NodeApi/JSFunction.cs @@ -9,17 +9,13 @@ namespace Microsoft.JavaScript.NodeApi; /// /// Represents a JavaScript Function value. /// -public readonly struct JSFunction : IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly struct JSFunction : IJSValue { private readonly JSValue _value; public static implicit operator JSValue(JSFunction value) => value.AsJSValue(); public static explicit operator JSFunction?(JSValue value) => value.As(); - public static explicit operator JSFunction(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a Function"); + public static explicit operator JSFunction(JSValue value) => value.CastTo(); private JSFunction(JSValue value) { @@ -274,14 +270,18 @@ public JSFunction( #region IJSValue implementation - public static bool CanBeConvertedFrom(JSValue value) => value.IsFunction(); - - public static JSFunction CreateUnchecked(JSValue value) => new(value); + public static bool CanCreateFrom(JSValue value) => value.IsFunction(); - #endregion +#if NET7_0_OR_GREATER + static JSFunction IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSFunction CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; +#endregion + /// /// Gets the name of the function, or an empty string if the function is unnamed. /// diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index 7e2e4fa8..3fdb8abf 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -8,17 +8,13 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly partial struct JSIterable : IEnumerable, IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly partial struct JSIterable : IJSValue, IEnumerable { private readonly JSValue _value; public static implicit operator JSValue(JSIterable value) => value.AsJSValue(); public static explicit operator JSIterable?(JSValue value) => value.As(); - public static explicit operator JSIterable(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not an Iterable."); + public static explicit operator JSIterable(JSValue value) => value.CastTo(); public static explicit operator JSIterable(JSObject obj) => (JSIterable)(JSValue)obj; public static implicit operator JSObject(JSIterable iterable) => (JSObject)iterable._value; @@ -31,14 +27,18 @@ private JSIterable(JSValue value) #region IJSValue implementation //TODO: (vmoroz) implement proper check using Symbol.iterator - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); - - public static JSIterable CreateUnchecked(JSValue value) => new(value); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - #endregion +#if NET7_0_OR_GREATER + static JSIterable IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSIterable CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; +#endregion + public Enumerator GetEnumerator() => new(_value); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/NodeApi/JSMap.cs b/src/NodeApi/JSMap.cs index 0ca6489e..87fd7cfa 100644 --- a/src/NodeApi/JSMap.cs +++ b/src/NodeApi/JSMap.cs @@ -8,17 +8,13 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly partial struct JSMap : IDictionary, IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly partial struct JSMap : IJSValue, IDictionary { private readonly JSValue _value; public static implicit operator JSValue(JSMap value) => value.AsJSValue(); public static explicit operator JSMap?(JSValue value) => value.As(); - public static explicit operator JSMap(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a Map."); + public static explicit operator JSMap(JSValue value) => value.CastTo(); public static explicit operator JSMap(JSObject obj) => (JSMap)(JSValue)obj; public static implicit operator JSObject(JSMap map) => (JSObject)map._value; @@ -48,14 +44,18 @@ public JSMap(JSIterable iterable) #region IJSValue implementation // TODO: (vmoroz) Implement using instanceof - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - public static JSMap CreateUnchecked(JSValue value) => new(value); - - #endregion +#if NET7_0_OR_GREATER + static JSMap IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSMap CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; + #endregion + public int Count => (int)_value["size"]; public Enumerator GetEnumerator() => new(_value); diff --git a/src/NodeApi/JSObject.cs b/src/NodeApi/JSObject.cs index 33a0296f..ff0c128c 100644 --- a/src/NodeApi/JSObject.cs +++ b/src/NodeApi/JSObject.cs @@ -8,17 +8,13 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly partial struct JSObject : IDictionary, IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly partial struct JSObject : IJSValue, IDictionary { private readonly JSValue _value; public static implicit operator JSValue(JSObject value) => value.AsJSValue(); public static explicit operator JSObject?(JSValue value) => value.As(); - public static explicit operator JSObject(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not an Object."); + public static explicit operator JSObject(JSValue value) => value.CastTo(); private JSObject(JSValue value) { @@ -31,14 +27,18 @@ public JSObject() : this(JSValue.CreateObject()) #region IJSValue implementation - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); - - public static JSObject CreateUnchecked(JSValue value) => new(value); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - #endregion +#if NET7_0_OR_GREATER + static JSObject IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSObject CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; +#endregion + public JSObject(IEnumerable> properties) : this() { foreach (KeyValuePair property in properties) diff --git a/src/NodeApi/JSPromise.cs b/src/NodeApi/JSPromise.cs index 1db14c45..de80900b 100644 --- a/src/NodeApi/JSPromise.cs +++ b/src/NodeApi/JSPromise.cs @@ -14,17 +14,13 @@ namespace Microsoft.JavaScript.NodeApi; /// Represents a JavaScript Promise object. /// /// -public readonly struct JSPromise : IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly struct JSPromise : IJSValue { private readonly JSValue _value; public static implicit operator JSValue(JSPromise value) => value.AsJSValue(); public static explicit operator JSPromise?(JSValue value) => value.As(); - public static explicit operator JSPromise(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a Promise."); + public static explicit operator JSPromise(JSValue value) => value.CastTo(); public static explicit operator JSPromise(JSObject obj) => (JSPromise)(JSValue)obj; public static implicit operator JSObject(JSPromise promise) => (JSObject)promise._value; @@ -150,14 +146,18 @@ private static async void ResolveDeferred( #region IJSValue implementation - public static bool CanBeConvertedFrom(JSValue value) => value.IsPromise(); - - public static JSPromise CreateUnchecked(JSValue value) => new(value); + public static bool CanCreateFrom(JSValue value) => value.IsPromise(); - #endregion +#if NET7_0_OR_GREATER + static JSPromise IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSPromise CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; +#endregion + /// /// Registers callbacks that are invoked when a promise is fulfilled and/or rejected, /// and returns a new chained promise. diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index d562a135..70680849 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -12,18 +12,14 @@ namespace Microsoft.JavaScript.NodeApi; /// /// Enables creation of JS Proxy objects with C# handler callbacks. /// -public readonly partial struct JSProxy : IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly partial struct JSProxy : IJSValue { private readonly JSValue _value; private readonly JSValue _revoke = default; public static implicit operator JSValue(JSProxy value) => value.AsJSValue(); public static explicit operator JSProxy?(JSValue value) => value.As(); - public static explicit operator JSProxy(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a Proxy."); + public static explicit operator JSProxy(JSValue value) => value.CastTo(); private JSProxy(JSValue value) { @@ -73,14 +69,18 @@ public JSProxy( #region IJSValue implementation // TODO: (vmoroz) Implement using instanceof - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - public static JSProxy CreateUnchecked(JSValue value) => new(value); - - #endregion +#if NET7_0_OR_GREATER + static JSProxy IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSProxy CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; + #endregion + /// /// Revokes the proxy, so that further access to the target is no longer trapped by /// the proxy handler. diff --git a/src/NodeApi/JSSet.cs b/src/NodeApi/JSSet.cs index ba8ec122..1f49256d 100644 --- a/src/NodeApi/JSSet.cs +++ b/src/NodeApi/JSSet.cs @@ -9,17 +9,13 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly partial struct JSSet : ISet, IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly partial struct JSSet : IJSValue, ISet { private readonly JSValue _value; public static implicit operator JSValue(JSSet value) => value.AsJSValue(); public static explicit operator JSSet?(JSValue value) => value.As(); - public static explicit operator JSSet(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a Set."); + public static explicit operator JSSet(JSValue value) => value.CastTo(); public static explicit operator JSSet(JSObject obj) => (JSSet)(JSValue)obj; public static implicit operator JSObject(JSSet set) => (JSObject)set._value; @@ -52,14 +48,18 @@ public JSSet(JSIterable iterable) #region IJSValue implementation // TODO: (vmoroz) Implement using instanceof - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - public static JSSet CreateUnchecked(JSValue value) => new(value); - - #endregion +#if NET7_0_OR_GREATER + static JSSet IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSSet CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; + #endregion + public int Count => (int)_value["size"]; bool ICollection.IsReadOnly => false; diff --git a/src/NodeApi/JSSymbol.cs b/src/NodeApi/JSSymbol.cs index 17640ce5..ce0dc764 100644 --- a/src/NodeApi/JSSymbol.cs +++ b/src/NodeApi/JSSymbol.cs @@ -6,10 +6,7 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly struct JSSymbol : IEquatable -#if NET7_0_OR_GREATER - , IJSValue -#endif +public readonly struct JSSymbol : IJSValue { private readonly JSValue _value; @@ -21,8 +18,7 @@ namespace Microsoft.JavaScript.NodeApi; public static implicit operator JSValue(JSSymbol value) => value.AsJSValue(); public static explicit operator JSSymbol?(JSValue value) => value.As(); - public static explicit operator JSSymbol(JSValue value) - => value.As() ?? throw new InvalidCastException("JSValue is not a Symbol."); + public static explicit operator JSSymbol(JSValue value) => value.CastTo(); private JSSymbol(JSValue value) { @@ -36,14 +32,18 @@ public JSSymbol(string? description = null) #region IJSValue implementation - public static bool CanBeConvertedFrom(JSValue value) => value.IsSymbol(); - - public static JSSymbol CreateUnchecked(JSValue value) => new(value); + public static bool CanCreateFrom(JSValue value) => value.IsSymbol(); - #endregion +#if NET7_0_OR_GREATER + static JSSymbol IJSValue.CreateUnchecked(JSValue value) => new(value); +#else + private static JSSymbol CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; +#endregion + /// /// Gets the symbol's description, or null if it does not have one. /// diff --git a/src/NodeApi/JSTypedArray.cs b/src/NodeApi/JSTypedArray.cs index e8282055..66343fa4 100644 --- a/src/NodeApi/JSTypedArray.cs +++ b/src/NodeApi/JSTypedArray.cs @@ -9,10 +9,7 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly struct JSTypedArray : IEquatable -#if NET7_0_OR_GREATER - , IJSValue> -#endif +public readonly struct JSTypedArray : IJSValue> where T : struct { private readonly JSValue _value; @@ -20,9 +17,7 @@ namespace Microsoft.JavaScript.NodeApi; public static implicit operator JSValue(JSTypedArray value) => value.AsJSValue(); public static explicit operator JSTypedArray?(JSValue value) => value.As>(); public static explicit operator JSTypedArray(JSValue value) - => value.As>() - ?? throw new InvalidCastException("JSValue is not a TypedArray."); - + => value.CastTo>(); private static int ElementSize { get; } = default(T) switch { @@ -112,14 +107,18 @@ public unsafe JSTypedArray(ReadOnlyMemory data) #region IJSValue> implementation //TODO: (vmoroz) Implement correctly - public static bool CanBeConvertedFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) => value.IsObject(); - public static JSTypedArray CreateUnchecked(JSValue value) => new(value); - - #endregion +#if NET7_0_OR_GREATER + static JSTypedArray IJSValue>.CreateUnchecked(JSValue value) => new(value); +#else + private static JSTypedArray CreateUnchecked(JSValue value) => new(value); +#endif public JSValue AsJSValue() => _value; + #endregion + /// /// Checks if this Memory is already owned by a JS TypedArray value, and if so /// returns that JS value. diff --git a/src/NodeApi/JSValue.cs b/src/NodeApi/JSValue.cs index 3329191d..db2356bd 100644 --- a/src/NodeApi/JSValue.cs +++ b/src/NodeApi/JSValue.cs @@ -391,29 +391,33 @@ public JSValueType TypeOf() => _handle.IsNull public bool IsBigInt() => TypeOf() == JSValueType.BigInt; - public bool Is() where TValue : struct + public bool Is() where TValue : struct, IJSValue #if NET7_0_OR_GREATER - , IJSValue => TValue.CanBeConvertedFrom(this); + => TValue.CanCreateFrom(this); #else - => IJSValueShim.CanBeConvertedFrom(this); + => IJSValueShim.CanCreateFrom(this); #endif - public TValue? As() where TValue : struct + public TValue? As() where TValue : struct, IJSValue #if NET7_0_OR_GREATER - , IJSValue - => TValue.CanBeConvertedFrom(this) ? TValue.CreateUnchecked(this) : default; + => TValue.CanCreateFrom(this) ? TValue.CreateUnchecked(this) : default; #else - => IJSValueShim.CanBeConvertedFrom(this) + => IJSValueShim.CanCreateFrom(this) ? IJSValueShim.CreateUnchecked(this) : default; #endif - public TValue AsUnchecked() where TValue : struct + public TValue AsUnchecked() where TValue : struct, IJSValue #if NET7_0_OR_GREATER - , IJSValue => TValue.CreateUnchecked(this); + => TValue.CreateUnchecked(this); #else => IJSValueShim.CreateUnchecked(this); #endif + public TValue CastTo() where TValue : struct, IJSValue + => As() + ?? throw new InvalidCastException("JSValue cannot be casted to target type."); + + public double GetValueDouble() => GetRuntime(out napi_env env, out napi_value handle) .GetValueDouble(env, handle, out double result).ThrowIfFailed(result); From d7b0c30316bee0644f8ee30eff8bbaad0b030b2a Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 2 Aug 2024 14:35:01 -0700 Subject: [PATCH 03/13] Disable IDE0051 for CreateUnchecked methods --- src/NodeApi/Interop/JSAbortSignal.cs | 4 +++- src/NodeApi/JSArray.cs | 2 ++ src/NodeApi/JSAsyncIterable.cs | 2 ++ src/NodeApi/JSBigInt.cs | 2 ++ src/NodeApi/JSDate.cs | 2 ++ src/NodeApi/JSFunction.cs | 4 +++- src/NodeApi/JSIterable.cs | 4 +++- src/NodeApi/JSMap.cs | 2 ++ src/NodeApi/JSObject.cs | 4 +++- src/NodeApi/JSPromise.cs | 4 +++- src/NodeApi/JSProxy.cs | 2 ++ src/NodeApi/JSSet.cs | 2 ++ src/NodeApi/JSSymbol.cs | 4 +++- src/NodeApi/JSTypedArray.cs | 2 ++ 14 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index 8ef84893..dca67637 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -47,12 +47,14 @@ public static explicit operator JSAbortSignal(CancellationToken? cancellation) // TODO: (vmoroz) Implement static JSAbortSignal IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSAbortSignal CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; -#endregion + #endregion private CancellationToken ToCancellationToken() { diff --git a/src/NodeApi/JSArray.cs b/src/NodeApi/JSArray.cs index 7e7bbddf..7e6115f6 100644 --- a/src/NodeApi/JSArray.cs +++ b/src/NodeApi/JSArray.cs @@ -53,7 +53,9 @@ public JSArray(JSValue[] array) #if NET7_0_OR_GREATER static JSArray IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSArray CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index b0bda8d4..c196e4f6 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -34,7 +34,9 @@ private JSAsyncIterable(JSValue value) #if NET7_0_OR_GREATER static JSAsyncIterable IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSAsyncIterable CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; diff --git a/src/NodeApi/JSBigInt.cs b/src/NodeApi/JSBigInt.cs index 819ce180..c5c8fd74 100644 --- a/src/NodeApi/JSBigInt.cs +++ b/src/NodeApi/JSBigInt.cs @@ -47,7 +47,9 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) #if NET7_0_OR_GREATER static JSBigInt IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSBigInt CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; diff --git a/src/NodeApi/JSDate.cs b/src/NodeApi/JSDate.cs index ab06c5f7..f7c86916 100644 --- a/src/NodeApi/JSDate.cs +++ b/src/NodeApi/JSDate.cs @@ -44,7 +44,9 @@ public JSDate(string dateString) #if NET7_0_OR_GREATER static JSDate IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSDate CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; diff --git a/src/NodeApi/JSFunction.cs b/src/NodeApi/JSFunction.cs index bf22e92c..ea3ed4a9 100644 --- a/src/NodeApi/JSFunction.cs +++ b/src/NodeApi/JSFunction.cs @@ -275,12 +275,14 @@ public JSFunction( #if NET7_0_OR_GREATER static JSFunction IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSFunction CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; -#endregion + #endregion /// /// Gets the name of the function, or an empty string if the function is unnamed. diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index 3fdb8abf..4a0fb939 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -32,12 +32,14 @@ private JSIterable(JSValue value) #if NET7_0_OR_GREATER static JSIterable IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSIterable CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; -#endregion + #endregion public Enumerator GetEnumerator() => new(_value); diff --git a/src/NodeApi/JSMap.cs b/src/NodeApi/JSMap.cs index 87fd7cfa..1d3fb761 100644 --- a/src/NodeApi/JSMap.cs +++ b/src/NodeApi/JSMap.cs @@ -49,7 +49,9 @@ public JSMap(JSIterable iterable) #if NET7_0_OR_GREATER static JSMap IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSMap CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; diff --git a/src/NodeApi/JSObject.cs b/src/NodeApi/JSObject.cs index ff0c128c..be89942d 100644 --- a/src/NodeApi/JSObject.cs +++ b/src/NodeApi/JSObject.cs @@ -32,12 +32,14 @@ public JSObject() : this(JSValue.CreateObject()) #if NET7_0_OR_GREATER static JSObject IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSObject CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; -#endregion + #endregion public JSObject(IEnumerable> properties) : this() { diff --git a/src/NodeApi/JSPromise.cs b/src/NodeApi/JSPromise.cs index de80900b..d9ac9a84 100644 --- a/src/NodeApi/JSPromise.cs +++ b/src/NodeApi/JSPromise.cs @@ -151,12 +151,14 @@ private static async void ResolveDeferred( #if NET7_0_OR_GREATER static JSPromise IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSPromise CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; -#endregion + #endregion /// /// Registers callbacks that are invoked when a promise is fulfilled and/or rejected, diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index 70680849..7848e215 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -74,7 +74,9 @@ public JSProxy( #if NET7_0_OR_GREATER static JSProxy IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSProxy CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; diff --git a/src/NodeApi/JSSet.cs b/src/NodeApi/JSSet.cs index 1f49256d..08c2cb6b 100644 --- a/src/NodeApi/JSSet.cs +++ b/src/NodeApi/JSSet.cs @@ -53,7 +53,9 @@ public JSSet(JSIterable iterable) #if NET7_0_OR_GREATER static JSSet IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSSet CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; diff --git a/src/NodeApi/JSSymbol.cs b/src/NodeApi/JSSymbol.cs index ce0dc764..e5469387 100644 --- a/src/NodeApi/JSSymbol.cs +++ b/src/NodeApi/JSSymbol.cs @@ -37,12 +37,14 @@ public JSSymbol(string? description = null) #if NET7_0_OR_GREATER static JSSymbol IJSValue.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSSymbol CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; -#endregion + #endregion /// /// Gets the symbol's description, or null if it does not have one. diff --git a/src/NodeApi/JSTypedArray.cs b/src/NodeApi/JSTypedArray.cs index 66343fa4..a067f2c7 100644 --- a/src/NodeApi/JSTypedArray.cs +++ b/src/NodeApi/JSTypedArray.cs @@ -112,7 +112,9 @@ public unsafe JSTypedArray(ReadOnlyMemory data) #if NET7_0_OR_GREATER static JSTypedArray IJSValue>.CreateUnchecked(JSValue value) => new(value); #else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. private static JSTypedArray CreateUnchecked(JSValue value) => new(value); +#pragma warning restore IDE0051 #endif public JSValue AsJSValue() => _value; From 4340ac4535474e7afba13adc7d2e7d858db4344d Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 2 Aug 2024 16:40:58 -0700 Subject: [PATCH 04/13] Implement CanCreateFrom methods --- src/NodeApi/Interop/JSAbortSignal.cs | 3 +-- src/NodeApi/JSAsyncIterable.cs | 4 ++-- src/NodeApi/JSIterable.cs | 4 ++-- src/NodeApi/JSMap.cs | 4 ++-- src/NodeApi/JSProxy.cs | 3 ++- src/NodeApi/JSSet.cs | 3 ++- src/NodeApi/JSTypedArray.cs | 20 ++++++++++++++++++-- 7 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index dca67637..691194de 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -41,10 +41,9 @@ public static explicit operator JSAbortSignal(CancellationToken? cancellation) #region IJSValue implementation - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) => value.IsObject() && value.InstanceOf(JSValue.Global["JSAbortSignal"]); #if NET7_0_OR_GREATER - // TODO: (vmoroz) Implement static JSAbortSignal IJSValue.CreateUnchecked(JSValue value) => new(value); #else #pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index c196e4f6..8c543e61 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -28,8 +28,8 @@ private JSAsyncIterable(JSValue value) #region IJSValue implementation - //TODO: (vmoroz) implement proper check using Symbol.asyncIterator - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) + => value.IsObject() && value.HasProperty(JSValue.Global["Symbol"]["asyncIterator"]); #if NET7_0_OR_GREATER static JSAsyncIterable IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index 4a0fb939..688a8485 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -26,8 +26,8 @@ private JSIterable(JSValue value) #region IJSValue implementation - //TODO: (vmoroz) implement proper check using Symbol.iterator - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) + => value.IsObject() && value.HasProperty(JSValue.Global["Symbol"]["iterator"]); #if NET7_0_OR_GREATER static JSIterable IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/src/NodeApi/JSMap.cs b/src/NodeApi/JSMap.cs index 1d3fb761..2909d470 100644 --- a/src/NodeApi/JSMap.cs +++ b/src/NodeApi/JSMap.cs @@ -43,8 +43,8 @@ public JSMap(JSIterable iterable) #region IJSValue implementation - // TODO: (vmoroz) Implement using instanceof - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) + => value.IsObject() && value.InstanceOf(JSValue.Global["Map"]); #if NET7_0_OR_GREATER static JSMap IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index 7848e215..68425627 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -69,7 +69,8 @@ public JSProxy( #region IJSValue implementation // TODO: (vmoroz) Implement using instanceof - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) + => value.IsObject() && value.InstanceOf(JSValue.Global["Proxy"]); #if NET7_0_OR_GREATER static JSProxy IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/src/NodeApi/JSSet.cs b/src/NodeApi/JSSet.cs index 08c2cb6b..988d0ded 100644 --- a/src/NodeApi/JSSet.cs +++ b/src/NodeApi/JSSet.cs @@ -48,7 +48,8 @@ public JSSet(JSIterable iterable) #region IJSValue implementation // TODO: (vmoroz) Implement using instanceof - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) + => value.IsObject() && value.InstanceOf(JSValue.Global["Set"]); #if NET7_0_OR_GREATER static JSSet IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/src/NodeApi/JSTypedArray.cs b/src/NodeApi/JSTypedArray.cs index a067f2c7..0244c1d0 100644 --- a/src/NodeApi/JSTypedArray.cs +++ b/src/NodeApi/JSTypedArray.cs @@ -9,6 +9,7 @@ namespace Microsoft.JavaScript.NodeApi; +//TODO: Add support for Uint8ClampedArray public readonly struct JSTypedArray : IJSValue> where T : struct { @@ -49,6 +50,21 @@ public static explicit operator JSTypedArray(JSValue value) _ => throw new InvalidCastException("Invalid typed-array type: " + typeof(T)), }; + private static string JSTypeName { get; } = default(T) switch + { + sbyte => "Int8Array", + byte => "Uint8Array", + short => "Int16Array", + ushort => "Uint16Array", + int => "Int32Array", + uint => "Uint32Array", + long => "BigInt64Array", + ulong => "BigUint64Array", + float => "Float32Array", + double => "Float64Array", + _ => throw new InvalidCastException("Invalid typed-array type: " + typeof(T)), + }; + private JSTypedArray(JSValue value) { _value = value; @@ -106,8 +122,8 @@ public unsafe JSTypedArray(ReadOnlyMemory data) #region IJSValue> implementation - //TODO: (vmoroz) Implement correctly - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + public static bool CanCreateFrom(JSValue value) + => value.IsObject() && value.InstanceOf(JSValue.Global[JSTypeName]); #if NET7_0_OR_GREATER static JSTypedArray IJSValue>.CreateUnchecked(JSValue value) => new(value); From c5f241d7925d5439f8c2acbfe7894d37a15e6bc0 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 2 Aug 2024 18:12:20 -0700 Subject: [PATCH 05/13] Minor fixes in JSAbortSignal --- src/NodeApi/Interop/JSAbortSignal.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index 691194de..9f097bf2 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -24,7 +24,7 @@ namespace Microsoft.JavaScript.NodeApi.Interop; public static explicit operator JSAbortSignal(JSValue value) => value.CastTo(); public static explicit operator JSAbortSignal(JSObject obj) => (JSAbortSignal)(JSValue)obj; - public static implicit operator JSObject(JSAbortSignal promise) => (JSObject)promise._value; + public static implicit operator JSObject(JSAbortSignal signal) => (JSObject)signal._value; private JSAbortSignal(JSValue value) { @@ -41,7 +41,8 @@ public static explicit operator JSAbortSignal(CancellationToken? cancellation) #region IJSValue implementation - public static bool CanCreateFrom(JSValue value) => value.IsObject() && value.InstanceOf(JSValue.Global["JSAbortSignal"]); + public static bool CanCreateFrom(JSValue value) + => value.IsObject() && value.InstanceOf(JSValue.Global["AbortSignal"]); #if NET7_0_OR_GREATER static JSAbortSignal IJSValue.CreateUnchecked(JSValue value) => new(value); From 637daeda66d348690831e5e2e849587b58e4688c Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 2 Aug 2024 22:28:26 -0700 Subject: [PATCH 06/13] Add unit test for JSAbortSignal casting --- src/NodeApi/JSValue.cs | 4 +- test/TestCases/napi-dotnet/JSValueCast.cs | 46 ++++++++++++++++++++++ test/TestCases/napi-dotnet/jsvalue_cast.js | 20 ++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 test/TestCases/napi-dotnet/JSValueCast.cs create mode 100644 test/TestCases/napi-dotnet/jsvalue_cast.js diff --git a/src/NodeApi/JSValue.cs b/src/NodeApi/JSValue.cs index db2356bd..b722bde9 100644 --- a/src/NodeApi/JSValue.cs +++ b/src/NodeApi/JSValue.cs @@ -400,10 +400,10 @@ public bool Is() where TValue : struct, IJSValue public TValue? As() where TValue : struct, IJSValue #if NET7_0_OR_GREATER - => TValue.CanCreateFrom(this) ? TValue.CreateUnchecked(this) : default; + => TValue.CanCreateFrom(this) ? TValue.CreateUnchecked(this) : default(TValue?); #else => IJSValueShim.CanCreateFrom(this) - ? IJSValueShim.CreateUnchecked(this) : default; + ? IJSValueShim.CreateUnchecked(this) : default(TValue?); #endif public TValue AsUnchecked() where TValue : struct, IJSValue diff --git a/test/TestCases/napi-dotnet/JSValueCast.cs b/test/TestCases/napi-dotnet/JSValueCast.cs new file mode 100644 index 00000000..aada1b4b --- /dev/null +++ b/test/TestCases/napi-dotnet/JSValueCast.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma warning disable IDE0060 // Unused parameters +#pragma warning disable IDE0301 // Collection initialization can be simplified + +using System; +using System.Collections.Generic; +using System.Numerics; +using Interop = Microsoft.JavaScript.NodeApi.Interop; + +namespace Microsoft.JavaScript.NodeApi.TestCases; + +/// +/// Tests type casting between JSValue and IJSValue derived types. +/// +[JSExport] +public static class JSValueCast +{ + [JSExport("testAbortSignalAs")] + public static string TestAbortSignalAs(JSValue value) + { + return (Interop.JSAbortSignal?)value is not null ? "ok" : "fail"; + } + + [JSExport("testAbortSignalIs")] + public static string TestAbortSignalIs(JSValue value) + { + return value.Is() ? "ok" : "fail"; + } + + [JSExport("testAbortSignalCast")] + public static string TestAbortSignalCast(JSValue value) + { + try + { + Interop.JSAbortSignal signal = (Interop.JSAbortSignal)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "fail roundrip"; + } + catch (InvalidCastException) + { + return "fail"; + } + } +} diff --git a/test/TestCases/napi-dotnet/jsvalue_cast.js b/test/TestCases/napi-dotnet/jsvalue_cast.js new file mode 100644 index 00000000..5dbc7433 --- /dev/null +++ b/test/TestCases/napi-dotnet/jsvalue_cast.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +const assert = require('assert'); + +/** @type {import('./napi-dotnet')} */ +const binding = require('../common').binding; + +const JSValueCast = binding.JSValueCast; +assert.strictEqual(typeof JSValueCast, 'object'); + +assert.strictEqual(typeof JSValueCast.testAbortSignalAs, 'function'); +assert.strictEqual(typeof JSValueCast.testAbortSignalIs, 'function'); +assert.strictEqual(typeof JSValueCast.testAbortSignalCast, 'function'); +assert.strictEqual(JSValueCast.testAbortSignalAs(AbortSignal.abort()), "ok"); +assert.strictEqual(JSValueCast.testAbortSignalAs({}), "fail"); +assert.strictEqual(JSValueCast.testAbortSignalIs(AbortSignal.abort()), "ok"); +assert.strictEqual(JSValueCast.testAbortSignalIs({}), "fail"); +assert.strictEqual(JSValueCast.testAbortSignalCast(AbortSignal.abort()), "ok"); +assert.strictEqual(JSValueCast.testAbortSignalCast({}), "fail"); From da80ce07bf9cc953204b7e22224652bb9e042337 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Sat, 3 Aug 2024 09:06:06 -0700 Subject: [PATCH 07/13] Add more tests --- src/NodeApi/JSAsyncIterable.cs | 2 +- test/TestCases/napi-dotnet/JSValueCast.cs | 142 +++++++++++++++++++-- test/TestCases/napi-dotnet/jsvalue_cast.js | 78 +++++++++-- 3 files changed, 201 insertions(+), 21 deletions(-) diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index 8c543e61..45791b2a 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -29,7 +29,7 @@ private JSAsyncIterable(JSValue value) #region IJSValue implementation public static bool CanCreateFrom(JSValue value) - => value.IsObject() && value.HasProperty(JSValue.Global["Symbol"]["asyncIterator"]); + => value.IsObject() && value.HasProperty(JSSymbol.AsyncIterator); #if NET7_0_OR_GREATER static JSAsyncIterable IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/test/TestCases/napi-dotnet/JSValueCast.cs b/test/TestCases/napi-dotnet/JSValueCast.cs index aada1b4b..fc2632d8 100644 --- a/test/TestCases/napi-dotnet/JSValueCast.cs +++ b/test/TestCases/napi-dotnet/JSValueCast.cs @@ -17,30 +17,150 @@ namespace Microsoft.JavaScript.NodeApi.TestCases; [JSExport] public static class JSValueCast { - [JSExport("testAbortSignalAs")] - public static string TestAbortSignalAs(JSValue value) + #region JSAbortSignal + + public static string ValueAsAbortSignal(JSValue value) + => (Interop.JSAbortSignal?)value is not null ? "ok" : "failed"; + + public static string ValueIsAbortSignal(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToAbortSignal(JSValue value) { - return (Interop.JSAbortSignal?)value is not null ? "ok" : "fail"; + try + { + Interop.JSAbortSignal signal = (Interop.JSAbortSignal)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } } - [JSExport("testAbortSignalIs")] - public static string TestAbortSignalIs(JSValue value) + #endregion + + #region JSArray + + public static string ValueAsArray(JSValue value) + => (JSArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsArray(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToArray(JSValue value) { - return value.Is() ? "ok" : "fail"; + try + { + JSArray signal = (JSArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } } - [JSExport("testAbortSignalCast")] - public static string TestAbortSignalCast(JSValue value) + #endregion + + #region JSAsyncIterable + + public static JSValue GetAsyncIterator(JSValue value) + => JSValue.Global["Symbol"]["asyncIterator"]; + + public static string ValueAsAsyncIterable(JSValue value) + => (JSAsyncIterable?)value is not null ? "ok" : "failed"; + + public static string ValueIsAsyncIterable(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToAsyncIterable(JSValue value) { try { - Interop.JSAbortSignal signal = (Interop.JSAbortSignal)value; + JSAsyncIterable signal = (JSAsyncIterable)value; JSValue value2 = signal; - return value.Handle == value2.Handle ? "ok" : "fail roundrip"; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; } catch (InvalidCastException) { - return "fail"; + return "failed"; } } + + #endregion + + #region JSBigInt + + public static string ValueAsBigInt(JSValue value) + => (JSBigInt?)value is not null ? "ok" : "failed"; + + public static string ValueIsBigInt(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToBigInt(JSValue value) + { + try + { + JSBigInt signal = (JSBigInt)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSDate + + public static string ValueAsDate(JSValue value) + => (JSDate?)value is not null ? "ok" : "failed"; + + public static string ValueIsDate(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToDate(JSValue value) + { + try + { + JSDate signal = (JSDate)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSFunction + + public static string ValueAsFunction(JSValue value) + => (JSFunction?)value is not null ? "ok" : "failed"; + + public static string ValueIsFunction(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToFunction(JSValue value) + { + try + { + JSFunction signal = (JSFunction)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion } diff --git a/test/TestCases/napi-dotnet/jsvalue_cast.js b/test/TestCases/napi-dotnet/jsvalue_cast.js index 5dbc7433..d8773b50 100644 --- a/test/TestCases/napi-dotnet/jsvalue_cast.js +++ b/test/TestCases/napi-dotnet/jsvalue_cast.js @@ -9,12 +9,72 @@ const binding = require('../common').binding; const JSValueCast = binding.JSValueCast; assert.strictEqual(typeof JSValueCast, 'object'); -assert.strictEqual(typeof JSValueCast.testAbortSignalAs, 'function'); -assert.strictEqual(typeof JSValueCast.testAbortSignalIs, 'function'); -assert.strictEqual(typeof JSValueCast.testAbortSignalCast, 'function'); -assert.strictEqual(JSValueCast.testAbortSignalAs(AbortSignal.abort()), "ok"); -assert.strictEqual(JSValueCast.testAbortSignalAs({}), "fail"); -assert.strictEqual(JSValueCast.testAbortSignalIs(AbortSignal.abort()), "ok"); -assert.strictEqual(JSValueCast.testAbortSignalIs({}), "fail"); -assert.strictEqual(JSValueCast.testAbortSignalCast(AbortSignal.abort()), "ok"); -assert.strictEqual(JSValueCast.testAbortSignalCast({}), "fail"); +assert.strictEqual(typeof JSValueCast.valueAsAbortSignal, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsAbortSignal, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToAbortSignal, 'function'); +assert.strictEqual(JSValueCast.valueAsAbortSignal(AbortSignal.abort()), "ok"); +assert.strictEqual(JSValueCast.valueAsAbortSignal({}), "failed"); +assert.strictEqual(JSValueCast.valueIsAbortSignal(AbortSignal.abort()), "ok"); +assert.strictEqual(JSValueCast.valueIsAbortSignal({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToAbortSignal(AbortSignal.abort()), "ok"); +assert.strictEqual(JSValueCast.valueCastToAbortSignal({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsArray, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsArray, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToArray, 'function'); +assert.strictEqual(JSValueCast.valueAsArray(["foo"]), "ok"); +assert.strictEqual(JSValueCast.valueAsArray({}), "failed"); +assert.strictEqual(JSValueCast.valueIsArray(["foo"]), "ok"); +assert.strictEqual(JSValueCast.valueIsArray({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToArray(["foo"]), "ok"); +assert.strictEqual(JSValueCast.valueCastToArray({}), "failed"); + +const asyncIterable = { + async *[Symbol.asyncIterator]() { + yield await Promise.resolve("a"); + }, +}; +const asyncIterable2 = Object.create(asyncIterable); + +assert.strictEqual(typeof JSValueCast.valueAsAsyncIterable, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsAsyncIterable, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToAsyncIterable, 'function'); +assert.strictEqual(JSValueCast.valueAsAsyncIterable(asyncIterable), "ok"); +assert.strictEqual(JSValueCast.valueAsAsyncIterable(asyncIterable2), "ok"); +assert.strictEqual(JSValueCast.valueAsAsyncIterable({}), "failed"); +assert.strictEqual(JSValueCast.valueIsAsyncIterable(asyncIterable), "ok"); +assert.strictEqual(JSValueCast.valueIsAsyncIterable(asyncIterable2), "ok"); +assert.strictEqual(JSValueCast.valueIsAsyncIterable({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToAsyncIterable(asyncIterable), "ok"); +assert.strictEqual(JSValueCast.valueCastToAsyncIterable(asyncIterable2), "ok"); +assert.strictEqual(JSValueCast.valueCastToAsyncIterable({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsBigInt, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsBigInt, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToBigInt, 'function'); +assert.strictEqual(JSValueCast.valueAsBigInt(42n), "ok"); +assert.strictEqual(JSValueCast.valueAsBigInt({}), "failed"); +assert.strictEqual(JSValueCast.valueIsBigInt(42n), "ok"); +assert.strictEqual(JSValueCast.valueIsBigInt({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToBigInt(42n), "ok"); +assert.strictEqual(JSValueCast.valueCastToBigInt({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsDate, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsDate, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToDate, 'function'); +assert.strictEqual(JSValueCast.valueAsDate(new Date()), "ok"); +assert.strictEqual(JSValueCast.valueAsDate({}), "failed"); +assert.strictEqual(JSValueCast.valueIsDate(new Date()), "ok"); +assert.strictEqual(JSValueCast.valueIsDate({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToDate(new Date()), "ok"); +assert.strictEqual(JSValueCast.valueCastToDate({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsFunction, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsFunction, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToFunction, 'function'); +assert.strictEqual(JSValueCast.valueAsFunction(() => { }), "ok"); +assert.strictEqual(JSValueCast.valueAsFunction({}), "failed"); +assert.strictEqual(JSValueCast.valueIsFunction(() => { }), "ok"); +assert.strictEqual(JSValueCast.valueIsFunction({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToFunction(() => { }), "ok"); +assert.strictEqual(JSValueCast.valueCastToFunction({}), "failed"); From dd8ae12c14f85f9e4ae23b2ba8608470e4cb6f48 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Sat, 3 Aug 2024 17:30:05 -0700 Subject: [PATCH 08/13] Add more tests --- src/NodeApi/JSIterable.cs | 2 +- src/NodeApi/JSProxy.cs | 5 +- test/TestCases/napi-dotnet/JSValueCast.cs | 411 ++++++++++++++++++++- test/TestCases/napi-dotnet/jsvalue_cast.js | 80 ++++ 4 files changed, 491 insertions(+), 7 deletions(-) diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index 688a8485..2768d2ee 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -27,7 +27,7 @@ private JSIterable(JSValue value) #region IJSValue implementation public static bool CanCreateFrom(JSValue value) - => value.IsObject() && value.HasProperty(JSValue.Global["Symbol"]["iterator"]); + => value.IsObject() && value.HasProperty(JSSymbol.Iterator); #if NET7_0_OR_GREATER static JSIterable IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index 68425627..09de715d 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -68,9 +68,8 @@ public JSProxy( #region IJSValue implementation - // TODO: (vmoroz) Implement using instanceof - public static bool CanCreateFrom(JSValue value) - => value.IsObject() && value.InstanceOf(JSValue.Global["Proxy"]); + // According to JavaScript specification we cannot differentiate Proxy instance from other objects. + public static bool CanCreateFrom(JSValue value) => value.IsObject(); #if NET7_0_OR_GREATER static JSProxy IJSValue.CreateUnchecked(JSValue value) => new(value); diff --git a/test/TestCases/napi-dotnet/JSValueCast.cs b/test/TestCases/napi-dotnet/JSValueCast.cs index fc2632d8..a322a0b7 100644 --- a/test/TestCases/napi-dotnet/JSValueCast.cs +++ b/test/TestCases/napi-dotnet/JSValueCast.cs @@ -67,9 +67,6 @@ public static string ValueCastToArray(JSValue value) #region JSAsyncIterable - public static JSValue GetAsyncIterator(JSValue value) - => JSValue.Global["Symbol"]["asyncIterator"]; - public static string ValueAsAsyncIterable(JSValue value) => (JSAsyncIterable?)value is not null ? "ok" : "failed"; @@ -163,4 +160,412 @@ public static string ValueCastToFunction(JSValue value) } #endregion + + #region JSIterable + + public static string ValueAsIterable(JSValue value) + => (JSIterable?)value is not null ? "ok" : "failed"; + + public static string ValueIsIterable(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToIterable(JSValue value) + { + try + { + JSIterable signal = (JSIterable)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSMap + + public static string ValueAsMap(JSValue value) + => (JSMap?)value is not null ? "ok" : "failed"; + + public static string ValueIsMap(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToMap(JSValue value) + { + try + { + JSMap signal = (JSMap)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSObject + + public static string ValueAsObject(JSValue value) + => (JSObject?)value is not null ? "ok" : "failed"; + + public static string ValueIsObject(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToObject(JSValue value) + { + try + { + JSObject signal = (JSObject)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSPromise + + public static string ValueAsPromise(JSValue value) + => (JSPromise?)value is not null ? "ok" : "failed"; + + public static string ValueIsPromise(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToPromise(JSValue value) + { + try + { + JSPromise signal = (JSPromise)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSProxy + + public static string ValueAsProxy(JSValue value) + => (JSProxy?)value is not null ? "ok" : "failed"; + + public static string ValueIsProxy(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToProxy(JSValue value) + { + try + { + JSProxy signal = (JSProxy)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSSet + + public static string ValueAsSet(JSValue value) + => (JSSet?)value is not null ? "ok" : "failed"; + + public static string ValueIsSet(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToSet(JSValue value) + { + try + { + JSSet signal = (JSSet)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSSymbol + + public static string ValueAsSymbol(JSValue value) + => (JSSymbol?)value is not null ? "ok" : "failed"; + + public static string ValueIsSymbol(JSValue value) + => value.Is() ? "ok" : "failed"; + + public static string ValueCastToSymbol(JSValue value) + { + try + { + JSSymbol signal = (JSSymbol)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayInt8(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayInt8(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayInt8(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayUint8(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayUint8(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayUint8(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayInt16(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayInt16(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayInt16(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayUint16(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayUint16(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayUint16(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayInt32(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayInt32(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayInt32(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayUint32(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayUint32(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayUint32(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayBigInt64(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayBigInt64(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayBigInt64(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayBigUint64(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayBigUint64(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayBigUint64(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayFloat32(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayFloat32(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayFloat32(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion + + #region JSTypedArray + + public static string ValueAsTypedArrayFloat64(JSValue value) + => (JSTypedArray?)value is not null ? "ok" : "failed"; + + public static string ValueIsTypedArrayFloat64(JSValue value) + => value.Is>() ? "ok" : "failed"; + + public static string ValueCastToTypedArrayFloat64(JSValue value) + { + try + { + JSTypedArray signal = (JSTypedArray)value; + JSValue value2 = signal; + return value.Handle == value2.Handle ? "ok" : "failed roundrip"; + } + catch (InvalidCastException) + { + return "failed"; + } + } + + #endregion } diff --git a/test/TestCases/napi-dotnet/jsvalue_cast.js b/test/TestCases/napi-dotnet/jsvalue_cast.js index d8773b50..622e5266 100644 --- a/test/TestCases/napi-dotnet/jsvalue_cast.js +++ b/test/TestCases/napi-dotnet/jsvalue_cast.js @@ -78,3 +78,83 @@ assert.strictEqual(JSValueCast.valueIsFunction(() => { }), "ok"); assert.strictEqual(JSValueCast.valueIsFunction({}), "failed"); assert.strictEqual(JSValueCast.valueCastToFunction(() => { }), "ok"); assert.strictEqual(JSValueCast.valueCastToFunction({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsIterable, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsIterable, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToIterable, 'function'); +assert.strictEqual(JSValueCast.valueAsIterable([]), "ok"); +assert.strictEqual(JSValueCast.valueAsIterable({}), "failed"); +assert.strictEqual(JSValueCast.valueIsIterable([]), "ok"); +assert.strictEqual(JSValueCast.valueIsIterable({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToIterable([]), "ok"); +assert.strictEqual(JSValueCast.valueCastToIterable({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsMap, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsMap, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToMap, 'function'); +assert.strictEqual(JSValueCast.valueAsMap(new Map()), "ok"); +assert.strictEqual(JSValueCast.valueAsMap({}), "failed"); +assert.strictEqual(JSValueCast.valueIsMap(new Map()), "ok"); +assert.strictEqual(JSValueCast.valueIsMap({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToMap(new Map()), "ok"); +assert.strictEqual(JSValueCast.valueCastToMap({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsObject, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsObject, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToObject, 'function'); +assert.strictEqual(JSValueCast.valueAsObject({}), "ok"); +assert.strictEqual(JSValueCast.valueAsObject(""), "failed"); +assert.strictEqual(JSValueCast.valueIsObject({}), "ok"); +assert.strictEqual(JSValueCast.valueIsObject(""), "failed"); +assert.strictEqual(JSValueCast.valueCastToObject({}), "ok"); +assert.strictEqual(JSValueCast.valueCastToObject(""), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsPromise, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsPromise, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToPromise, 'function'); +assert.strictEqual(JSValueCast.valueAsPromise(Promise.resolve(123)), "ok"); +assert.strictEqual(JSValueCast.valueAsPromise({}), "failed"); +assert.strictEqual(JSValueCast.valueIsPromise(Promise.resolve(123)), "ok"); +assert.strictEqual(JSValueCast.valueIsPromise({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToPromise(Promise.resolve(123)), "ok"); +assert.strictEqual(JSValueCast.valueCastToPromise({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsProxy, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsProxy, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToProxy, 'function'); +assert.strictEqual(JSValueCast.valueAsProxy(new Proxy({}, {})), "ok"); +assert.strictEqual(JSValueCast.valueAsProxy("1"), "failed"); +assert.strictEqual(JSValueCast.valueIsProxy(new Proxy({}, {})), "ok"); +assert.strictEqual(JSValueCast.valueIsProxy("1"), "failed"); +assert.strictEqual(JSValueCast.valueCastToProxy(new Proxy({}, {})), "ok"); +assert.strictEqual(JSValueCast.valueCastToProxy("1"), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsSet, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsSet, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToSet, 'function'); +assert.strictEqual(JSValueCast.valueAsSet(new Set()), "ok"); +assert.strictEqual(JSValueCast.valueAsSet({}), "failed"); +assert.strictEqual(JSValueCast.valueIsSet(new Set()), "ok"); +assert.strictEqual(JSValueCast.valueIsSet({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToSet(new Set()), "ok"); +assert.strictEqual(JSValueCast.valueCastToSet({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsSymbol, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsSymbol, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToSymbol, 'function'); +assert.strictEqual(JSValueCast.valueAsSymbol(Symbol.iterator), "ok"); +assert.strictEqual(JSValueCast.valueAsSymbol({}), "failed"); +assert.strictEqual(JSValueCast.valueIsSymbol(Symbol.iterator), "ok"); +assert.strictEqual(JSValueCast.valueIsSymbol({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToSymbol(Symbol.iterator), "ok"); +assert.strictEqual(JSValueCast.valueCastToSymbol({}), "failed"); + +assert.strictEqual(typeof JSValueCast.valueAsTypedArrayInt8, 'function'); +assert.strictEqual(typeof JSValueCast.valueIsTypedArrayInt8, 'function'); +assert.strictEqual(typeof JSValueCast.valueCastToTypedArrayInt8, 'function'); +assert.strictEqual(JSValueCast.valueAsTypedArrayInt8(new Int8Array(8)), "ok"); +assert.strictEqual(JSValueCast.valueAsTypedArrayInt8({}), "failed"); +assert.strictEqual(JSValueCast.valueIsTypedArrayInt8(new Int8Array(8)), "ok"); +assert.strictEqual(JSValueCast.valueIsTypedArrayInt8({}), "failed"); +assert.strictEqual(JSValueCast.valueCastToTypedArrayInt8(new Int8Array(8)), "ok"); +assert.strictEqual(JSValueCast.valueCastToTypedArrayInt8({}), "failed"); From 9a8ffa1cfedc7befa9212ff1daf2f72727943747 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Sun, 4 Aug 2024 10:16:45 -0700 Subject: [PATCH 09/13] Pack Microsoft.Bcl.AsyncInterfaces.dll for net472 --- src/node-api-dotnet/pack.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/node-api-dotnet/pack.js b/src/node-api-dotnet/pack.js index 4a7f8667..32890aec 100644 --- a/src/node-api-dotnet/pack.js +++ b/src/node-api-dotnet/pack.js @@ -70,6 +70,7 @@ function packMainPackage() { `NodeApi/${assemblyName}.runtimeconfig.json`, `NodeApi/${assemblyName}.dll`, `NodeApi.DotNetHost/${assemblyName}.DotNetHost.dll`, + `NodeApi/Microsoft.Bcl.AsyncInterfaces.dll`, `NodeApi/System.Memory.dll`, `NodeApi/System.Runtime.CompilerServices.Unsafe.dll`, `NodeApi/System.Threading.Tasks.Extensions.dll`, @@ -171,6 +172,12 @@ function copyFrameworkSpecificBinaries(targetFrameworks, packageStageDir, ...bin binFileName.startsWith('System.') && !binFileName.includes('MetadataLoadContext') ) return; + + // Exclude Microsoft.Bcl.AsyncInterfaces from new platforms + if ( + tfm.includes('.') && + binFileName.startsWith('Microsoft.Bcl.AsyncInterfaces') + ) return; const binPath = path.join(outBinDir, projectName, tfm, rids[0], binFileName); copyFile(binPath, path.join(tfmStageDir, binFileName)); From 9b28d1350b92b43ab55fa9e98053a151de1877cc Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Sun, 4 Aug 2024 18:34:10 -0700 Subject: [PATCH 10/13] Add comments --- src/NodeApi/IJSValue.cs | 46 +++++++++++++++++++--- src/NodeApi/Interop/JSAbortSignal.cs | 52 ++++++++++++++++++++++++- src/NodeApi/JSArray.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSAsyncIterable.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSBigInt.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSDate.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSFunction.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSIterable.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSMap.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSObject.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSPromise.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSProxy.cs | 55 ++++++++++++++++++++++++++- src/NodeApi/JSSet.cs | 48 ++++++++++++++++++++++- src/NodeApi/JSSymbol.cs | 47 +++++++++++++++++++++++ src/NodeApi/JSTypedArray.cs | 49 ++++++++++++++++++++++++ src/NodeApi/JSValue.cs | 57 ++++++++++++++++++++-------- 16 files changed, 750 insertions(+), 27 deletions(-) diff --git a/src/NodeApi/IJSValue.cs b/src/NodeApi/IJSValue.cs index 08d1a8e4..b83b14f0 100644 --- a/src/NodeApi/IJSValue.cs +++ b/src/NodeApi/IJSValue.cs @@ -9,22 +9,47 @@ namespace Microsoft.JavaScript.NodeApi; -// A static interface that helps with the conversion of JSValue to a specific type. +/// +/// A base interface for a struct that represents a JavaScript value type or a built-in +/// object type. It provides functionality for converting between the struct +/// and . +/// +/// The derived struct type. public interface IJSValue : IEquatable where TSelf : struct, IJSValue { - public JSValue AsJSValue(); + /// + /// Converts the derived struct `TSelf` to a with + /// the same `napi_value` handle. + /// + /// + /// with the same `napi_value` handle as the derived struct. + /// + JSValue AsJSValue(); #if NET7_0_OR_GREATER - public static abstract bool CanCreateFrom(JSValue value); + /// + /// Checks id the derived struct `TSelf` can be created from a . + /// + static abstract bool CanCreateFrom(JSValue value); - public static abstract TSelf CreateUnchecked(JSValue value); + /// + /// Creates a new instance of the derived struct `TSelf` from a without + /// checking the enclosed handle type. + /// + static abstract TSelf CreateUnchecked(JSValue value); #endif } #if !NET7_0_OR_GREATER -// A static class that helps with the conversion of JSValue to a specific type. -internal static class IJSValueShim where T : struct +/// +/// Implements IJSValue interface static functions for the previous .Net versions. +/// +/// +internal static class IJSValueShim where T : struct, IJSValue { + /// + /// A static field to keep a reference to the CanCreateFrom public method. + /// private static readonly Func s_canBeCreatedFrom = (Func)Delegate.CreateDelegate( typeof(Func), @@ -32,6 +57,9 @@ internal static class IJSValueShim where T : struct "CanCreateFrom", BindingFlags.Static | BindingFlags.Public)!); + /// + /// A static field to keep a reference to the CreateUnchecked private method. + /// private static readonly Funcs_createUnchecked = (Func)Delegate.CreateDelegate( typeof(Func), @@ -39,8 +67,14 @@ internal static class IJSValueShim where T : struct "CreateUnchecked", BindingFlags.Static | BindingFlags.NonPublic)!); + /// + /// Invokes `T.CanCreateFrom` static public method. + /// public static bool CanCreateFrom(JSValue value) => s_canBeCreatedFrom(value); + /// + /// Invokes `T.CreateUnchecked` static private method. + /// public static T CreateUnchecked(JSValue value) => s_createUnchecked(value); } #endif diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index 9f097bf2..694deab9 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -19,8 +19,29 @@ namespace Microsoft.JavaScript.NodeApi.Interop; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSAbortSignal value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSAbortSignal?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSAbortSignal(JSValue value) => value.CastTo(); public static explicit operator JSAbortSignal(JSObject obj) => (JSAbortSignal)(JSValue)obj; @@ -36,14 +57,35 @@ public static explicit operator CancellationToken(JSAbortSignal signal) public static explicit operator JSAbortSignal(CancellationToken cancellation) => FromCancellationToken(cancellation); + public static explicit operator JSAbortSignal(CancellationToken? cancellation) => cancellation.HasValue ? FromCancellationToken(cancellation.Value) : default; #region IJSValue implementation - public static bool CanCreateFrom(JSValue value) - => value.IsObject() && value.InstanceOf(JSValue.Global["AbortSignal"]); + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// + public static bool CanCreateFrom(JSValue value) => + value.IsObject() && value.InstanceOf(JSValue.Global["AbortSignal"]); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSAbortSignal IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -52,6 +94,12 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSArray.cs b/src/NodeApi/JSArray.cs index 7e6115f6..9fd9fec6 100644 --- a/src/NodeApi/JSArray.cs +++ b/src/NodeApi/JSArray.cs @@ -12,8 +12,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSArray arr) => arr.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSArray?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSArray(JSValue value) => value.CastTo(); public static explicit operator JSArray(JSObject obj) => (JSArray)(JSValue)obj; @@ -48,8 +69,28 @@ public JSArray(JSValue[] array) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsArray(); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSArray IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -58,6 +99,12 @@ public JSArray(JSValue[] array) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index 45791b2a..821459c9 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -13,8 +13,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSAsyncIterable value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSAsyncIterable?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSAsyncIterable(JSValue value) => value.CastTo(); @@ -28,9 +49,29 @@ private JSAsyncIterable(JSValue value) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsObject() && value.HasProperty(JSSymbol.AsyncIterator); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSAsyncIterable IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -39,6 +80,12 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSBigInt.cs b/src/NodeApi/JSBigInt.cs index c5c8fd74..1a2047e8 100644 --- a/src/NodeApi/JSBigInt.cs +++ b/src/NodeApi/JSBigInt.cs @@ -11,8 +11,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSBigInt value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSBigInt?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSBigInt(JSValue value) => value.CastTo(); public static implicit operator JSBigInt(BigInteger value) => new(value); @@ -42,8 +63,28 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsBigInt(); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSBigInt IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -52,6 +93,12 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSDate.cs b/src/NodeApi/JSDate.cs index f7c86916..f44cb981 100644 --- a/src/NodeApi/JSDate.cs +++ b/src/NodeApi/JSDate.cs @@ -11,8 +11,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSDate value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSDate?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSDate(JSValue value) => value.CastTo(); private JSDate(JSValue value) @@ -39,8 +60,28 @@ public JSDate(string dateString) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsDate(); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSDate IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -49,6 +90,12 @@ public JSDate(string dateString) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSFunction.cs b/src/NodeApi/JSFunction.cs index ea3ed4a9..37f04529 100644 --- a/src/NodeApi/JSFunction.cs +++ b/src/NodeApi/JSFunction.cs @@ -13,8 +13,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSFunction value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSFunction?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSFunction(JSValue value) => value.CastTo(); private JSFunction(JSValue value) @@ -270,8 +291,28 @@ public JSFunction( #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsFunction(); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSFunction IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -280,6 +321,12 @@ public JSFunction( #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index 2768d2ee..e9351cb4 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -12,8 +12,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSIterable value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSIterable?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSIterable(JSValue value) => value.CastTo(); public static explicit operator JSIterable(JSObject obj) => (JSIterable)(JSValue)obj; @@ -26,9 +47,29 @@ private JSIterable(JSValue value) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsObject() && value.HasProperty(JSSymbol.Iterator); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSIterable IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -37,6 +78,12 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSMap.cs b/src/NodeApi/JSMap.cs index 2909d470..5732ce53 100644 --- a/src/NodeApi/JSMap.cs +++ b/src/NodeApi/JSMap.cs @@ -12,8 +12,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSMap value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSMap?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSMap(JSValue value) => value.CastTo(); public static explicit operator JSMap(JSObject obj) => (JSMap)(JSValue)obj; @@ -43,9 +64,29 @@ public JSMap(JSIterable iterable) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsObject() && value.InstanceOf(JSValue.Global["Map"]); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSMap IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -54,6 +95,12 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSObject.cs b/src/NodeApi/JSObject.cs index be89942d..a2380e18 100644 --- a/src/NodeApi/JSObject.cs +++ b/src/NodeApi/JSObject.cs @@ -12,8 +12,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSObject value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSObject?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSObject(JSValue value) => value.CastTo(); private JSObject(JSValue value) @@ -27,8 +48,28 @@ public JSObject() : this(JSValue.CreateObject()) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsObject(); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSObject IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -37,6 +78,12 @@ public JSObject() : this(JSValue.CreateObject()) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSPromise.cs b/src/NodeApi/JSPromise.cs index d9ac9a84..3a17bd44 100644 --- a/src/NodeApi/JSPromise.cs +++ b/src/NodeApi/JSPromise.cs @@ -18,8 +18,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSPromise value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSPromise?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSPromise(JSValue value) => value.CastTo(); public static explicit operator JSPromise(JSObject obj) => (JSPromise)(JSValue)obj; @@ -146,8 +167,28 @@ private static async void ResolveDeferred( #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsPromise(); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSPromise IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -156,6 +197,12 @@ private static async void ResolveDeferred( #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index 09de715d..72a92c8c 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -17,8 +17,29 @@ namespace Microsoft.JavaScript.NodeApi; private readonly JSValue _value; private readonly JSValue _revoke = default; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSProxy value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSProxy?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSProxy(JSValue value) => value.CastTo(); private JSProxy(JSValue value) @@ -68,9 +89,33 @@ public JSProxy( #region IJSValue implementation - // According to JavaScript specification we cannot differentiate Proxy instance from other objects. - public static bool CanCreateFrom(JSValue value) => value.IsObject(); + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// + public static bool CanCreateFrom(JSValue value) + { + // According to JavaScript specification we cannot differentiate Proxy instance + // from other objects. + return value.IsObject(); + } + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSProxy IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -79,6 +124,12 @@ public JSProxy( #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSSet.cs b/src/NodeApi/JSSet.cs index 988d0ded..7cc3aa7d 100644 --- a/src/NodeApi/JSSet.cs +++ b/src/NodeApi/JSSet.cs @@ -13,8 +13,29 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSSet value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSSet?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSSet(JSValue value) => value.CastTo(); public static explicit operator JSSet(JSObject obj) => (JSSet)(JSValue)obj; @@ -47,10 +68,29 @@ public JSSet(JSIterable iterable) #region IJSValue implementation - // TODO: (vmoroz) Implement using instanceof + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsObject() && value.InstanceOf(JSValue.Global["Set"]); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSSet IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -59,6 +99,12 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSSymbol.cs b/src/NodeApi/JSSymbol.cs index e5469387..73d39beb 100644 --- a/src/NodeApi/JSSymbol.cs +++ b/src/NodeApi/JSSymbol.cs @@ -16,8 +16,29 @@ namespace Microsoft.JavaScript.NodeApi; private static readonly Lazy s_asyncIteratorSymbol = new(() => new JSReference(JSValue.Global["Symbol"]["asyncIterator"])); + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSSymbol value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or `null` if it was failed. + /// public static explicit operator JSSymbol?(JSValue value) => value.As(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSSymbol(JSValue value) => value.CastTo(); private JSSymbol(JSValue value) @@ -32,8 +53,28 @@ public JSSymbol(string? description = null) #region IJSValue implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsSymbol(); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSSymbol IJSValue.CreateUnchecked(JSValue value) => new(value); #else @@ -42,6 +83,12 @@ public JSSymbol(string? description = null) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSTypedArray.cs b/src/NodeApi/JSTypedArray.cs index 0244c1d0..221a6515 100644 --- a/src/NodeApi/JSTypedArray.cs +++ b/src/NodeApi/JSTypedArray.cs @@ -15,8 +15,31 @@ namespace Microsoft.JavaScript.NodeApi; { private readonly JSValue _value; + /// + /// Implicitly converts a to a . + /// + /// The to convert. public static implicit operator JSValue(JSTypedArray value) => value.AsJSValue(); + + /// + /// Explicitly converts a to a + /// nullable . + /// + /// The to convert. + /// + /// The if it was successfully created or + /// `null` if it was failed. + /// public static explicit operator JSTypedArray?(JSValue value) => value.As>(); + + /// + /// Explicitly converts a to a . + /// + /// The to convert. + /// struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be created based on this `JSValue`. + /// public static explicit operator JSTypedArray(JSValue value) => value.CastTo>(); @@ -122,9 +145,29 @@ public unsafe JSTypedArray(ReadOnlyMemory data) #region IJSValue> implementation + /// + /// Determines whether a can be created from + /// the specified . + /// + /// The to check. + /// + /// true if a can be created from + /// the specified ; otherwise, false. + /// public static bool CanCreateFrom(JSValue value) => value.IsObject() && value.InstanceOf(JSValue.Global[JSTypeName]); + /// + /// Creates a new instance of from + /// the specified . + /// + /// + /// The to create a from. + /// + /// + /// A new instance of created from + /// the specified . + /// #if NET7_0_OR_GREATER static JSTypedArray IJSValue>.CreateUnchecked(JSValue value) => new(value); #else @@ -133,6 +176,12 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// public JSValue AsJSValue() => _value; #endregion diff --git a/src/NodeApi/JSValue.cs b/src/NodeApi/JSValue.cs index b722bde9..2b5e453c 100644 --- a/src/NodeApi/JSValue.cs +++ b/src/NodeApi/JSValue.cs @@ -391,33 +391,58 @@ public JSValueType TypeOf() => _handle.IsNull public bool IsBigInt() => TypeOf() == JSValueType.BigInt; - public bool Is() where TValue : struct, IJSValue + /// + /// Checks if the T struct can be created from this `JSValue`. + /// + /// A struct that implements IJSValue interface. + /// + /// `true` if the T struct can be created from this `JSValue`. Otherwise it returns `false`. + /// + public bool Is() where T : struct, IJSValue #if NET7_0_OR_GREATER - => TValue.CanCreateFrom(this); + => T.CanCreateFrom(this); #else - => IJSValueShim.CanCreateFrom(this); + => IJSValueShim.CanCreateFrom(this); #endif - public TValue? As() where TValue : struct, IJSValue -#if NET7_0_OR_GREATER - => TValue.CanCreateFrom(this) ? TValue.CreateUnchecked(this) : default(TValue?); -#else - => IJSValueShim.CanCreateFrom(this) - ? IJSValueShim.CreateUnchecked(this) : default(TValue?); -#endif + /// + /// Tries to create a T struct from this `JSValue`. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue + => Is() ? AsUnchecked() : default(T?); - public TValue AsUnchecked() where TValue : struct, IJSValue + /// + /// Creates a T struct from this `JSValue` without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this `JSValue`. + public T AsUnchecked() where T : struct, IJSValue #if NET7_0_OR_GREATER - => TValue.CreateUnchecked(this); + => T.CreateUnchecked(this); #else - => IJSValueShim.CreateUnchecked(this); + => IJSValueShim.CreateUnchecked(this); #endif - public TValue CastTo() where TValue : struct, IJSValue - => As() + /// + /// Creates a T struct from this `JSValue`. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this `JSValue`. + /// + /// Thrown when the T struct cannot be crated based on this `JSValue`. + /// + public T CastTo() where T : struct, IJSValue + => As() ?? throw new InvalidCastException("JSValue cannot be casted to target type."); - public double GetValueDouble() => GetRuntime(out napi_env env, out napi_value handle) .GetValueDouble(env, handle, out double result).ThrowIfFailed(result); From d9b784a907b9e9421f201bb9cb74479d2d367bb3 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Sun, 4 Aug 2024 19:19:09 -0700 Subject: [PATCH 11/13] Make CanCreateFrom private --- src/NodeApi/IJSValue.cs | 4 ++-- src/NodeApi/Interop/JSAbortSignal.cs | 26 ++++++++++++++++---------- src/NodeApi/JSArray.cs | 25 ++++++++++++++++--------- src/NodeApi/JSAsyncIterable.cs | 24 +++++++++++++++--------- src/NodeApi/JSBigInt.cs | 25 ++++++++++++++++--------- src/NodeApi/JSDate.cs | 25 ++++++++++++++++--------- src/NodeApi/JSFunction.cs | 25 ++++++++++++++++--------- src/NodeApi/JSIterable.cs | 24 +++++++++++++++--------- src/NodeApi/JSMap.cs | 24 +++++++++++++++--------- src/NodeApi/JSObject.cs | 25 ++++++++++++++++--------- src/NodeApi/JSPromise.cs | 25 ++++++++++++++++--------- src/NodeApi/JSProxy.cs | 24 +++++++++++++++--------- src/NodeApi/JSSet.cs | 8 +++++++- src/NodeApi/JSSymbol.cs | 25 ++++++++++++++++--------- src/NodeApi/JSTypedArray.cs | 24 +++++++++++++++--------- 15 files changed, 212 insertions(+), 121 deletions(-) diff --git a/src/NodeApi/IJSValue.cs b/src/NodeApi/IJSValue.cs index b83b14f0..1799e3d8 100644 --- a/src/NodeApi/IJSValue.cs +++ b/src/NodeApi/IJSValue.cs @@ -48,14 +48,14 @@ public interface IJSValue : IEquatable where TSelf : struct, IJS internal static class IJSValueShim where T : struct, IJSValue { /// - /// A static field to keep a reference to the CanCreateFrom public method. + /// A static field to keep a reference to the CanCreateFrom private method. /// private static readonly Func s_canBeCreatedFrom = (Func)Delegate.CreateDelegate( typeof(Func), typeof(T).GetMethod( "CanCreateFrom", - BindingFlags.Static | BindingFlags.Public)!); + BindingFlags.Static | BindingFlags.NonPublic)!); /// /// A static field to keep a reference to the CreateUnchecked private method. diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index 694deab9..9ca4ac73 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -63,6 +63,14 @@ public static explicit operator JSAbortSignal(CancellationToken? cancellation) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -72,8 +80,14 @@ public static explicit operator JSAbortSignal(CancellationToken? cancellation) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => - value.IsObject() && value.InstanceOf(JSValue.Global["AbortSignal"]); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsObject() && value.InstanceOf(JSValue.Global["AbortSignal"]); /// /// Creates a new instance of from @@ -94,14 +108,6 @@ public static bool CanCreateFrom(JSValue value) => #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion private CancellationToken ToCancellationToken() diff --git a/src/NodeApi/JSArray.cs b/src/NodeApi/JSArray.cs index 9fd9fec6..4f31e18e 100644 --- a/src/NodeApi/JSArray.cs +++ b/src/NodeApi/JSArray.cs @@ -69,6 +69,14 @@ public JSArray(JSValue[] array) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -78,7 +86,14 @@ public JSArray(JSValue[] array) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => value.IsArray(); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsArray(); /// /// Creates a new instance of from @@ -99,14 +114,6 @@ public JSArray(JSValue[] array) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion /// diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index 821459c9..7adfc3f1 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -49,6 +49,14 @@ private JSAsyncIterable(JSValue value) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -58,7 +66,13 @@ private JSAsyncIterable(JSValue value) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif => value.IsObject() && value.HasProperty(JSSymbol.AsyncIterator); /// @@ -80,14 +94,6 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion #pragma warning disable IDE0060 // Unused parameter diff --git a/src/NodeApi/JSBigInt.cs b/src/NodeApi/JSBigInt.cs index 1a2047e8..ab82042f 100644 --- a/src/NodeApi/JSBigInt.cs +++ b/src/NodeApi/JSBigInt.cs @@ -63,6 +63,14 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -72,7 +80,14 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => value.IsBigInt(); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsBigInt(); /// /// Creates a new instance of from @@ -93,14 +108,6 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion public int GetWordCount() => _value.GetBigIntWordCount(); diff --git a/src/NodeApi/JSDate.cs b/src/NodeApi/JSDate.cs index f44cb981..ef782827 100644 --- a/src/NodeApi/JSDate.cs +++ b/src/NodeApi/JSDate.cs @@ -60,6 +60,14 @@ public JSDate(string dateString) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -69,7 +77,14 @@ public JSDate(string dateString) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => value.IsDate(); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsDate(); /// /// Creates a new instance of from @@ -90,14 +105,6 @@ public JSDate(string dateString) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion diff --git a/src/NodeApi/JSFunction.cs b/src/NodeApi/JSFunction.cs index 37f04529..ab49db1a 100644 --- a/src/NodeApi/JSFunction.cs +++ b/src/NodeApi/JSFunction.cs @@ -291,6 +291,14 @@ public JSFunction( #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -300,7 +308,14 @@ public JSFunction( /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => value.IsFunction(); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsFunction(); /// /// Creates a new instance of from @@ -321,14 +336,6 @@ public JSFunction( #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion /// diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index e9351cb4..08d614d7 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -47,6 +47,14 @@ private JSIterable(JSValue value) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -56,7 +64,13 @@ private JSIterable(JSValue value) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif => value.IsObject() && value.HasProperty(JSSymbol.Iterator); /// @@ -78,14 +92,6 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion public Enumerator GetEnumerator() => new(_value); diff --git a/src/NodeApi/JSMap.cs b/src/NodeApi/JSMap.cs index 5732ce53..1b0251b0 100644 --- a/src/NodeApi/JSMap.cs +++ b/src/NodeApi/JSMap.cs @@ -64,6 +64,14 @@ public JSMap(JSIterable iterable) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -73,7 +81,13 @@ public JSMap(JSIterable iterable) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif => value.IsObject() && value.InstanceOf(JSValue.Global["Map"]); /// @@ -95,14 +109,6 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion public int Count => (int)_value["size"]; diff --git a/src/NodeApi/JSObject.cs b/src/NodeApi/JSObject.cs index a2380e18..cc363868 100644 --- a/src/NodeApi/JSObject.cs +++ b/src/NodeApi/JSObject.cs @@ -48,6 +48,14 @@ public JSObject() : this(JSValue.CreateObject()) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -57,7 +65,14 @@ public JSObject() : this(JSValue.CreateObject()) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => value.IsObject(); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsObject(); /// /// Creates a new instance of from @@ -78,14 +93,6 @@ public JSObject() : this(JSValue.CreateObject()) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion public JSObject(IEnumerable> properties) : this() diff --git a/src/NodeApi/JSPromise.cs b/src/NodeApi/JSPromise.cs index 3a17bd44..3f5cdf91 100644 --- a/src/NodeApi/JSPromise.cs +++ b/src/NodeApi/JSPromise.cs @@ -167,6 +167,14 @@ private static async void ResolveDeferred( #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -176,7 +184,14 @@ private static async void ResolveDeferred( /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => value.IsPromise(); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsPromise(); /// /// Creates a new instance of from @@ -197,14 +212,6 @@ private static async void ResolveDeferred( #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion /// diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index 72a92c8c..3afa4899 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -89,6 +89,14 @@ public JSProxy( #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -98,7 +106,13 @@ public JSProxy( /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif { // According to JavaScript specification we cannot differentiate Proxy instance // from other objects. @@ -124,14 +138,6 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion /// diff --git a/src/NodeApi/JSSet.cs b/src/NodeApi/JSSet.cs index 7cc3aa7d..f426d2e4 100644 --- a/src/NodeApi/JSSet.cs +++ b/src/NodeApi/JSSet.cs @@ -77,7 +77,13 @@ public JSSet(JSIterable iterable) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif => value.IsObject() && value.InstanceOf(JSValue.Global["Set"]); /// diff --git a/src/NodeApi/JSSymbol.cs b/src/NodeApi/JSSymbol.cs index 73d39beb..e9630f83 100644 --- a/src/NodeApi/JSSymbol.cs +++ b/src/NodeApi/JSSymbol.cs @@ -53,6 +53,14 @@ public JSSymbol(string? description = null) #region IJSValue implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -62,7 +70,14 @@ public JSSymbol(string? description = null) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) => value.IsSymbol(); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif + => value.IsSymbol(); /// /// Creates a new instance of from @@ -83,14 +98,6 @@ public JSSymbol(string? description = null) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion /// diff --git a/src/NodeApi/JSTypedArray.cs b/src/NodeApi/JSTypedArray.cs index 221a6515..7f2cf45b 100644 --- a/src/NodeApi/JSTypedArray.cs +++ b/src/NodeApi/JSTypedArray.cs @@ -145,6 +145,14 @@ public unsafe JSTypedArray(ReadOnlyMemory data) #region IJSValue> implementation + /// + /// Converts the to a . + /// + /// + /// The representation of the . + /// + public JSValue AsJSValue() => _value; + /// /// Determines whether a can be created from /// the specified . @@ -154,7 +162,13 @@ public unsafe JSTypedArray(ReadOnlyMemory data) /// true if a can be created from /// the specified ; otherwise, false. /// - public static bool CanCreateFrom(JSValue value) +#if NET7_0_OR_GREATER + static bool IJSValue>.CanCreateFrom(JSValue value) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue value) +#pragma warning restore IDE0051 +#endif => value.IsObject() && value.InstanceOf(JSValue.Global[JSTypeName]); /// @@ -176,14 +190,6 @@ public static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion /// From 43dd2893c807572f1d3b24c9f11768de91883036 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Wed, 7 Aug 2024 10:19:27 -0700 Subject: [PATCH 12/13] Use cast operations for all IJSValue types --- src/NodeApi/IJSValue.cs | 50 ++++++++++++++++++++++------ src/NodeApi/Interop/JSAbortSignal.cs | 39 +++++++++++++++++++--- src/NodeApi/JSArray.cs | 39 +++++++++++++++++++--- src/NodeApi/JSAsyncIterable.cs | 39 +++++++++++++++++++--- src/NodeApi/JSBigInt.cs | 39 +++++++++++++++++++--- src/NodeApi/JSDate.cs | 39 +++++++++++++++++++--- src/NodeApi/JSFunction.cs | 39 +++++++++++++++++++--- src/NodeApi/JSIterable.cs | 39 +++++++++++++++++++--- src/NodeApi/JSMap.cs | 39 +++++++++++++++++++--- src/NodeApi/JSObject.cs | 39 +++++++++++++++++++--- src/NodeApi/JSPromise.cs | 39 +++++++++++++++++++--- src/NodeApi/JSProxy.cs | 39 +++++++++++++++++++--- src/NodeApi/JSSet.cs | 49 ++++++++++++++++++++++----- src/NodeApi/JSSymbol.cs | 39 +++++++++++++++++++--- src/NodeApi/JSTypedArray.cs | 43 +++++++++++++++++++++--- src/NodeApi/JSValue.cs | 23 ++++++++++++- 16 files changed, 561 insertions(+), 72 deletions(-) diff --git a/src/NodeApi/IJSValue.cs b/src/NodeApi/IJSValue.cs index 1799e3d8..20a6c708 100644 --- a/src/NodeApi/IJSValue.cs +++ b/src/NodeApi/IJSValue.cs @@ -18,17 +18,47 @@ namespace Microsoft.JavaScript.NodeApi; public interface IJSValue : IEquatable where TSelf : struct, IJSValue { /// - /// Converts the derived struct `TSelf` to a with - /// the same `napi_value` handle. + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// with the same `napi_value` handle as the derived struct. + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - JSValue AsJSValue(); + bool Is() where T : struct, IJSValue; + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + T? As() where T : struct, IJSValue; + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + T AsUnchecked() where T : struct, IJSValue; + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + T CastTo() where T : struct, IJSValue; #if NET7_0_OR_GREATER /// - /// Checks id the derived struct `TSelf` can be created from a . + /// Checks if the derived struct `TSelf` can be created from a . /// static abstract bool CanCreateFrom(JSValue value); @@ -50,27 +80,27 @@ internal static class IJSValueShim where T : struct, IJSValue /// /// A static field to keep a reference to the CanCreateFrom private method. /// - private static readonly Func s_canBeCreatedFrom = + private static readonly Func s_canCreateFrom = (Func)Delegate.CreateDelegate( typeof(Func), typeof(T).GetMethod( - "CanCreateFrom", + nameof(CanCreateFrom), BindingFlags.Static | BindingFlags.NonPublic)!); /// /// A static field to keep a reference to the CreateUnchecked private method. /// - private static readonly Funcs_createUnchecked = + private static readonly Func s_createUnchecked = (Func)Delegate.CreateDelegate( typeof(Func), typeof(T).GetMethod( - "CreateUnchecked", + nameof(CreateUnchecked), BindingFlags.Static | BindingFlags.NonPublic)!); /// /// Invokes `T.CanCreateFrom` static public method. /// - public static bool CanCreateFrom(JSValue value) => s_canBeCreatedFrom(value); + public static bool CanCreateFrom(JSValue value) => s_canCreateFrom(value); /// /// Invokes `T.CreateUnchecked` static private method. diff --git a/src/NodeApi/Interop/JSAbortSignal.cs b/src/NodeApi/Interop/JSAbortSignal.cs index 9ca4ac73..0373de24 100644 --- a/src/NodeApi/Interop/JSAbortSignal.cs +++ b/src/NodeApi/Interop/JSAbortSignal.cs @@ -23,7 +23,7 @@ namespace Microsoft.JavaScript.NodeApi.Interop; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSAbortSignal value) => value.AsJSValue(); + public static implicit operator JSValue(JSAbortSignal signal) => signal._value; /// /// Explicitly converts a to a nullable . @@ -64,12 +64,43 @@ public static explicit operator JSAbortSignal(CancellationToken? cancellation) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSArray.cs b/src/NodeApi/JSArray.cs index 4f31e18e..a9573eeb 100644 --- a/src/NodeApi/JSArray.cs +++ b/src/NodeApi/JSArray.cs @@ -16,7 +16,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSArray arr) => arr.AsJSValue(); + public static implicit operator JSValue(JSArray arr) => arr._value; /// /// Explicitly converts a to a nullable . @@ -70,12 +70,43 @@ public JSArray(JSValue[] array) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSAsyncIterable.cs b/src/NodeApi/JSAsyncIterable.cs index 7adfc3f1..3cdb444c 100644 --- a/src/NodeApi/JSAsyncIterable.cs +++ b/src/NodeApi/JSAsyncIterable.cs @@ -17,7 +17,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSAsyncIterable value) => value.AsJSValue(); + public static implicit operator JSValue(JSAsyncIterable iterable) => iterable._value; /// /// Explicitly converts a to a nullable . @@ -50,12 +50,43 @@ private JSAsyncIterable(JSValue value) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSBigInt.cs b/src/NodeApi/JSBigInt.cs index ab82042f..249e3acd 100644 --- a/src/NodeApi/JSBigInt.cs +++ b/src/NodeApi/JSBigInt.cs @@ -15,7 +15,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSBigInt value) => value.AsJSValue(); + public static implicit operator JSValue(JSBigInt value) => value._value; /// /// Explicitly converts a to a nullable . @@ -64,12 +64,43 @@ public JSBigInt(BigInteger value) : this(JSValue.CreateBigInt(value)) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSDate.cs b/src/NodeApi/JSDate.cs index ef782827..625c8d26 100644 --- a/src/NodeApi/JSDate.cs +++ b/src/NodeApi/JSDate.cs @@ -15,7 +15,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSDate value) => value.AsJSValue(); + public static implicit operator JSValue(JSDate date) => date._value; /// /// Explicitly converts a to a nullable . @@ -61,12 +61,43 @@ public JSDate(string dateString) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSFunction.cs b/src/NodeApi/JSFunction.cs index ab49db1a..67e91e30 100644 --- a/src/NodeApi/JSFunction.cs +++ b/src/NodeApi/JSFunction.cs @@ -17,7 +17,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSFunction value) => value.AsJSValue(); + public static implicit operator JSValue(JSFunction function) => function._value; /// /// Explicitly converts a to a nullable . @@ -292,12 +292,43 @@ public JSFunction( #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSIterable.cs b/src/NodeApi/JSIterable.cs index 08d614d7..f36c05cf 100644 --- a/src/NodeApi/JSIterable.cs +++ b/src/NodeApi/JSIterable.cs @@ -16,7 +16,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSIterable value) => value.AsJSValue(); + public static implicit operator JSValue(JSIterable iterable) => iterable._value; /// /// Explicitly converts a to a nullable . @@ -48,12 +48,43 @@ private JSIterable(JSValue value) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSMap.cs b/src/NodeApi/JSMap.cs index 1b0251b0..ef113e69 100644 --- a/src/NodeApi/JSMap.cs +++ b/src/NodeApi/JSMap.cs @@ -16,7 +16,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSMap value) => value.AsJSValue(); + public static implicit operator JSValue(JSMap map) => map._value; /// /// Explicitly converts a to a nullable . @@ -65,12 +65,43 @@ public JSMap(JSIterable iterable) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSObject.cs b/src/NodeApi/JSObject.cs index cc363868..4ff1be65 100644 --- a/src/NodeApi/JSObject.cs +++ b/src/NodeApi/JSObject.cs @@ -16,7 +16,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSObject value) => value.AsJSValue(); + public static implicit operator JSValue(JSObject obj) => obj._value; /// /// Explicitly converts a to a nullable . @@ -49,12 +49,43 @@ public JSObject() : this(JSValue.CreateObject()) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSPromise.cs b/src/NodeApi/JSPromise.cs index 3f5cdf91..ccd10ea8 100644 --- a/src/NodeApi/JSPromise.cs +++ b/src/NodeApi/JSPromise.cs @@ -22,7 +22,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSPromise value) => value.AsJSValue(); + public static implicit operator JSValue(JSPromise promise) => promise._value; /// /// Explicitly converts a to a nullable . @@ -168,12 +168,43 @@ private static async void ResolveDeferred( #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSProxy.cs b/src/NodeApi/JSProxy.cs index 3afa4899..ce4e9f23 100644 --- a/src/NodeApi/JSProxy.cs +++ b/src/NodeApi/JSProxy.cs @@ -21,7 +21,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSProxy value) => value.AsJSValue(); + public static implicit operator JSValue(JSProxy proxy) => proxy._value; /// /// Explicitly converts a to a nullable . @@ -90,12 +90,43 @@ public JSProxy( #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSSet.cs b/src/NodeApi/JSSet.cs index f426d2e4..d6041af1 100644 --- a/src/NodeApi/JSSet.cs +++ b/src/NodeApi/JSSet.cs @@ -17,7 +17,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSSet value) => value.AsJSValue(); + public static implicit operator JSValue(JSSet set) => set._value; /// /// Explicitly converts a to a nullable . @@ -68,6 +68,45 @@ public JSSet(JSIterable iterable) #region IJSValue implementation + /// + /// Checks if the T struct can be created from this instance`. + /// + /// A struct that implements IJSValue interface. + /// + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. + /// + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); + /// /// Determines whether a can be created from /// the specified . @@ -105,14 +144,6 @@ private static bool CanCreateFrom(JSValue value) #pragma warning restore IDE0051 #endif - /// - /// Converts the to a . - /// - /// - /// The representation of the . - /// - public JSValue AsJSValue() => _value; - #endregion public int Count => (int)_value["size"]; diff --git a/src/NodeApi/JSSymbol.cs b/src/NodeApi/JSSymbol.cs index e9630f83..f1babbf8 100644 --- a/src/NodeApi/JSSymbol.cs +++ b/src/NodeApi/JSSymbol.cs @@ -20,7 +20,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSSymbol value) => value.AsJSValue(); + public static implicit operator JSValue(JSSymbol symbol) => symbol._value; /// /// Explicitly converts a to a nullable . @@ -54,12 +54,43 @@ public JSSymbol(string? description = null) #region IJSValue implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where T : struct, IJSValue => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public T? As() where T : struct, IJSValue => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public T AsUnchecked() where T : struct, IJSValue => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public T CastTo() where T : struct, IJSValue => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSTypedArray.cs b/src/NodeApi/JSTypedArray.cs index 7f2cf45b..58b38b38 100644 --- a/src/NodeApi/JSTypedArray.cs +++ b/src/NodeApi/JSTypedArray.cs @@ -19,7 +19,7 @@ namespace Microsoft.JavaScript.NodeApi; /// Implicitly converts a to a . /// /// The to convert. - public static implicit operator JSValue(JSTypedArray value) => value.AsJSValue(); + public static implicit operator JSValue(JSTypedArray array) => array._value; /// /// Explicitly converts a to a @@ -146,12 +146,47 @@ public unsafe JSTypedArray(ReadOnlyMemory data) #region IJSValue> implementation /// - /// Converts the to a . + /// Checks if the T struct can be created from this instance`. /// + /// A struct that implements IJSValue interface. /// - /// The representation of the . + /// `true` if the T struct can be created from this instance. Otherwise it returns `false`. /// - public JSValue AsJSValue() => _value; + public bool Is() where TOther : struct, IJSValue + => _value.Is(); + + /// + /// Tries to create a T struct from this instance. + /// It returns `null` if the T struct cannot be created. + /// + /// A struct that implements IJSValue interface. + /// + /// Nullable value that contains T struct if it was successfully created + /// or `null` if it was failed. + /// + public TOther? As() where TOther : struct, IJSValue + => _value.As(); + + /// + /// Creates a T struct from this instance without checking the enclosed handle type. + /// It must be used only when the handle type is known to be correct. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + public TOther AsUnchecked() where TOther : struct, IJSValue + => _value.AsUnchecked(); + + /// + /// Creates a T struct from this instance. + /// It throws `InvalidCastException` in case of failure. + /// + /// A struct that implements IJSValue interface. + /// T struct created based on this instance. + /// + /// Thrown when the T struct cannot be crated based on this instance. + /// + public TOther CastTo() where TOther : struct, IJSValue + => _value.CastTo(); /// /// Determines whether a can be created from diff --git a/src/NodeApi/JSValue.cs b/src/NodeApi/JSValue.cs index 2b5e453c..fa223a04 100644 --- a/src/NodeApi/JSValue.cs +++ b/src/NodeApi/JSValue.cs @@ -15,7 +15,7 @@ namespace Microsoft.JavaScript.NodeApi; -public readonly struct JSValue : IEquatable +public readonly struct JSValue : IJSValue { private readonly napi_value _handle = default; private readonly JSValueScope? _scope = null; @@ -391,6 +391,8 @@ public JSValueType TypeOf() => _handle.IsNull public bool IsBigInt() => TypeOf() == JSValueType.BigInt; + #region IJSValue implementation + /// /// Checks if the T struct can be created from this `JSValue`. /// @@ -443,6 +445,25 @@ public T CastTo() where T : struct, IJSValue => As() ?? throw new InvalidCastException("JSValue cannot be casted to target type."); +#if NET7_0_OR_GREATER + static bool IJSValue.CanCreateFrom(JSValue _) +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static bool CanCreateFrom(JSValue _) +#pragma warning restore IDE0051 +#endif + => true; + +#if NET7_0_OR_GREATER + static JSValue IJSValue.CreateUnchecked(JSValue value) => value; +#else +#pragma warning disable IDE0051 // It is used by the IJSValueShim class through reflection. + private static JSValue CreateUnchecked(JSValue value) => value; +#pragma warning restore IDE0051 +#endif + + #endregion + public double GetValueDouble() => GetRuntime(out napi_env env, out napi_value handle) .GetValueDouble(env, handle, out double result).ThrowIfFailed(result); From 7921c3c2b0dd755685e515e243a33d7b59556fa6 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Wed, 7 Aug 2024 17:37:22 -0700 Subject: [PATCH 13/13] Add name of the type to the cast exception message --- src/NodeApi/JSValue.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NodeApi/JSValue.cs b/src/NodeApi/JSValue.cs index fa223a04..421b0273 100644 --- a/src/NodeApi/JSValue.cs +++ b/src/NodeApi/JSValue.cs @@ -443,7 +443,8 @@ public T AsUnchecked() where T : struct, IJSValue /// public T CastTo() where T : struct, IJSValue => As() - ?? throw new InvalidCastException("JSValue cannot be casted to target type."); + ?? throw new InvalidCastException( + $"JSValue cannot be casted to target type {typeof(T).Name}."); #if NET7_0_OR_GREATER static bool IJSValue.CanCreateFrom(JSValue _)