/*
* Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PropertyProxy.h"
#include
#include
#include
#include "BaseObjectJS.h"
class PropCtx {
PropertyProxy* proxy_;
BASE_NS::string memb_;
public:
PropCtx(PropertyProxy* p, BASE_NS::string m);
~PropCtx();
napi_value GetValue(NapiApi::FunctionContext<>& info);
void SetValue(NapiApi::FunctionContext<>& info);
};
PropCtx::PropCtx(PropertyProxy* p, BASE_NS::string m)
{
proxy_ = p;
memb_ = m;
}
PropCtx::~PropCtx()
{
}
napi_value PropCtx::GetValue(NapiApi::FunctionContext<>& info)
{
return proxy_->GetValue(info, memb_);
}
void PropCtx::SetValue(NapiApi::FunctionContext<>& info)
{
proxy_->SetValue(info, memb_);
}
void PropertyProxy::UpdateLocal()
{
// should execute in engine thread.
duh.Lock();
UpdateLocalValues();
duh.Unlock();
}
int32_t PropertyProxy::UpdateRemote()
{
// executed in engine thread (ie. happens between frames)
duh.Lock();
// make sure the handler is not called..
prop_->OnChanged()->RemoveHandler(changeToken_);
UpdateRemoteValues();
// add it back.
changeToken_ = prop_->OnChanged()->AddHandler(changeHandler_);
updateToken_ = nullptr;
duh.Unlock();
return 0;
}
void PropertyProxy::ScheduleUpdate()
{
// create a task to engine queue to sync the property.
if (updateToken_ == nullptr) {
// sync not queued, so queue sync.
updateToken_ = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddTask(updateTask_);
}
}
PropertyProxy::PropertyProxy(META_NS::IProperty::Ptr prop) : prop_(prop)
{
changeHandler_ = META_NS::MakeCallback(this, &PropertyProxy::UpdateLocal);
changeToken_ = prop_->OnChanged()->AddHandler(changeHandler_);
updateTask_ = META_NS::MakeCallback(this, &PropertyProxy::UpdateRemote);
}
void PropertyProxy::SyncGet()
{
// initialize current values.
ExecSyncTask([this]() {
// executed in engine thread
UpdateLocal();
return META_NS::IAny::Ptr {};
});
}
PropertyProxy::~PropertyProxy()
{
if (prop_) {
prop_->OnChanged()->RemoveHandler(changeToken_);
}
for (auto t : accessors) {
delete t;
}
}
void PropertyProxy::Create(napi_env env, const BASE_NS::string jsName)
{
NapiApi::MyInstanceState* mis;
napi_get_instance_data(env, (void**)&mis);
auto ref = NapiApi::Object(env, mis->getRef());
auto cl = ref.Get(jsName.c_str());
if (cl) {
napi_value value;
napi_new_instance(env, cl, 0, nullptr, &value);
obj = NapiApi::StrongRef { NapiApi::Object(env, value) };
} else {
CORE_LOG_F("Could not create property object for %s", jsName.c_str());
}
}
void PropertyProxy::Hook(const BASE_NS::string member)
{
auto* accessor = new PropCtx(this, member.c_str());
accessors.push_back(accessor);
auto value = obj.GetValue();
napi_property_descriptor desc { member.c_str(), nullptr, nullptr,
[](napi_env e, napi_callback_info i) -> napi_value {
NapiApi::FunctionContext<> info(e, i);
auto pc = (PropCtx*)info.GetData();
return pc->GetValue(info);
},
[](napi_env e, napi_callback_info i) -> napi_value {
NapiApi::FunctionContext<> info(e, i);
auto pc = (PropCtx*)info.GetData();
pc->SetValue(info);
return {};
},
nullptr, napi_default_jsproperty, (void*)accessor };
napi_status status = napi_define_properties(obj.GetEnv(), value, 1, &desc);
}
PropertyProxy::operator napi_value()
{
return obj.GetValue();
}
NapiApi::Value PropertyProxy::Value()
{
return { obj.GetEnv(), obj.GetValue() };
}
const META_NS::IProperty::Ptr& PropertyProxy::GetProperty() const
{
return prop_;
}