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