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 "SceneJS.h"
17 #include "LightJS.h"
18 #include "MaterialJS.h"
19 static constexpr BASE_NS::Uid IO_QUEUE { "be88e9a0-9cd8-45ab-be48-937953dc258f" };
20 #include <meta/api/make_callback.h>
21 #include <meta/interface/intf_task_queue.h>
22 #include <meta/interface/intf_task_queue_registry.h>
23 #include <meta/interface/property/property_events.h>
24 #include <scene_plugin/api/camera.h> //for the classid...
25 #include <scene_plugin/api/environment_uid.h>
26 #include <scene_plugin/api/material_uid.h>
27 #include <scene_plugin/api/render_configuration_uid.h>
28 #include <scene_plugin/api/scene_uid.h>
29 #include <scene_plugin/interface/intf_ecs_scene.h>
30 #include <scene_plugin/interface/intf_material.h>
31 #include <scene_plugin/interface/intf_render_configuration.h>
32 #include <scene_plugin/interface/intf_scene.h>
33
34 #include <core/image/intf_image_loader_manager.h>
35 #include <core/intf_engine.h>
36 #include <render/device/intf_gpu_resource_manager.h>
37 #include <render/intf_render_context.h>
38
39 #ifdef __SCENE_ADAPTER__
40 #include "3d_widget_adapter_log.h"
41 #include "scene_adapter/scene_adapter.h"
42 #endif
43
44 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
45 using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
46
47 static META_NS::ITaskQueue::Ptr releaseThread;
48 static constexpr BASE_NS::Uid JS_RELEASE_THREAD { "3784fa96-b25b-4e9c-bbf1-e897d36f73af" };
49
Init(napi_env env,napi_value exports)50 void SceneJS::Init(napi_env env, napi_value exports)
51 {
52 using namespace NapiApi;
53 // clang-format off
54 auto loadFun = [](napi_env e,napi_callback_info cb) -> napi_value
55 {
56 FunctionContext<> fc(e,cb);
57 return SceneJS::Load(fc);
58 };
59
60 napi_property_descriptor props[] = {
61 // static methods
62 napi_property_descriptor{ "load", nullptr, loadFun, nullptr, nullptr, nullptr, (napi_property_attributes)(napi_static|napi_default_method)},
63 // properties
64 GetSetProperty<NapiApi::Object, SceneJS, &SceneJS::GetEnvironment, &SceneJS::SetEnvironment>("environment"),
65 GetProperty<NapiApi::Array,SceneJS, &SceneJS::GetAnimations>("animations"),
66 // animations
67 GetProperty<BASE_NS::string, SceneJS, &SceneJS::GetRoot>("root"),
68 // scene methods
69 Method<NapiApi::FunctionContext<BASE_NS::string>, SceneJS, &SceneJS::GetNode>("getNodeByPath"),
70 Method<NapiApi::FunctionContext<>, SceneJS, &SceneJS::GetResourceFactory>("getResourceFactory"),
71 Method<NapiApi::FunctionContext<>, SceneJS, &SceneJS::Dispose>("destroy"),
72 Method<NapiApi::FunctionContext<>, SceneJS, &SceneJS::RenderFrame>("renderFrame"),
73
74 // SceneResourceFactory methods
75 Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateCamera>("createCamera"),
76 Method<NapiApi::FunctionContext<NapiApi::Object,uint32_t>, SceneJS, &SceneJS::CreateLight>("createLight"),
77 Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateNode>("createNode"),
78 Method<NapiApi::FunctionContext<NapiApi::Object,uint32_t>, SceneJS, &SceneJS::CreateMaterial>("createMaterial"),
79 Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateShader>("createShader"),
80 Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateImage>("createImage"),
81 Method<NapiApi::FunctionContext<NapiApi::Object>, SceneJS, &SceneJS::CreateEnvironment>("createEnvironment")
82 };
83 // clang-format on
84
85 napi_value func;
86 auto status = napi_define_class(env, "Scene", NAPI_AUTO_LENGTH, BaseObject::ctor<SceneJS>(), nullptr,
87 sizeof(props) / sizeof(props[0]), props, &func);
88
89 napi_set_named_property(env, exports, "Scene", func);
90
91 NapiApi::MyInstanceState* mis;
92 GetInstanceData(env, (void**)&mis);
93 mis->StoreCtor("Scene", func);
94 }
95 class AsyncStateBase {
96 public:
~AsyncStateBase()97 virtual ~AsyncStateBase()
98 {
99 // assert that the promise has been fulfilled.
100 CORE_ASSERT(deferred == nullptr);
101 // assert that the threadsafe func has been released.
102 CORE_ASSERT(termfun == nullptr);
103 }
104
105 // inherit from this
106 napi_deferred deferred { nullptr };
107 napi_threadsafe_function termfun { nullptr };
108 napi_value result { nullptr };
109 virtual bool finally(napi_env env) = 0;
110 template<typename A>
Flip(A & a)111 A Flip(A& a)
112 {
113 A tmp = a;
114 a = nullptr;
115 return tmp;
116 }
CallIt()117 void CallIt()
118 {
119 // should be called from engine thread only.
120 // use an extra task in engine to trigger this
121 // to woraround an issue wherer CallIt is called IN an eventhandler.
122 // as there seems to be cases where (uncommon, have no repro. but has happend)
123 // napi_release_function waits for threadsafe function completion
124 // and the "js function" is waiting for the enghen thread (which is blocked releasing the function)
125 if (auto tf = Flip(termfun)) {
126 META_NS::GetTaskQueueRegistry()
127 .GetTaskQueue(JS_RELEASE_THREAD)
128 ->AddTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move([tf]() {
129 napi_call_threadsafe_function(tf, nullptr, napi_threadsafe_function_call_mode::napi_tsfn_blocking);
130 napi_release_threadsafe_function(tf, napi_threadsafe_function_release_mode::napi_tsfn_release);
131 return false;
132 })));
133 }
134 }
135 // callable from js thread
Fail(napi_env env)136 void Fail(napi_env env)
137 {
138 napi_status status;
139 if (auto df = Flip(deferred)) {
140 status = napi_reject_deferred(env, df, result);
141 }
142 if (auto tf = Flip(termfun)) {
143 // if called from java thread. then release it here...
144 napi_release_threadsafe_function(tf, napi_threadsafe_function_release_mode::napi_tsfn_release);
145 }
146 }
Success(napi_env env)147 void Success(napi_env env)
148 {
149 napi_status status;
150 // return success
151 if (auto df = Flip(deferred)) {
152 status = napi_resolve_deferred(env, df, result);
153 }
154 if (auto tf = Flip(termfun)) {
155 // if called from java thread. then release it here...
156 napi_release_threadsafe_function(tf, napi_threadsafe_function_release_mode::napi_tsfn_release);
157 }
158 }
159 };
MakePromise(napi_env env,AsyncStateBase * data)160 napi_value MakePromise(napi_env env, AsyncStateBase* data)
161 {
162 napi_value promise;
163 napi_status status = napi_create_promise(env, &data->deferred, &promise);
164 napi_value name;
165 napi_create_string_latin1(env, "a", 1, &name);
166 status = napi_create_threadsafe_function(
167 env, nullptr, nullptr, name, 1, 1, data /*finalize_data*/,
168 [](napi_env env, void* finalize_data, void* finalize_hint) {
169 AsyncStateBase* data = (AsyncStateBase*)finalize_data;
170 delete data;
171 },
172 data /*context*/,
173 [](napi_env env, napi_value js_callback, void* context, void* inData) {
174 // IN JS THREAD. (so careful with the calls to engine)
175 napi_status status;
176 AsyncStateBase* data = (AsyncStateBase*)context;
177 status = napi_get_undefined(env, &data->result);
178 if (data->finally(env)) {
179 data->Success(env);
180 } else {
181 data->Fail(env);
182 }
183 },
184 &data->termfun);
185 return promise;
186 }
FetchResourceOrUri(napi_env e,napi_value arg)187 BASE_NS::string FetchResourceOrUri(napi_env e, napi_value arg)
188 {
189 napi_valuetype type;
190 napi_typeof(e, arg, &type);
191 if (type == napi_string) {
192 BASE_NS::string uri = NapiApi::Value<BASE_NS::string>(e, arg);
193 // set default format as system resource
194 uri.insert(0, "file://");
195 return uri;
196 }
197 if (type == napi_object) {
198 NapiApi::Object resource(e, arg);
199 uint32_t id = resource.Get<uint32_t>("id");
200 uint32_t type = resource.Get<uint32_t>("type");
201 NapiApi::Array parms = resource.Get<NapiApi::Array>("params");
202 BASE_NS::string uri;
203 if ((id == 0) && (type == 30000)) {
204 // seems like a correct rawfile.
205 uri = parms.Get<BASE_NS::string>(0);
206 }
207 if (!uri.empty()) {
208 // add the schema then
209 uri.insert(0, "OhosRawFile://");
210 }
211 return uri;
212 }
213 return "";
214 }
215
FetchResourceOrUri(NapiApi::FunctionContext<> & ctx)216 BASE_NS::string FetchResourceOrUri(NapiApi::FunctionContext<>& ctx)
217 {
218 BASE_NS::string uri;
219 NapiApi::FunctionContext<NapiApi::Object> resourceContext(ctx);
220 NapiApi::FunctionContext<BASE_NS::string> uriContext(ctx);
221
222 if (uriContext) {
223 // actually not supported anymore.
224 uri = uriContext.Arg<0>();
225 // check if there is a protocol
226 auto t = uri.find("://");
227 if (t == BASE_NS::string::npos) {
228 // no proto . so use default
229 // set default format as system resource
230 uri.insert(0, "file://");
231 }
232 } else if (resourceContext) {
233 // get it from resource then
234 NapiApi::Object resource = resourceContext.Arg<0>();
235 uint32_t id = resource.Get<uint32_t>("id");
236 uint32_t type = resource.Get<uint32_t>("type");
237 NapiApi::Array parms = resource.Get<NapiApi::Array>("params");
238 if ((id == 0) && (type == 30000)) { // 30000 : type
239 // seems like a correct rawfile.
240 uri = parms.Get<BASE_NS::string>(0);
241 }
242 if (!uri.empty()) {
243 // add the schema then
244 uri.insert(0, "OhosRawFile://");
245 }
246 }
247 return uri;
248 }
249
Load(NapiApi::FunctionContext<> & ctx)250 napi_value SceneJS::Load(NapiApi::FunctionContext<>& ctx)
251 {
252 BASE_NS::string uri = FetchResourceOrUri(ctx);
253
254 if (uri.empty()) {
255 // unsupported input..
256 return {};
257 }
258 // make sure slashes are correct.. *eh*
259 for (;;) {
260 auto t = uri.find_first_of('\\');
261 if (t == BASE_NS::string::npos) {
262 break;
263 }
264 uri[t] = '/';
265 }
266
267 auto &tr = META_NS::GetTaskQueueRegistry();
268 releaseThread = META_NS::GetTaskQueueRegistry().GetTaskQueue(JS_RELEASE_THREAD);
269 if (!releaseThread) {
270 auto &obr = META_NS::GetObjectRegistry();
271 releaseThread = obr.Create<META_NS::ITaskQueue>(META_NS::ClassId::ThreadedTaskQueue);
272 tr.RegisterTaskQueue(releaseThread, JS_RELEASE_THREAD);
273 }
274
275 struct AsyncState : public AsyncStateBase {
276 BASE_NS::string uri;
277 SCENE_NS::IScene::Ptr scene;
278 META_NS::IEvent::Token onLoadedToken { 0 };
279 bool finally(napi_env env) override
280 {
281 if (scene) {
282 auto obj = interface_pointer_cast<META_NS::IObject>(scene);
283 result = CreateJsObj(env, "Scene", obj, true, 0, nullptr);
284 // link the native object to js object.
285 StoreJsObj(obj, { env, result });
286
287 NapiApi::Object me(env, result);
288 auto curenv = me.Get<NapiApi::Object>("environment");
289 if (!curenv) {
290 // setup default env
291 NapiApi::Object argsIn(env);
292 argsIn.Set("name", "DefaultEnv");
293
294 auto* tro = (SceneJS*)(me.Native<TrueRootObject>());
295 auto res = tro->CreateEnvironment(me, argsIn);
296 res.Set("backgroundType", NapiApi::Value<uint32_t>(env, 1)); // image.. but with null.
297 me.Set("environment", res);
298 }
299
300 #ifdef __SCENE_ADAPTER__
301 // set SceneAdapter
302 auto oo = GetRootObject(env, result);
303
304 auto sceneAdapter = std::make_shared<OHOS::Render3D::SceneAdapter>();
305 sceneAdapter->SetSceneObj(oo->GetNativeObject());
306 auto sceneJs = static_cast<SceneJS*>(oo);
307 sceneJs->scene_ = sceneAdapter;
308 #endif
309 return true;
310 }
311 scene.reset();
312 return false;
313 };
314 };
315 AsyncState* data = new AsyncState();
316 data->uri = uri;
317
318 auto fun = [data]() {
319 // IN ENGINE THREAD! (so no js calls)
320 using namespace SCENE_NS;
321 auto& obr = META_NS::GetObjectRegistry();
322 auto params = interface_pointer_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
323 auto scene = interface_pointer_cast<SCENE_NS::IScene>(obr.Create(SCENE_NS::ClassId::Scene, params));
324 if (!scene) {
325 // insta fail
326 data->CallIt();
327 return false;
328 }
329 data->scene = scene;
330
331 auto onLoaded = META_NS::MakeCallback<META_NS::IOnChanged>([data]() {
332 bool complete = false;
333 auto scene = data->scene;
334 auto status = scene->Status()->GetValue();
335 if (status == SCENE_NS::IScene::SCENE_STATUS_READY) {
336 // still in engine thread..
337 complete = true;
338 } else if (status == SCENE_NS::IScene::SCENE_STATUS_LOADING_FAILED) {
339 data->scene.reset(); // make sure we don't have anything in result if error.
340 complete = true;
341 }
342
343 if (complete) {
344 scene->Status()->OnChanged()->RemoveHandler(data->onLoadedToken);
345 data->onLoadedToken = 0;
346 if (scene) {
347 auto& obr = META_NS::GetObjectRegistry();
348 // make sure we have renderconfig
349 auto rc = scene->RenderConfiguration()->GetValue();
350 if (!rc) {
351 // Create renderconfig
352 rc = obr.Create<SCENE_NS::IRenderConfiguration>(SCENE_NS::ClassId::RenderConfiguration);
353 scene->RenderConfiguration()->SetValue(rc);
354 }
355 /*if (auto env = rc->Environment()->GetValue(); !env) {
356 // create default env then
357 env = scene->CreateNode<SCENE_NS::IEnvironment>("default_env");
358 env->Background()->SetValue(SCENE_NS::IEnvironment::CUBEMAP);
359 rc->Environment()->SetValue(env);
360 }*/
361
362 interface_cast<IEcsScene>(scene)->RenderMode()->SetValue(IEcsScene::RenderMode::RENDER_ALWAYS);
363 auto params = interface_pointer_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
364 auto duh = params->GetArrayPropertyByName<IntfWeakPtr>("Scenes");
365 duh->AddValue(interface_pointer_cast<CORE_NS::IInterface>(scene));
366 }
367 // call the threadsafe func here. (let the javascript know we are done)
368 data->CallIt();
369 }
370 });
371 data->onLoadedToken = scene->Status()->OnChanged()->AddHandler(onLoaded);
372 scene->Asynchronous()->SetValue(false);
373 scene->Uri()->SetValue(data->uri);
374 return false;
375 };
376 // Should it be possible to cancel? (ie. do we need to store the token for something..)
377 META_NS::GetTaskQueueRegistry()
378 .GetTaskQueue(ENGINE_THREAD)
379 ->AddTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move(fun)));
380
381 return MakePromise(ctx, data);
382 }
383
RenderFrame(NapiApi::FunctionContext<> & ctx)384 napi_value SceneJS::RenderFrame(NapiApi::FunctionContext<>& ctx)
385 {
386 if (ctx.ArgCount() > 1) {
387 CORE_LOG_E("render frame %d", __LINE__);
388 return ctx.GetUndefined();
389 }
390 bool res = true;
391 #ifdef __SCENE_ADAPTER__
392 auto sceneAdapter = std::static_pointer_cast<OHOS::Render3D::SceneAdapter>(scene_);
393 if (sceneAdapter) {
394 sceneAdapter->SetNeedsRepaint(false);
395 sceneAdapter->RenderFrame(false);
396 }
397 #endif
398 return ctx.GetBoolean(res);
399 }
400
Dispose(NapiApi::FunctionContext<> & ctx)401 napi_value SceneJS::Dispose(NapiApi::FunctionContext<>& ctx)
402 {
403 LOG_F("SCENE_JS::Dispose");
404 DisposeNative();
405 return {};
406 }
DisposeNative()407 void SceneJS::DisposeNative()
408 {
409 LOG_F("SCENE_JS::DisposeNative");
410 // dispose
411 while (!disposables_.empty()) {
412 auto env = disposables_.begin()->second.GetObject();
413 if (env) {
414 NapiApi::Function func = env.Get<NapiApi::Function>("destroy");
415 if (func) {
416 func.Invoke(env);
417 }
418 }
419 }
420
421 // dispose all cameras/env/etcs.
422 while (!strongDisposables_.empty()) {
423 auto it = strongDisposables_.begin();
424 auto token = it->first;
425 auto env = it->second.GetObject();
426 if (env) {
427 NapiApi::Function func = env.Get<NapiApi::Function>("destroy");
428 if (func) {
429 func.Invoke(env);
430 }
431 }
432 }
433 if (auto env = environmentJS_.GetObject()) {
434 NapiApi::Function func = env.Get<NapiApi::Function>("destroy");
435 if (func) {
436 func.Invoke(env);
437 }
438 }
439 environmentJS_.Reset();
440 for (auto b : bitmaps_) {
441 b.second.reset();
442 }
443 if (auto scene = interface_pointer_cast<SCENE_NS::IScene>(GetNativeObject())) {
444 // reset the native object refs
445 SetNativeObject(nullptr, false);
446 SetNativeObject(nullptr, true);
447
448 ExecSyncTask([scn = BASE_NS::move(scene)]() {
449 auto r = scn->RenderConfiguration()->GetValue();
450 auto e = r->Environment()->GetValue();
451 e.reset();
452 r.reset();
453 scn->RenderConfiguration()->SetValue(nullptr);
454 return META_NS::IAny::Ptr {};
455 });
456 }
457 }
GetInstanceImpl(uint32_t id)458 void* SceneJS::GetInstanceImpl(uint32_t id)
459 {
460 if (id == SceneJS::ID) {
461 return this;
462 }
463 return nullptr;
464 }
Finalize(napi_env env)465 void SceneJS::Finalize(napi_env env)
466 {
467 // hmm.. do i need to do something BEFORE the object gets deleted..
468 BaseObject<SceneJS>::Finalize(env);
469 }
470
SceneJS(napi_env e,napi_callback_info i)471 SceneJS::SceneJS(napi_env e, napi_callback_info i) : BaseObject<SceneJS>(e, i)
472 {
473 LOG_F("SceneJS ++");
474 NapiApi::FunctionContext<NapiApi::Object> fromJs(e, i);
475
476 if (!fromJs) {
477 // okay internal create. we will receive the object after.
478 return;
479 }
480
481 // Construct native object
482 SCENE_NS::IScene::Ptr scene;
483 ExecSyncTask([&scene]() {
484 using namespace SCENE_NS;
485 auto& obr = META_NS::GetObjectRegistry();
486 auto params = interface_pointer_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
487 scene = interface_pointer_cast<SCENE_NS::IScene>(obr.Create(SCENE_NS::ClassId::Scene, params));
488 if (scene) {
489 // only asynch false works.. (otherwise nodes are in random state when scene is ready.. )
490 scene->Asynchronous()->SetValue(false);
491 interface_cast<IEcsScene>(scene)->RenderMode()->SetValue(IEcsScene::RenderMode::RENDER_ALWAYS);
492 auto duh = params->GetArrayPropertyByName<IntfWeakPtr>("Scenes");
493 duh->AddValue(interface_pointer_cast<CORE_NS::IInterface>(scene));
494 }
495 return META_NS::IAny::Ptr {};
496 });
497
498 // process constructor args..
499 NapiApi::Object meJs(e, fromJs.This());
500 SetNativeObject(interface_pointer_cast<META_NS::IObject>(scene), true /* KEEP STRONG REF */);
501 StoreJsObj(interface_pointer_cast<META_NS::IObject>(scene), meJs);
502
503 NapiApi::Object args = fromJs.Arg<0>();
504 if (args) {
505 if (auto name = args.Get("name")) {
506 meJs.Set("name", name);
507 }
508 if (auto uri = args.Get("uri")) {
509 meJs.Set("uri", uri);
510 }
511 }
512 }
513
~SceneJS()514 SceneJS::~SceneJS()
515 {
516 LOG_F("SceneJS --");
517 if (!GetNativeObject()) {
518 return;
519 }
520 ExecSyncTask([scene = GetNativeObject()]() {
521 auto& obr = META_NS::GetObjectRegistry();
522 auto params = interface_pointer_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
523 auto duh = params->GetArrayPropertyByName<IntfWeakPtr>("Scenes");
524 if (duh) {
525 for (auto i = 0; i < duh->GetSize();) {
526 auto w = duh->GetValueAt(i);
527 if (w.lock() == nullptr) {
528 duh->RemoveAt(i);
529 } else {
530 i++;
531 }
532 }
533 }
534 return META_NS::IAny::Ptr {};
535 });
536 }
537
GetNode(NapiApi::FunctionContext<BASE_NS::string> & ctx)538 napi_value SceneJS::GetNode(NapiApi::FunctionContext<BASE_NS::string>& ctx)
539 {
540 // verify that path starts from "correct root" and then let the root node handle the rest.
541 NapiApi::Object meJs(ctx, ctx.This());
542 NapiApi::Object root = meJs.Get<NapiApi::Object>("root");
543 BASE_NS::string rootName = root.Get<BASE_NS::string>("name");
544 NapiApi::Function func = root.Get<NapiApi::Function>("getNodeByPath");
545 BASE_NS::string path = ctx.Arg<0>();
546
547 // remove the "root nodes name" (make sure it also matches though..)
548 auto pos = path.find('/', 0);
549 BASE_NS::string_view step = path.substr(0, pos);
550 if (step != rootName) {
551 // root not matching
552 return ctx.GetNull();
553 }
554
555 if (pos != BASE_NS::string_view::npos) {
556 BASE_NS::string rest(path.substr(pos + 1));
557 napi_value newpath = nullptr;
558 napi_status status = napi_create_string_utf8(ctx, rest.c_str(), rest.length(), &newpath);
559 if (newpath) {
560 return func.Invoke(root, 1, &newpath);
561 }
562 return ctx.GetNull();
563 }
564 return root;
565 }
GetRoot(NapiApi::FunctionContext<> & ctx)566 napi_value SceneJS::GetRoot(NapiApi::FunctionContext<>& ctx)
567 {
568 if (auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject())) {
569 SCENE_NS::INode::Ptr root;
570 auto cb = META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>([scene, &root]() {
571 root = scene->RootNode()->GetValue();
572 if (root) {
573 // make sure our direct descendants exist.
574 root->BuildChildren(SCENE_NS::INode::BuildBehavior::NODE_BUILD_ONLY_DIRECT_CHILDREN);
575 }
576 return META_NS::IAny::Ptr {};
577 });
578 META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddWaitableTask(cb)->Wait();
579 auto obj = interface_pointer_cast<META_NS::IObject>(root);
580
581 if (auto cached = FetchJsObj(obj)) {
582 // always return the same js object.
583 return cached;
584 }
585
586 NapiApi::StrongRef sceneRef { ctx, ctx.This() };
587 if (!GetNativeMeta<SCENE_NS::IScene>(sceneRef.GetObject())) {
588 CORE_LOG_F("INVALID SCENE!");
589 }
590
591 NapiApi::Object argJS(ctx);
592 napi_value args[] = { sceneRef.GetObject(), argJS };
593
594 return CreateFromNativeInstance(ctx, obj, false /*these are owned by the scene*/, BASE_NS::countof(args), args);
595 }
596 return ctx.GetUndefined();
597 }
598
GetEnvironment(NapiApi::FunctionContext<> & ctx)599 napi_value SceneJS::GetEnvironment(NapiApi::FunctionContext<>& ctx)
600 {
601 if (auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject())) {
602 SCENE_NS::IEnvironment::Ptr environment;
603 ExecSyncTask([scene, &environment]() {
604 auto rc = scene->RenderConfiguration()->GetValue();
605 if (rc) {
606 environment = rc->Environment()->GetValue();
607 }
608 return META_NS::IAny::Ptr {};
609 });
610 if (environment) {
611 auto obj = interface_pointer_cast<META_NS::IObject>(environment);
612 if (auto cached = FetchJsObj(obj)) {
613 // always return the same js object.
614 return cached;
615 }
616
617 NapiApi::StrongRef sceneRef { ctx, ctx.This() };
618 if (!GetNativeMeta<SCENE_NS::IScene>(sceneRef.GetObject())) {
619 CORE_LOG_F("INVALID SCENE!");
620 }
621
622 NapiApi::Object argJS(ctx);
623 napi_value args[] = { sceneRef.GetObject(), argJS };
624
625 environmentJS_ = { ctx, CreateFromNativeInstance(ctx, obj, false, BASE_NS::countof(args), args) };
626 return environmentJS_.GetValue();
627 }
628 }
629 return ctx.GetNull();
630 }
631
SetEnvironment(NapiApi::FunctionContext<NapiApi::Object> & ctx)632 void SceneJS::SetEnvironment(NapiApi::FunctionContext<NapiApi::Object>& ctx)
633 {
634 NapiApi::Object env = ctx.Arg<0>();
635
636 if (auto currentlySet = environmentJS_.GetObject()) {
637 if ((napi_value)currentlySet == (napi_value)env) {
638 // setting the exactly the same environment. do nothing.
639 return;
640 }
641 }
642 environmentJS_.Reset();
643 if (env) {
644 environmentJS_ = { env };
645 }
646 SCENE_NS::IEnvironment::Ptr environment;
647 if (env) {
648 environment = GetNativeMeta<SCENE_NS::IEnvironment>(env);
649 }
650 if (auto scene = interface_cast<SCENE_NS::IScene>(GetNativeObject())) {
651 ExecSyncTask([scene, environment]() {
652 auto rc = scene->RenderConfiguration()->GetValue();
653 if (!rc) {
654 // no render config, so create it.
655 auto& obr = META_NS::GetObjectRegistry();
656 rc = obr.Create<SCENE_NS::IRenderConfiguration>(SCENE_NS::ClassId::RenderConfiguration);
657 scene->RenderConfiguration()->SetValue(rc);
658 }
659 if (rc) {
660 rc->Environment()->SetValue(environment);
661 }
662 return META_NS::IAny::Ptr {};
663 });
664 }
665 }
666
667 // resource factory
668
GetResourceFactory(NapiApi::FunctionContext<> & ctx)669 napi_value SceneJS::GetResourceFactory(NapiApi::FunctionContext<>& ctx)
670 {
671 // just return this. as scene is the factory also.
672 return ctx.This();
673 }
CreateEnvironment(NapiApi::Object scene,NapiApi::Object argsIn)674 NapiApi::Object SceneJS::CreateEnvironment(NapiApi::Object scene, NapiApi::Object argsIn)
675 {
676 napi_env env = scene.GetEnv();
677 napi_value args[] = { scene, argsIn };
678 auto result = NapiApi::Object(GetJSConstructor(env, "Environment"), BASE_NS::countof(args), args);
679 auto ref = NapiApi::StrongRef { env, result };
680 return { env, ref.GetValue() };
681 }
CreateEnvironment(NapiApi::FunctionContext<NapiApi::Object> & ctx)682 napi_value SceneJS::CreateEnvironment(NapiApi::FunctionContext<NapiApi::Object>& ctx)
683 {
684 struct AsyncState : public AsyncStateBase {
685 NapiApi::StrongRef this_;
686 NapiApi::StrongRef args_;
687 bool finally(napi_env env) override
688 {
689 auto* tro = (SceneJS*)(this_.GetObject().Native<TrueRootObject>());
690 result = tro->CreateEnvironment(this_.GetObject(), args_.GetObject());
691 return true;
692 };
693 };
694 AsyncState* data = new AsyncState();
695 data->this_ = NapiApi::StrongRef(ctx, ctx.This());
696 data->args_ = NapiApi::StrongRef(ctx, ctx.Arg<0>());
697 napi_value result = MakePromise(ctx, data);
698 // and just instantly complete it
699 data->finally(ctx);
700 data->Success(ctx);
701 return result;
702 }
703
CreateCamera(NapiApi::FunctionContext<NapiApi::Object> & ctx)704 napi_value SceneJS::CreateCamera(NapiApi::FunctionContext<NapiApi::Object>& ctx)
705 {
706 struct AsyncState : public AsyncStateBase {
707 NapiApi::StrongRef this_;
708 NapiApi::StrongRef args_;
709 bool finally(napi_env env) override
710 {
711 napi_value args[] = {
712 this_.GetValue(), // scene..
713 args_.GetValue() // params.
714 };
715 result = NapiApi::Object(GetJSConstructor(env, "Camera"), BASE_NS::countof(args), args);
716 return true;
717 };
718 };
719 AsyncState* data = new AsyncState();
720 data->this_ = NapiApi::StrongRef(ctx, ctx.This());
721 data->args_ = NapiApi::StrongRef(ctx, ctx.Arg<0>());
722 napi_value result = MakePromise(ctx, data);
723 // and just instantly complete it
724 data->finally(ctx);
725 data->Success(ctx);
726 return result;
727 }
728
CreateLight(NapiApi::FunctionContext<NapiApi::Object,uint32_t> & ctx)729 napi_value SceneJS::CreateLight(NapiApi::FunctionContext<NapiApi::Object, uint32_t>& ctx)
730 {
731 struct AsyncState : public AsyncStateBase {
732 NapiApi::StrongRef this_;
733 NapiApi::StrongRef args_;
734 uint32_t lightType_;
735 bool finally(napi_env env) override
736 {
737 napi_value args[] = {
738 this_.GetValue(), // scene..
739 args_.GetValue() // params.
740 };
741 NapiApi::Function func;
742 switch (lightType_) {
743 case BaseLight::DIRECTIONAL: {
744 func = GetJSConstructor(env, "DirectionalLight");
745 break;
746 }
747 case BaseLight::POINT: {
748 func = GetJSConstructor(env, "PointLight");
749 break;
750 }
751 case BaseLight::SPOT: {
752 func = GetJSConstructor(env, "SpotLight");
753 break;
754 }
755 default:
756 break;
757 }
758 if (!func) {
759 return false;
760 }
761 result = NapiApi::Object(func, BASE_NS::countof(args), args);
762 return true;
763 };
764 };
765 AsyncState* data = new AsyncState();
766 data->this_ = NapiApi::StrongRef(ctx, ctx.This());
767 data->args_ = NapiApi::StrongRef(ctx, ctx.Arg<0>());
768 data->lightType_ = ctx.Arg<1>();
769 napi_value result = MakePromise(ctx, data);
770 // and just instantly complete it
771 data->finally(ctx);
772 data->Success(ctx);
773 return result;
774 }
775
CreateNode(NapiApi::FunctionContext<NapiApi::Object> & ctx)776 napi_value SceneJS::CreateNode(NapiApi::FunctionContext<NapiApi::Object>& ctx)
777 {
778 struct AsyncState : public AsyncStateBase {
779 NapiApi::StrongRef this_;
780 NapiApi::StrongRef args_;
781 bool finally(napi_env env) override
782 {
783 napi_value args[] = {
784 this_.GetValue(), // scene..
785 args_.GetValue() // params.
786 };
787 result = NapiApi::Object(GetJSConstructor(env, "Node"), BASE_NS::countof(args), args);
788 return true;
789 };
790 };
791 AsyncState* data = new AsyncState();
792 data->this_ = NapiApi::StrongRef(ctx, ctx.This());
793 data->args_ = NapiApi::StrongRef(ctx, ctx.Arg<0>());
794
795 napi_value ret = MakePromise(ctx, data);
796 // and just instantly complete it
797 data->finally(ctx);
798 data->Success(ctx);
799 return ret;
800 }
801
CreateMaterial(NapiApi::FunctionContext<NapiApi::Object,uint32_t> & ctx)802 napi_value SceneJS::CreateMaterial(NapiApi::FunctionContext<NapiApi::Object, uint32_t>& ctx)
803 {
804 struct AsyncState : public AsyncStateBase {
805 NapiApi::StrongRef this_;
806 NapiApi::StrongRef args_;
807 uint32_t type_;
808 BASE_NS::string name_;
809 SCENE_NS::IMaterial::Ptr material_;
810 SCENE_NS::IScene::Ptr scene_;
811 bool finally(napi_env env) override
812 {
813 napi_value args[] = {
814 this_.GetValue(), // scene..
815 args_.GetValue() // params.
816 };
817
818 if (type_ == BaseMaterial::SHADER) {
819 MakeNativeObjectParam(env, material_, BASE_NS::countof(args), args);
820 NapiApi::Object materialJS(GetJSConstructor(env, "ShaderMaterial"), BASE_NS::countof(args), args);
821 result = materialJS;
822 } else {
823 // fail..
824 material_.reset();
825 }
826 return true;
827 };
828 };
829 AsyncState* data = new AsyncState();
830 data->this_ = NapiApi::StrongRef(ctx, ctx.This());
831 data->args_ = NapiApi::StrongRef(ctx, ctx.Arg<0>());
832 data->type_ = ctx.Arg<1>();
833 if (ctx.Arg<0>()) {
834 NapiApi::Object parms = ctx.Arg<0>();
835 data->name_ = parms.Get<BASE_NS::string>("name");
836 }
837
838 napi_value result = MakePromise(ctx, data);
839 data->scene_ = interface_pointer_cast<SCENE_NS::IScene>(GetNativeObject());
840
841 // create an engine task and complete it there..
842 auto fun = [data]() {
843 data->material_ = data->scene_->CreateMaterial(data->name_);
844 data->CallIt();
845 return false;
846 };
847
848 META_NS::GetTaskQueueRegistry()
849 .GetTaskQueue(ENGINE_THREAD)
850 ->AddTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move(fun)));
851
852 return result;
853 }
854
CreateShader(NapiApi::FunctionContext<NapiApi::Object> & ctx)855 napi_value SceneJS::CreateShader(NapiApi::FunctionContext<NapiApi::Object>& ctx)
856 {
857 struct AsyncState : public AsyncStateBase {
858 NapiApi::StrongRef this_;
859 NapiApi::StrongRef args_;
860 BASE_NS::string uri_;
861 BASE_NS::string name_;
862 SCENE_NS::IShader::Ptr shader_;
863 bool finally(napi_env env) override
864 {
865 napi_value args[] = {
866 this_.GetValue(), // scene..
867 args_.GetValue() // params.
868 };
869 NapiApi::Object parms(env, args[1]);
870
871 napi_value null;
872 napi_get_null(env, &null);
873 parms.Set("Material", null); // not bound to anything...
874
875 MakeNativeObjectParam(env, shader_, BASE_NS::countof(args), args);
876 NapiApi::Object shaderJS(GetJSConstructor(env, "Shader"), BASE_NS::countof(args), args);
877 result = shaderJS;
878
879 return true;
880 };
881 };
882 AsyncState* data = new AsyncState();
883 data->this_ = NapiApi::StrongRef(ctx, ctx.This());
884 data->args_ = NapiApi::StrongRef(ctx, ctx.Arg<0>());
885 if (ctx.Arg<0>()) {
886 NapiApi::Object parms = ctx.Arg<0>();
887 data->name_ = parms.Get<BASE_NS::string>("name");
888 data->uri_ = FetchResourceOrUri(ctx, parms.Get("uri"));
889 }
890
891 napi_value result = MakePromise(ctx, data);
892
893 auto fun = [data]() {
894 auto& obr = META_NS::GetObjectRegistry();
895 auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
896 auto renderContext = doc->GetPropertyByName<IntfPtr>("RenderContext")->GetValue();
897
898 auto params =
899 interface_pointer_cast<META_NS::IMetadata>(META_NS::GetObjectRegistry().Create(META_NS::ClassId::Object));
900
901 params->AddProperty(META_NS::ConstructProperty<IntfPtr>(
902 "RenderContext", renderContext, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
903
904 params->AddProperty(META_NS::ConstructProperty<BASE_NS::string>(
905 "uri", data->uri_, META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE));
906
907 data->shader_ = META_NS::GetObjectRegistry().Create<SCENE_NS::IShader>(SCENE_NS::ClassId::Shader, params);
908 data->CallIt();
909 return false;
910 };
911
912 META_NS::GetTaskQueueRegistry()
913 .GetTaskQueue(ENGINE_THREAD)
914 ->AddTask(META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move(fun)));
915
916 return result;
917 }
918
StoreBitmap(BASE_NS::string_view uri,SCENE_NS::IBitmap::Ptr bitmap)919 void SceneJS::StoreBitmap(BASE_NS::string_view uri, SCENE_NS::IBitmap::Ptr bitmap)
920 {
921 CORE_NS::UniqueLock lock(mutex_);
922 if (bitmap) {
923 bitmaps_[uri] = bitmap;
924 } else {
925 // setting null. releases.
926 bitmaps_.erase(uri);
927 }
928 }
FetchBitmap(BASE_NS::string_view uri)929 SCENE_NS::IBitmap::Ptr SceneJS::FetchBitmap(BASE_NS::string_view uri)
930 {
931 CORE_NS::UniqueLock lock(mutex_);
932 auto it = bitmaps_.find(uri);
933 if (it != bitmaps_.end()) {
934 return it->second;
935 }
936 return {};
937 }
938
CreateImage(NapiApi::FunctionContext<NapiApi::Object> & ctx)939 napi_value SceneJS::CreateImage(NapiApi::FunctionContext<NapiApi::Object>& ctx)
940 {
941 using namespace RENDER_NS;
942
943 struct AsyncState : public AsyncStateBase {
944 NapiApi::StrongRef this_;
945 NapiApi::StrongRef args_;
946 BASE_NS::shared_ptr<IRenderContext> renderContext_;
947 BASE_NS::string name_;
948 BASE_NS::string uri_;
949 SCENE_NS::IBitmap::Ptr bitmap_;
950 RenderHandleReference imageHandle_;
951 SceneJS* owner_;
952 CORE_NS::IImageLoaderManager::LoadResult imageLoadResult_;
953 bool cached_ { false };
954 bool finally(napi_env env) override
955 {
956 if (!bitmap_) {
957 // return the error string..
958 napi_create_string_utf8(env, imageLoadResult_.error, NAPI_AUTO_LENGTH, &result);
959 return false;
960 }
961 if (!cached_) {
962 // create the jsobject if we don't have one.
963 napi_value args[] = {
964 this_.GetValue(), // scene..
965 args_.GetValue() // params.
966 };
967 MakeNativeObjectParam(env, bitmap_, BASE_NS::countof(args), args);
968 owner_->StoreBitmap(uri_, BASE_NS::move(bitmap_));
969 NapiApi::Object imageJS(GetJSConstructor(env, "Image"), BASE_NS::countof(args), args);
970 result = imageJS;
971 }
972 return true;
973 };
974 };
975 AsyncState* data = new AsyncState();
976 data->owner_ = this;
977 data->this_ = NapiApi::StrongRef(ctx, ctx.This());
978 data->args_ = NapiApi::StrongRef(ctx, ctx.Arg<0>());
979
980 NapiApi::Object args = ctx.Arg<0>();
981 if (args) {
982 if (auto n = args.Get<BASE_NS::string>("name")) {
983 data->name_ = n;
984 }
985 data->uri_ = FetchResourceOrUri(ctx, args.Get("uri"));
986 }
987
988 napi_value result = MakePromise(ctx, data);
989 if (auto bitmap = FetchBitmap(data->uri_)) {
990 // no aliasing.. so the returned bitmaps name is.. the old one.
991 // *fix*
992 // oh we have it already, no need to do anything in engine side.
993 data->cached_ = true;
994 data->bitmap_ = bitmap;
995 auto obj = interface_pointer_cast<META_NS::IObject>(data->bitmap_);
996 data->result = FetchJsObj(obj);
997 data->Success(ctx);
998 return result;
999 }
1000
1001 auto& obr = META_NS::GetObjectRegistry();
1002 auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
1003 data->renderContext_ =
1004 interface_pointer_cast<IRenderContext>(doc->GetPropertyByName<IntfPtr>("RenderContext")->GetValue());
1005
1006 // create an IO task (to load the cpu data)
1007 auto fun = [data]() -> META_NS::IAny::Ptr {
1008 uint32_t imageLoaderFlags = CORE_NS::IImageLoaderManager::IMAGE_LOADER_GENERATE_MIPS;
1009 auto& imageLoaderMgr = data->renderContext_->GetEngine().GetImageLoaderManager();
1010 data->imageLoadResult_ = imageLoaderMgr.LoadImage(data->uri_, imageLoaderFlags);
1011 return {};
1012 };
1013
1014 // create final engine task (to create gpu resource)
1015 auto fun2 = [data](const META_NS::IAny::Ptr&) -> META_NS::IAny::Ptr {
1016 if (!data->imageLoadResult_.success) {
1017 CORE_LOG_E("Could not load image asset: %s", data->imageLoadResult_.error);
1018 data->bitmap_ = nullptr;
1019 } else {
1020 auto& gpuResourceMgr = data->renderContext_->GetDevice().GetGpuResourceManager();
1021 RenderHandleReference imageHandle {};
1022 GpuImageDesc gpuDesc = gpuResourceMgr.CreateGpuImageDesc(data->imageLoadResult_.image->GetImageDesc());
1023 gpuDesc.usageFlags = CORE_IMAGE_USAGE_SAMPLED_BIT | CORE_IMAGE_USAGE_TRANSFER_DST_BIT;
1024 if (gpuDesc.engineCreationFlags & EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_GENERATE_MIPS) {
1025 gpuDesc.usageFlags |= CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
1026 }
1027 gpuDesc.memoryPropertyFlags = CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
1028 data->imageHandle_ = gpuResourceMgr.Create(data->uri_, gpuDesc, std::move(data->imageLoadResult_.image));
1029 data->bitmap_ = META_NS::GetObjectRegistry().Create<SCENE_NS::IBitmap>(SCENE_NS::ClassId::Bitmap);
1030 data->bitmap_->Uri()->SetValue(data->uri_);
1031 data->bitmap_->SetRenderHandle(data->imageHandle_, { gpuDesc.width, gpuDesc.height });
1032 }
1033 data->CallIt();
1034 return {};
1035 };
1036
1037 // execute first step in io thread..
1038 // second in engine thread.
1039 // and the final in js thread (with threadsafefunction)
1040 auto ioqueue = META_NS::GetTaskQueueRegistry().GetTaskQueue(IO_QUEUE);
1041 auto enginequeue = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD);
1042 ioqueue->AddWaitableTask(META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>(BASE_NS::move(fun)))
1043 ->Then(META_NS::MakeCallback<META_NS::IFutureContinuation>(BASE_NS::move(fun2)), enginequeue);
1044
1045 return result;
1046 }
GetAnimations(NapiApi::FunctionContext<> & ctx)1047 napi_value SceneJS::GetAnimations(NapiApi::FunctionContext<>& ctx)
1048 {
1049 auto scene = interface_pointer_cast<SCENE_NS::IScene>(GetThisNativeObject(ctx));
1050 if (!scene) {
1051 return ctx.GetUndefined();
1052 }
1053
1054 BASE_NS::vector<META_NS::IAnimation::Ptr> animRes;
1055 ExecSyncTask([scene, &animRes]() {
1056 animRes = scene->GetAnimations();
1057 return META_NS::IAny::Ptr {};
1058 });
1059
1060 napi_value tmp;
1061 auto status = napi_create_array_with_length(ctx, animRes.size(), &tmp);
1062 size_t i = 0;
1063 napi_value args[] = { ctx.This() };
1064 for (const auto& node : animRes) {
1065 napi_value val;
1066 val = CreateFromNativeInstance(
1067 ctx, interface_pointer_cast<META_NS::IObject>(node), false, BASE_NS::countof(args), args);
1068 status = napi_set_element(ctx, tmp, i++, val);
1069
1070 // disposables_[
1071 }
1072
1073 return tmp;
1074 }
1075
DisposeHook(uintptr_t token,NapiApi::Object obj)1076 void SceneJS::DisposeHook(uintptr_t token, NapiApi::Object obj)
1077 {
1078 disposables_[token] = { obj };
1079 }
ReleaseDispose(uintptr_t token)1080 void SceneJS::ReleaseDispose(uintptr_t token)
1081 {
1082 auto it = disposables_.find(token);
1083 if (it != disposables_.end()) {
1084 it->second.Reset();
1085 disposables_.erase(it->first);
1086 }
1087 }
1088
StrongDisposeHook(uintptr_t token,NapiApi::Object obj)1089 void SceneJS::StrongDisposeHook(uintptr_t token, NapiApi::Object obj)
1090 {
1091 strongDisposables_[token] = { obj };
1092 }
ReleaseStrongDispose(uintptr_t token)1093 void SceneJS::ReleaseStrongDispose(uintptr_t token)
1094 {
1095 auto it = strongDisposables_.find(token);
1096 if (it != strongDisposables_.end()) {
1097 it->second.Reset();
1098 strongDisposables_.erase(it->first);
1099 }
1100 }
1101
1102 #ifdef __OHOS_PLATFORM__
1103 // This will circumvent the broken napi_set_instance_data and napi_get_instance_data implementations
1104 // on ohos platform
1105 static void* g_instanceData = nullptr;
1106
SetInstanceData(napi_env env,void * data,napi_finalize finalizeCb,void * finalizeHint)1107 napi_status SetInstanceData(napi_env env, void* data, napi_finalize finalizeCb, void* finalizeHint)
1108 {
1109 g_instanceData = data;
1110 return napi_ok;
1111 }
1112
GetInstanceData(napi_env env,void ** data)1113 napi_status GetInstanceData(napi_env env, void** data)
1114 {
1115 if (data) {
1116 *data = g_instanceData;
1117 }
1118
1119 return napi_ok;
1120 }
1121
1122 #else // __OHOS_PLATFORM__
1123
SetInstanceData(napi_env env,void * data,napi_finalize finalizeCb,void * finalizeHint)1124 napi_status SetInstanceData(napi_env env, void* data, napi_finalize finalizeCb, void* finalizeHint)
1125 {
1126 return napi_set_instance_data(env, data, finalizeCb, finalizeHint);
1127 }
1128
GetInstanceData(napi_env env,void ** data)1129 napi_status GetInstanceData(napi_env env, void** data)
1130 {
1131 return napi_get_instance_data(env, data);
1132 }
1133
1134 #endif // __OHOS_PLATFORM__
1135
1136