1 /*
2 * Copyright (c) 2023-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 "js_drawable_descriptor.h"
17
18 #include "napi/native_api.h"
19 #include "napi/native_common.h"
20 #include "napi/native_node_api.h"
21 #ifndef PREVIEW
22 #include "pixel_map_napi.h"
23 #else
24 #include "image_source_preview.h"
25 #endif
26 #include "resource_manager.h"
27
28 #include "base/log.h"
29
30 namespace {
31 constexpr char DRAWABLE_BASE[] = "DrawableDescriptor";
32 constexpr char DRAWABLE_LAYERED[] = "LayeredDrawableDescriptor";
33 constexpr char DRAWABLE_ANIMATED[] = "AnimatedDrawableDescriptor";
34 constexpr char DRAWABLE_PIXELMAP[] = "PixelMapDrawableDescriptor";
35 constexpr int32_t PARAMS_NUM_ONE = 1;
36 constexpr int32_t PARAMS_NUM_THREE = 3;
37
38 constexpr int32_t FOREGROUND_INDEX = 0;
39 constexpr int32_t BACKGROUND_INDEX = 1;
40 constexpr int32_t MASK_INDEX = 2;
41
UpdateLayeredParam(OHOS::Ace::Napi::LayeredDrawableDescriptor * layeredDrawable,int32_t pos,std::shared_ptr<OHOS::Media::PixelMap> pixelMap)42 void UpdateLayeredParam(OHOS::Ace::Napi::LayeredDrawableDescriptor* layeredDrawable, int32_t pos,
43 std::shared_ptr<OHOS::Media::PixelMap> pixelMap)
44 {
45 if (!layeredDrawable || !pixelMap) {
46 return;
47 }
48 switch (pos) {
49 case FOREGROUND_INDEX:
50 layeredDrawable->SetForeground(pixelMap);
51 break;
52 case BACKGROUND_INDEX:
53 layeredDrawable->SetBackground(pixelMap);
54 break;
55 case MASK_INDEX:
56 layeredDrawable->SetMask(pixelMap);
57 break;
58 default:
59 HILOGW("Arg[%{public}d] index error", pos);
60 }
61 }
62 } // namespace
63
64 namespace OHOS::Ace::Napi {
65 thread_local napi_ref JsDrawableDescriptor::baseConstructor_;
66 thread_local napi_ref JsDrawableDescriptor::layeredConstructor_;
67 thread_local napi_ref JsDrawableDescriptor::animatedConstructor_;
68 thread_local napi_ref JsDrawableDescriptor::pixelMapConstructor_;
69
GetPixelMapArray(napi_env env,napi_value arg,std::vector<std::shared_ptr<Media::PixelMap>> & pixelMaps)70 static bool GetPixelMapArray(napi_env env, napi_value arg, std::vector<std::shared_ptr<Media::PixelMap>>& pixelMaps)
71 {
72 bool isArray = false;
73 uint32_t length = 0;
74 napi_is_array(env, arg, &isArray);
75 if (!isArray) {
76 return false;
77 }
78 napi_get_array_length(env, arg, &length);
79 if (length < 1) {
80 return false;
81 }
82 for (uint32_t i = 0; i < length; i++) {
83 napi_value pixelMapValue = nullptr;
84 napi_get_element(env, arg, i, &pixelMapValue);
85 if (pixelMapValue == nullptr) {
86 continue;
87 }
88 Media::PixelMapNapi* pixmapNapi = nullptr;
89 napi_unwrap(env, pixelMapValue, reinterpret_cast<void**>(&pixmapNapi));
90 if (pixmapNapi == nullptr) {
91 continue;
92 }
93 pixelMaps.push_back(*(pixmapNapi->GetPixelMap()));
94 }
95 if (pixelMaps.size() <= 0) {
96 return false;
97 }
98 return true;
99 }
100
AnimatedConstructor(napi_env env,napi_callback_info info)101 napi_value JsDrawableDescriptor::AnimatedConstructor(napi_env env, napi_callback_info info)
102 {
103 napi_escapable_handle_scope scope = nullptr;
104 napi_open_escapable_handle_scope(env, &scope);
105
106 size_t argc = 2;
107 napi_value argv[2] = { nullptr };
108 napi_value thisVar = nullptr;
109 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
110 if (status != napi_ok) {
111 GET_AND_THROW_LAST_ERROR((env));
112 napi_close_escapable_handle_scope(env, scope);
113 return nullptr;
114 }
115 if (argc < 1) {
116 napi_close_escapable_handle_scope(env, scope);
117 return nullptr;
118 }
119 std::vector<std::shared_ptr<Media::PixelMap>> pixelMaps;
120 if (!GetPixelMapArray(env, argv[0], pixelMaps)) {
121 napi_close_escapable_handle_scope(env, scope);
122 return nullptr;
123 }
124
125 // napi_get_named_property
126 napi_value napiDuration;
127 napi_value napiIterations;
128 int32_t duration = -1;
129 int32_t iterations = 1;
130 if (argc > 1 && argv[1]) {
131 napi_get_named_property(env, argv[1], "duration", &napiDuration);
132 napi_get_named_property(env, argv[1], "iterations", &napiIterations);
133 napi_get_value_int32(env, napiDuration, &duration);
134 napi_get_value_int32(env, napiIterations, &iterations);
135 }
136 // create JsDrawable
137 auto* animatedDrawable = new AnimatedDrawableDescriptor(pixelMaps, duration, iterations);
138 // wrap to napi_value
139 napi_wrap(env, thisVar, animatedDrawable, Destructor, nullptr, nullptr);
140 napi_escape_handle(env, scope, thisVar, &thisVar);
141 napi_close_escapable_handle_scope(env, scope);
142 return thisVar;
143 }
144
Constructor(napi_env env,napi_callback_info info)145 napi_value JsDrawableDescriptor::Constructor(napi_env env, napi_callback_info info)
146 {
147 napi_escapable_handle_scope scope = nullptr;
148 napi_open_escapable_handle_scope(env, &scope);
149 napi_value thisVar = nullptr;
150 napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
151 if (status != napi_ok) {
152 GET_AND_THROW_LAST_ERROR((env));
153 napi_close_escapable_handle_scope(env, scope);
154 return nullptr;
155 }
156 // create JsDrawable
157 auto* drawable = new DrawableDescriptor;
158 // wrap to napi_value
159 napi_wrap(env, thisVar, drawable, Destructor, nullptr, nullptr);
160 napi_escape_handle(env, scope, thisVar, &thisVar);
161 napi_close_escapable_handle_scope(env, scope);
162 return thisVar;
163 }
164
PixelMapConstructor(napi_env env,napi_callback_info info)165 napi_value JsDrawableDescriptor::PixelMapConstructor(napi_env env, napi_callback_info info)
166 {
167 napi_escapable_handle_scope scope = nullptr;
168 napi_open_escapable_handle_scope(env, &scope);
169 size_t argc = PARAMS_NUM_ONE;
170 napi_value argv[argc];
171 napi_value thisVar = nullptr;
172 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
173 if (status != napi_ok) {
174 GET_AND_THROW_LAST_ERROR((env));
175 napi_close_escapable_handle_scope(env, scope);
176 return nullptr;
177 }
178 auto* drawable = new DrawableDescriptor;
179 if (argc == 0) {
180 HILOGW("JsDrawableDescriptor::PixelMapConstructor get null pixelMap param");
181 napi_wrap(env, thisVar, drawable, Destructor, nullptr, nullptr);
182 napi_escape_handle(env, scope, thisVar, &thisVar);
183 napi_close_escapable_handle_scope(env, scope);
184 return thisVar;
185 }
186 napi_value argPixelMap = argv[0];
187 napi_valuetype type;
188 napi_typeof(env, argPixelMap, &type);
189 if (type == napi_object) {
190 auto pixelMap = GetPixelMapFromNapi(env, argPixelMap);
191 if (pixelMap) {
192 drawable->SetPixelMap(pixelMap);
193 }
194 }
195
196 // wrap to napi_value
197 napi_wrap(env, thisVar, drawable, Destructor, nullptr, nullptr);
198 napi_escape_handle(env, scope, thisVar, &thisVar);
199 napi_close_escapable_handle_scope(env, scope);
200 return thisVar;
201 }
202
LayeredConstructor(napi_env env,napi_callback_info info)203 napi_value JsDrawableDescriptor::LayeredConstructor(napi_env env, napi_callback_info info)
204 {
205 napi_escapable_handle_scope scope = nullptr;
206 napi_open_escapable_handle_scope(env, &scope);
207 size_t argc = PARAMS_NUM_THREE;
208 napi_value argv[argc];
209 napi_value thisVar = nullptr;
210 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
211 if (status != napi_ok) {
212 GET_AND_THROW_LAST_ERROR((env));
213 napi_close_escapable_handle_scope(env, scope);
214 return nullptr;
215 }
216 auto pos = -1;
217 auto* layeredDrawable = new LayeredDrawableDescriptor;
218 if (argc == 0) {
219 napi_wrap(env, thisVar, layeredDrawable, Destructor, nullptr, nullptr);
220 napi_escape_handle(env, scope, thisVar, &thisVar);
221 napi_close_escapable_handle_scope(env, scope);
222 return thisVar;
223 }
224 std::shared_ptr<Media::PixelMap> foregroundPixelMap = nullptr;
225 auto updateUndefinedPixelMap = [&pos, &layeredDrawable, &foregroundPixelMap]() -> void {
226 if (pos == MASK_INDEX) {
227 std::shared_ptr<Global::Resource::ResourceManager> resMgr(Global::Resource::CreateResourceManager());
228 layeredDrawable->InitialMask(resMgr);
229 } else if (pos == BACKGROUND_INDEX && foregroundPixelMap) {
230 UpdateLayeredParam(layeredDrawable, pos, foregroundPixelMap);
231 }
232 };
233 napi_valuetype type;
234 for (const auto& arg : argv) {
235 pos++;
236 napi_typeof(env, arg, &type);
237 if (type == napi_undefined) {
238 updateUndefinedPixelMap();
239 continue;
240 }
241 auto pixelMap = GetPixelMapFromDrawableNapi(env, arg);
242 if (!pixelMap) {
243 updateUndefinedPixelMap();
244 } else {
245 UpdateLayeredParam(layeredDrawable, pos, pixelMap);
246 if (pos == FOREGROUND_INDEX)
247 foregroundPixelMap = std::move(pixelMap);
248 }
249 }
250 napi_wrap(env, thisVar, layeredDrawable, Destructor, nullptr, nullptr);
251 napi_escape_handle(env, scope, thisVar, &thisVar);
252 napi_close_escapable_handle_scope(env, scope);
253 return thisVar;
254 }
255
GetPixelMapFromNapi(napi_env env,napi_value napiValue)256 std::shared_ptr<Media::PixelMap> JsDrawableDescriptor::GetPixelMapFromNapi(napi_env env, napi_value napiValue)
257 {
258 Media::PixelMapNapi* pixelMapNapi = nullptr;
259 napi_unwrap(env, napiValue, reinterpret_cast<void**>(&pixelMapNapi));
260 if (pixelMapNapi == nullptr) {
261 HILOGI("JsDrawableDescriptor::GetPixelMapFromNapi Argv is invalid");
262 return nullptr;
263 }
264 auto pixelMap = *(pixelMapNapi->GetPixelMap());
265 if (pixelMap == nullptr) {
266 HILOGI("JsDrawableDescriptor::GetPixelMapFromNapi GetPixelNapiInner from media is nullptr");
267 return nullptr;
268 }
269 return pixelMap;
270 }
271
GetPixelMapFromDrawableNapi(napi_env env,napi_value napiValue)272 std::shared_ptr<Media::PixelMap> JsDrawableDescriptor::GetPixelMapFromDrawableNapi(napi_env env, napi_value napiValue)
273 {
274 DrawableDescriptor* drawableNapi = nullptr;
275 napi_unwrap(env, napiValue, reinterpret_cast<void**>(&drawableNapi));
276 if (drawableNapi == nullptr) {
277 HILOGW("JsDrawableDescriptor::GetPixelMapFromDrawableNapi Argv is invalid");
278 return nullptr;
279 }
280 auto pixelMap = drawableNapi->GetPixelMap();
281 if (pixelMap == nullptr) {
282 HILOGW("JsDrawableDescriptor::GetPixelMapFromDrawableNapi GetPixelNapiInner from media is nullptr");
283 return nullptr;
284 }
285 return pixelMap;
286 }
287
Destructor(napi_env,void * data,void *)288 void JsDrawableDescriptor::Destructor(napi_env /* env */, void* data, void* /* hint */)
289 {
290 auto* field = reinterpret_cast<DrawableDescriptor*>(data);
291 delete field;
292 }
293
InitDrawable(napi_env env)294 napi_value JsDrawableDescriptor::InitDrawable(napi_env env)
295 {
296 napi_value cons = nullptr;
297 napi_property_descriptor baseDes[] = {
298 DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
299 };
300 NAPI_CALL(env, napi_define_class(env, DRAWABLE_BASE, NAPI_AUTO_LENGTH, Constructor, nullptr,
301 sizeof(baseDes) / sizeof(napi_property_descriptor), baseDes, &cons));
302 NAPI_CALL(env, napi_create_reference(env, cons, 1, &baseConstructor_));
303 return cons;
304 }
305
InitPixelMapDrawable(napi_env env)306 napi_value JsDrawableDescriptor::InitPixelMapDrawable(napi_env env)
307 {
308 napi_value cons = nullptr;
309 napi_property_descriptor pixelDes[] = {
310 DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
311 };
312 NAPI_CALL(env, napi_define_class(env, DRAWABLE_PIXELMAP, NAPI_AUTO_LENGTH, PixelMapConstructor, nullptr,
313 sizeof(pixelDes) / sizeof(napi_property_descriptor), pixelDes, &cons));
314 NAPI_CALL(env, napi_create_reference(env, cons, 1, &pixelMapConstructor_));
315 return cons;
316 }
317
InitLayeredDrawable(napi_env env)318 napi_value JsDrawableDescriptor::InitLayeredDrawable(napi_env env)
319 {
320 napi_value cons = nullptr;
321 napi_property_descriptor layeredDes[] = {
322 DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
323 DECLARE_NAPI_FUNCTION("getForeground", GetForeground),
324 DECLARE_NAPI_FUNCTION("getBackground", GetBackground),
325 DECLARE_NAPI_FUNCTION("getMask", GetMask),
326 DECLARE_NAPI_STATIC_FUNCTION("getMaskClipPath", GetMaskClipPath),
327 };
328 NAPI_CALL(env, napi_define_class(env, DRAWABLE_LAYERED, NAPI_AUTO_LENGTH, LayeredConstructor, nullptr,
329 sizeof(layeredDes) / sizeof(napi_property_descriptor), layeredDes, &cons));
330 NAPI_CALL(env, napi_create_reference(env, cons, 1, &layeredConstructor_));
331 return cons;
332 }
333
InitAnimatedDrawable(napi_env env)334 napi_value JsDrawableDescriptor::InitAnimatedDrawable(napi_env env)
335 {
336 napi_value cons = nullptr;
337 napi_property_descriptor animatedDes[] = {
338 DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
339 };
340 NAPI_CALL(env, napi_define_class(env, DRAWABLE_ANIMATED, NAPI_AUTO_LENGTH, AnimatedConstructor, nullptr,
341 sizeof(animatedDes) / sizeof(napi_property_descriptor), animatedDes, &cons));
342 NAPI_CALL(env, napi_create_reference(env, cons, 1, &animatedConstructor_));
343 return cons;
344 }
345
ToNapi(napi_env env,DrawableDescriptor * drawable,DrawableDescriptor::DrawableType type)346 napi_value JsDrawableDescriptor::ToNapi(
347 napi_env env, DrawableDescriptor* drawable, DrawableDescriptor::DrawableType type)
348 {
349 if (!drawable) {
350 return nullptr;
351 }
352 if (!baseConstructor_ || !layeredConstructor_ || !animatedConstructor_ || !pixelMapConstructor_) {
353 // init js class constructor by importing module manually
354 napi_value globalValue;
355 napi_get_global(env, &globalValue);
356 napi_value func;
357 napi_get_named_property(env, globalValue, "requireNapi", &func);
358
359 napi_value module;
360 napi_create_string_utf8(env, MODULE_NAME, NAPI_AUTO_LENGTH, &module);
361 napi_value returnValue;
362 napi_call_function(env, globalValue, func, 1, &module, &returnValue);
363 }
364
365 napi_value constructor = nullptr;
366 napi_value result = nullptr;
367 napi_status status;
368 switch (type) {
369 case DrawableDescriptor::DrawableType::ANIMATED:
370 status = napi_get_reference_value(env, animatedConstructor_, &constructor);
371 break;
372 case DrawableDescriptor::DrawableType::LAYERED:
373 status = napi_get_reference_value(env, layeredConstructor_, &constructor);
374 break;
375 case DrawableDescriptor::DrawableType::BASE:
376 status = napi_get_reference_value(env, baseConstructor_, &constructor);
377 break;
378 case DrawableDescriptor::DrawableType::PIXELMAP:
379 status = napi_get_reference_value(env, pixelMapConstructor_, &constructor);
380 break;
381 default:
382 status = napi_status::napi_invalid_arg;
383 break;
384 }
385 if (status == napi_status::napi_ok) {
386 NAPI_CALL(env, napi_new_instance(env, constructor, 0, nullptr, &result));
387 NAPI_CALL(env, napi_wrap(env, result, drawable, Destructor, nullptr, nullptr));
388 } else {
389 HILOGI("create reference failed, drawable constructor is null");
390 }
391
392 return result;
393 }
394
GetPixelMap(napi_env env,napi_callback_info info)395 napi_value JsDrawableDescriptor::GetPixelMap(napi_env env, napi_callback_info info)
396 {
397 napi_escapable_handle_scope scope = nullptr;
398 napi_open_escapable_handle_scope(env, &scope);
399 napi_value thisVar = nullptr;
400 napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
401 if (status != napi_ok) {
402 GET_AND_THROW_LAST_ERROR((env));
403 napi_close_escapable_handle_scope(env, scope);
404 return nullptr;
405 }
406 void* native = nullptr;
407 napi_unwrap(env, thisVar, &native);
408 auto* drawable = reinterpret_cast<DrawableDescriptor*>(native);
409 if (!drawable) {
410 napi_close_escapable_handle_scope(env, scope);
411 return nullptr;
412 }
413 auto pixmap = drawable->GetPixelMap();
414
415 napi_value result = Media::PixelMapNapi::CreatePixelMap(env, pixmap);
416 napi_escape_handle(env, scope, result, &result);
417 napi_close_escapable_handle_scope(env, scope);
418 return result;
419 }
420
GetForeground(napi_env env,napi_callback_info info)421 napi_value JsDrawableDescriptor::GetForeground(napi_env env, napi_callback_info info)
422 {
423 napi_escapable_handle_scope scope = nullptr;
424 napi_open_escapable_handle_scope(env, &scope);
425 napi_value thisVar = nullptr;
426 napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
427 if (status != napi_ok) {
428 GET_AND_THROW_LAST_ERROR((env));
429 napi_close_escapable_handle_scope(env, scope);
430 return nullptr;
431 }
432 void* native = nullptr;
433 napi_unwrap(env, thisVar, &native);
434 auto* drawable = reinterpret_cast<LayeredDrawableDescriptor*>(native);
435 if (!drawable) {
436 napi_close_escapable_handle_scope(env, scope);
437 return nullptr;
438 }
439
440 auto foreground = drawable->GetForeground();
441 napi_value result = ToNapi(env, foreground.release(), DrawableDescriptor::DrawableType::BASE);
442 napi_escape_handle(env, scope, result, &result);
443 napi_close_escapable_handle_scope(env, scope);
444 return result;
445 }
446
GetBackground(napi_env env,napi_callback_info info)447 napi_value JsDrawableDescriptor::GetBackground(napi_env env, napi_callback_info info)
448 {
449 napi_escapable_handle_scope scope = nullptr;
450 napi_open_escapable_handle_scope(env, &scope);
451 napi_value thisVar = nullptr;
452 napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
453 if (status != napi_ok) {
454 GET_AND_THROW_LAST_ERROR((env));
455 napi_close_escapable_handle_scope(env, scope);
456 return nullptr;
457 }
458 void* native = nullptr;
459 napi_unwrap(env, thisVar, &native);
460 auto* drawable = reinterpret_cast<LayeredDrawableDescriptor*>(native);
461 if (!drawable) {
462 napi_close_escapable_handle_scope(env, scope);
463 return nullptr;
464 }
465
466 auto background = drawable->GetBackground();
467 napi_value result = ToNapi(env, background.release(), DrawableDescriptor::DrawableType::BASE);
468 napi_escape_handle(env, scope, result, &result);
469 napi_close_escapable_handle_scope(env, scope);
470 return result;
471 }
472
GetMask(napi_env env,napi_callback_info info)473 napi_value JsDrawableDescriptor::GetMask(napi_env env, napi_callback_info info)
474 {
475 napi_escapable_handle_scope scope = nullptr;
476 napi_open_escapable_handle_scope(env, &scope);
477 napi_value thisVar = nullptr;
478 napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
479 if (status != napi_ok) {
480 GET_AND_THROW_LAST_ERROR((env));
481 napi_close_escapable_handle_scope(env, scope);
482 return nullptr;
483 }
484 void* native = nullptr;
485 napi_unwrap(env, thisVar, &native);
486 auto* drawable = reinterpret_cast<LayeredDrawableDescriptor*>(native);
487 if (!drawable) {
488 napi_close_escapable_handle_scope(env, scope);
489 return nullptr;
490 }
491
492 auto mask = drawable->GetMask();
493 napi_value result = ToNapi(env, mask.release(), DrawableDescriptor::DrawableType::BASE);
494 napi_escape_handle(env, scope, result, &result);
495 napi_close_escapable_handle_scope(env, scope);
496 return result;
497 }
498
GetMaskClipPath(napi_env env,napi_callback_info info)499 napi_value JsDrawableDescriptor::GetMaskClipPath(napi_env env, napi_callback_info info)
500 {
501 auto path = OHOS::Ace::Napi::LayeredDrawableDescriptor::GetStaticMaskClipPath();
502 napi_value result = nullptr;
503 if (napi_ok != napi_create_string_utf8(env, path.c_str(), NAPI_AUTO_LENGTH, &result)) {
504 HILOGI("JsDrawableDescriptor Failed");
505 }
506 return result;
507 }
508
Export(napi_env env,napi_value exports)509 napi_value JsDrawableDescriptor::Export(napi_env env, napi_value exports)
510 {
511 // export base class
512 napi_value cons = InitDrawable(env);
513 NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_BASE, cons));
514
515 cons = InitPixelMapDrawable(env);
516 NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_PIXELMAP, cons));
517
518 // export child class
519 cons = InitLayeredDrawable(env);
520 NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_LAYERED, cons));
521
522 // export child class
523 cons = InitAnimatedDrawable(env);
524 NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_ANIMATED, cons));
525
526 return exports;
527 }
528 } // namespace OHOS::Ace::Napi
529