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