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