• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 <future>
17 #include <napi/native_api.h>
18 #include <napi/native_node_api.h>
19 #include <queue>
20 #include <set>
21 #include <string>
22 #include <unistd.h>
23 #include "nlohmann/json.hpp"
24 #include "fcntl.h"
25 #include "ipc_transactor.h"
26 #include "common_utils.h"
27 #include "frontend_api_defines.h"
28 #include "api_caller_client.h"
29 #include "callback_code_napi.h"
30 
31 namespace OHOS::perftest {
32     using namespace nlohmann;
33     using namespace std;
34 
35     static constexpr size_t NAPI_MAX_BUF_LEN = 1024;
36     static constexpr size_t NAPI_MAX_ARG_COUNT = 8;
37     static constexpr size_t BACKEND_OBJ_GC_BATCH = 100;
38     // type of unexpected or napi-internal error
39     static constexpr napi_status NAPI_ERR = napi_status::napi_generic_failure;
40     // the name of property that represents the objectRef of the backend object
41     static constexpr char PROP_BACKEND_OBJ_REF[] = "backendObjRef";
42     /**For dfx usage, records the uncalled js apis. */
43     static set<string> g_unCalledJsFuncNames;
44     /**For gc usage, records the backend objRefs about to delete. */
45     static queue<string> g_backendObjsAboutToDelete;
46     static mutex g_gcQueueMutex;
47     /**IPC client. */
48     static ApiCallerClient g_apiCallerClient;
49     static future<void> g_establishConnectionFuture;
50 
51     /** Convert js string to cpp string.*/
JsStrToCppStr(napi_env env,napi_value jsStr)52     static string JsStrToCppStr(napi_env env, napi_value jsStr)
53     {
54         if (jsStr == nullptr) {
55             return "";
56         }
57         napi_valuetype type;
58         NAPI_CALL_BASE(env, napi_typeof(env, jsStr, &type), "");
59         if (type == napi_undefined || type == napi_null) {
60             return "";
61         }
62         size_t bufSize = 0;
63         char buf[NAPI_MAX_BUF_LEN] = {0};
64         NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, jsStr, buf, NAPI_MAX_BUF_LEN, &bufSize), "");
65         return string(buf, bufSize);
66     }
67 
68     /**Lifecycle function, establish connection async, called externally.*/
ScheduleEstablishConnection(napi_env env,napi_callback_info info)69     static napi_value ScheduleEstablishConnection(napi_env env, napi_callback_info info)
70     {
71         size_t argc = 1;
72         napi_value value = nullptr;
73         napi_value argv[1] = {0};
74         NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &value, nullptr));
75         NAPI_ASSERT(env, argc > 0, "Need session token argument!");
76         auto token = JsStrToCppStr(env, argv[0]);
77         g_establishConnectionFuture = async(launch::async, [env, token]() {
78             auto &instance = CallbackCodeNapi::Get();
79             using namespace std::placeholders;
80             auto callbackHandler = std::bind(&CallbackCodeNapi::HandleCallbackEvent, &instance, env, _1, _2);
81             auto result = g_apiCallerClient.InitAndConnectPeer(token, callbackHandler);
82             LOG_I("End setup transaction connection, result=%{public}d", result);
83         });
84         return nullptr;
85     }
86 
87     /**Wait connection result sync if need.*/
WaitForConnectionIfNeed()88     static void WaitForConnectionIfNeed()
89     {
90         if (g_establishConnectionFuture.valid()) {
91             LOG_I("Begin WaitForConnection");
92             g_establishConnectionFuture.get();
93         }
94     }
95 
96     /**Encapsulates the data objects needed in once api transaction.*/
97     struct TransactionContext {
98         napi_value jsThis_ = nullptr;
99         napi_value *jsArgs_ = nullptr;
100         ApiCallInfo callInfo_;
101     };
102 
CreateJsException(napi_env env,uint32_t code,string_view msg)103     static napi_value CreateJsException(napi_env env, uint32_t code, string_view msg)
104     {
105         napi_value codeValue;
106         napi_value msgValue;
107         napi_value errorValue;
108         napi_create_uint32(env, code, &codeValue);
109         napi_create_string_utf8(env, msg.data(), NAPI_AUTO_LENGTH, &msgValue);
110         napi_create_error(env, nullptr, msgValue, &errorValue);
111         napi_set_named_property(env, errorValue, "code", codeValue);
112         return errorValue;
113     }
114 
115     /**Set object constructor function to global as attribute.*/
MountJsConstructorToGlobal(napi_env env,string_view typeName,napi_value function)116     static napi_status MountJsConstructorToGlobal(napi_env env, string_view typeName, napi_value function)
117     {
118         NAPI_ASSERT_BASE(env, function != nullptr, "Null constructor function", napi_invalid_arg);
119         const string name = "constructor_" + string(typeName);
120         napi_value global = nullptr;
121         NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR);
122         NAPI_CALL_BASE(env, napi_set_named_property(env, global, name.c_str(), function), NAPI_ERR);
123         return napi_ok;
124     }
125 
126     /**Get object constructor function from global as attribute.*/
GetJsConstructorFromGlobal(napi_env env,string_view typeName,napi_value * pFunction)127     static napi_status GetJsConstructorFromGlobal(napi_env env, string_view typeName, napi_value *pFunction)
128     {
129         NAPI_ASSERT_BASE(env, pFunction != nullptr, "Null constructor receiver", napi_invalid_arg);
130         const string name = "constructor_" + string(typeName);
131         napi_value global = nullptr;
132         NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR);
133         NAPI_CALL_BASE(env, napi_get_named_property(env, global, name.c_str(), pFunction), NAPI_ERR);
134         return napi_ok;
135     }
136 
137     /**Conversion between value and string, using the builtin JSON methods.*/
ValueStringConvert(napi_env env,napi_value in,napi_value * out,bool stringify)138     static napi_status ValueStringConvert(napi_env env, napi_value in, napi_value *out, bool stringify)
139     {
140         if (in == nullptr || out == nullptr) {
141             return napi_invalid_arg;
142         }
143         napi_value global = nullptr;
144         napi_value jsonProp = nullptr;
145         napi_value jsonFunc = nullptr;
146         NAPI_CALL_BASE(env, napi_get_global(env, &global), NAPI_ERR);
147         NAPI_CALL_BASE(env, napi_get_named_property(env, global, "JSON", &jsonProp), NAPI_ERR);
148         if (stringify) {
149             NAPI_CALL_BASE(env, napi_get_named_property(env, jsonProp, "stringify", &jsonFunc), NAPI_ERR);
150         } else {
151             NAPI_CALL_BASE(env, napi_get_named_property(env, jsonProp, "parse", &jsonFunc), NAPI_ERR);
152         }
153         napi_value argv[1] = {in};
154         NAPI_CALL_BASE(env, napi_call_function(env, jsonProp, jsonFunc, 1, argv, out), NAPI_ERR);
155         return napi_ok;
156     }
157 
158     /**Unmarshal object from json, throw error and return false if the object cannot be deserialized.*/
UnmarshalObject(napi_env env,const json & in,napi_value * pOut,napi_value jsThis)159     static napi_status UnmarshalObject(napi_env env, const json &in, napi_value *pOut, napi_value jsThis)
160     {
161         NAPI_ASSERT_BASE(env, pOut != nullptr, "Illegal arguments", napi_invalid_arg);
162         const auto type = in.type();
163         if (type == nlohmann::detail::value_t::null) { // return null
164             NAPI_CALL_BASE(env, napi_get_null(env, pOut), NAPI_ERR);
165             return napi_ok;
166         }
167         if (type != nlohmann::detail::value_t::string) { // non-string value, convert and return object
168             NAPI_CALL_BASE(env, napi_create_string_utf8(env, in.dump().c_str(), NAPI_AUTO_LENGTH, pOut), NAPI_ERR);
169             NAPI_CALL_BASE(env, ValueStringConvert(env, *pOut, pOut, false), NAPI_ERR);
170             return napi_ok;
171         }
172         const auto cppString = in.get<string>();
173         string frontendTypeName;
174         bool bindJsThis = false;
175         for (const auto &classDef : FRONTEND_CLASS_DEFS) {
176             const auto objRefFormat = string(classDef->name_) + "#";
177             if (cppString.find(objRefFormat) == 0) {
178                 frontendTypeName = string(classDef->name_);
179                 bindJsThis = classDef->bindClassObject_;
180                 break;
181             }
182         }
183         NAPI_CALL_BASE(env, napi_create_string_utf8(env, cppString.c_str(), NAPI_AUTO_LENGTH, pOut), NAPI_ERR);
184         if (frontendTypeName.empty()) { // plain string, return it
185             return napi_ok;
186         }
187         LOG_I("Convert to frontend object: '%{public}s'", frontendTypeName.c_str());
188         // covert to wrapper object and bind the backend objectRef
189         napi_value refValue = *pOut;
190         napi_value constructor = nullptr;
191         NAPI_CALL_BASE(env, GetJsConstructorFromGlobal(env, frontendTypeName, &constructor), NAPI_ERR);
192         NAPI_CALL_BASE(env, napi_new_instance(env, constructor, 1, &refValue, pOut), NAPI_ERR);
193         NAPI_CALL_BASE(env, napi_set_named_property(env, *pOut, PROP_BACKEND_OBJ_REF, refValue), NAPI_ERR);
194         if (bindJsThis) { // bind the jsThis object
195             LOG_I("Bind jsThis");
196             NAPI_ASSERT_BASE(env, jsThis != nullptr, "null jsThis", NAPI_ERR);
197             NAPI_CALL_BASE(env, napi_set_named_property(env, *pOut, "boundObject", jsThis), NAPI_ERR);
198         }
199         return napi_ok;
200     }
201 
202     /**Evaluate and convert transaction reply to object. Return the exception raised during the
203      * transaction if any, else return the result object. */
UnmarshalReply(napi_env env,const TransactionContext & ctx,const ApiReplyInfo & reply)204     static napi_value UnmarshalReply(napi_env env, const TransactionContext &ctx, const ApiReplyInfo &reply)
205     {
206         LOG_I("Start to Unmarshal transaction result");
207         const auto &message = reply.exception_.message_;
208         ErrCode code = reply.exception_.code_;
209         if (reply.exception_.code_ != NO_ERROR) {
210             LOG_I("ErrorInfo: code='%{public}u', message='%{public}s'", code, message.c_str());
211             return CreateJsException(env, code, message);
212         }
213         LOG_I("Start to Unmarshal return value: %{public}s", reply.resultValue_.dump().c_str());
214         const auto resultType = reply.resultValue_.type();
215         napi_value result = nullptr;
216         if (resultType == nlohmann::detail::value_t::null) { // return null
217             NAPI_CALL(env, napi_get_null(env, &result));
218         } else if (resultType == nlohmann::detail::value_t::array) { // return array
219             NAPI_CALL(env, napi_create_array_with_length(env, reply.resultValue_.size(), &result));
220             for (size_t idx = 0; idx < reply.resultValue_.size(); idx++) {
221                 napi_value item = nullptr;
222                 NAPI_CALL(env, UnmarshalObject(env, reply.resultValue_.at(idx), &item, ctx.jsThis_));
223                 NAPI_CALL(env, napi_set_element(env, result, idx, item));
224             }
225         } else { // return single value
226             NAPI_CALL(env, UnmarshalObject(env, reply.resultValue_, &result, ctx.jsThis_));
227         }
228         return result;
229     }
230 
231     /**Call api with parameters out, wait for and return result value or throw raised exception.*/
TransactSync(napi_env env,TransactionContext & ctx)232     napi_value TransactSync(napi_env env, TransactionContext &ctx)
233     {
234         WaitForConnectionIfNeed();
235         LOG_I("TargetApi=%{public}s", ctx.callInfo_.apiId_.data());
236         auto reply = ApiReplyInfo();
237         g_apiCallerClient.Transact(ctx.callInfo_, reply);
238         auto resultValue = UnmarshalReply(env, ctx, reply);
239         auto isError = false;
240         NAPI_CALL(env, napi_is_error(env, resultValue, &isError));
241         if (isError) {
242             NAPI_CALL(env, napi_throw(env, resultValue));
243             NAPI_CALL(env, napi_get_undefined(env, &resultValue)); // return undefined it's error
244         }
245         // notify backend objects deleting
246         if (g_backendObjsAboutToDelete.size() >= BACKEND_OBJ_GC_BATCH) {
247             auto gcCall = ApiCallInfo {.apiId_ = "BackendObjectsCleaner"};
248             unique_lock<mutex> lock(g_gcQueueMutex);
249             for (size_t count = 0; count < BACKEND_OBJ_GC_BATCH; count++) {
250                 gcCall.paramList_.emplace_back(g_backendObjsAboutToDelete.front());
251                 g_backendObjsAboutToDelete.pop();
252             }
253             lock.unlock();
254             auto gcReply = ApiReplyInfo();
255             g_apiCallerClient.Transact(gcCall, gcReply);
256         }
257         return resultValue;
258     }
259 
260     /**Encapsulates the data objects needed for async transaction.*/
261     struct AsyncTransactionCtx {
262         TransactionContext ctx_;
263         ApiReplyInfo reply_;
264         napi_async_work asyncWork_ = nullptr;
265         napi_deferred deferred_ = nullptr;
266         napi_ref jsThisRef_ = nullptr;
267     };
268 
269     /**Call api with parameters out, return a promise.*/
TransactAsync(napi_env env,TransactionContext & ctx)270     static napi_value TransactAsync(napi_env env, TransactionContext &ctx)
271     {
272         constexpr uint32_t refCount = 1;
273         LOG_I("TargetApi=%{public}s", ctx.callInfo_.apiId_.data());
274         napi_value resName;
275         NAPI_CALL(env, napi_create_string_latin1(env, __FUNCTION__, NAPI_AUTO_LENGTH, &resName));
276         auto aCtx = new AsyncTransactionCtx();
277         aCtx->ctx_ = ctx;
278         napi_value promise;
279         NAPI_CALL(env, napi_create_promise(env, &(aCtx->deferred_), &promise));
280         NAPI_CALL(env, napi_create_reference(env, ctx.jsThis_, refCount, &(aCtx->jsThisRef_)));
281         napi_create_async_work(
282             env, nullptr, resName,
283             [](napi_env env, void *data) {
284                 auto aCtx = reinterpret_cast<AsyncTransactionCtx *>(data);
285                 // NOT:: use 'auto&' rather than 'auto', or the result will be set to copy-constructed temp-object
286                 auto &ctx = aCtx->ctx_;
287                 g_apiCallerClient.Transact(ctx.callInfo_, aCtx->reply_);
288             },
289             [](napi_env env, napi_status status, void *data) {
290                 auto aCtx = reinterpret_cast<AsyncTransactionCtx *>(data);
291                 napi_handle_scope scope = nullptr;
292                 napi_open_handle_scope(env, &scope);
293                 if (scope == nullptr) {
294                     return;
295                 }
296                 napi_get_reference_value(env, aCtx->jsThisRef_, &(aCtx->ctx_.jsThis_));
297                 auto resultValue = UnmarshalReply(env, aCtx->ctx_, aCtx->reply_);
298                 napi_delete_reference(env, aCtx->jsThisRef_);
299                 auto isError = false;
300                 napi_is_error(env, resultValue, &isError);
301                 if (isError) {
302                     napi_reject_deferred(env, aCtx->deferred_, resultValue);
303                 } else {
304                     napi_resolve_deferred(env, aCtx->deferred_, resultValue);
305                 }
306                 napi_delete_async_work(env, aCtx->asyncWork_);
307                 napi_close_handle_scope(env, scope);
308                 delete aCtx;
309             },
310             (void *)aCtx, &(aCtx->asyncWork_));
311         napi_queue_async_work(env, aCtx->asyncWork_);
312         return promise;
313     }
314 
GetBackendObjRefProp(napi_env env,napi_value value,napi_value * pOut)315     static napi_status GetBackendObjRefProp(napi_env env, napi_value value, napi_value* pOut)
316     {
317         napi_valuetype type = napi_undefined;
318         NAPI_CALL_BASE(env, napi_typeof(env, value, &type), NAPI_ERR);
319         if (type != napi_object) {
320             *pOut = nullptr;
321             return napi_ok;
322         }
323         bool hasRef = false;
324         NAPI_CALL_BASE(env, napi_has_named_property(env, value, PROP_BACKEND_OBJ_REF, &hasRef), NAPI_ERR);
325         if (!hasRef) {
326             *pOut = nullptr;
327         } else {
328             NAPI_CALL_BASE(env, napi_get_named_property(env, value, PROP_BACKEND_OBJ_REF, pOut), NAPI_ERR);
329         }
330         return napi_ok;
331     }
332 
PreprocessTransaction(napi_env env,TransactionContext & ctx,napi_value paramArray,napi_value & error)333     static void PreprocessTransaction(napi_env env, TransactionContext &ctx, napi_value paramArray, napi_value &error)
334     {
335         const auto &id = ctx.callInfo_.apiId_;
336         if (id  == "PerfTest.create") {
337             auto err = ApiCallErr(NO_ERROR);
338             bool isArray;
339             NAPI_CALL_RETURN_VOID(env, napi_is_array(env, paramArray, &isArray));
340             if (!isArray) {
341                 LOG_E("PreprocessTransaction failed, invalid type of paramArray");
342                 return;
343             }
344             uint32_t arrayLength;
345             NAPI_CALL_RETURN_VOID(env, napi_get_array_length(env, paramArray, &arrayLength));
346             if (arrayLength != 1) {
347                 LOG_E("PreprocessTransaction failed, invalid length of paramArray");
348                 return;
349             }
350             napi_value jsParam;
351             NAPI_CALL_RETURN_VOID(env, napi_get_element(env, paramArray, 0, &jsParam));
352             CallbackCodeNapi::Get().PreprocessCallback(env, ctx.callInfo_, ctx.jsThis_, jsParam, err);
353             if (err.code_ != NO_ERROR) {
354                 error = CreateJsException(env, err.code_, err.message_);
355             }
356         }
357     }
358 
359     /**Generic js-api callback.*/
GenericCallback(napi_env env,napi_callback_info info)360     static napi_value GenericCallback(napi_env env, napi_callback_info info)
361     {
362         // extract callback data
363         TransactionContext ctx;
364         napi_value argv[NAPI_MAX_ARG_COUNT] = {nullptr};
365         auto count = NAPI_MAX_ARG_COUNT;
366         void *pData = nullptr;
367         NAPI_CALL(env, napi_get_cb_info(env, info, &count, argv, &(ctx.jsThis_), &pData));
368         NAPI_ASSERT(env, pData != nullptr, "Null methodDef");
369         ctx.jsArgs_ = argv;
370         auto methodDef = reinterpret_cast<const FrontendMethodDef *>(pData);
371         g_unCalledJsFuncNames.erase(string(methodDef->name_)); // api used
372         // 1. marshal parameters into json-array
373         napi_value paramArray = nullptr;
374         NAPI_CALL(env, napi_create_array_with_length(env, count, &paramArray));
375         if (count > NAPI_MAX_ARG_COUNT) {
376             count = NAPI_MAX_ARG_COUNT;
377         }
378         for (size_t idx = 0; idx < count; idx++) {
379             napi_value item = nullptr; // convert to backendObjRef if any
380             NAPI_CALL(env, GetBackendObjRefProp(env, argv[idx], &item));
381             if (item == nullptr) {
382                 item = argv[idx];
383             }
384             NAPI_CALL(env, napi_set_element(env, paramArray, idx, item));
385         }
386         napi_value jsTempObj = nullptr;
387         NAPI_CALL(env, ValueStringConvert(env, paramArray, &jsTempObj, true));
388         ctx.callInfo_.paramList_ = nlohmann::json::parse(JsStrToCppStr(env, jsTempObj));
389         // 2. marshal jsThis into json (backendObjRef)
390         if (!methodDef->static_) {
391             NAPI_CALL(env, GetBackendObjRefProp(env, ctx.jsThis_, &jsTempObj));
392             ctx.callInfo_.callerObjRef_ = JsStrToCppStr(env, jsTempObj);
393         }
394         // 3. fill-in apiId
395         ctx.callInfo_.apiId_ = methodDef->name_;
396         napi_value error = nullptr;
397         PreprocessTransaction(env, ctx, paramArray, error);
398         if (error != nullptr) {
399             NAPI_CALL(env, napi_throw(env, error));
400             NAPI_CALL(env, napi_get_undefined(env, &error));
401             return error;
402         }
403         // 4. call out, sync or async
404         if (methodDef->fast_) {
405             return TransactSync(env, ctx);
406         } else {
407             return TransactAsync(env, ctx);
408         }
409     }
410 
411     /**Exports perftest js wrapper-classes and its global constructor.*/
ExportClass(napi_env env,napi_value exports,const FrontEndClassDef & classDef)412     static napi_status ExportClass(napi_env env, napi_value exports, const FrontEndClassDef &classDef)
413     {
414         NAPI_ASSERT_BASE(env, exports != nullptr, "Illegal export params", NAPI_ERR);
415         const auto name = classDef.name_.data();
416         const auto methodNeatNameOffset = classDef.name_.length() + 1;
417         auto descs = make_unique<napi_property_descriptor[]>(classDef.methodCount_);
418         for (size_t idx = 0; idx < classDef.methodCount_; idx++) {
419             const auto &methodDef = classDef.methods_[idx];
420             g_unCalledJsFuncNames.insert(string(methodDef.name_));
421             const auto neatName = methodDef.name_.substr(methodNeatNameOffset);
422             napi_property_descriptor desc = DECLARE_NAPI_FUNCTION(neatName.data(), GenericCallback);
423             if (methodDef.static_) {
424                 desc.attributes = napi_static;
425             }
426             desc.data = (void *)(&methodDef);
427             descs[idx] = desc;
428         }
429         constexpr auto initializer = [](napi_env env, napi_callback_info info) {
430             auto argc = NAPI_MAX_ARG_COUNT;
431             napi_value argv[NAPI_MAX_ARG_COUNT] = { nullptr };
432             napi_value jsThis = nullptr;
433             NAPI_CALL_BASE(env, napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr), jsThis);
434             auto ref = make_unique<string>(argc <= 0 ? "" : JsStrToCppStr(env, argv[0]));
435             auto finalizer = [](napi_env env, void *data, void *hint) {
436                 auto ref = reinterpret_cast<string *>(data);
437                 if (ref->length() > 0) {
438                     LOG_I("Finalizing object: %{public}s", ref->c_str());
439                     unique_lock<mutex> lock(g_gcQueueMutex);
440                     g_backendObjsAboutToDelete.push(*ref);
441                 }
442                 delete ref;
443             };
444             NAPI_CALL_BASE(env, napi_wrap(env, jsThis, ref.release(), finalizer, nullptr, nullptr), jsThis);
445             return jsThis;
446         };
447         // define class, provide the js-class members(property) and initializer.
448         napi_value ctor = nullptr;
449         NAPI_CALL_BASE(env, napi_define_class(env, name, NAPI_AUTO_LENGTH, initializer, nullptr,
450                                               classDef.methodCount_, descs.get(), &ctor), NAPI_ERR);
451         NAPI_CALL_BASE(env, napi_set_named_property(env, exports, name, ctor), NAPI_ERR);
452         NAPI_CALL_BASE(env, MountJsConstructorToGlobal(env, name, ctor), NAPI_ERR);
453         return napi_ok;
454     }
455 
456     /**Exports enumerator class.*/
ExportEnumerator(napi_env env,napi_value exports,const FrontendEnumeratorDef & enumDef)457     static napi_status ExportEnumerator(napi_env env, napi_value exports, const FrontendEnumeratorDef &enumDef)
458     {
459         NAPI_ASSERT_BASE(env, exports != nullptr, "Illegal export params", NAPI_ERR);
460         napi_value enumerator;
461         NAPI_CALL_BASE(env, napi_create_object(env, &enumerator), NAPI_ERR);
462         for (size_t idx = 0; idx < enumDef.valueCount_; idx++) {
463             const auto &def = enumDef.values_[idx];
464             napi_value prop = nullptr;
465             NAPI_CALL_BASE(env, napi_create_string_utf8(env, def.valueJson_.data(), NAPI_AUTO_LENGTH, &prop), NAPI_ERR);
466             NAPI_CALL_BASE(env, ValueStringConvert(env, prop, &prop, false), NAPI_ERR);
467             NAPI_CALL_BASE(env, napi_set_named_property(env, enumerator, def.name_.data(), prop), NAPI_ERR);
468         }
469         NAPI_CALL_BASE(env, napi_set_named_property(env, exports, enumDef.name_.data(), enumerator), NAPI_ERR);
470         return napi_ok;
471     }
472 
473     /**Function used for statistics, return an array of uncalled js-api names.*/
GetUnCalledJsApis(napi_env env,napi_callback_info info)474     static napi_value GetUnCalledJsApis(napi_env env, napi_callback_info info)
475     {
476         napi_value nameArray;
477         NAPI_CALL(env, napi_create_array_with_length(env, g_unCalledJsFuncNames.size(), &nameArray));
478         size_t idx = 0;
479         for (auto &name : g_unCalledJsFuncNames) {
480             napi_value nameItem = nullptr;
481             NAPI_CALL(env, napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &nameItem));
482             NAPI_CALL(env, napi_set_element(env, nameArray, idx, nameItem));
483             idx++;
484         }
485         return nameArray;
486     }
487 
Export(napi_env env,napi_value exports)488     napi_value Export(napi_env env, napi_value exports)
489     {
490         LOG_I("Begin export perftest apis");
491         // export transaction-environment-lifecycle callbacks and dfx functions
492         napi_property_descriptor props[] = {
493             DECLARE_NAPI_STATIC_FUNCTION("scheduleEstablishConnection", ScheduleEstablishConnection),
494             DECLARE_NAPI_STATIC_FUNCTION("getUnCalledJsApis", GetUnCalledJsApis),
495         };
496         NAPI_CALL(env, napi_define_properties(env, exports, sizeof(props) / sizeof(props[0]), props));
497         NAPI_CALL(env, ExportClass(env, exports, PERF_TEST_DEF));
498         NAPI_CALL(env, ExportEnumerator(env, exports, PERF_METRIC_DEF));
499         LOG_I("End export perftest apis");
500         return exports;
501     }
502 
503     static napi_module module = {
504         .nm_version = 1,
505         .nm_flags = 0,
506         .nm_filename = nullptr,
507         .nm_register_func = Export,
508         .nm_modname = "test.PerfTest",
509         .nm_priv = ((void *)0),
510         .reserved = {0},
511     };
512 
RegisterModule(void)513     extern "C" __attribute__((constructor)) void RegisterModule(void)
514     {
515         napi_module_register(&module);
516     }
517 } // namespace OHOS::perftest
518 
519 // put register functions out of namespace to ensure C-linkage
520 extern const char _binary_perftest_exporter_js_start[];
521 extern const char _binary_perftest_exporter_js_end[];
522 extern const char _binary_perftest_exporter_abc_start[];
523 extern const char _binary_perftest_exporter_abc_end[];
524 
NAPI_test_PerfTest_GetJSCode(const char ** buf,int * bufLen)525 extern "C" __attribute__((visibility("default"))) void NAPI_test_PerfTest_GetJSCode(const char **buf, int *bufLen)
526 {
527     if (buf != nullptr) {
528         *buf = _binary_perftest_exporter_js_start;
529     }
530     if (bufLen != nullptr) {
531         *bufLen = _binary_perftest_exporter_js_end - _binary_perftest_exporter_js_start;
532     }
533 }
534 
NAPI_test_PerfTest_GetABCCode(const char ** buf,int * bufLen)535 extern "C" __attribute__((visibility("default"))) void NAPI_test_PerfTest_GetABCCode(const char **buf, int *bufLen)
536 {
537     if (buf != nullptr) {
538         *buf = _binary_perftest_exporter_abc_start;
539     }
540     if (bufLen != nullptr) {
541         *bufLen = _binary_perftest_exporter_abc_end - _binary_perftest_exporter_abc_start;
542     }
543 }
544