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/interface/intf_task_queue_registry.h>
20 #include <napi_api.h>
21 #include "BaseObjectJS.h"
22
23 class PropCtx {
24 PropertyProxy* proxy_;
25 BASE_NS::string memb_;
26
27 public:
28 PropCtx(PropertyProxy* p, BASE_NS::string m);
29 ~PropCtx();
30 napi_value GetValue(NapiApi::FunctionContext<>& info);
31 void SetValue(NapiApi::FunctionContext<>& info);
32 };
33
PropCtx(PropertyProxy * p,BASE_NS::string m)34 PropCtx::PropCtx(PropertyProxy* p, BASE_NS::string m)
35 {
36 proxy_ = p;
37 memb_ = m;
38 }
~PropCtx()39 PropCtx::~PropCtx()
40 {
41 }
GetValue(NapiApi::FunctionContext<> & info)42 napi_value PropCtx::GetValue(NapiApi::FunctionContext<>& info)
43 {
44 return proxy_->GetValue(info, memb_);
45 }
SetValue(NapiApi::FunctionContext<> & info)46 void PropCtx::SetValue(NapiApi::FunctionContext<>& info)
47 {
48 proxy_->SetValue(info, memb_);
49 }
50
UpdateLocal()51 void PropertyProxy::UpdateLocal()
52 {
53 // should execute in engine thread.
54 duh.Lock();
55 UpdateLocalValues();
56 duh.Unlock();
57 }
UpdateRemote()58 int32_t PropertyProxy::UpdateRemote()
59 {
60 // executed in engine thread (ie. happens between frames)
61 duh.Lock();
62 // make sure the handler is not called..
63 prop_->OnChanged()->RemoveHandler(changeToken_);
64
65 UpdateRemoteValues();
66
67 // add it back.
68 changeToken_ = prop_->OnChanged()->AddHandler(changeHandler_);
69 updateToken_ = nullptr;
70 duh.Unlock();
71
72 return 0;
73 }
74
ScheduleUpdate()75 void PropertyProxy::ScheduleUpdate()
76 {
77 // create a task to engine queue to sync the property.
78 if (updateToken_ == nullptr) {
79 // sync not queued, so queue sync.
80 updateToken_ = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddTask(updateTask_);
81 }
82 }
83
PropertyProxy(META_NS::IProperty::Ptr prop)84 PropertyProxy::PropertyProxy(META_NS::IProperty::Ptr prop) : prop_(prop)
85 {
86 changeHandler_ = META_NS::MakeCallback<META_NS::IOnChanged>(this, &PropertyProxy::UpdateLocal);
87 changeToken_ = prop_->OnChanged()->AddHandler(changeHandler_);
88 updateTask_ = META_NS::MakeCallback<META_NS::ITaskQueueTask>(this, &PropertyProxy::UpdateRemote);
89 }
SyncGet()90 void PropertyProxy::SyncGet()
91 {
92 // initialize current values.
93 ExecSyncTask([this]() {
94 // executed in engine thread
95 UpdateLocal();
96 return META_NS::IAny::Ptr {};
97 });
98 }
~PropertyProxy()99 PropertyProxy::~PropertyProxy()
100 {
101 if (prop_) {
102 prop_->OnChanged()->RemoveHandler(changeToken_);
103 }
104
105 for (auto t : accessors) {
106 delete t;
107 }
108 }
109
Create(napi_env env,const BASE_NS::string jsName)110 void PropertyProxy::Create(napi_env env, const BASE_NS::string jsName)
111 {
112 NapiApi::MyInstanceState* mis;
113 napi_get_instance_data(env, (void**)&mis);
114 auto ref = NapiApi::Object(env, mis->getRef());
115 auto cl = ref.Get(jsName.c_str());
116 if (cl) {
117 napi_value value;
118 napi_new_instance(env, cl, 0, nullptr, &value);
119 obj = NapiApi::StrongRef { NapiApi::Object(env, value) };
120 } else {
121 CORE_LOG_F("Could not create property object for %s", jsName.c_str());
122 }
123 }
124
Hook(const BASE_NS::string member)125 void PropertyProxy::Hook(const BASE_NS::string member)
126 {
127 auto* accessor = new PropCtx(this, member.c_str());
128 accessors.push_back(accessor);
129 auto value = obj.GetValue();
130
131 napi_property_descriptor desc { member.c_str(), nullptr, nullptr,
132
133 [](napi_env e, napi_callback_info i) -> napi_value {
134 NapiApi::FunctionContext<> info(e, i);
135 auto pc = (PropCtx*)info.GetData();
136 return pc->GetValue(info);
137 },
138 [](napi_env e, napi_callback_info i) -> napi_value {
139 NapiApi::FunctionContext<> info(e, i);
140 auto pc = (PropCtx*)info.GetData();
141 pc->SetValue(info);
142 return {};
143 },
144 nullptr, napi_default_jsproperty, (void*)accessor };
145 napi_status status = napi_define_properties(obj.GetEnv(), value, 1, &desc);
146 }
147 PropertyProxy::operator napi_value()
148 {
149 return obj.GetValue();
150 }
151
Value()152 NapiApi::Value<NapiApi::Object> PropertyProxy::Value()
153 {
154 return { obj.GetEnv(), obj.GetValue() };
155 }
156
GetProperty() const157 const META_NS::IProperty::Ptr& PropertyProxy::GetProperty() const
158 {
159 return prop_;
160 }
161