• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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