• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // This file provides core interop helpers used by the code generated by the
16 // templates.
17 
18 #ifndef DAWN_NODE_INTEROP_CORE_WEBGPU_H_
19 #define DAWN_NODE_INTEROP_CORE_WEBGPU_H_
20 
21 #include <cstdint>
22 #include <optional>
23 #include <string>
24 #include <type_traits>
25 #include <unordered_map>
26 #include <variant>
27 #include <vector>
28 
29 #include "napi.h"
30 
31 #include "src/dawn_node/utils/Debug.h"
32 
33 #define ENABLE_INTEROP_LOGGING 0  // Enable for verbose interop logging
34 
35 #if ENABLE_INTEROP_LOGGING
36 #    define INTEROP_LOG(...) LOG(__VA_ARGS__)
37 #else
38 #    define INTEROP_LOG(...)
39 #endif
40 
41 // A helper macro for constructing a PromiseInfo with the current file, function and line.
42 // See PromiseInfo
43 #define PROMISE_INFO                     \
44     ::wgpu::interop::PromiseInfo {       \
45         __FILE__, __FUNCTION__, __LINE__ \
46     }
47 
48 namespace wgpu { namespace interop {
49 
50     ////////////////////////////////////////////////////////////////////////////////
51     // Primitive JavaScript types
52     ////////////////////////////////////////////////////////////////////////////////
53     using Object = Napi::Object;
54     using ArrayBuffer = Napi::ArrayBuffer;
55     using Int8Array = Napi::TypedArrayOf<int8_t>;
56     using Int16Array = Napi::TypedArrayOf<int16_t>;
57     using Int32Array = Napi::TypedArrayOf<int32_t>;
58     using Uint8Array = Napi::TypedArrayOf<uint8_t>;
59     using Uint16Array = Napi::TypedArrayOf<uint16_t>;
60     using Uint32Array = Napi::TypedArrayOf<uint32_t>;
61     using Float32Array = Napi::TypedArrayOf<float>;
62     using Float64Array = Napi::TypedArrayOf<double>;
63     using DataView = Napi::TypedArray;
64 
65     template <typename T>
66     using FrozenArray = std::vector<T>;
67 
68     ////////////////////////////////////////////////////////////////////////////////
69     // Result
70     ////////////////////////////////////////////////////////////////////////////////
71 
72     // Result is used to hold an success / error state by functions that perform JS <-> C++
73     // conversion
74     struct [[nodiscard]] Result {
75         // Returns true if the operation succeeded, false if there was an error
76         inline operator bool() const {
77             return error.empty();
78         }
79 
80         // If Result is an error, then a new Error is returned with the
81         // stringified values append to the error message.
82         // If Result is a success, then a success Result is returned.
83         template <typename... VALUES>
AppendResult84         Result Append(VALUES && ... values) {
85             if (*this) {
86                 return *this;
87             }
88             std::stringstream ss;
89             ss << error << "\n";
90             utils::Write(ss, std::forward<VALUES>(values)...);
91             return {ss.str()};
92         }
93 
94         // The error message, if the operation failed.
95         std::string error;
96     };
97 
98     // A successful result
99     extern Result Success;
100 
101     // Returns a Result with the given error message
102     Result Error(std::string msg);
103 
104     ////////////////////////////////////////////////////////////////////////////////
105     // Interface<T>
106     ////////////////////////////////////////////////////////////////////////////////
107 
108     // Interface<T> is a templated wrapper around a JavaScript object, which
109     // implements the template-generated interface type T. Interfaces are returned
110     // by either calling T::Bind() or T::Create().
111     template <typename T>
112     class Interface {
113       public:
114         // Constructs an Interface with no JS object.
Interface()115         inline Interface() {
116         }
117 
118         // Constructs an Interface wrapping the given JS object.
119         // The JS object must have been created with a call to T::Bind().
Interface(Napi::Object o)120         explicit inline Interface(Napi::Object o) : object(o) {
121         }
122 
123         // Implicit conversion operators to Napi objects.
napi_value()124         inline operator napi_value() const {
125             return object;
126         }
127         inline operator const Napi::Value &() const {
128             return object;
129         }
130         inline operator const Napi::Object &() const {
131             return object;
132         }
133 
134         // Member and dereference operators
135         inline T* operator->() const {
136             return T::Unwrap(object);
137         }
138         inline T* operator*() const {
139             return T::Unwrap(object);
140         }
141 
142         // As<IMPL>() returns the unwrapped object cast to the implementation type.
143         // The interface implementation *must* be of the template type IMPL.
144         template <typename IMPL>
As()145         inline IMPL* As() const {
146             return static_cast<IMPL*>(T::Unwrap(object));
147         }
148 
149       private:
150         Napi::Object object;
151     };
152 
153     ////////////////////////////////////////////////////////////////////////////////
154     // Promise<T>
155     ////////////////////////////////////////////////////////////////////////////////
156 
157     // Info holds details about where the promise was constructed.
158     // Used for printing debug messages when a promise is finalized without being resolved
159     // or rejected.
160     // Use the PROMISE_INFO macro to populate this structure.
161     struct PromiseInfo {
162         const char* file = nullptr;
163         const char* function = nullptr;
164         int line = 0;
165     };
166 
167     namespace detail {
168         // Base class for Promise<T> specializations.
169         class PromiseBase {
170           public:
171             // Implicit conversion operators to Napi promises.
napi_value()172             inline operator napi_value() const {
173                 return state->deferred.Promise();
174             }
Value()175             inline operator Napi::Value() const {
176                 return state->deferred.Promise();
177             }
Promise()178             inline operator Napi::Promise() const {
179                 return state->deferred.Promise();
180             }
181 
182             // Reject() rejects the promise with the given failure value.
Reject(Napi::Value value)183             void Reject(Napi::Value value) const {
184                 state->deferred.Reject(value);
185                 state->resolved_or_rejected = true;
186             }
Reject(Napi::Error err)187             void Reject(Napi::Error err) const {
188                 Reject(err.Value());
189             }
Reject(std::string err)190             void Reject(std::string err) const {
191                 Reject(Napi::Error::New(state->deferred.Env(), err));
192             }
193 
194           protected:
Resolve(Napi::Value value)195             void Resolve(Napi::Value value) const {
196                 state->deferred.Resolve(value);
197                 state->resolved_or_rejected = true;
198             }
199 
200             struct State {
201                 Napi::Promise::Deferred deferred;
202                 PromiseInfo info;
203                 bool resolved_or_rejected = false;
204             };
205 
PromiseBase(Napi::Env env,const PromiseInfo & info)206             PromiseBase(Napi::Env env, const PromiseInfo& info)
207                 : state(new State{Napi::Promise::Deferred::New(env), info}) {
208                 state->deferred.Promise().AddFinalizer(
209                     [](Napi::Env, State* state) {
210                         // TODO(https://github.com/gpuweb/cts/issues/784):
211                         // Devices are never destroyed, so we always end up
212                         // leaking the Device.lost promise. Enable this once
213                         // fixed.
214                         if ((false)) {
215                             if (!state->resolved_or_rejected) {
216                                 ::wgpu::utils::Fatal("Promise not resolved or rejected",
217                                                      state->info.file, state->info.line,
218                                                      state->info.function);
219                             }
220                         }
221                         delete state;
222                     },
223                     state);
224             }
225 
226             State* const state;
227         };
228     }  // namespace detail
229 
230     // Promise<T> is a templated wrapper around a JavaScript promise, which can
231     // resolve to the template type T.
232     template <typename T>
233     class Promise : public detail::PromiseBase {
234       public:
235         // Constructor
Promise(Napi::Env env,const PromiseInfo & info)236         Promise(Napi::Env env, const PromiseInfo& info) : PromiseBase(env, info) {
237         }
238 
239         // Resolve() fulfills the promise with the given value.
Resolve(T && value)240         void Resolve(T&& value) const {
241             PromiseBase::Resolve(ToJS(state->deferred.Env(), std::forward<T>(value)));
242         }
243     };
244 
245     // Specialization for Promises that resolve with no value
246     template <>
247     class Promise<void> : public detail::PromiseBase {
248       public:
249         // Constructor
Promise(Napi::Env env,const PromiseInfo & info)250         Promise(Napi::Env env, const PromiseInfo& info) : PromiseBase(env, info) {
251         }
252 
253         // Resolve() fulfills the promise.
Resolve()254         void Resolve() const {
255             PromiseBase::Resolve(state->deferred.Env().Undefined());
256         }
257     };
258 
259     ////////////////////////////////////////////////////////////////////////////////
260     // Converter<T>
261     ////////////////////////////////////////////////////////////////////////////////
262 
263     // Converter<T> is specialized for each type T which can be converted from C++
264     // to JavaScript, or JavaScript to C++.
265     // Each specialization of Converter<T> is expected to have two static methods
266     // with the signatures:
267     //
268     //  // FromJS() converts the JavaScript value 'in' to the C++ value 'out'.
269     //  static Result FromJS(Napi::Env, Napi::Value in, T& out);
270     //
271     //  // ToJS() converts the C++ value 'in' to a JavaScript value, and returns
272     //  // this value.
273     //  static Napi::Value ToJS(Napi::Env, T in);
274     template <typename T>
275     class Converter {};
276 
277     template <>
278     class Converter<Napi::Object> {
279       public:
FromJS(Napi::Env,Napi::Value value,Napi::Object & out)280         static inline Result FromJS(Napi::Env, Napi::Value value, Napi::Object& out) {
281             if (value.IsObject()) {
282                 out = value.ToObject();
283                 return Success;
284             }
285             return Error("value is not an object");
286         }
ToJS(Napi::Env,Napi::Object value)287         static inline Napi::Value ToJS(Napi::Env, Napi::Object value) {
288             return value;
289         }
290     };
291 
292     template <>
293     class Converter<ArrayBuffer> {
294       public:
FromJS(Napi::Env,Napi::Value value,ArrayBuffer & out)295         static inline Result FromJS(Napi::Env, Napi::Value value, ArrayBuffer& out) {
296             if (value.IsArrayBuffer()) {
297                 out = value.As<ArrayBuffer>();
298                 return Success;
299             }
300             return Error("value is not a ArrayBuffer");
301         };
ToJS(Napi::Env,ArrayBuffer value)302         static inline Napi::Value ToJS(Napi::Env, ArrayBuffer value) {
303             return value;
304         }
305     };
306 
307     template <>
308     class Converter<Napi::TypedArray> {
309       public:
FromJS(Napi::Env,Napi::Value value,Napi::TypedArray & out)310         static inline Result FromJS(Napi::Env, Napi::Value value, Napi::TypedArray& out) {
311             if (value.IsTypedArray()) {
312                 out = value.As<Napi::TypedArray>();
313                 return Success;
314             }
315             return Error("value is not a TypedArray");
316         };
ToJS(Napi::Env,ArrayBuffer value)317         static inline Napi::Value ToJS(Napi::Env, ArrayBuffer value) {
318             return value;
319         }
320     };
321 
322     template <typename T>
323     class Converter<Napi::TypedArrayOf<T>> {
324       public:
325         // clang-format off
326         // The Napi element type of T
327         static constexpr napi_typedarray_type element_type =
328               std::is_same<T, int8_t>::value   ? napi_int8_array
329             : std::is_same<T, uint8_t>::value  ? napi_uint8_array
330             : std::is_same<T, int16_t>::value  ? napi_int16_array
331             : std::is_same<T, uint16_t>::value ? napi_uint16_array
332             : std::is_same<T, int32_t>::value  ? napi_int32_array
333             : std::is_same<T, uint32_t>::value ? napi_uint32_array
334             : std::is_same<T, float>::value    ? napi_float32_array
335             : std::is_same<T, double>::value   ? napi_float64_array
336             : std::is_same<T, int64_t>::value  ? napi_bigint64_array
337             : std::is_same<T, uint64_t>::value ? napi_biguint64_array
338             : static_cast<napi_typedarray_type>(-1);
339         // clang-format on
340         static_assert(static_cast<int>(element_type) >= 0,
341                       "unsupported T type for Napi::TypedArrayOf<T>");
FromJS(Napi::Env,Napi::Value value,Napi::TypedArrayOf<T> & out)342         static inline Result FromJS(Napi::Env, Napi::Value value, Napi::TypedArrayOf<T>& out) {
343             if (value.IsTypedArray()) {
344                 auto arr = value.As<Napi::TypedArrayOf<T>>();
345                 if (arr.TypedArrayType() == element_type) {
346                     out = arr;
347                     return Success;
348                 }
349                 return Error("value is not a TypedArray of the correct element type");
350             }
351             return Error("value is not a TypedArray");
352         };
ToJS(Napi::Env,ArrayBuffer value)353         static inline Napi::Value ToJS(Napi::Env, ArrayBuffer value) {
354             return value;
355         }
356     };
357 
358     template <>
359     class Converter<std::string> {
360       public:
361         static Result FromJS(Napi::Env, Napi::Value, std::string&);
362         static Napi::Value ToJS(Napi::Env, std::string);
363     };
364 
365     template <>
366     class Converter<bool> {
367       public:
368         static Result FromJS(Napi::Env, Napi::Value, bool&);
369         static Napi::Value ToJS(Napi::Env, bool);
370     };
371 
372     template <>
373     class Converter<int8_t> {
374       public:
375         static Result FromJS(Napi::Env, Napi::Value, int8_t&);
376         static Napi::Value ToJS(Napi::Env, int8_t);
377     };
378 
379     template <>
380     class Converter<uint8_t> {
381       public:
382         static Result FromJS(Napi::Env, Napi::Value, uint8_t&);
383         static Napi::Value ToJS(Napi::Env, uint8_t);
384     };
385 
386     template <>
387     class Converter<int16_t> {
388       public:
389         static Result FromJS(Napi::Env, Napi::Value, int16_t&);
390         static Napi::Value ToJS(Napi::Env, int16_t);
391     };
392 
393     template <>
394     class Converter<uint16_t> {
395       public:
396         static Result FromJS(Napi::Env, Napi::Value, uint16_t&);
397         static Napi::Value ToJS(Napi::Env, uint16_t);
398     };
399 
400     template <>
401     class Converter<int32_t> {
402       public:
403         static Result FromJS(Napi::Env, Napi::Value, int32_t&);
404         static Napi::Value ToJS(Napi::Env, int32_t);
405     };
406 
407     template <>
408     class Converter<uint32_t> {
409       public:
410         static Result FromJS(Napi::Env, Napi::Value, uint32_t&);
411         static Napi::Value ToJS(Napi::Env, uint32_t);
412     };
413 
414     template <>
415     class Converter<int64_t> {
416       public:
417         static Result FromJS(Napi::Env, Napi::Value, int64_t&);
418         static Napi::Value ToJS(Napi::Env, int64_t);
419     };
420 
421     template <>
422     class Converter<uint64_t> {
423       public:
424         static Result FromJS(Napi::Env, Napi::Value, uint64_t&);
425         static Napi::Value ToJS(Napi::Env, uint64_t);
426     };
427 
428     template <>
429     class Converter<float> {
430       public:
431         static Result FromJS(Napi::Env, Napi::Value, float&);
432         static Napi::Value ToJS(Napi::Env, float);
433     };
434 
435     template <>
436     class Converter<double> {
437       public:
438         static Result FromJS(Napi::Env, Napi::Value, double&);
439         static Napi::Value ToJS(Napi::Env, double);
440     };
441 
442     template <typename T>
443     class Converter<Interface<T>> {
444       public:
FromJS(Napi::Env env,Napi::Value value,Interface<T> & out)445         static Result FromJS(Napi::Env env, Napi::Value value, Interface<T>& out) {
446             if (!value.IsObject()) {
447                 return Error("value is not object");
448             }
449             auto obj = value.As<Napi::Object>();
450             if (!T::Unwrap(obj)) {
451                 return Error("object is not of the correct interface type");
452             }
453             out = Interface<T>(obj);
454             return Success;
455         }
ToJS(Napi::Env env,const Interface<T> & value)456         static Napi::Value ToJS(Napi::Env env, const Interface<T>& value) {
457             return {env, value};
458         }
459     };
460 
461     template <typename T>
462     class Converter<std::optional<T>> {
463       public:
FromJS(Napi::Env env,Napi::Value value,std::optional<T> & out)464         static Result FromJS(Napi::Env env, Napi::Value value, std::optional<T>& out) {
465             if (value.IsNull() || value.IsUndefined()) {
466                 out.reset();
467                 return Success;
468             }
469             T v{};
470             auto res = Converter<T>::FromJS(env, value, v);
471             if (!res) {
472                 return res;
473             }
474             out = std::move(v);
475             return Success;
476         }
ToJS(Napi::Env env,std::optional<T> value)477         static Napi::Value ToJS(Napi::Env env, std::optional<T> value) {
478             if (value.has_value()) {
479                 return Converter<T>::ToJS(env, value.value());
480             }
481             return env.Null();
482         }
483     };
484 
485     template <typename T>
486     class Converter<std::vector<T>> {
487       public:
FromJS(Napi::Env env,Napi::Value value,std::vector<T> & out)488         static inline Result FromJS(Napi::Env env, Napi::Value value, std::vector<T>& out) {
489             if (!value.IsArray()) {
490                 return Error("value is not an array");
491             }
492             auto arr = value.As<Napi::Array>();
493             std::vector<T> vec(arr.Length());
494             for (size_t i = 0; i < vec.size(); i++) {
495                 auto res = Converter<T>::FromJS(env, arr[static_cast<uint32_t>(i)], vec[i]);
496                 if (!res) {
497                     return res.Append("for array element ", i);
498                 }
499             }
500             out = std::move(vec);
501             return Success;
502         }
ToJS(Napi::Env env,const std::vector<T> & vec)503         static inline Napi::Value ToJS(Napi::Env env, const std::vector<T>& vec) {
504             auto arr = Napi::Array::New(env, vec.size());
505             for (size_t i = 0; i < vec.size(); i++) {
506                 arr.Set(static_cast<uint32_t>(i), Converter<T>::ToJS(env, vec[i]));
507             }
508             return arr;
509         }
510     };
511 
512     template <typename K, typename V>
513     class Converter<std::unordered_map<K, V>> {
514       public:
FromJS(Napi::Env env,Napi::Value value,std::unordered_map<K,V> & out)515         static inline Result FromJS(Napi::Env env,
516                                     Napi::Value value,
517                                     std::unordered_map<K, V>& out) {
518             if (!value.IsObject()) {
519                 return Error("value is not an object");
520             }
521             auto obj = value.ToObject();
522             auto keys = obj.GetPropertyNames();
523             std::unordered_map<K, V> map(keys.Length());
524             for (uint32_t i = 0; i < static_cast<uint32_t>(keys.Length()); i++) {
525                 K key{};
526                 V value{};
527                 auto key_res = Converter<K>::FromJS(env, keys[i], key);
528                 if (!key_res) {
529                     return key_res.Append("for object key");
530                 }
531                 auto value_res = Converter<V>::FromJS(env, obj.Get(keys[i]), value);
532                 if (!value_res) {
533                     return value_res.Append("for object value of key: ", key);
534                 }
535                 map[key] = value;
536             }
537             out = std::move(map);
538             return Success;
539         }
ToJS(Napi::Env env,std::unordered_map<K,V> value)540         static inline Napi::Value ToJS(Napi::Env env, std::unordered_map<K, V> value) {
541             auto obj = Napi::Object::New(env);
542             for (auto it : value) {
543                 obj.Set(Converter<K>::ToJS(env, it.first), Converter<V>::ToJS(env, it.second));
544             }
545             return obj;
546         }
547     };
548 
549     template <typename... TYPES>
550     class Converter<std::variant<TYPES...>> {
551         template <typename TY>
TryFromJS(Napi::Env env,Napi::Value value,std::variant<TYPES...> & out)552         static inline Result TryFromJS(Napi::Env env,
553                                        Napi::Value value,
554                                        std::variant<TYPES...>& out) {
555             TY v{};
556             auto res = Converter<TY>::FromJS(env, value, v);
557             if (!res) {
558                 return Error("no possible types matched");
559             }
560             out = std::move(v);
561             return Success;
562         }
563 
564         template <typename T0, typename T1, typename... TN>
TryFromJS(Napi::Env env,Napi::Value value,std::variant<TYPES...> & out)565         static inline Result TryFromJS(Napi::Env env,
566                                        Napi::Value value,
567                                        std::variant<TYPES...>& out) {
568             if (TryFromJS<T0>(env, value, out)) {
569                 return Success;
570             }
571             return TryFromJS<T1, TN...>(env, value, out);
572         }
573 
574       public:
FromJS(Napi::Env env,Napi::Value value,std::variant<TYPES...> & out)575         static inline Result FromJS(Napi::Env env, Napi::Value value, std::variant<TYPES...>& out) {
576             return TryFromJS<TYPES...>(env, value, out);
577         }
ToJS(Napi::Env env,std::variant<TYPES...> value)578         static inline Napi::Value ToJS(Napi::Env env, std::variant<TYPES...> value) {
579             return std::visit(
580                 [&](auto&& v) {
581                     using T = std::remove_cv_t<std::remove_reference_t<decltype(v)>>;
582                     return Converter<T>::ToJS(env, v);
583                 },
584                 value);
585         }
586     };
587 
588     template <typename T>
589     class Converter<Promise<T>> {
590       public:
FromJS(Napi::Env,Napi::Value,Promise<T> &)591         static inline Result FromJS(Napi::Env, Napi::Value, Promise<T>&) {
592             UNIMPLEMENTED();
593         }
ToJS(Napi::Env,Promise<T> promise)594         static inline Napi::Value ToJS(Napi::Env, Promise<T> promise) {
595             return promise;
596         }
597     };
598 
599     ////////////////////////////////////////////////////////////////////////////////
600     // Helpers
601     ////////////////////////////////////////////////////////////////////////////////
602 
603     // FromJS() is a helper function which delegates to
604     // Converter<T>::FromJS()
605     template <typename T>
FromJS(Napi::Env env,Napi::Value value,T & out)606     inline Result FromJS(Napi::Env env, Napi::Value value, T& out) {
607         return Converter<T>::FromJS(env, value, out);
608     }
609 
610     // FromJSOptional() is similar to FromJS(), but if 'value' is either null
611     // or undefined then 'out' is left unassigned.
612     template <typename T>
FromJSOptional(Napi::Env env,Napi::Value value,T & out)613     inline Result FromJSOptional(Napi::Env env, Napi::Value value, T& out) {
614         if (value.IsNull() || value.IsUndefined()) {
615             return Success;
616         }
617         return Converter<T>::FromJS(env, value, out);
618     }
619 
620     // ToJS() is a helper function which delegates to Converter<T>::ToJS()
621     template <typename T>
ToJS(Napi::Env env,T && value)622     inline Napi::Value ToJS(Napi::Env env, T&& value) {
623         return Converter<std::remove_cv_t<std::remove_reference_t<T>>>::ToJS(
624             env, std::forward<T>(value));
625     }
626 
627     // DefaultedParameter can be used in the tuple parameter types passed to
628     // FromJS(const Napi::CallbackInfo& info, PARAM_TYPES& args), for parameters
629     // that have a default value. If the argument is omitted in the call, then
630     // DefaultedParameter::default_value will be assigned to
631     // DefaultedParameter::value.
632     template <typename T>
633     struct DefaultedParameter {
634         T value;          // The argument value assigned by FromJS()
635         T default_value;  // The default value if no argument supplied
636 
637         // Implicit conversion operator. Returns value.
638         inline operator const T&() const {
639             return value;
640         }
641     };
642 
643     // IsDefaultedParameter<T>::value is true iff T is of type DefaultedParameter.
644     template <typename T>
645     struct IsDefaultedParameter {
646         static constexpr bool value = false;
647     };
648     template <typename T>
649     struct IsDefaultedParameter<DefaultedParameter<T>> {
650         static constexpr bool value = true;
651     };
652 
653     // FromJS() is a helper function for bulk converting the arguments of 'info'.
654     // PARAM_TYPES is a std::tuple<> describing the C++ function parameter types.
655     // Parameters may be of the templated DefaultedParameter type, in which case
656     // the parameter will default to the default-value if omitted.
657     template <typename PARAM_TYPES, int BASE_INDEX = 0>
658     inline Result FromJS(const Napi::CallbackInfo& info, PARAM_TYPES& args) {
659         if constexpr (BASE_INDEX < std::tuple_size_v<PARAM_TYPES>) {
660             using T = std::tuple_element_t<BASE_INDEX, PARAM_TYPES>;
661             auto& value = info[BASE_INDEX];
662             auto& out = std::get<BASE_INDEX>(args);
663             if constexpr (IsDefaultedParameter<T>::value) {
664                 // Parameter has a default value.
665                 // Check whether the argument was provided.
666                 if (value.IsNull() || value.IsUndefined()) {
667                     // Use default value for this parameter
668                     out.value = out.default_value;
669                 } else {
670                     // Argument was provided
671                     auto res = FromJS(info.Env(), value, out.value);
672                     if (!res) {
673                         return res;
674                     }
675                 }
676             } else {
677                 // Parameter does not have a default value.
678                 auto res = FromJS(info.Env(), value, out);
679                 if (!res) {
680                     return res;
681                 }
682             }
683             // Convert the rest of the arguments
684             return FromJS<PARAM_TYPES, BASE_INDEX + 1>(info, args);
685         } else {
686             return Success;
687         }
688     }
689 
690 }}  // namespace wgpu::interop
691 
692 #endif  //  DAWN_NODE_INTEROP_CORE_WEBGPU_H_
693