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