• 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 
16 #include "PropertyProxy.h"
17 
18 #include <meta/api/make_callback.h>
19 #include <meta/api/util.h>
20 #include <meta/interface/intf_metadata.h>
21 #include <meta/interface/intf_task_queue_registry.h>
22 #include <napi_api.h>
23 #include <scene/ext/intf_internal_scene.h>
24 
25 #include "BaseObjectJS.h"
26 #include "Vec2Proxy.h"
27 #include "Vec3Proxy.h"
28 #include "Vec4Proxy.h"
29 
~PropertyProxy()30 PropertyProxy::~PropertyProxy()
31 {
32     // should be a no-op.
33     RemoveHandlers();
34 }
35 
UpdateLocal()36 void PropertyProxy::UpdateLocal()
37 {
38     // should execute in engine thread.
39     Lock();
40     if (prop_) {
41         UpdateLocalValues();
42     }
43     Unlock();
44 }
UpdateRemote()45 int32_t PropertyProxy::UpdateRemote()
46 {
47     // executed in engine thread (ie. happens between frames)
48     Lock();
49     if (prop_) {
50         // make sure the handler is not called..
51         auto token = changeToken_;
52         if (token) {
53             prop_->OnChanged()->RemoveHandler(token);
54         }
55 
56         UpdateRemoteValues();
57 
58         if (auto ext = interface_pointer_cast<SCENE_NS::IInternalScene>(GetExtra())) {
59             ext->SyncProperty(prop_, META_NS::EngineSyncDirection::AUTO);
60         }
61 
62         // add it back.
63         if (token) {
64             changeToken_ = prop_->OnChanged()->AddHandler(changeHandler_);
65         }
66     }
67     updateToken_ = nullptr;
68     Unlock();
69     return 0;
70 }
71 
ScheduleUpdate()72 void PropertyProxy::ScheduleUpdate()
73 {
74     // create a task to engine queue to sync the property.
75     if (updateToken_ == nullptr) {
76         // sync not queued, so queue sync.
77         updateToken_ = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddTask(updateTask_);
78     }
79 }
80 
PropertyProxy(META_NS::IProperty::Ptr prop)81 PropertyProxy::PropertyProxy(META_NS::IProperty::Ptr prop) : prop_(prop)
82 {
83     assert(prop_);
84     changeHandler_ = META_NS::MakeCallback<META_NS::IOnChanged>(this, &PropertyProxy::UpdateLocal);
85     updateTask_ = META_NS::MakeCallback<META_NS::ITaskQueueTask>(this, &PropertyProxy::UpdateRemote);
86     changeToken_ = prop_->OnChanged()->AddHandler(changeHandler_);
87 }
88 
SetExtra(const BASE_NS::shared_ptr<CORE_NS::IInterface> extra)89 void PropertyProxy::SetExtra(const BASE_NS::shared_ptr<CORE_NS::IInterface> extra)
90 {
91     extra_ = extra;
92 }
93 
GetExtra() const94 const BASE_NS::shared_ptr<CORE_NS::IInterface> PropertyProxy::GetExtra() const
95 {
96     return extra_.lock();
97 }
98 
RemoveHandlers()99 void PropertyProxy::RemoveHandlers()
100 {
101     if (!changeHandler_) {
102         return;
103     }
104     ExecSyncTask([this]() {
105         if (updateToken_) {
106             META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->CancelTask(updateToken_);
107             updateToken_ = nullptr;
108         }
109         if (prop_ && changeToken_) {
110             prop_->OnChanged()->RemoveHandler(changeToken_);
111         }
112         changeToken_ = {};
113         prop_.reset();
114         changeHandler_.reset();
115         updateTask_.reset();
116         return META_NS::IAny::Ptr {};
117     });
118 }
SyncGet()119 void PropertyProxy::SyncGet()
120 {
121     // initialize current values.
122     ExecSyncTask([this]() {
123         // executed in engine thread
124         UpdateLocal();
125         return META_NS::IAny::Ptr {};
126     });
127 }
Lock()128 void PropertyProxy::Lock()
129 {
130     lock_.Lock();
131 }
Unlock()132 void PropertyProxy::Unlock()
133 {
134     lock_.Unlock();
135 }
136 
MemberProxy(ObjectPropertyProxy * p,BASE_NS::string m)137 ObjectPropertyProxy::MemberProxy::MemberProxy(ObjectPropertyProxy* p, BASE_NS::string m) : proxy_(p), memb_(m) {}
~MemberProxy()138 ObjectPropertyProxy::MemberProxy::~MemberProxy() {}
Name() const139 const BASE_NS::string_view ObjectPropertyProxy::MemberProxy::Name() const
140 {
141     return memb_;
142 }
143 
Getter(napi_env e,napi_callback_info i)144 napi_value ObjectPropertyProxy::MemberProxy::Getter(napi_env e, napi_callback_info i)
145 {
146     NapiApi::FunctionContext<> info(e, i);
147     auto pc = static_cast<ObjectPropertyProxy::MemberProxy*>(info.GetData());
148     if ((pc) && (pc->proxy_)) {
149         return pc->proxy_->GetMemberValue(info.Env(), pc->memb_);
150     }
151     return info.GetUndefined();
152 }
Setter(napi_env e,napi_callback_info i)153 napi_value ObjectPropertyProxy::MemberProxy::Setter(napi_env e, napi_callback_info i)
154 {
155     NapiApi::FunctionContext<> info(e, i);
156     auto pc = static_cast<ObjectPropertyProxy::MemberProxy*>(info.GetData());
157     if ((pc) && (pc->proxy_)) {
158         pc->proxy_->SetMemberValue(info, pc->memb_);
159     }
160     return info.GetUndefined();
161 }
162 
Create(napi_env env,const BASE_NS::string jsName)163 void ObjectPropertyProxy::Create(napi_env env, const BASE_NS::string jsName)
164 {
165     if (jsName.empty()) {
166         obj_ = NapiApi::StrongRef { NapiApi::Object(env) };
167     } else {
168         NapiApi::MyInstanceState* mis;
169         GetInstanceData(env, reinterpret_cast<void**>(&mis));
170         auto ref = NapiApi::Object(env, mis->getRef());
171         auto cl = ref.Get(jsName.c_str());
172         if (cl) {
173             napi_value value;
174             napi_new_instance(env, cl, 0, nullptr, &value);
175             obj_ = NapiApi::StrongRef { NapiApi::Object(env, value) };
176         }
177     }
178     if (obj_.IsEmpty()) {
179         CORE_LOG_F("Could not create property object for %s", jsName.c_str());
180     }
181 }
182 
Hook(const BASE_NS::string member)183 void ObjectPropertyProxy::Hook(const BASE_NS::string member)
184 {
185     if (obj_.IsEmpty()) {
186         return;
187     }
188     auto ValueObject = obj_.GetObject();
189 
190     auto* accessor = new MemberProxy(this, member);
191     accessors_.push_back(BASE_NS::unique_ptr<MemberProxy>(accessor));
192     ValueObject.AddProperty({ accessor->Name().data(), nullptr, nullptr, MemberProxy::Getter, MemberProxy::Setter,
193         nullptr, napi_default_jsproperty, static_cast<void*>(accessor) });
194 }
195 
Reset()196 void ObjectPropertyProxy::Reset()
197 {
198     napi_status status;
199     if (obj_.IsEmpty()) {
200         return;
201     }
202 
203     RemoveHandlers();
204 
205     NapiApi::Env env(obj_.GetEnv());
206     // unhook all hooked members.
207     auto ValueObject = obj_.GetObject();
208     for (; !accessors_.empty();) {
209         auto it(accessors_.begin());
210         // remove the property
211         ValueObject.DeleteProperty((*it)->Name());
212         // delete accessor
213         accessors_.erase(it);
214     }
215     bool exception = false;
216     status = napi_is_exception_pending(env, &exception);
217     if (exception) {
218         // if it's set, just clear it.
219         // interestingly even though the napi_*_property might have returned napi_pending_exception (or other error
220         // state) it seems that napi_is_exception_pending WILL return false though.
221         napi_value res;
222         status = napi_get_and_clear_last_exception(env, &res);
223     }
224 }
225 
Destroy()226 void ObjectPropertyProxy::Destroy()
227 {
228     Reset();
229     // release ref.
230     obj_.Reset();
231 }
232 
Value()233 napi_value ObjectPropertyProxy::Value()
234 {
235     return obj_.GetValue();
236 }
237 
SetValue(NapiApi::FunctionContext<> & info)238 void ObjectPropertyProxy::SetValue(NapiApi::FunctionContext<>& info)
239 {
240     NapiApi::FunctionContext<NapiApi::Object> data { info };
241     if (data) {
242         SetValue(data.Arg<0>());
243     }
244 }
245 
ObjectPropertyProxy(META_NS::IProperty::Ptr prop)246 ObjectPropertyProxy::ObjectPropertyProxy(META_NS::IProperty::Ptr prop) : PropertyProxy(prop) {}
247 
~ObjectPropertyProxy()248 ObjectPropertyProxy::~ObjectPropertyProxy()
249 {
250     Reset();
251 }
Reset()252 void BitmapProxy::Reset()
253 {
254     Lock();
255     if (!prop_) {
256         Unlock();
257         return;
258     }
259     BASE_NS::string name = prop_->GetName();
260     RemoveHandlers();
261 
262     if (!obj_.IsEmpty()) {
263         obj_.GetObject().DeleteProperty(name);
264     }
265     obj_.Reset();
266     scene_.Reset();
267     Unlock();
268 }
269 
BitmapProxy(NapiApi::Object scn,NapiApi::Object obj,META_NS::Property<SCENE_NS::IBitmap::Ptr> prop)270 BitmapProxy::BitmapProxy(NapiApi::Object scn, NapiApi::Object obj, META_NS::Property<SCENE_NS::IBitmap::Ptr> prop)
271     : PropertyProxy(prop)
272 {
273     scene_ = scn;
274     obj_ = obj;
275     SyncGet();
276 }
~BitmapProxy()277 BitmapProxy::~BitmapProxy()
278 {
279     // Unhook the objects.
280     Reset();
281 }
282 
UpdateLocalValues()283 void BitmapProxy::UpdateLocalValues()
284 {
285     // executed in engine thread (locks handled outside)
286     if (prop_) {
287         value = META_NS::Property<SCENE_NS::IBitmap::Ptr>(prop_)->GetValue();
288     }
289 }
UpdateRemoteValues()290 void BitmapProxy::UpdateRemoteValues()
291 {
292     // executed in engine thread (locks handled outside)
293     if (prop_) {
294         META_NS::Property<SCENE_NS::IBitmap::Ptr>(prop_)->SetValue(value);
295     }
296 }
SetValue(const SCENE_NS::IBitmap::Ptr v)297 void BitmapProxy::SetValue(const SCENE_NS::IBitmap::Ptr v)
298 {
299     Lock();
300     if (value != v) {
301         value = v;
302         ScheduleUpdate();
303     }
304     Unlock();
305 }
Value()306 napi_value BitmapProxy::Value()
307 {
308     // should be executed in the javascript thread.
309     Lock();
310     SCENE_NS::IBitmap::Ptr res = value;
311     Unlock();
312     if (auto cached = FetchJsObj(res)) {
313         return cached.ToNapiValue();
314     }
315     NapiApi::Env env(obj_.GetEnv());
316 
317     if (res) {
318         NapiApi::Object parms(env);
319         napi_value args[] = {
320             scene_.GetValue(),
321             parms.ToNapiValue()
322         };
323         MakeNativeObjectParam(env, res, BASE_NS::countof(args), args);
324 
325         auto size = res->Size()->GetValue();
326         BASE_NS::string uri;
327         if (auto m = interface_cast<META_NS::IMetadata>(res)) {
328             if (auto p = m->GetProperty<BASE_NS::string>("Uri")) {
329                 uri = p->GetValue();
330             }
331         }
332         auto name = interface_cast<META_NS::IObject>(res)->GetName();
333         parms.Set("uri", uri);
334         NapiApi::Object imageJS(GetJSConstructor(env, "Image"), BASE_NS::countof(args), args);
335         return imageJS.ToNapiValue();
336     }
337     return env.GetNull();
338 }
SetValue(NapiApi::FunctionContext<> & info)339 void BitmapProxy::SetValue(NapiApi::FunctionContext<>& info)
340 {
341     NapiApi::FunctionContext<NapiApi::Object> data { info };
342     if (data) {
343         NapiApi::Object val = data.Arg<0>();
344         auto bitmap = GetNativeMeta<SCENE_NS::IBitmap>(val);
345         Lock();
346         value = bitmap;
347         ScheduleUpdate();
348         Unlock();
349     }
350 }
351 
PropertyToProxy(NapiApi::Object scene,NapiApi::Object obj,const META_NS::IProperty::Ptr & t)352 BASE_NS::shared_ptr<PropertyProxy> PropertyToProxy(
353     NapiApi::Object scene, NapiApi::Object obj, const META_NS::IProperty::Ptr& t)
354 {
355     if (META_NS::IsCompatibleWith<float>(t)) {
356         return BASE_NS::shared_ptr { new TypeProxy<float>(obj, t) };
357     }
358     if (META_NS::IsCompatibleWith<int32_t>(t)) {
359         return BASE_NS::shared_ptr { new TypeProxy<int32_t>(obj, t) };
360     }
361     if (META_NS::IsCompatibleWith<uint32_t>(t)) {
362         return BASE_NS::shared_ptr { new TypeProxy<uint32_t>(obj, t) };
363     }
364     if (META_NS::IsCompatibleWith<int64_t>(t)) {
365         return BASE_NS::shared_ptr { new TypeProxy<int64_t>(obj, t) };
366     }
367     if (META_NS::IsCompatibleWith<uint64_t>(t)) {
368         return BASE_NS::shared_ptr { new TypeProxy<uint64_t>(obj, t) };
369     }
370     if (META_NS::IsCompatibleWith<BASE_NS::Math::Vec2>(t)) {
371         return BASE_NS::shared_ptr { new Vec2Proxy(obj.GetEnv(), t) };
372     }
373     if (META_NS::IsCompatibleWith<BASE_NS::Math::Vec3>(t)) {
374         return BASE_NS::shared_ptr { new Vec3Proxy(obj.GetEnv(), t) };
375     }
376     if (META_NS::IsCompatibleWith<BASE_NS::Math::Vec4>(t)) {
377         return BASE_NS::shared_ptr { new Vec4Proxy(obj.GetEnv(), t) };
378     }
379     if (META_NS::IsCompatibleWith<SCENE_NS::IBitmap::Ptr>(t)) {
380         return BASE_NS::shared_ptr { new BitmapProxy(scene, obj, t) };
381     }
382     auto any = META_NS::GetInternalAny(t);
383     LOG_F("Unsupported property type [%s]", any ? any->GetTypeIdString().c_str() : "<Unknown>");
384     return nullptr;
385 }
386 
DummyFunc(napi_env e,napi_callback_info i)387 static napi_value DummyFunc(napi_env e, napi_callback_info i)
388 {
389     NapiApi::FunctionContext<> info(e, i);
390     return info.GetUndefined();
391 };
PropProxGet(napi_env e,napi_callback_info i)392 static napi_value PropProxGet(napi_env e, napi_callback_info i)
393 {
394     NapiApi::FunctionContext<> info(e, i);
395     auto pc = static_cast<PropertyProxy*>(info.GetData());
396     if (pc) {
397         return pc->Value();
398     }
399     return info.GetUndefined();
400 };
PropProxSet(napi_env e,napi_callback_info i)401 static napi_value PropProxSet(napi_env e, napi_callback_info i)
402 {
403     NapiApi::FunctionContext<>info (e, i);
404     auto pc = static_cast<PropertyProxy*>(info.GetData());
405     if (pc) {
406         pc->SetValue(info);
407     }
408     return info.GetUndefined();
409 };
CreateProxyDesc(const char * name,BASE_NS::shared_ptr<PropertyProxy> proxy)410 napi_property_descriptor CreateProxyDesc(const char* name, BASE_NS::shared_ptr<PropertyProxy> proxy)
411 {
412     napi_property_descriptor desc { name, nullptr, nullptr, nullptr, nullptr, nullptr, napi_default_jsproperty,
413         static_cast<void*>(proxy.get()) };
414     if (proxy) {
415         desc.getter = PropProxGet;
416         desc.setter = PropProxSet;
417     } else {
418         desc.getter = desc.setter = DummyFunc;
419     }
420     return desc;
421 }
422