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