1 /* 2 * Copyright (C) 2024 Huawei Device Co., Ltd. 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 #ifndef NAPI_API 16 #define NAPI_API 17 #define NAPI_VERSION 8 18 19 #ifdef __OHOS_PLATFORM__ 20 #include "napi/native_api.h" 21 #else 22 #include <node_api.h> 23 #endif 24 25 #include <meta/interface/intf_object.h> 26 27 #include <base/containers/string.h> 28 #include <base/containers/unique_ptr.h> 29 #include <base/containers/vector.h> 30 31 #define LOG_F(...) CORE_LOG_F(__VA_ARGS__) 32 #define LOG_E(...) CORE_LOG_E(__VA_ARGS__) 33 #define LOG_V(...) 34 #define LOG_W(...) 35 36 namespace NapiApi { 37 class Env { 38 napi_env env_ { nullptr }; 39 40 public: 41 Env() = default; Env(napi_env env)42 explicit Env(napi_env env) : env_(env) {} 43 operator bool() const 44 { 45 return (env_); 46 } napi_env()47 operator napi_env() const 48 { 49 return env_; 50 } GetUndefined()51 napi_value GetUndefined() const 52 { 53 if (!env_) { 54 return {}; 55 } 56 napi_value undefined; 57 napi_get_undefined(env_, &undefined); 58 return undefined; 59 } GetNull()60 napi_value GetNull() const 61 { 62 if (!env_) { 63 return {}; 64 } 65 napi_value null; 66 napi_get_null(env_, &null); 67 return null; 68 } GetBoolean(bool value)69 napi_value GetBoolean(bool value) const 70 { 71 if (!env_) { 72 return {}; 73 } 74 napi_value val; 75 napi_get_boolean(env_, value, &val); 76 return val; 77 } GetNumber(uint32_t value)78 napi_value GetNumber(uint32_t value) const 79 { 80 if (!env_) { 81 return {}; 82 } 83 napi_value val; 84 napi_create_uint32(env_, value, &val); 85 return val; 86 } GetNumber(int32_t value)87 napi_value GetNumber(int32_t value) const 88 { 89 if (!env_) { 90 return {}; 91 } 92 napi_value val; 93 napi_create_int32(env_, value, &val); 94 return val; 95 } GetNumber(uint64_t value)96 napi_value GetNumber(uint64_t value) const 97 { 98 if (!env_) { 99 return {}; 100 } 101 napi_value val; 102 napi_create_bigint_uint64(env_, value, &val); 103 return val; 104 } GetNumber(int64_t value)105 napi_value GetNumber(int64_t value) const 106 { 107 if (!env_) { 108 return {}; 109 } 110 napi_value val; 111 napi_create_bigint_int64(env_, value, &val); 112 return val; 113 } GetNumber(float value)114 napi_value GetNumber(float value) const 115 { 116 if (!env_) { 117 return {}; 118 } 119 napi_value val; 120 napi_create_double(env_, value, &val); 121 return val; 122 } GetNumber(double value)123 napi_value GetNumber(double value) const 124 { 125 if (!env_) { 126 return {}; 127 } 128 napi_value val; 129 napi_create_double(env_, value, &val); 130 return val; 131 } GetString(const BASE_NS::string_view value)132 napi_value GetString(const BASE_NS::string_view value) const 133 { 134 napi_value val; 135 napi_create_string_utf8(env_, value.data(), value.length(), &val); 136 return val; 137 } 138 }; 139 template<typename type> 140 bool ValidateType(napi_valuetype jstype, bool isArray); 141 142 template<typename type> 143 class Value { 144 napi_env env_ { nullptr }; 145 napi_value value_ { nullptr }; 146 napi_valuetype jstype = napi_undefined; 147 148 public: 149 using Type = type; 150 Value() = default; Value(napi_env env,Type v)151 Value(napi_env env, Type v) 152 { 153 Init(env, v); 154 } 155 156 void Init(napi_env env, Type v); 157 Value(napi_env env,napi_value v)158 Value(napi_env env, napi_value v) : env_(env) 159 { 160 if ((env == nullptr) || (v == nullptr)) { 161 return; 162 } 163 // validate type 164 napi_status status = napi_invalid_arg; 165 status = napi_typeof(env_, v, &jstype); 166 if (status != napi_ok) { 167 // okay then failed. 168 return; 169 } 170 bool isArray = false; 171 napi_is_array(env_, v, &isArray); 172 173 if (ValidateType<type>(jstype, isArray)) { 174 value_ = v; 175 } else { 176 jstype = napi_undefined; 177 } 178 } IsValid()179 bool IsValid() const 180 { 181 return (env_ && value_); 182 } IsNull()183 bool IsNull() const 184 { 185 return (napi_null == jstype); 186 } IsDefined()187 bool IsDefined() const 188 { 189 return !IsUndefined(); 190 } IsUndefined()191 bool IsUndefined() const 192 { 193 return (napi_undefined == jstype); 194 } IsUndefinedOrNull()195 bool IsUndefinedOrNull() const 196 { 197 if (!IsValid()) { 198 return true; 199 } 200 return ((napi_null == jstype) || (napi_undefined == jstype)); 201 } IsDefinedAndNotNull()202 bool IsDefinedAndNotNull() const 203 { 204 return ((napi_null != jstype) && (napi_undefined != jstype)); 205 } 206 type valueOrDefault(const type defaultValue = {}); type()207 operator type() 208 { 209 return valueOrDefault(); 210 } GetEnv()211 napi_env GetEnv() const 212 { 213 return env_; 214 } ToNapiValue()215 napi_value ToNapiValue() const 216 { 217 return value_; 218 } 219 }; 220 class Function; 221 222 class Object { 223 public: 224 operator bool() const 225 { 226 if (!env_ || !object_) { 227 return false; 228 } 229 return (napi_object == jstype); 230 } IsDefined()231 bool IsDefined() const 232 { 233 return (napi_undefined != jstype); 234 } IsNull()235 bool IsNull() const 236 { 237 return (napi_null == jstype); 238 } 239 Object() = default; 240 explicit Object(Function ctor); 241 Object(Function ctor, size_t argc, napi_value args[]); Object(napi_env env)242 explicit Object(napi_env env) : env_(env) 243 { 244 if (!env_) { 245 return; 246 } 247 napi_create_object(env, &object_); 248 if (object_) { 249 napi_typeof(env, object_, &jstype); 250 } 251 if (jstype != napi_object) { 252 // value was not an object! 253 env_ = {}; 254 object_ = nullptr; 255 jstype = napi_undefined; 256 } 257 } Object(napi_env env,napi_value v)258 Object(napi_env env, napi_value v) : env_(env), object_(v) 259 { 260 if (!env_) { 261 return; 262 } 263 if (v) { 264 napi_typeof(env_, v, &jstype); 265 } 266 if (jstype != napi_object) { 267 // value was not an object! 268 env_ = {}; 269 object_ = nullptr; 270 jstype = napi_undefined; 271 } 272 } 273 274 template<class T> Native()275 T* Native() 276 { 277 if (!env_ || !object_) { 278 return nullptr; 279 } 280 T* me = nullptr; 281 napi_unwrap(env_, object_, (void**)&me); 282 return me; 283 } Set(const BASE_NS::string_view name,napi_value value)284 void Set(const BASE_NS::string_view name, napi_value value) 285 { 286 if (!env_ || !object_) { 287 return; 288 } 289 // could check if it is declared. and optionally add it. (now it just adds it if not declared) 290 napi_set_named_property(env_, object_, BASE_NS::string(name).c_str(), value); 291 } 292 template<typename type> Set(const BASE_NS::string_view name,NapiApi::Value<type> value)293 void Set(const BASE_NS::string_view name, NapiApi::Value<type> value) 294 { 295 Set(name, value.ToNapiValue()); 296 } 297 Set(const BASE_NS::string_view name,NapiApi::Object value)298 void Set(const BASE_NS::string_view name, NapiApi::Object value) 299 { 300 Set(name, value.ToNapiValue()); 301 } 302 Set(const BASE_NS::string_view name,const BASE_NS::string_view v)303 void Set(const BASE_NS::string_view name, const BASE_NS::string_view v) 304 { 305 if (!env_ || !object_) { 306 return; 307 } 308 napi_status status; 309 napi_value value = MakeTempString(v); 310 status = napi_set_named_property(env_, object_, BASE_NS::string(name).c_str(), value); 311 } 312 Has(const BASE_NS::string_view name)313 bool Has(const BASE_NS::string_view name) 314 { 315 if (!env_ || !object_) { 316 return false; 317 } 318 bool res; 319 napi_value key = MakeTempString(name); 320 napi_status status = napi_has_property(env_, object_, key, &res); 321 if (napi_ok != status) { 322 return false; 323 } 324 return res; 325 } 326 Get(const BASE_NS::string_view name)327 napi_value Get(const BASE_NS::string_view name) 328 { 329 auto tmp = GetNamedPropertyAndType(name); 330 if (tmp.jstype == napi_null) { 331 return nullptr; 332 } 333 if (tmp.jstype == napi_undefined) { 334 return nullptr; 335 } 336 return tmp.res; 337 } 338 template<typename t> Get(const BASE_NS::string_view name)339 Value<t> Get(const BASE_NS::string_view name) 340 { 341 return Value<t>(env_, Get(name)); 342 } IsUndefined(const BASE_NS::string_view name)343 bool IsUndefined(const BASE_NS::string_view name) 344 { 345 auto tmp = GetNamedPropertyAndType(name); 346 if (!tmp.success) { 347 return true; 348 } 349 return (tmp.jstype == napi_undefined); 350 } 351 IsNull(const BASE_NS::string_view name)352 bool IsNull(const BASE_NS::string_view name) 353 { 354 auto tmp = GetNamedPropertyAndType(name); 355 if (!tmp.success) { 356 return true; 357 } 358 return (tmp.jstype == napi_null); 359 } 360 IsUndefinedOrNull(const BASE_NS::string_view name)361 bool IsUndefinedOrNull(const BASE_NS::string_view name) 362 { 363 auto tmp = GetNamedPropertyAndType(name); 364 if (!tmp.success) { 365 return true; 366 } 367 return ((tmp.jstype == napi_null) || (tmp.jstype == napi_undefined)); 368 } 369 ToNapiValue()370 napi_value ToNapiValue() const 371 { 372 return object_; 373 } 374 GetEnv()375 napi_env GetEnv() const 376 { 377 return env_; 378 } 379 380 // note this compares the "value of object_" IsSame(NapiApi::Object other)381 bool IsSame(NapiApi::Object other) 382 { 383 return object_ == other.object_; 384 } 385 386 // This API represents the invocation of the Strict Equality algorithm as defined in Section 7.2.14 of the 387 // ECMAScript Language Specification. StrictEqual(napi_value other)388 bool StrictEqual(napi_value other) 389 { 390 bool equal; 391 if (env_) { 392 // this is undefined. so return false. 393 return false; 394 } 395 napi_strict_equals(env_, object_, other, &equal); 396 // make it a bit more strict by checking wrapping 397 void *l = nullptr; 398 void *r = nullptr; 399 napi_unwrap(env_, object_, &l); 400 napi_unwrap(env_, other, &r); 401 if (l || r) { 402 if (l == r) { 403 if (!equal) { 404 CORE_LOG_F("napi_strict_equals returns false, but wrapped native objects are same"); 405 } 406 return true; 407 } else { 408 if (equal) { 409 CORE_LOG_F("napi_strict_equals returns true, but wrapped native objects are different"); 410 } 411 return false; 412 } 413 } 414 return equal; 415 } StrictEqual(NapiApi::Object other)416 bool StrictEqual(NapiApi::Object other) 417 { 418 return StrictEqual(other.object_); 419 } AddProperty(const napi_property_descriptor desc)420 void AddProperty(const napi_property_descriptor desc) 421 { 422 if (!env_ || !object_) { 423 return; 424 } 425 napi_status status = napi_define_properties(env_, object_, 1, &desc); 426 } DeleteProperty(const BASE_NS::string_view name)427 bool DeleteProperty(const BASE_NS::string_view name) 428 { 429 if (!env_ || !object_) { 430 return false; 431 } 432 // remove property from object. 433 napi_status status; 434 bool result { false }; 435 napi_value key = MakeTempString(name); 436 status = napi_delete_property(env_, object_, key, &result); 437 return result; 438 } 439 440 protected: 441 struct NameAndType { 442 bool success { false }; 443 napi_value res { nullptr }; 444 napi_valuetype jstype { napi_undefined }; 445 }; GetNamedPropertyAndType(const BASE_NS::string_view name)446 NameAndType GetNamedPropertyAndType(const BASE_NS::string_view name) 447 { 448 napi_value res; 449 napi_status status; 450 napi_valuetype jstype; 451 if (!env_ || !object_) { 452 return { false, nullptr, napi_undefined }; 453 } 454 status = napi_get_named_property(env_, object_, BASE_NS::string(name).c_str(), &res); 455 if ((!res) || (napi_ok != status)) { 456 return { false, nullptr, napi_undefined }; 457 } 458 napi_typeof(env_, res, &jstype); 459 return { true, res, jstype }; 460 } MakeTempString(const BASE_NS::string_view v)461 napi_value MakeTempString(const BASE_NS::string_view v) 462 { 463 return env_.GetString(v); 464 } 465 466 private: 467 napi_valuetype jstype = napi_undefined; 468 NapiApi::Env env_ { nullptr }; 469 napi_value object_ { nullptr }; 470 }; 471 472 template<typename... Types> 473 class FunctionContext { 474 public: 475 template<typename First, typename... Rest> validate(size_t index)476 inline bool validate(size_t index) 477 { 478 napi_valuetype jstype; 479 napi_status status = napi_invalid_arg; 480 status = napi_typeof(env_, args[index], &jstype); 481 bool isArray = false; 482 napi_is_array(env_, args[index], &isArray); 483 484 bool ret = ValidateType<First>(jstype, isArray); 485 if (ret) { 486 if constexpr (sizeof...(Rest) == 0) { 487 return true; 488 } 489 if constexpr (sizeof...(Rest) > 0) { 490 return validate<Rest...>(index + 1); 491 } 492 } 493 return false; 494 } 495 template<typename... ot> FunctionContext(FunctionContext<ot...> other)496 FunctionContext(FunctionContext<ot...> other) : FunctionContext(other.GetEnv(), other.GetInfo()) 497 {} FunctionContext(const napi_env env,const napi_callback_info info)498 FunctionContext(const napi_env env, const napi_callback_info info) 499 { 500 if ((!env) || (!info)) { 501 return; 502 } 503 napi_status status { napi_ok }; 504 status = napi_get_cb_info(env, info, &argCount_, nullptr, &jsThis_, &data_); 505 if (status != napi_ok) { 506 return; 507 } 508 env_ = NapiApi::Env(env); 509 info_ = info; 510 if constexpr (sizeof...(Types) > 0) { 511 // validate arg count first 512 if (argc != argCount_) { 513 // non matching arg count. fail 514 jsThis_ = nullptr; 515 data_ = nullptr; 516 info_ = nullptr; 517 return; 518 } 519 520 // get the arguments 521 status = napi_get_cb_info(env, info, &argCount_, args, nullptr, nullptr); 522 if (!validate<Types...>(0)) { 523 // non matching types in context! 524 jsThis_ = nullptr; 525 data_ = nullptr; 526 info_ = nullptr; 527 return; 528 } 529 } 530 } 531 operator bool() const 532 { 533 return (env_ && info_); 534 } GetData()535 void* GetData() const 536 { 537 return data_; 538 } Env()539 NapiApi::Env Env() const 540 { 541 return env_; 542 } GetEnv()543 napi_env GetEnv() const 544 { 545 return env_; 546 } GetInfo()547 napi_callback_info GetInfo() const 548 { 549 return info_; 550 } 551 This()552 NapiApi::Object This() 553 { 554 return { env_, jsThis_ }; 555 } value(size_t index)556 napi_value value(size_t index) 557 { 558 if (index < argc) { 559 return args[index]; 560 } 561 return nullptr; 562 } 563 564 template<size_t I, typename T, typename... TypesI> 565 struct GetTypeImpl { 566 using type = typename GetTypeImpl<I - 1, TypesI...>::type; 567 }; 568 template<typename T, typename... TypesI> 569 struct GetTypeImpl<0, T, TypesI...> { 570 using type = T; 571 }; 572 573 template<size_t index> 574 auto Arg() 575 { 576 if constexpr (sizeof...(Types) > 0) { 577 if constexpr (index < sizeof...(Types)) { 578 return Value<typename GetTypeImpl<index, Types...>::type> { env_, args[index] }; 579 } 580 if constexpr (index >= sizeof...(Types)) { 581 static_assert(index < sizeof...(Types), "Index out of range !"); 582 return Value<void*>((napi_env) nullptr, (void*)nullptr); 583 } 584 } 585 if constexpr (sizeof...(Types) == 0) { 586 return; 587 } 588 } 589 590 size_t ArgCount() const 591 { 592 return argCount_; 593 } 594 595 napi_value GetUndefined() 596 { 597 return env_.GetUndefined(); 598 } 599 napi_value GetNull() 600 { 601 return env_.GetNull(); 602 } 603 napi_value GetBoolean(bool value) 604 { 605 return env_.GetBoolean(value); 606 } 607 napi_value GetNumber(int32_t value) 608 { 609 return env_.GetNumber(value); 610 } 611 napi_value GetNumber(uint32_t value) 612 { 613 return env_.GetNumber(value); 614 } 615 napi_value GetNumber(float value) 616 { 617 return env_.GetNumber(value); 618 } 619 napi_value GetNumber(double value) 620 { 621 return env_.GetNumber(value); 622 } 623 napi_value GetString(const BASE_NS::string_view value) 624 { 625 return env_.GetString(value); 626 } 627 628 private: 629 napi_value jsThis_ { nullptr }; 630 void* data_ { nullptr }; 631 const size_t argc { sizeof...(Types) }; 632 napi_value args[sizeof...(Types) + 1] {}; 633 NapiApi::Env env_ { nullptr }; 634 napi_callback_info info_ { nullptr }; 635 size_t argCount_ { 0 }; 636 }; 637 638 class Array { 639 public: 640 Array() = default; 641 Array(napi_env env, size_t count) : env_(env) 642 { 643 napi_create_array_with_length(env, count, &array_); 644 } 645 Array(napi_env env, napi_value v) 646 { 647 napi_valuetype jstype; 648 napi_typeof(env, v, &jstype); 649 if (jstype != napi_object) { 650 return; 651 } 652 bool isArray = false; 653 napi_is_array(env, v, &isArray); 654 if (!isArray) { 655 return; 656 } 657 env_ = env; 658 array_ = v; 659 } 660 661 operator napi_value() const 662 { 663 return array_; 664 } 665 666 napi_env GetEnv() const 667 { 668 return env_; 669 } 670 671 size_t Count() const 672 { 673 uint32_t size; 674 napi_get_array_length(env_, array_, &size); 675 return size; 676 } 677 void Set_value(size_t index, napi_value v) const 678 { 679 napi_set_element(env_, array_, index, v); 680 } 681 682 napi_value Get_value(size_t index) const 683 { 684 napi_value result; 685 napi_get_element(env_, array_, index, &result); 686 return result; 687 } 688 napi_valuetype Type(size_t index) const 689 { 690 napi_value element; 691 napi_get_element(env_, array_, index, &element); 692 napi_valuetype jstype; 693 napi_status status = napi_invalid_arg; 694 status = napi_typeof(env_, element, &jstype); 695 return jstype; 696 } 697 template<typename T> 698 Value<T> Get(size_t index) const 699 { 700 return Value<T> { env_, Get_value(index) }; 701 } 702 template<typename T> 703 void Set(size_t index, T t) const 704 { 705 Set_value(index, Value<T>(env_, t).ToNapiValue()); 706 } 707 708 private: 709 napi_env env_ { nullptr }; 710 napi_value array_ { nullptr }; 711 }; 712 713 class Function { 714 public: 715 bool IsDefined() 716 { 717 return (napi_undefined != jstype); 718 } 719 bool IsNull() 720 { 721 return (napi_null == jstype); 722 } 723 Function() = default; 724 Function(napi_env env, napi_value v) : env_(env), func_(v) 725 { 726 if (env_) { 727 napi_typeof(env_, v, &jstype); 728 } 729 if (jstype != napi_function) { 730 // value was not a function 731 env_ = {}; 732 func_ = nullptr; 733 } 734 } 735 operator napi_value() const 736 { 737 return func_; 738 } 739 740 NapiApi::Env Env() const 741 { 742 return env_; 743 } 744 napi_env GetEnv() const 745 { 746 return env_; 747 } 748 napi_value Invoke(NapiApi::Object thisJS, size_t argc = 0, napi_value* argv = nullptr) const 749 { 750 if (!env_) { 751 return nullptr; 752 } 753 if (!func_) { 754 return env_.GetUndefined(); 755 } 756 napi_value res; 757 napi_call_function(env_, thisJS.ToNapiValue(), func_, argc, argv, &res); 758 return res; 759 } 760 761 private: 762 NapiApi::Env env_ { nullptr }; 763 napi_value func_ { nullptr }; 764 napi_valuetype jstype = napi_undefined; 765 }; 766 767 class MyInstanceState { 768 public: 769 MyInstanceState(napi_env env, napi_value obj) : env_(env) 770 { 771 napi_create_reference(env_, obj, 1, &ref_); 772 } 773 MyInstanceState(NapiApi::Object obj) : MyInstanceState(obj.GetEnv(), obj.ToNapiValue()) {} 774 ~MyInstanceState() 775 { 776 uint32_t res; 777 napi_reference_unref(env_, ref_, &res); 778 } 779 napi_value getRef() 780 { 781 napi_value tmp; 782 napi_get_reference_value(env_, ref_, &tmp); 783 return tmp; 784 } 785 786 void StoreCtor(BASE_NS::string_view name, napi_value ctor) 787 { 788 NapiApi::Object exp(env_, getRef()); 789 exp.Set(name, ctor); 790 } 791 napi_value FetchCtor(BASE_NS::string_view name) 792 { 793 NapiApi::Object exp(env_, getRef()); 794 return exp.Get(name); 795 } 796 797 private: 798 napi_ref ref_; 799 napi_env env_; 800 }; 801 template<typename type> 802 bool ValidateType(napi_valuetype jstype, bool isArray) 803 { 804 /* 805 napi_undefined, 806 napi_null, 807 napi_symbol, 808 napi_function, 809 napi_external, 810 napi_bigint, 811 */ 812 813 if constexpr (BASE_NS::is_same_v<type, BASE_NS::string>) { 814 if (jstype == napi_string) { 815 return true; 816 } 817 } 818 if constexpr (BASE_NS::is_same_v<type, bool>) { 819 if (jstype == napi_boolean) { 820 return true; 821 } 822 } 823 // yup.. 824 if constexpr (BASE_NS::is_same_v<type, float>) { 825 if (jstype == napi_number) { 826 return true; 827 } 828 } 829 if constexpr (BASE_NS::is_same_v<type, double>) { 830 if (jstype == napi_number) { 831 return true; 832 } 833 } 834 if constexpr (BASE_NS::is_same_v<type, uint32_t>) { 835 if (jstype == napi_number) { 836 return true; 837 } 838 } 839 if constexpr (BASE_NS::is_same_v<type, int32_t>) { 840 if (jstype == napi_number) { 841 return true; 842 } 843 } 844 if constexpr (BASE_NS::is_same_v<type, int64_t>) { 845 if (jstype == napi_number) { 846 return true; 847 } 848 } 849 if constexpr (BASE_NS::is_same_v<type, uint64_t>) { 850 if (jstype == napi_number) { 851 return true; 852 } 853 } 854 if constexpr (BASE_NS::is_same_v<type, NapiApi::Object>) { 855 if (jstype == napi_object) { 856 return true; 857 } 858 // allow undefined and null also 859 if (jstype == napi_undefined) { 860 return true; 861 } 862 if (jstype == napi_null) { 863 return true; 864 } 865 } 866 if constexpr (BASE_NS::is_same_v<type, NapiApi::Array>) { 867 if (jstype == napi_object) { 868 return isArray; 869 } 870 } 871 if constexpr (BASE_NS::is_same_v<type, NapiApi::Function>) { 872 if (jstype == napi_function) { 873 return true; 874 } 875 } 876 return false; 877 } 878 879 template<typename type> 880 type NapiApi::Value<type>::valueOrDefault(const type defaultValue) 881 { 882 if (!value_) { 883 return defaultValue; 884 } 885 napi_status status = napi_invalid_arg; 886 type value {}; 887 if constexpr (BASE_NS::is_same_v<type, BASE_NS::string>) { 888 size_t length; 889 status = napi_get_value_string_utf8(env_, value_, nullptr, 0, &length); 890 if (status != napi_ok) { 891 // return default if failed. 892 return defaultValue; 893 } 894 value.reserve(length + 1); 895 value.resize(length); 896 status = napi_get_value_string_utf8(env_, value_, value.data(), length + 1, &length); 897 } 898 if constexpr (BASE_NS::is_same_v<type, bool>) { 899 status = napi_get_value_bool(env_, value_, &value); 900 } 901 if constexpr (BASE_NS::is_same_v<type, float>) { 902 double tmp; 903 status = napi_get_value_double(env_, value_, &tmp); 904 value = tmp; 905 } 906 if constexpr (BASE_NS::is_same_v<type, double>) { 907 status = napi_get_value_double(env_, value_, &value); 908 } 909 if constexpr (BASE_NS::is_same_v<type, uint32_t>) { 910 status = napi_get_value_uint32(env_, value_, &value); 911 } 912 if constexpr (BASE_NS::is_same_v<type, int32_t>) { 913 status = napi_get_value_int32(env_, value_, &value); 914 } 915 if constexpr (BASE_NS::is_same_v<type, int64_t>) { 916 status = napi_get_value_int64(env_, value_, &value); 917 } 918 if constexpr (BASE_NS::is_same_v<type, uint64_t>) { 919 int64_t tmp; 920 status = napi_get_value_int64(env_, value_, &tmp); 921 value = static_cast<uint64_t>(tmp); 922 } 923 if constexpr (BASE_NS::is_same_v<type, NapiApi::Object>) { 924 status = napi_ok; 925 value = NapiApi::Object(env_, value_); 926 } 927 if constexpr (BASE_NS::is_same_v<type, NapiApi::Function>) { 928 status = napi_ok; 929 value = NapiApi::Function(env_, value_); 930 } 931 if constexpr (BASE_NS::is_same_v<type, NapiApi::Array>) { 932 status = napi_ok; 933 value = NapiApi::Array(env_, value_); 934 } 935 if (status != napi_ok) { 936 // return default if failed. 937 return defaultValue; 938 } 939 return value; 940 } 941 942 inline Object::Object(Function ctor) : Object(ctor, 0, nullptr) 943 { 944 } 945 946 inline Object::Object(Function ctor, size_t argc, napi_value args[]) 947 { 948 env_ = ctor.Env(); 949 napi_new_instance(env_, ctor, argc, args, &object_); 950 if (object_) { 951 napi_typeof(env_, object_, &jstype); 952 } 953 if (jstype != napi_object) { 954 // value was not an object! 955 env_ = {}; 956 object_ = nullptr; 957 jstype = napi_undefined; 958 } 959 } 960 961 template<typename Object, napi_value (Object::*F)(NapiApi::FunctionContext<>&)> 962 static inline napi_value Getter(napi_env env, napi_callback_info info) 963 { 964 NapiApi::FunctionContext fc(env, info); 965 if (fc && fc.This()) { 966 if (auto scj = fc.This().template Native<Object>()) { 967 if (auto ret = (scj->*F)(fc)) { 968 return ret; 969 } 970 } 971 } 972 return fc.GetUndefined(); 973 }; 974 975 template<typename Type, typename Object, void (Object::*F)(NapiApi::FunctionContext<Type>&)> 976 static inline napi_value Setter(napi_env env, napi_callback_info info) 977 { 978 NapiApi::FunctionContext<Type> fc(env, info); 979 if (fc && fc.This()) { 980 if (auto scj = fc.This().template Native<Object>()) { 981 (scj->*F)(fc); 982 } 983 } 984 return fc.GetUndefined(); 985 }; 986 987 template<typename FC, typename Object, napi_value (Object::*F)(FC&)> 988 static inline napi_value MethodI(napi_env env, napi_callback_info info) 989 { 990 FC fc(env, info); 991 if (fc && fc.This()) { 992 if (auto scj = fc.This().template Native<Object>()) { 993 return (scj->*F)(fc); 994 } 995 } 996 return fc.GetUndefined(); 997 }; 998 999 template<typename FC, typename Object, napi_value (Object::*F)(FC&)> 1000 static inline napi_property_descriptor Method( 1001 const char* const name, napi_property_attributes flags = napi_default_method) 1002 { 1003 return napi_property_descriptor { name, nullptr, MethodI<FC, Object, F>, nullptr, nullptr, nullptr, flags, 1004 nullptr }; 1005 } 1006 1007 template<typename Type, typename Object, void (Object::*F2)(NapiApi::FunctionContext<Type>&)> 1008 static inline napi_property_descriptor SetProperty( 1009 const char* const name, napi_property_attributes flags = napi_default_jsproperty) 1010 { 1011 static_assert(F2 != nullptr); 1012 return napi_property_descriptor { name, nullptr, nullptr, nullptr, Setter<Type, Object, F2>, nullptr, flags, 1013 nullptr }; 1014 } 1015 1016 template<typename Type, typename Object, napi_value (Object::*F)(NapiApi::FunctionContext<>&)> 1017 static inline napi_property_descriptor GetProperty( 1018 const char* const name, napi_property_attributes flags = napi_default_jsproperty) 1019 { 1020 static_assert(F != nullptr); 1021 return napi_property_descriptor { name, nullptr, nullptr, Getter<Object, F>, nullptr, nullptr, flags, nullptr }; 1022 } 1023 1024 template<typename Type, typename Object, napi_value (Object::*F)(NapiApi::FunctionContext<>&), 1025 void (Object::*F2)(NapiApi::FunctionContext<Type>&)> 1026 static inline napi_property_descriptor GetSetProperty( 1027 const char* const name, napi_property_attributes flags = napi_default_jsproperty) 1028 { 1029 static_assert(F != nullptr); 1030 static_assert(F2 != nullptr); 1031 return napi_property_descriptor { name, nullptr, nullptr, Getter<Object, F>, Setter<Type, Object, F2>, nullptr, 1032 flags, nullptr }; 1033 } 1034 class WeakRef { 1035 public: 1036 WeakRef() = default; 1037 WeakRef(NapiApi::WeakRef&& ref) noexcept 1038 { 1039 env_ = ref.env_; 1040 ref_ = ref.ref_; 1041 ref.env_ = nullptr; 1042 ref.ref_ = nullptr; 1043 } 1044 1045 WeakRef(const NapiApi::WeakRef& ref) 1046 { 1047 if (!ref.IsEmpty()) { 1048 napi_status stat; 1049 env_ = ref.env_; 1050 stat = napi_create_reference(env_, ref.GetValue(), 0, &ref_); 1051 } 1052 } 1053 NapiApi::WeakRef& operator=(NapiApi::WeakRef&& ref) noexcept 1054 { 1055 env_ = ref.env_; 1056 ref_ = ref.ref_; 1057 ref.env_ = nullptr; 1058 ref.ref_ = nullptr; 1059 return *this; 1060 } 1061 NapiApi::WeakRef& operator=(const NapiApi::WeakRef& ref) 1062 { 1063 if (&ref != this) { 1064 if (!ref.IsEmpty()) { 1065 napi_status stat; 1066 // unh just create a new one.. 1067 env_ = ref.env_; 1068 stat = napi_create_reference(env_, ref.GetValue(), 0, &ref_); 1069 } 1070 } 1071 return *this; 1072 } 1073 WeakRef(NapiApi::Object obj) 1074 { 1075 env_ = obj.GetEnv(); 1076 napi_create_reference(env_, obj.ToNapiValue(), 0, &ref_); 1077 } 1078 WeakRef(napi_env env, napi_value obj) 1079 { 1080 env_ = env; 1081 napi_create_reference(env_, obj, 0, &ref_); 1082 } 1083 ~WeakRef() 1084 { 1085 Reset(); 1086 } 1087 bool IsEmpty() const 1088 { 1089 if (env_ && ref_) { 1090 // possibly actually check the ref? 1091 return false; 1092 } 1093 return true; 1094 } 1095 NapiApi::Object GetObject() 1096 { 1097 if (env_ && ref_) { 1098 napi_value value; 1099 napi_get_reference_value(env_, ref_, &value); 1100 return NapiApi::Object(env_, value); 1101 } 1102 return {}; 1103 } 1104 napi_env GetEnv() 1105 { 1106 return env_; 1107 } 1108 napi_value GetValue() const 1109 { 1110 if (env_ && ref_) { 1111 napi_value value; 1112 napi_get_reference_value(env_, ref_, &value); 1113 return value; 1114 } 1115 return {}; 1116 } 1117 void Reset() 1118 { 1119 if (env_ && ref_) { 1120 napi_delete_reference(env_, ref_); 1121 } 1122 env_ = nullptr; 1123 ref_ = nullptr; 1124 } 1125 1126 private: 1127 napi_env env_ { nullptr }; 1128 napi_ref ref_ { nullptr }; 1129 }; 1130 1131 class StrongRef { 1132 public: 1133 StrongRef() = default; 1134 uint32_t GetRefCount() 1135 { 1136 // this is racy, but for debug use "fine" 1137 if (!IsEmpty()) { 1138 uint32_t cnt; 1139 napi_reference_ref(env_, ref_, &cnt); 1140 napi_reference_unref(env_, ref_, &cnt); 1141 return cnt; 1142 } 1143 return 0; 1144 } 1145 StrongRef(NapiApi::StrongRef&& ref) noexcept 1146 { 1147 if (!ref.IsEmpty()) { 1148 env_ = ref.env_; 1149 ref_ = ref.ref_; 1150 ref.env_ = nullptr; 1151 ref.ref_ = nullptr; 1152 } 1153 } 1154 StrongRef(const NapiApi::StrongRef& ref) 1155 { 1156 if (!ref.IsEmpty()) { 1157 env_ = ref.env_; 1158 ref_ = ref.ref_; 1159 Ref(); 1160 } 1161 } 1162 NapiApi::StrongRef& operator=(NapiApi::StrongRef&& ref) noexcept 1163 { 1164 if (&ref != this) { 1165 Reset(); 1166 env_ = ref.env_; 1167 ref_ = ref.ref_; 1168 ref.env_ = nullptr; 1169 ref.ref_ = nullptr; 1170 } 1171 return *this; 1172 } 1173 NapiApi::StrongRef& operator=(const NapiApi::StrongRef& ref) 1174 { 1175 if (&ref != this) { 1176 Reset(); 1177 if (!ref.IsEmpty()) { 1178 env_ = ref.env_; 1179 ref_ = ref.ref_; 1180 Ref(); 1181 } 1182 } 1183 return *this; 1184 } 1185 1186 StrongRef(napi_env env, napi_ref ref) 1187 { 1188 if (env && ref) { 1189 env_ = env; 1190 ref_ = ref; 1191 Ref(); 1192 } 1193 } 1194 1195 explicit StrongRef(const NapiApi::Object obj) 1196 { 1197 if (obj) { 1198 env_ = obj.GetEnv(); 1199 napi_create_reference(env_, obj.ToNapiValue(), 1, &ref_); 1200 } 1201 } 1202 explicit StrongRef(const NapiApi::Value<NapiApi::Object> objValue) 1203 : StrongRef(objValue.GetEnv(), objValue.ToNapiValue()) 1204 {} 1205 1206 StrongRef(napi_env env, napi_value obj) 1207 { 1208 if (env && obj) { 1209 env_ = env; 1210 napi_create_reference(env_, obj, 1, &ref_); 1211 } 1212 } 1213 ~StrongRef() 1214 { 1215 if (!IsEmpty()) { 1216 Reset(); 1217 } 1218 } 1219 bool IsEmpty() const 1220 { 1221 return !(env_ && ref_); 1222 } 1223 NapiApi::Object GetObject() const 1224 { 1225 return NapiApi::Object(env_, (napi_value)GetValue()); 1226 } 1227 napi_env GetEnv() const 1228 { 1229 return env_; 1230 } 1231 napi_value GetValue() const 1232 { 1233 if (IsEmpty()) { 1234 return {}; 1235 } 1236 napi_value value; 1237 napi_get_reference_value(env_, ref_, &value); 1238 return value; 1239 } 1240 void Reset() 1241 { 1242 if (IsEmpty()) { 1243 return; 1244 } 1245 uint32_t cnt; 1246 napi_status stat = napi_reference_unref(env_, ref_, &cnt); 1247 if (stat != napi_ok) { 1248 return; 1249 } 1250 if (cnt == 0) { 1251 // that was the last reference. 1252 napi_delete_reference(env_, ref_); 1253 } 1254 env_ = nullptr; 1255 ref_ = nullptr; 1256 } 1257 1258 private: 1259 napi_env env_ { nullptr }; 1260 napi_ref ref_ { nullptr }; 1261 void Ref() 1262 { 1263 napi_status stat; 1264 uint32_t cnt; 1265 stat = napi_reference_ref(env_, ref_, &cnt); 1266 } 1267 }; 1268 1269 template<typename T> 1270 void Value<T>::Init(napi_env env, Type v) 1271 { 1272 if (env == nullptr) { 1273 return; 1274 } 1275 env_ = env; 1276 if constexpr (BASE_NS::is_same_v<Type, float>) { 1277 napi_create_double(env_, v, &value_); 1278 } 1279 if constexpr (BASE_NS::is_same_v<Type, double>) { 1280 napi_create_double(env_, v, &value_); 1281 } 1282 if constexpr (BASE_NS::is_same_v<Type, uint32_t>) { 1283 napi_create_uint32(env_, v, &value_); 1284 } 1285 if constexpr (BASE_NS::is_same_v<Type, int32_t>) { 1286 napi_create_int32(env_, v, &value_); 1287 } 1288 if constexpr (BASE_NS::is_same_v<Type, int64_t>) { 1289 napi_create_int64(env_, v, &value_); 1290 } 1291 if constexpr (BASE_NS::is_same_v<Type, uint64_t>) { 1292 int64_t tmp = static_cast<int64_t>(v); 1293 napi_create_int64(env_, tmp, &value_); 1294 } 1295 if constexpr (BASE_NS::is_same_v<Type, NapiApi::Object>) { 1296 value_ = v.ToNapiValue(); 1297 } 1298 } 1299 1300 } // namespace NapiApi 1301 1302 NapiApi::Object FetchJsObj(const META_NS::IObject::Ptr& obj, BASE_NS::string_view name = "_JSW"); 1303 template<typename t> 1304 NapiApi::Object FetchJsObj(const t& obj, BASE_NS::string_view name = "_JSW") 1305 { 1306 return FetchJsObj(interface_pointer_cast<META_NS::IObject>(obj)); 1307 } 1308 // creates a new reference to jsobj. returns napi_value from reference. 1309 NapiApi::Object StoreJsObj(const META_NS::IObject::Ptr& obj, NapiApi::Object jsobj, BASE_NS::string_view name = "_JSW"); 1310 NapiApi::Function GetJSConstructor(napi_env env, const BASE_NS::string_view jsName); 1311 1312 // extracts the uri from "string" or "Resource" 1313 BASE_NS::string FetchResourceOrUri(napi_env e, napi_value arg); 1314 BASE_NS::string FetchResourceOrUri(NapiApi::FunctionContext<>& ctx); 1315 1316 // Instance data 1317 napi_status SetInstanceData(napi_env env, void* data, napi_finalize finalizeCb, void* finalizeHint); 1318 napi_status GetInstanceData(napi_env env, void** data); 1319 1320 // little helper macros 1321 1322 // declare NAPI_API_JS_NAME ... 1323 #define NAPI_API_xs(s) NAPI_API_s(s) 1324 #define NAPI_API_s(s) #s 1325 #define NAPI_API_xcn(s) NAPI_API_cn(s) 1326 #define NAPI_API_cn(s) s##JS 1327 #define NAPI_API_JS_NAME_STRING NAPI_API_xs(NAPI_API_JS_NAME) 1328 #define NAPI_API_CLASS_NAME NAPI_API_xcn(NAPI_API_JS_NAME) 1329 1330 #define DeclareGet(type, name, getter) \ 1331 node_props.push_back(NapiApi::GetProperty<type, NAPI_API_CLASS_NAME, &NAPI_API_CLASS_NAME::getter>(name)); 1332 #define DeclareSet(type, name, setter) \ 1333 node_props.push_back(NapiApi::SetProperty<type, NAPI_API_CLASS_NAME, &NAPI_API_CLASS_NAME::setter>(name)); 1334 #define DeclareGetSet(type, name, getter, setter) \ 1335 node_props.push_back(NapiApi::GetSetProperty<type, NAPI_API_CLASS_NAME, &NAPI_API_CLASS_NAME::getter, \ 1336 &NAPI_API_CLASS_NAME::setter>(name)); 1337 #define DeclareMethod(name, function, ...) \ 1338 node_props.push_back( \ 1339 NapiApi::Method<NapiApi::FunctionContext<__VA_ARGS__>, NAPI_API_CLASS_NAME, &NAPI_API_CLASS_NAME::function>( \ 1340 name)); 1341 #define DeclareClass() \ 1342 { \ 1343 napi_value func; \ 1344 auto status = napi_define_class(env, NAPI_API_JS_NAME_STRING, NAPI_AUTO_LENGTH, \ 1345 BaseObject::ctor<NAPI_API_CLASS_NAME>(), nullptr, node_props.size(), node_props.data(), &func); \ 1346 NapiApi::MyInstanceState* mis; \ 1347 GetInstanceData(env, (void**)&mis); \ 1348 mis->StoreCtor(NAPI_API_JS_NAME_STRING, func); \ 1349 } 1350 1351 #endif 1352