• 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 BASE_OBJECT_JS_H
16 #define BASE_OBJECT_JS_H
17 #include <meta/api/make_callback.h>
18 #include <meta/interface/intf_object.h>
19 #include <meta/interface/intf_task_queue_registry.h>
20 #include <scene/interface/intf_scene.h>
21 #include <napi_api.h>
22 
23 // tasks execute in the engine/render thread.
24 static constexpr BASE_NS::Uid ENGINE_THREAD { "2070e705-d061-40e4-bfb7-90fad2c280af" };
25 
26 // tasks execute in the javascript mainthread. *NOT IMPLEMENTED*
27 static constexpr BASE_NS::Uid JS_THREAD { "b2e8cef3-453a-4651-b564-5190f8b5190d" };
28 
29 class TrueRootObject {
30 public:
31     // Store a reference to a native IObject to the "JSBridge" object
32     // Optionally make the reference strong so that the lifetime is controlled by JS.
33     // for example. scene is kept strongly, and objects owned by scene are weak.
34 
35     virtual void SetNativeObject(META_NS::IObject::Ptr real, bool Strong);
36     virtual META_NS::IObject::Ptr GetNativeObject();
37     virtual void* GetInstanceImpl(uint32_t id) = 0;
38 
39     virtual void Finalize(napi_env env);
40     virtual void DisposeNative(void*) = 0;
41 
42     virtual bool IsStrong() const;
43 
44 protected:
45     TrueRootObject();
46     virtual ~TrueRootObject() = default;
47     static void destroy(TrueRootObject* object);
48 
49 private:
50     META_NS::IObject::Ptr obj_;
51     META_NS::IObject::WeakPtr objW_;
52 };
53 
54 template<class FinalObject>
55 class BaseObject : public TrueRootObject {
56 protected:
57     bool disposed_ { false };
~BaseObject()58     virtual ~BaseObject() {}
BaseObject(napi_env env,napi_callback_info info)59     BaseObject(napi_env env, napi_callback_info info) : TrueRootObject()
60     {
61         napi_value thisVar = nullptr;
62         napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
63         auto DTOR = [](napi_env env, void* nativeObject, void* finalize) {
64             TrueRootObject* ptr = static_cast<TrueRootObject*>(nativeObject);
65             ptr->Finalize(env);
66             TrueRootObject::destroy(ptr);
67         };
68         napi_wrap(env, thisVar, reinterpret_cast<void*>((TrueRootObject*)this), DTOR, nullptr, nullptr);
69     }
70     template<typename Object>
ctor()71     static inline napi_callback ctor()
72     {
73         napi_callback ctor = [](napi_env env, napi_callback_info info) -> napi_value {
74             napi_value thisVar = nullptr;
75             // fetch the "this" from javascript.
76             napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
77             // The BaseObject constructor actually handles wrapping..
78             auto r = BASE_NS::make_unique<Object>(env, info);
79             r.release();
80             return thisVar;
81         };
82         return ctor;
83     }
84 };
85 
GetRootObject(NapiApi::Object o)86 inline TrueRootObject* GetRootObject(NapiApi::Object o)
87 {
88     TrueRootObject* p { nullptr };
89     if (o) {
90         napi_unwrap(o.GetEnv(), o.ToNapiValue(), (void**)&p);
91     }
92     return p;
93 }
94 
95 template<typename... types>
GetThisRootObject(NapiApi::FunctionContext<types...> & ctx)96 inline TrueRootObject* GetThisRootObject(NapiApi::FunctionContext<types...>& ctx)
97 {
98     return GetRootObject(ctx.This());
99 }
100 
101 template<typename FC>
GetThisNativeObject(FC & ctx)102 inline auto GetThisNativeObject(FC& ctx)
103 {
104     TrueRootObject* instance = GetThisRootObject(ctx);
105     if (!instance) {
106         return META_NS::IObject::Ptr {};
107     }
108     return instance->GetNativeObject();
109 }
110 
111 template<typename Object, napi_value (Object::*F)(NapiApi::FunctionContext<>&)>
TROGetter(napi_env env,napi_callback_info info)112 static inline napi_value TROGetter(napi_env env, napi_callback_info info)
113 {
114     NapiApi::FunctionContext fc(env, info);
115     if (fc) {
116         if (TrueRootObject* instance = GetThisRootObject(fc)) {
117             if (Object* impl = (Object*)instance->GetInstanceImpl(Object::ID)) {
118                 return (impl->*F)(fc);
119             }
120         };
121     }
122     return fc.GetUndefined();
123 }
124 template<typename Type, typename Object, void (Object::*F)(NapiApi::FunctionContext<Type>&)>
TROSetter(napi_env env,napi_callback_info info)125 static inline napi_value TROSetter(napi_env env, napi_callback_info info)
126 {
127     NapiApi::FunctionContext<Type> fc(env, info);
128     if (fc) {
129         if (TrueRootObject* instance = GetThisRootObject(fc)) {
130             if (Object* impl = (Object*)instance->GetInstanceImpl(Object::ID)) {
131                 (impl->*F)(fc);
132             }
133         }
134     }
135     return fc.GetUndefined();
136 };
137 
138 template<typename Type, typename Object, void (Object::*F2)(NapiApi::FunctionContext<Type>&)>
139 static inline napi_property_descriptor TROSetProperty(
140     const char* const name, napi_property_attributes flags = napi_default_jsproperty)
141 {
142     static_assert(F2 != nullptr);
143     return napi_property_descriptor { name, nullptr, nullptr, nullptr, TROSetter<Type, Object, F2>, nullptr, flags,
144         nullptr };
145 }
146 
147 template<typename Type, typename Object, napi_value (Object::*F)(NapiApi::FunctionContext<>&)>
148 static inline napi_property_descriptor TROGetProperty(
149     const char* const name, napi_property_attributes flags = napi_default_jsproperty)
150 {
151     static_assert(F != nullptr);
152     return napi_property_descriptor { name, nullptr, nullptr, TROGetter<Object, F>, nullptr, nullptr, flags, nullptr };
153 }
154 
155 template<typename Type, typename Object, napi_value (Object::*F)(NapiApi::FunctionContext<>&),
156     void (Object::*F2)(NapiApi::FunctionContext<Type>&)>
157 static inline napi_property_descriptor TROGetSetProperty(
158     const char* const name, napi_property_attributes flags = napi_default_jsproperty)
159 {
160     static_assert(F != nullptr);
161     static_assert(F2 != nullptr);
162     return napi_property_descriptor { name, nullptr, nullptr, TROGetter<Object, F>, TROSetter<Type, Object, F2>,
163         nullptr, flags, nullptr };
164 }
165 
166 template<typename FC, typename Object, napi_value (Object::*F)(FC&)>
TROMethod(napi_env env,napi_callback_info info)167 napi_value TROMethod(napi_env env, napi_callback_info info)
168 {
169     FC fc(env, info);
170     if (fc) {
171         if (TrueRootObject* instance = GetThisRootObject(fc)) {
172             if (Object* impl = (Object*)instance->GetInstanceImpl(Object::ID)) {
173                 return (impl->*F)(fc);
174             }
175         }
176     }
177     return fc.GetUndefined();
178 }
179 
180 template<typename FC, typename Object, napi_value (Object::*F)(FC&)>
181 inline napi_property_descriptor MakeTROMethod(
182     const char* const name, napi_property_attributes flags = napi_default_method)
183 {
184     return napi_property_descriptor { name, nullptr, TROMethod<FC, Object, F>, nullptr, nullptr, nullptr, flags,
185         nullptr };
186 }
187 
188 template<typename type>
GetNativeMeta(NapiApi::Object obj)189 auto GetNativeMeta(NapiApi::Object obj)
190 {
191     if (obj) {
192         auto* tro = obj.Native<TrueRootObject>();
193         if (tro) {
194             return interface_pointer_cast<type>(tro->GetNativeObject());
195         }
196     }
197     return typename type::Ptr {};
198 }
199 
200 //  Create a javascript object that wraps specified IObject.
201 //  uses the classid of obj to create correct wrapper.
202 //  (if the wrapper already exists, returns a new reference to the wrapper)
203 NapiApi::Object CreateFromNativeInstance(napi_env env, const META_NS::IObject::Ptr& obj,
204     bool strong, uint32_t argc, napi_value* argv, BASE_NS::string_view pname = "_JSW");
205 
206 NapiApi::Object CreateJsObj(napi_env env, const BASE_NS::string_view jsName, META_NS::IObject::Ptr real, bool strong,
207     uint32_t argc, napi_value* argv);
208 
209 // check for type.
210 bool IsInstanceOf(const NapiApi::Object& obj, const BASE_NS::string_view jsName);
211 
212 // run synchronous task in specific tq.
213 template<typename func>
ExecSyncTask(const META_NS::ITaskQueue::Ptr tq,func && fun)214 META_NS::IAny::Ptr ExecSyncTask(const META_NS::ITaskQueue::Ptr tq, func&& fun)
215 {
216     return tq
217         ->AddWaitableTask(BASE_NS::move(META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>(BASE_NS::move(fun))))
218         ->GetResult();
219 }
220 
221 // run task synchronously in engine thread.
222 template<typename func>
ExecSyncTask(func && fun)223 META_NS::IAny::Ptr ExecSyncTask(func&& fun)
224 {
225     return ExecSyncTask(META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD), BASE_NS::move(fun));
226 }
227 template<class type>
MakeNativeObjectParam(napi_env env,const type & obj,uint32_t argc,napi_value * argv)228 void MakeNativeObjectParam(napi_env env, const type& obj, uint32_t argc, napi_value* argv)
229 {
230     // okay.. we "know" that arg[1] should be params.. so add the native object there automatically
231     if (argc > 1) {
232         napi_value res;
233         if (auto mobj = interface_pointer_cast<META_NS::IObject>(obj)) {
234             META_NS::IObject::WeakPtr* data = new META_NS::IObject::WeakPtr();
235             *data = mobj;
236             napi_create_external(
237                 env, (void*)data,
238                 [](napi_env env, void* data, void* finalize_hint) { delete (META_NS::IObject::WeakPtr*)data; }, nullptr,
239                 &res);
240             NapiApi::Object arg(env, argv[1]);
241             arg.Set("NativeObject", res);
242         }
243     }
244 }
245 template<class type>
GetNativeObjectParam(NapiApi::Object args)246 auto GetNativeObjectParam(NapiApi::Object args)
247 {
248     typename type::Ptr ret;
249     if (auto prm = args.Get("NativeObject")) {
250         META_NS::IObject::WeakPtr* ptr { nullptr };
251         napi_get_value_external(args.GetEnv(), prm, (void**)&ptr);
252         if (ptr) {
253             ret = interface_pointer_cast<type>(*ptr);
254         }
255         napi_value null;
256         napi_get_null(args.GetEnv(), &null);
257         args.Set("NativeObject", null); // hope to release it now.
258     }
259     return ret;
260 }
261 void DebugNativesHavingJS(void);
262 
263 template<class T>
GetJsWrapper(NapiApi::Object o)264 inline T* GetJsWrapper(NapiApi::Object o)
265 {
266     if (auto* tro = o.Native<TrueRootObject>()) {
267         return static_cast<T*>(tro->GetInstanceImpl(T::ID));
268     }
269     return nullptr;
270 }
271 #endif
272