18bf80f4bSopenharmony_ci/* 28bf80f4bSopenharmony_ci * Copyright (C) 2024 Huawei Device Co., Ltd. 38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License. 58bf80f4bSopenharmony_ci * You may obtain a copy of the License at 68bf80f4bSopenharmony_ci * 78bf80f4bSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 88bf80f4bSopenharmony_ci * 98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and 138bf80f4bSopenharmony_ci * limitations under the License. 148bf80f4bSopenharmony_ci */ 158bf80f4bSopenharmony_ci#include "BaseObjectJS.h" 168bf80f4bSopenharmony_ci 178bf80f4bSopenharmony_ci#include <meta/interface/intf_metadata.h> 188bf80f4bSopenharmony_ci#include <meta/interface/property/construct_property.h> 198bf80f4bSopenharmony_ci#include <scene_plugin/interface/intf_mesh.h> 208bf80f4bSopenharmony_ci#include <scene_plugin/interface/intf_nodes.h> 218bf80f4bSopenharmony_ci 228bf80f4bSopenharmony_ci// this class is used to store a reference to a JS object in the metaobject. 238bf80f4bSopenharmony_ciclass JSWrapperState : public CORE_NS::IInterface { 248bf80f4bSopenharmony_cipublic: 258bf80f4bSopenharmony_ci static constexpr BASE_NS::Uid UID { "2ef39765-91f2-46c4-b85f-7cad40dd3bcd" }; 268bf80f4bSopenharmony_ci const IInterface* GetInterface(const BASE_NS::Uid& uid) const override; 278bf80f4bSopenharmony_ci IInterface* GetInterface(const BASE_NS::Uid& uid) override; 288bf80f4bSopenharmony_ci void Ref() override; 298bf80f4bSopenharmony_ci void Unref() override; 308bf80f4bSopenharmony_ci JSWrapperState(NapiApi::Object obj, BASE_NS::string_view name); 318bf80f4bSopenharmony_ci ~JSWrapperState(); 328bf80f4bSopenharmony_ci NapiApi::Object GetObject(); 338bf80f4bSopenharmony_ci 348bf80f4bSopenharmony_ciprivate: 358bf80f4bSopenharmony_ci BASE_NS::string name_; 368bf80f4bSopenharmony_ci volatile int32_t count_ { 0 }; 378bf80f4bSopenharmony_ci napi_env env_; 388bf80f4bSopenharmony_ci napi_ref ref_; 398bf80f4bSopenharmony_ci}; 408bf80f4bSopenharmony_ci 418bf80f4bSopenharmony_ciconst CORE_NS::IInterface* JSWrapperState::GetInterface(const BASE_NS::Uid& uid) const 428bf80f4bSopenharmony_ci{ 438bf80f4bSopenharmony_ci if (uid == CORE_NS::IInterface::UID) { 448bf80f4bSopenharmony_ci return this; 458bf80f4bSopenharmony_ci } 468bf80f4bSopenharmony_ci if (uid == JSWrapperState::UID) { 478bf80f4bSopenharmony_ci return this; 488bf80f4bSopenharmony_ci } 498bf80f4bSopenharmony_ci return nullptr; 508bf80f4bSopenharmony_ci} 518bf80f4bSopenharmony_ciCORE_NS::IInterface* JSWrapperState::GetInterface(const BASE_NS::Uid& uid) 528bf80f4bSopenharmony_ci{ 538bf80f4bSopenharmony_ci if (uid == CORE_NS::IInterface::UID) { 548bf80f4bSopenharmony_ci return this; 558bf80f4bSopenharmony_ci } 568bf80f4bSopenharmony_ci if (uid == JSWrapperState::UID) { 578bf80f4bSopenharmony_ci return this; 588bf80f4bSopenharmony_ci } 598bf80f4bSopenharmony_ci return nullptr; 608bf80f4bSopenharmony_ci} 618bf80f4bSopenharmony_civoid JSWrapperState::Ref() 628bf80f4bSopenharmony_ci{ 638bf80f4bSopenharmony_ci CORE_NS::AtomicIncrement(&count_); 648bf80f4bSopenharmony_ci} 658bf80f4bSopenharmony_civoid JSWrapperState::Unref() 668bf80f4bSopenharmony_ci{ 678bf80f4bSopenharmony_ci if (CORE_NS::AtomicDecrement(&count_) == 0) { 688bf80f4bSopenharmony_ci delete this; 698bf80f4bSopenharmony_ci } 708bf80f4bSopenharmony_ci} 718bf80f4bSopenharmony_ciJSWrapperState::JSWrapperState(NapiApi::Object obj, BASE_NS::string_view name) 728bf80f4bSopenharmony_ci{ 738bf80f4bSopenharmony_ci name_ = name; 748bf80f4bSopenharmony_ci LOG_F("JSWrapperState ++ %s", name_.c_str()); 758bf80f4bSopenharmony_ci env_ = obj.GetEnv(); 768bf80f4bSopenharmony_ci // Create a WEAK reference to the object 778bf80f4bSopenharmony_ci napi_create_reference(env_, obj, 0, &ref_); 788bf80f4bSopenharmony_ci} 798bf80f4bSopenharmony_ciJSWrapperState::~JSWrapperState() 808bf80f4bSopenharmony_ci{ 818bf80f4bSopenharmony_ci // release the reference. 828bf80f4bSopenharmony_ci 838bf80f4bSopenharmony_ci napi_delete_reference(env_, ref_); 848bf80f4bSopenharmony_ci LOG_F("JSWrapperState -- %s", name_.c_str()); 858bf80f4bSopenharmony_ci} 868bf80f4bSopenharmony_ciNapiApi::Object JSWrapperState::GetObject() 878bf80f4bSopenharmony_ci{ 888bf80f4bSopenharmony_ci napi_value value; 898bf80f4bSopenharmony_ci napi_get_reference_value(env_, ref_, &value); 908bf80f4bSopenharmony_ci return { env_, value }; 918bf80f4bSopenharmony_ci} 928bf80f4bSopenharmony_ci 938bf80f4bSopenharmony_ciTrueRootObject::TrueRootObject() {} 948bf80f4bSopenharmony_civoid TrueRootObject::SetNativeObject(META_NS::IObject::Ptr real, bool strong) 958bf80f4bSopenharmony_ci{ 968bf80f4bSopenharmony_ci if (strong) { 978bf80f4bSopenharmony_ci obj_ = real; 988bf80f4bSopenharmony_ci } else { 998bf80f4bSopenharmony_ci objW_ = real; 1008bf80f4bSopenharmony_ci } 1018bf80f4bSopenharmony_ci} 1028bf80f4bSopenharmony_ciMETA_NS::IObject::Ptr TrueRootObject::GetNativeObject() 1038bf80f4bSopenharmony_ci{ 1048bf80f4bSopenharmony_ci // if we have a strong ref... 1058bf80f4bSopenharmony_ci if (obj_) { 1068bf80f4bSopenharmony_ci // return that directly. 1078bf80f4bSopenharmony_ci return obj_; 1088bf80f4bSopenharmony_ci } 1098bf80f4bSopenharmony_ci // otherwise try to lock the weak reference. 1108bf80f4bSopenharmony_ci return objW_.lock(); 1118bf80f4bSopenharmony_ci} 1128bf80f4bSopenharmony_civoid TrueRootObject::Finalize(napi_env env) 1138bf80f4bSopenharmony_ci{ 1148bf80f4bSopenharmony_ci // Synchronously destroy the lume object in engine thread.. (only for strong refs.) 1158bf80f4bSopenharmony_ci if (obj_) { 1168bf80f4bSopenharmony_ci ExecSyncTask([obj = BASE_NS::move(obj_)]() { return META_NS::IAny::Ptr {}; }); 1178bf80f4bSopenharmony_ci } 1188bf80f4bSopenharmony_ci // and reset the weak ref too. (which may be null anyway) 1198bf80f4bSopenharmony_ci objW_.reset(); 1208bf80f4bSopenharmony_ci} 1218bf80f4bSopenharmony_ciNapiApi::Function GetJSConstructor(napi_env env, const BASE_NS::string_view jsName) 1228bf80f4bSopenharmony_ci{ 1238bf80f4bSopenharmony_ci NapiApi::MyInstanceState* mis; 1248bf80f4bSopenharmony_ci napi_get_instance_data(env, (void**)&mis); 1258bf80f4bSopenharmony_ci return NapiApi::Function(env, mis->FetchCtor(jsName)); 1268bf80f4bSopenharmony_ci} 1278bf80f4bSopenharmony_ciNapiApi::Object CreateJsObj(napi_env env, const BASE_NS::string_view jsName, META_NS::IObject::Ptr real, bool strong, 1288bf80f4bSopenharmony_ci uint32_t argc, napi_value* argv) 1298bf80f4bSopenharmony_ci{ 1308bf80f4bSopenharmony_ci NapiApi::Object obj(GetJSConstructor(env, jsName), argc, argv); 1318bf80f4bSopenharmony_ci if (!obj) { 1328bf80f4bSopenharmony_ci return {}; 1338bf80f4bSopenharmony_ci } 1348bf80f4bSopenharmony_ci auto oo = GetRootObject(env, obj); 1358bf80f4bSopenharmony_ci oo->SetNativeObject(real, strong); 1368bf80f4bSopenharmony_ci return obj; 1378bf80f4bSopenharmony_ci} 1388bf80f4bSopenharmony_cibool IsInstanceOf(const NapiApi::Object& obj, const BASE_NS::string_view jsName) 1398bf80f4bSopenharmony_ci{ 1408bf80f4bSopenharmony_ci auto env = obj.GetEnv(); 1418bf80f4bSopenharmony_ci auto cl = GetJSConstructor(env, jsName); 1428bf80f4bSopenharmony_ci bool result = false; 1438bf80f4bSopenharmony_ci auto status = napi_instanceof(env, obj, cl, &result); 1448bf80f4bSopenharmony_ci return result; 1458bf80f4bSopenharmony_ci} 1468bf80f4bSopenharmony_ci 1478bf80f4bSopenharmony_ciNapiApi::Object FetchJsObj(const META_NS::IObject::Ptr& obj) 1488bf80f4bSopenharmony_ci{ 1498bf80f4bSopenharmony_ci using namespace META_NS; 1508bf80f4bSopenharmony_ci 1518bf80f4bSopenharmony_ci // access hidden property. 1528bf80f4bSopenharmony_ci if (auto AppMeta = interface_pointer_cast<IMetadata>(obj)) { 1538bf80f4bSopenharmony_ci if (auto wrapper = AppMeta->GetPropertyByName<SharedPtrIInterface>("_JSW")) { 1548bf80f4bSopenharmony_ci // The native object already contains a JS object. 1558bf80f4bSopenharmony_ci return interface_cast<JSWrapperState>(wrapper->GetValue())->GetObject(); 1568bf80f4bSopenharmony_ci } 1578bf80f4bSopenharmony_ci } 1588bf80f4bSopenharmony_ci return nullptr; 1598bf80f4bSopenharmony_ci} 1608bf80f4bSopenharmony_ciNapiApi::Object StoreJsObj(const META_NS::IObject::Ptr& obj, NapiApi::Object jsobj) 1618bf80f4bSopenharmony_ci{ 1628bf80f4bSopenharmony_ci using namespace META_NS; 1638bf80f4bSopenharmony_ci if (auto AppMeta = interface_pointer_cast<IMetadata>(obj)) { 1648bf80f4bSopenharmony_ci // Add a reference to the JS object to the native object. 1658bf80f4bSopenharmony_ci auto res = BASE_NS::shared_ptr<JSWrapperState>(new JSWrapperState(jsobj, obj->GetClassName())); 1668bf80f4bSopenharmony_ci auto& obr = GetObjectRegistry(); 1678bf80f4bSopenharmony_ci auto wrapper = AppMeta->GetPropertyByName<SharedPtrIInterface>("_JSW"); 1688bf80f4bSopenharmony_ci // check if the property exists. 1698bf80f4bSopenharmony_ci if (wrapper) { 1708bf80f4bSopenharmony_ci // .. does the wrapper exist? (ie. reference from native to js) 1718bf80f4bSopenharmony_ci if (auto val = interface_cast<JSWrapperState>(wrapper->GetValue())) { 1728bf80f4bSopenharmony_ci // validate it 1738bf80f4bSopenharmony_ci auto ref = val->GetObject(); 1748bf80f4bSopenharmony_ci bool equals = false; 1758bf80f4bSopenharmony_ci if (ref) { 1768bf80f4bSopenharmony_ci // we have ref.. so make the compare here 1778bf80f4bSopenharmony_ci napi_strict_equals(jsobj.GetEnv(), jsobj, ref, &equals); 1788bf80f4bSopenharmony_ci if (!equals) { 1798bf80f4bSopenharmony_ci // this may be a problem 1808bf80f4bSopenharmony_ci // (there should only be a 1-1 mapping between js objects and native objects) 1818bf80f4bSopenharmony_ci CORE_LOG_F("_JSW exists and points to different object!"); 1828bf80f4bSopenharmony_ci } else { 1838bf80f4bSopenharmony_ci // objects already linked 1848bf80f4bSopenharmony_ci return ref; 1858bf80f4bSopenharmony_ci } 1868bf80f4bSopenharmony_ci } else { 1878bf80f4bSopenharmony_ci // creating a new jsobject for existing native object that was bound before (but js object was GC:d) 1888bf80f4bSopenharmony_ci // this is fine. 1898bf80f4bSopenharmony_ci CORE_LOG_V( 1908bf80f4bSopenharmony_ci "Rewrapping an object! (creating a new js object to replace a GC'd one for a native object)"); 1918bf80f4bSopenharmony_ci } 1928bf80f4bSopenharmony_ci } 1938bf80f4bSopenharmony_ci } else { 1948bf80f4bSopenharmony_ci // create the wrapper property 1958bf80f4bSopenharmony_ci wrapper = ConstructProperty<SharedPtrIInterface>( 1968bf80f4bSopenharmony_ci "_JSW", nullptr, ObjectFlagBits::INTERNAL | ObjectFlagBits::NATIVE); 1978bf80f4bSopenharmony_ci AppMeta->AddProperty(wrapper); 1988bf80f4bSopenharmony_ci } 1998bf80f4bSopenharmony_ci // link native to js. 2008bf80f4bSopenharmony_ci wrapper->SetValue(interface_pointer_cast<CORE_NS::IInterface>(res)); 2018bf80f4bSopenharmony_ci return res->GetObject(); 2028bf80f4bSopenharmony_ci } 2038bf80f4bSopenharmony_ci napi_value val; 2048bf80f4bSopenharmony_ci napi_get_null(jsobj.GetEnv(), &val); 2058bf80f4bSopenharmony_ci return { jsobj.GetEnv(), val }; 2068bf80f4bSopenharmony_ci} 2078bf80f4bSopenharmony_ciNapiApi::Object CreateFromNativeInstance( 2088bf80f4bSopenharmony_ci napi_env env, const META_NS::IObject::Ptr& obj, bool strong, uint32_t argc, napi_value* argv) 2098bf80f4bSopenharmony_ci{ 2108bf80f4bSopenharmony_ci napi_value null; 2118bf80f4bSopenharmony_ci napi_get_null(env, &null); 2128bf80f4bSopenharmony_ci if (obj == nullptr) { 2138bf80f4bSopenharmony_ci return {}; 2148bf80f4bSopenharmony_ci } 2158bf80f4bSopenharmony_ci using namespace META_NS; 2168bf80f4bSopenharmony_ci NapiApi::Object nodeJS = FetchJsObj(obj); 2178bf80f4bSopenharmony_ci if (nodeJS) { 2188bf80f4bSopenharmony_ci // we have a cached js object for this native object 2198bf80f4bSopenharmony_ci return nodeJS; 2208bf80f4bSopenharmony_ci } 2218bf80f4bSopenharmony_ci // no js object. create it. 2228bf80f4bSopenharmony_ci BASE_NS::string name { obj->GetClassName() }; 2238bf80f4bSopenharmony_ci // specialize/remap class names & interfaces. 2248bf80f4bSopenharmony_ci if (name == "Bitmap") { 2258bf80f4bSopenharmony_ci name = "Image"; 2268bf80f4bSopenharmony_ci } else if (name == "Tonemap") { 2278bf80f4bSopenharmony_ci name = "ToneMappingSettings"; 2288bf80f4bSopenharmony_ci } else if (name == "PostProcess") { 2298bf80f4bSopenharmony_ci name = "PostProcessSettings"; 2308bf80f4bSopenharmony_ci } else if (name == "Material") { 2318bf80f4bSopenharmony_ci // okay. specialize then... 2328bf80f4bSopenharmony_ci SCENE_NS::IMaterial* mat = interface_cast<SCENE_NS::IMaterial>(obj); 2338bf80f4bSopenharmony_ci auto shdr = mat->MaterialShader()->GetValue(); 2348bf80f4bSopenharmony_ci auto shdruri = shdr->Uri()->GetValue(); 2358bf80f4bSopenharmony_ci if (!shdruri.empty()) { 2368bf80f4bSopenharmony_ci name = "ShaderMaterial"; 2378bf80f4bSopenharmony_ci } else { 2388bf80f4bSopenharmony_ci // hide other material types.. 2398bf80f4bSopenharmony_ci return {}; 2408bf80f4bSopenharmony_ci } 2418bf80f4bSopenharmony_ci } else if (name == "Shader") { 2428bf80f4bSopenharmony_ci // possible specialization? 2438bf80f4bSopenharmony_ci name = "Shader"; 2448bf80f4bSopenharmony_ci } else if (name == "Node") { 2458bf80f4bSopenharmony_ci if (interface_cast<SCENE_NS::INode>(obj)->GetMesh()) { 2468bf80f4bSopenharmony_ci name = "Geometry"; 2478bf80f4bSopenharmony_ci } else { 2488bf80f4bSopenharmony_ci name = "Node"; 2498bf80f4bSopenharmony_ci } 2508bf80f4bSopenharmony_ci } 2518bf80f4bSopenharmony_ci 2528bf80f4bSopenharmony_ci MakeNativeObjectParam(env, obj, argc, argv); 2538bf80f4bSopenharmony_ci 2548bf80f4bSopenharmony_ci nodeJS = CreateJsObj(env, name, obj, strong, argc, argv); 2558bf80f4bSopenharmony_ci if (!nodeJS) { 2568bf80f4bSopenharmony_ci // EEK. could not create the object. 2578bf80f4bSopenharmony_ci CORE_LOG_E("Could not create JSObject for Class %s", name.c_str()); 2588bf80f4bSopenharmony_ci return {}; 2598bf80f4bSopenharmony_ci } 2608bf80f4bSopenharmony_ci return StoreJsObj(obj, nodeJS); 2618bf80f4bSopenharmony_ci}