1 /*
2 * Copyright (c) 2021-2022 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 "frameworks/bridge/js_frontend/engine/quickjs/qjs_utils.h"
17
18 #include <sys/types.h>
19 #include <unistd.h>
20
21 #include "base/log/event_report.h"
22 #include "base/log/log.h"
23 #include "base/utils/macros.h"
24 #include "core/common/ace_application_info.h"
25 #include "core/common/ace_engine.h"
26
27 namespace OHOS::Ace::Framework {
28
29 namespace {
30
31 const char JS_CRASH[] = "Js Crash";
32 const char JS_CALLBACK_FAILED[] = "JS framework execute callback failed";
33 const char DESTROY_APP_ERROR[] = "Destroy application failed";
34 const char DESTROY_PAGE_FAILED[] = "Destroy page instance failed";
35 const char LOAD_JS_BUNDLE_FAILED[] = "JS framework load js bundle failed";
36 const char EVAL_BUFFER_FAILED[] = "Eval buffer failed";
37 const char READ_OBJECT_FAILED[] = "Read object failed";
38 const char COMPILE_AND_RUN_BUNDLE_FAILED[] = "Js compile and run bundle failed";
39 const char LOAD_JS_FRAMEWORK_FAILED[] = "Loading JS framework failed";
40 const char FIRE_EVENT_FAILED[] = "Fire event failed";
41
42 // The quickjs engine reports that the line number is biased, so the "OFFSET_PREVIEW_JS"
43 // and "OFFSET_PREVIEW_ETS" are used for correction.
44 const int32_t OFFSET_PREVIEW_JS = 9;
45 const int32_t OFFSET_PREVIEW_ETS = 14;
46 // The "STATE_MGMT_FILE" is used to filter the file "stateMgmt.js" in the error message,
47 // which belongs to the framework.
48 const char STATE_MGMT_FILE[] = "(stateMgmt.js";
49 // The "BUNDLE_FILE_FLAG" is used to filter the files in the error message, which belongs
50 // to the js bundle.
51 const char BUNDLE_FILE_FLAG[] = "(./";
52
GetReason(JsErrorType errorType)53 std::string GetReason(JsErrorType errorType)
54 {
55 std::string reasonStr;
56 switch (errorType) {
57 case OHOS::Ace::Framework::JsErrorType::JS_CRASH:
58 reasonStr = JS_CRASH;
59 break;
60 case OHOS::Ace::Framework::JsErrorType::JS_CALLBACK_ERROR:
61 reasonStr = JS_CALLBACK_FAILED;
62 break;
63 case OHOS::Ace::Framework::JsErrorType::EVAL_BUFFER_ERROR:
64 reasonStr = EVAL_BUFFER_FAILED;
65 break;
66 case OHOS::Ace::Framework::JsErrorType::DESTROY_APP_ERROR:
67 reasonStr = DESTROY_APP_ERROR;
68 break;
69 case OHOS::Ace::Framework::JsErrorType::DESTROY_PAGE_ERROR:
70 reasonStr = DESTROY_PAGE_FAILED;
71 break;
72 case OHOS::Ace::Framework::JsErrorType::LOAD_JS_BUNDLE_ERROR:
73 reasonStr = LOAD_JS_BUNDLE_FAILED;
74 break;
75 case OHOS::Ace::Framework::JsErrorType::READ_OBJECT_ERROR:
76 reasonStr = READ_OBJECT_FAILED;
77 break;
78 case OHOS::Ace::Framework::JsErrorType::COMPILE_AND_RUN_BUNDLE_ERROR:
79 reasonStr = COMPILE_AND_RUN_BUNDLE_FAILED;
80 break;
81 case OHOS::Ace::Framework::JsErrorType::LOAD_JS_FRAMEWORK_ERROR:
82 reasonStr = LOAD_JS_FRAMEWORK_FAILED;
83 break;
84 case OHOS::Ace::Framework::JsErrorType::FIRE_EVENT_ERROR:
85 reasonStr = FIRE_EVENT_FAILED;
86 break;
87 default:
88 reasonStr = JS_CRASH;
89 break;
90 }
91
92 return reasonStr;
93 }
94
GenerateSummaryBody(const char * exceptionStr,const char * stackTrace,int32_t instanceId,const char * pageUrl)95 std::string GenerateSummaryBody(
96 const char* exceptionStr, const char* stackTrace, int32_t instanceId, const char* pageUrl)
97 {
98 std::string abilityName;
99 if (AceEngine::Get().GetContainer(instanceId) != nullptr) {
100 abilityName = AceEngine::Get().GetContainer(instanceId)->GetHostClassName();
101 }
102
103 std::string summaryBody;
104 summaryBody.append("Lifetime: ")
105 .append(std::to_string(OHOS::Ace::AceApplicationInfo::GetInstance().GetLifeTime()))
106 .append("s")
107 .append("\n");
108
109 if (!abilityName.empty()) {
110 summaryBody.append("Ability: ").append(abilityName).append("\n");
111 }
112
113 if (pageUrl != nullptr) {
114 summaryBody.append("page: ").append(pageUrl).append("\n");
115 }
116
117 summaryBody.append("Js-Engine: Quick JS\n");
118 if (exceptionStr == nullptr && stackTrace == nullptr) {
119 return summaryBody;
120 }
121
122 summaryBody.append("Stacktrace: ");
123 if (exceptionStr != nullptr) {
124 summaryBody.append(exceptionStr).append("\n");
125 }
126 if (stackTrace != nullptr) {
127 summaryBody.append(stackTrace);
128 }
129
130 return summaryBody;
131 }
132
133 } // namespace
134
135 #if defined(USE_CLANG_COVERAGE) || defined(CLANG_COVERAGE)
136 std::stack<QJSHandleScope*> QJSHandleScope::qjsHandleScopeStack_;
137 #else
138 thread_local std::stack<QJSHandleScope*> QJSHandleScope::qjsHandleScopeStack_;
139 #endif
140
ScopedString(JSContext * ctx,JSValueConst val)141 ScopedString::ScopedString(JSContext* ctx, JSValueConst val) : context_(ctx)
142 {
143 ACE_DCHECK(ctx != nullptr);
144 stringValue_ = JS_ToCString(ctx, val);
145 }
146
ScopedString(JSContext * ctx,JSAtom atom)147 ScopedString::ScopedString(JSContext* ctx, JSAtom atom) : context_(ctx)
148 {
149 ACE_DCHECK(ctx != nullptr);
150 stringValue_ = JS_AtomToCString(ctx, atom);
151 }
152
ScopedString(JSValueConst val)153 ScopedString::ScopedString(JSValueConst val) : context_(QJSContext::Current())
154 {
155 stringValue_ = JS_ToCString(context_, val);
156 }
157
ScopedString(JSAtom atom)158 ScopedString::ScopedString(JSAtom atom) : context_(QJSContext::Current())
159 {
160 stringValue_ = JS_AtomToCString(context_, atom);
161 }
162
~ScopedString()163 ScopedString::~ScopedString()
164 {
165 JS_FreeCString(context_, stringValue_);
166 }
167
operator ==(const char * rhs)168 bool ScopedString::operator==(const char* rhs)
169 {
170 return std::strcmp(get(), rhs) == 0;
171 }
operator ==(const std::string & rhs)172 bool ScopedString::operator==(const std::string& rhs)
173 {
174 return rhs == get();
175 }
operator ==(const ScopedString & rhs)176 bool ScopedString::operator==(const ScopedString& rhs)
177 {
178 return std::strcmp(get(), rhs.get()) == 0;
179 }
180
get() const181 const char* ScopedString::get() const
182 {
183 return stringValue_;
184 }
185
str()186 std::string ScopedString::str()
187 {
188 return (stringValue_ != nullptr) ? std::string(stringValue_) : std::string();
189 }
190
Stringify(JSValueConst val)191 std::string ScopedString::Stringify(JSValueConst val)
192 {
193 JSContext* ctx = QJSContext::Current();
194 JSValue globalObj = JS_GetGlobalObject(ctx);
195 JSValue thisObj = JS_GetPropertyStr(ctx, globalObj, "JSON");
196 JSValue funcObj = JS_GetPropertyStr(ctx, thisObj, "stringify");
197 JSValue retVal = JS_Call(ctx, funcObj, thisObj, 1, &val);
198 std::string str = ScopedString(retVal).str();
199 js_std_loop(ctx);
200 JS_FreeValue(ctx, retVal);
201 JS_FreeValue(ctx, funcObj);
202 JS_FreeValue(ctx, thisObj);
203 JS_FreeValue(ctx, globalObj);
204 return str;
205 }
206
Stringify(JSContext * ctx,JSValueConst val)207 std::string ScopedString::Stringify(JSContext* ctx, JSValueConst val)
208 {
209 JSValue globalObj = JS_GetGlobalObject(ctx);
210 JSValue thisObj = JS_GetPropertyStr(ctx, globalObj, "JSON");
211 JSValue funcObj = JS_GetPropertyStr(ctx, thisObj, "stringify");
212 JSValue retVal = JS_Call(ctx, funcObj, thisObj, 1, &val);
213 std::string str = ScopedString(ctx, retVal).str();
214 js_std_loop(ctx);
215 JS_FreeValue(ctx, retVal);
216 JS_FreeValue(ctx, funcObj);
217 JS_FreeValue(ctx, thisObj);
218 JS_FreeValue(ctx, globalObj);
219 return str;
220 }
221
operator std::string() const222 ScopedString::operator std::string() const
223 {
224 return stringValue_;
225 }
226
NewStringLen(JSContext * ctx,const char * str,size_t len)227 JSValue QJSUtils::NewStringLen(JSContext* ctx, const char* str, size_t len)
228 {
229 ACE_DCHECK(!QJSHandleScope::qjsHandleScopeStack_.empty());
230 QJSHandleScope* scope = QJSHandleScope::qjsHandleScopeStack_.top();
231 JSValue retVal = JS_NewStringLen(ctx, str, len);
232 scope->jsValues_.push_back(retVal);
233 return retVal;
234 }
235
NewString(JSContext * ctx,const char * str)236 JSValue QJSUtils::NewString(JSContext* ctx, const char* str)
237 {
238 ACE_DCHECK(!QJSHandleScope::qjsHandleScopeStack_.empty());
239 QJSHandleScope* scope = QJSHandleScope::qjsHandleScopeStack_.top();
240 JSValue retVal = JS_NewString(ctx, str);
241 scope->jsValues_.push_back(retVal);
242 return retVal;
243 }
244
ParseJSON(JSContext * ctx,const char * buf,size_t bufLen,const char * filename)245 JSValue QJSUtils::ParseJSON(JSContext* ctx, const char* buf, size_t bufLen, const char* filename)
246 {
247 ACE_DCHECK(!QJSHandleScope::qjsHandleScopeStack_.empty());
248 QJSHandleScope* scope = QJSHandleScope::qjsHandleScopeStack_.top();
249 JSValue retVal = JS_ParseJSON(ctx, buf, bufLen, filename);
250 scope->jsValues_.push_back(retVal);
251 return retVal;
252 }
253
NewObject(JSContext * ctx)254 JSValue QJSUtils::NewObject(JSContext* ctx)
255 {
256 ACE_DCHECK(!QJSHandleScope::qjsHandleScopeStack_.empty());
257 QJSHandleScope* scope = QJSHandleScope::qjsHandleScopeStack_.top();
258 JSValue retVal = JS_NewObject(ctx);
259 scope->jsValues_.push_back(retVal);
260 return retVal;
261 }
262
Call(JSContext * ctx,JSValueConst funcObj,JSValueConst thisObj,int32_t argc,JSValueConst * argv)263 JSValue QJSUtils::Call(JSContext* ctx, JSValueConst funcObj, JSValueConst thisObj, int32_t argc, JSValueConst* argv)
264 {
265 ACE_DCHECK(!QJSHandleScope::qjsHandleScopeStack_.empty());
266 QJSHandleScope* scope = QJSHandleScope::qjsHandleScopeStack_.top();
267 JSValue retVal = JS_Call(ctx, funcObj, thisObj, argc, argv);
268 scope->jsValues_.push_back(retVal);
269 js_std_loop(ctx);
270 return retVal;
271 }
272
Eval(JSContext * ctx,const char * input,size_t inputLen,const char * filename,int32_t evalFlags)273 JSValue QJSUtils::Eval(JSContext* ctx, const char* input, size_t inputLen, const char* filename, int32_t evalFlags)
274 {
275 ACE_DCHECK(!QJSHandleScope::qjsHandleScopeStack_.empty());
276 QJSHandleScope* scope = QJSHandleScope::qjsHandleScopeStack_.top();
277 JSValue retVal = JS_Eval(ctx, input, inputLen, filename, evalFlags);
278 scope->jsValues_.push_back(retVal);
279 js_std_loop(ctx);
280 return retVal;
281 }
282
GetPropertyStr(JSContext * ctx,JSValueConst thisObj,const char * prop)283 JSValue QJSUtils::GetPropertyStr(JSContext* ctx, JSValueConst thisObj, const char* prop)
284 {
285 ACE_DCHECK(!QJSHandleScope::qjsHandleScopeStack_.empty());
286 QJSHandleScope* scope = QJSHandleScope::qjsHandleScopeStack_.top();
287 JSValue retVal = JS_GetPropertyStr(ctx, thisObj, prop);
288 scope->jsValues_.push_back(retVal);
289 return retVal;
290 }
291
JsStdDumpErrorAce(JSContext * ctx,JsErrorType errorType,int32_t instanceId,const char * pageUrl,const RefPtr<JsAcePage> & page,bool isEts)292 void QJSUtils::JsStdDumpErrorAce(JSContext* ctx, JsErrorType errorType, int32_t instanceId, const char* pageUrl,
293 const RefPtr<JsAcePage>& page, bool isEts)
294 {
295 RefPtr<RevSourceMap> pageMap;
296 RefPtr<RevSourceMap> appMap;
297 if (page) {
298 pageMap = page->GetPageMap();
299 appMap = page->GetAppMap();
300 }
301 JSValue exceptionVal = JS_GetException(ctx);
302 BOOL isError = JS_IsError(ctx, exceptionVal);
303 if (!isError) {
304 LOGE("Throw: ");
305 }
306 ScopedString printLog(ctx, exceptionVal);
307 LOGE("[Engine Log] [DUMP] %{public}s", printLog.get());
308
309 QJSHandleScope handleScope(ctx);
310 if (isError) {
311 JSValue val = QJSUtils::GetPropertyStr(ctx, exceptionVal, "stack");
312 if (!JS_IsUndefined(val)) {
313 const char* stackTrace = JS_ToCString(ctx, val);
314 std::string stack = stackTrace;
315 if (stackTrace != nullptr) {
316 if (pageMap || appMap) {
317 stack = JsDumpSourceFile(stackTrace, pageMap, appMap, isEts);
318 }
319 LOGE("[Engine Log] %{public}s", stack.c_str());
320
321 std::string reasonStr = GetReason(errorType);
322 std::string summaryBody = GenerateSummaryBody(printLog.get(), stack.c_str(), instanceId, pageUrl);
323 EventReport::JsErrReport(AceApplicationInfo::GetInstance().GetPackageName(), reasonStr, summaryBody);
324 JS_FreeValue(ctx, exceptionVal);
325 JS_FreeCString(ctx, stackTrace);
326 }
327 }
328 }
329 }
330
JsDumpSourceFile(const char * stack,const RefPtr<RevSourceMap> & pageMap,const RefPtr<RevSourceMap> & appMap,bool isEts)331 std::string QJSUtils::JsDumpSourceFile(const char* stack, const RefPtr<RevSourceMap>& pageMap,
332 const RefPtr<RevSourceMap>& appMap, bool isEts)
333 {
334 const std::string closeBrace = ")";
335 const std::string openBrace = "(";
336 const std::string suffix = ".js";
337 std::string ans = "";
338 std::string tempStack = stack;
339 std::string::size_type appFlag = tempStack.find("app.js");
340 bool isAppPage = appMap && (appFlag != std::string::npos);
341
342 // find per line of stack
343 std::vector<std::string> res;
344 ExtractEachInfo(tempStack, res);
345
346 for (uint32_t i = 0; i < res.size(); i++) {
347 std::string temp = res[i];
348 if (isEts && temp.find(STATE_MGMT_FILE) != std::string::npos) {
349 continue;
350 }
351 std::string::size_type closeBracePos = temp.find(closeBrace);
352 std::string::size_type openBracePos = temp.find(openBrace);
353
354 std::string line = "";
355 GetPosInfo(temp, closeBracePos, line);
356 // because the function can be called by jsfw or native, but the same
357 // is that the line is empty. So, this can be the terminal judgement
358 if (line == "") {
359 LOGI("the stack without line info");
360 if (isEts) {
361 continue;
362 }
363 break;
364 }
365
366 // if the page is end with ".jtc", push into stack
367 if (temp.find(suffix) != suffix.npos) {
368 const std::string sourceInfo = GetSourceInfo(line, pageMap, appMap, isAppPage, isEts);
369 if (sourceInfo == "") {
370 break;
371 }
372 if (isEts && sourceInfo.find(BUNDLE_FILE_FLAG) == 0) {
373 continue;
374 }
375 temp.replace(openBracePos, closeBracePos - openBracePos + 1, sourceInfo);
376 }
377 ans = ans + temp + "\n";
378 }
379 if (ans == "") {
380 return tempStack;
381 }
382 return ans;
383 }
384
ExtractEachInfo(const std::string & tempStack,std::vector<std::string> & res)385 void QJSUtils::ExtractEachInfo(const std::string& tempStack, std::vector<std::string>& res)
386 {
387 std::string tempStr = "";
388 for (uint32_t i = 0; i < tempStack.length(); i++) {
389 if (tempStack[i] == '\n') {
390 res.push_back(tempStr);
391 tempStr = "";
392 } else {
393 tempStr += tempStack[i];
394 }
395 }
396 // that's for only one line in the error stack
397 res.push_back(tempStr);
398 }
399
GetPosInfo(const std::string & temp,int32_t start,std::string & line)400 void QJSUtils::GetPosInfo(const std::string& temp, int32_t start, std::string& line)
401 {
402 // find line, column
403 for (int32_t i = start - 1; i > 0; i--) {
404 if ((temp[i] >= '0' && temp[i] <= '9') || temp[i] == '-') {
405 line = temp[i] + line;
406 } else {
407 break;
408 }
409 }
410 }
411
GetSourceInfo(const std::string & line,const RefPtr<RevSourceMap> & pageMap,const RefPtr<RevSourceMap> & appMap,bool isAppPage,bool isEts)412 std::string QJSUtils::GetSourceInfo(const std::string& line, const RefPtr<RevSourceMap>& pageMap,
413 const RefPtr<RevSourceMap>& appMap, bool isAppPage, bool isEts)
414 {
415 std::string sourceInfo;
416 MappingInfo mapInfo = isAppPage ?
417 appMap->Find(StringToInt(line) + (isEts ? OFFSET_PREVIEW_ETS : OFFSET_PREVIEW_JS),1) :
418 pageMap->Find(StringToInt(line) + (isEts ? OFFSET_PREVIEW_ETS : OFFSET_PREVIEW_JS), 1);
419 if (mapInfo.row == 0) {
420 return "";
421 }
422 sourceInfo = "(" + mapInfo.sources + ":" + std::to_string(mapInfo.row) + ")";
423 return sourceInfo;
424 }
425
JsDumpMemoryStats(JSContext * ctx)426 void QJSUtils::JsDumpMemoryStats(JSContext* ctx)
427 {
428 #if ACE_DEBUG
429 LOGD("JS Memory ---------------------");
430 JSMemoryUsage s;
431 JSRuntime* rt = JS_GetRuntime(ctx);
432 if (!rt) {
433 return;
434 }
435
436 JS_ComputeMemoryUsage(rt, &s);
437
438 LOGD("malloc limit: %" PRId64 " ------------", (int64_t)(ssize_t)s.malloc_limit);
439
440 LOGD(" COUNT SIZE");
441
442 if (s.malloc_count) {
443 LOGD("%8" PRId64 " %8" PRId64 " Bytes Memory allocated (%0.1f per block)", s.malloc_count, s.malloc_size,
444 (double)s.malloc_size / s.malloc_count);
445 LOGD("%8" PRId64 " %8" PRId64 " Bytes Memory used (%d overhead, %0.1f average slack)", s.memory_used_count,
446 s.memory_used_size, 8, ((double)(s.malloc_size - s.memory_used_size) / s.memory_used_count));
447 }
448 if (s.atom_count) {
449 LOGD("%8" PRId64 " %8" PRId64 " Bytes Atoms (%0.1f per atom)", s.atom_count, s.atom_size,
450 (double)s.atom_size / s.atom_count);
451 }
452 if (s.str_count) {
453 LOGD("%8" PRId64 " %8" PRId64 " Bytes Strings (%0.1f per string)", s.str_count, s.str_size,
454 (double)s.str_size / s.str_count);
455 }
456 if (s.obj_count) {
457 LOGD("%8" PRId64 " %8" PRId64 " Bytes Objects (%0.1f per object)", s.obj_count, s.obj_size,
458 (double)s.obj_size / s.obj_count);
459 LOGD("%8" PRId64 " %8" PRId64 " Bytes Properties (%0.1f per object)", s.prop_count, s.prop_size,
460 (double)s.prop_count / s.obj_count);
461 LOGD("%8" PRId64 " %8" PRId64 " Bytes Shapes (%0.1f per shape)", s.shape_count, s.shape_size,
462 (double)s.shape_size / s.shape_count);
463 }
464 if (s.js_func_count) {
465 LOGD("%8" PRId64 " %8" PRId64 " Bytes Bytecode functions", s.js_func_count, s.js_func_size);
466 LOGD("%8" PRId64 " %8" PRId64 " Bytes Bytecode (%0.1f per function) ", s.js_func_count, s.js_func_code_size,
467 (double)s.js_func_code_size / s.js_func_count);
468 if (s.js_func_pc2line_count) {
469 LOGD("%8" PRId64 " %8" PRId64 " Bytes Pc2line (%0.1f per function)", s.js_func_pc2line_count,
470 s.js_func_pc2line_size, (double)s.js_func_pc2line_size / s.js_func_pc2line_count);
471 }
472 }
473 if (s.c_func_count) {
474 LOGD("%8" PRId64 " C functions", s.c_func_count);
475 }
476 if (s.array_count) {
477 LOGD("%8" PRId64 " arrays", s.array_count);
478 if (s.fast_array_count) {
479 LOGD("%8" PRId64 " fast arrays", s.fast_array_count);
480 LOGD("%8" PRId64 " %8" PRId64 " (%0.1f per fast array) elements", s.fast_array_elements,
481 s.fast_array_elements * (int)sizeof(JSValue), (double)s.fast_array_elements / s.fast_array_count);
482 }
483 }
484 if (s.binary_object_count) {
485 LOGD("%8" PRId64 " %8" PRId64 " binary objects", s.binary_object_count, s.binary_object_size);
486 }
487 #endif
488
489 LOGD("JS Memory ---------------------");
490 }
491
JsGetArrayLength(JSContext * ctx,JSValueConst arrayObject)492 int32_t QJSUtils::JsGetArrayLength(JSContext* ctx, JSValueConst arrayObject)
493 {
494 int32_t result = JS_IsArray(ctx, arrayObject);
495 if (result == TRUE) {
496 QJSHandleScope handleScope(ctx);
497 JSValue propLength = QJSUtils::GetPropertyStr(ctx, arrayObject, "length");
498 int32_t length = -1;
499
500 if (JS_ToInt32(ctx, &length, propLength) < 0) {
501 length = -1;
502 }
503
504 JS_FreeValue(ctx, propLength);
505 return length;
506 }
507 if (result < 0) {
508 JsStdDumpErrorAce(ctx);
509 }
510 return -1;
511 }
512
GetObjectKeys(JSValueConst obj,int flags)513 std::vector<std::string> QJSUtils::GetObjectKeys(JSValueConst obj, int flags)
514 {
515 return QJSUtils::GetObjectKeys(QJSContext::Current(), obj, flags);
516 }
517
AlwaysMatch(std::string)518 bool AlwaysMatch(std::string)
519 {
520 return true;
521 }
522
GetObjectKeys(JSContext * ctx,JSValueConst obj,int flags)523 std::vector<std::string> QJSUtils::GetObjectKeys(JSContext* ctx, JSValueConst obj, int flags)
524 {
525 return QJSUtils::GetFilteredObjectKeys(ctx, obj, AlwaysMatch, flags);
526 }
527
GetFilteredObjectKeys(JSContext * ctx,JSValueConst obj,bool (* matcherFunc)(std::string),int flags)528 std::vector<std::string> QJSUtils::GetFilteredObjectKeys(
529 JSContext* ctx, JSValueConst obj, bool (*matcherFunc)(std::string), int flags)
530 {
531 std::vector<std::string> result;
532 if (JS_IsUndefined(obj)) {
533 return result;
534 }
535
536 if (!JS_IsObject(obj)) {
537 return result;
538 }
539
540 JSPropertyEnum* properties = nullptr;
541 uint32_t propLength = 0;
542 int res = JS_GetOwnPropertyNames(ctx, &properties, &propLength, obj, flags);
543 if (res == -1) {
544 free(properties);
545 return result;
546 }
547
548 for (uint32_t i = 0; i < propLength; i++) {
549 ScopedString keyStr(ctx, properties[i].atom);
550 if (matcherFunc(keyStr.get())) {
551 result.push_back(keyStr.get());
552 }
553 JS_FreeAtom(ctx, properties[i].atom);
554 }
555
556 free(properties);
557 return result;
558 }
559
560 /**
561 * obtain the properties of given object fromMap and return in pTab and len.
562 * @param ctx: context
563 * @param fromMap: elmt's attributes object
564 * @param pTab: result array of properties
565 * @param len: length of the result array
566 * NOTE: caller needs to free pTab array after use
567 */
CheckAndGetJsProperty(JSContext * ctx,JSValueConst jsObj,JSPropertyEnum ** pTab,uint32_t * len)568 bool QJSUtils::CheckAndGetJsProperty(JSContext* ctx, JSValueConst jsObj, JSPropertyEnum** pTab, uint32_t* len)
569 {
570 int err = JS_GetOwnPropertyNames(ctx, pTab, len, jsObj, JS_GPN_STRING_MASK);
571 if (err < 0) {
572 LOGE("the object's Property enum is nullptr");
573 return false;
574 }
575 return true;
576 }
577
578 // get the top -level registered variable types
579 // this function is called during the initial variable registration process
typeAsString(JSValueConst targetValue)580 std::string QJSUtils::typeAsString(JSValueConst targetValue)
581 {
582 JSContext* ctx = QJSContext::Current();
583 if (JS_IsNumber(targetValue) || JS_IsInteger(targetValue) || JS_IsBigFloat(targetValue)) {
584 return "number";
585 } else if (JS_IsBool(targetValue)) {
586 return "boolean";
587 } else if (JS_IsString(targetValue)) {
588 return "string";
589 } else if (JS_IsArray(ctx, targetValue)) {
590 return "array";
591 } else if (JS_IsObject(targetValue)) {
592 return "object";
593 } else if (JS_IsFunction(ctx, targetValue)) {
594 return "function";
595 } else if (JS_IsNull(targetValue)) {
596 return "null";
597 } else if (JS_IsUndefined(targetValue)) {
598 return "undefined";
599 } else {
600 LOGW("typeAsString, cannot guess type ");
601 }
602
603 return "undefined";
604 }
605
GetArgvSafe(int idx,int argc,JSValueConst * argv)606 JSValue QJSUtils::GetArgvSafe(int idx, int argc, JSValueConst* argv)
607 {
608 if (idx < 0 || idx >= argc) {
609 return JS_UNDEFINED;
610 }
611
612 return argv[idx];
613 }
614
DefineGlobalFunction(JSContext * ctx,JSCFunction cFunc,const char * name,const int paramNum)615 void QJSUtils::DefineGlobalFunction(JSContext* ctx, JSCFunction cFunc, const char* name, const int paramNum)
616 {
617 JSValue global = JS_GetGlobalObject(ctx);
618 JSValue jsFuncVal = JS_NewCFunction2(ctx, cFunc, name, paramNum, JS_CFUNC_generic, 0);
619 JS_SetPropertyStr(ctx, global, name, jsFuncVal);
620 JS_FreeValue(ctx, global);
621 }
622
DefineGlobalModuleFunction(JSContext * ctx,JSCFunction cFunc,const char * moduleName,const char * funcName,const int paramNum)623 void QJSUtils::DefineGlobalModuleFunction(
624 JSContext* ctx, JSCFunction cFunc, const char* moduleName, const char* funcName, const int paramNum)
625 {
626 JSValue global = JS_GetGlobalObject(ctx);
627 JSValue jsFocusControl = JS_NewObject(ctx);
628 JSValue jsFuncVal = JS_NewCFunction2(ctx, cFunc, funcName, paramNum, JS_CFUNC_generic, 0);
629 JS_SetPropertyStr(ctx, jsFocusControl, funcName, jsFuncVal);
630 JS_SetPropertyStr(ctx, global, moduleName, jsFocusControl);
631 JS_FreeValue(ctx, global);
632 }
633
634 #if defined(USE_CLANG_COVERAGE) || defined(CLANG_COVERAGE)
635 std::stack<JSContext*> QJSContext::s_qjsContextStack;
636 #else
637 thread_local std::stack<JSContext*> QJSContext::s_qjsContextStack;
638 #endif
639
Current()640 JSContext* QJSContext::Current()
641 {
642 ACE_DCHECK(s_qjsContextStack.size() > 0 &&
643 "QJSContext::Current() called outside of the lifetime of a QJSContext::Scope "
644 "object. Did you forget QJSContext::Scope scope(ctx); ?");
645 return s_qjsContextStack.top();
646 }
647
Scope(JSContext * ctx)648 QJSContext::Scope::Scope(JSContext* ctx)
649 {
650 s_qjsContextStack.push(ctx);
651 }
652
~Scope()653 QJSContext::Scope::~Scope()
654 {
655 s_qjsContextStack.pop();
656 }
657
QJSHandleScope(JSContext * ctx)658 QJSHandleScope::QJSHandleScope(JSContext* ctx)
659 {
660 ACE_DCHECK(ctx != nullptr);
661 static int scopeId = 0;
662 scopeId_ = scopeId++;
663 context_ = ctx;
664 qjsHandleScopeStack_.push(this);
665 }
666
~QJSHandleScope()667 QJSHandleScope::~QJSHandleScope()
668 {
669 qjsHandleScopeStack_.pop();
670 for (const auto& val : jsValues_) {
671 JS_FreeValue(context_, val);
672 }
673 }
674
GetCurrent()675 QJSHandleScope* QJSHandleScope::GetCurrent()
676 {
677 return qjsHandleScopeStack_.top();
678 }
679
Push(JSValue val)680 void QJSHandleScope::Push(JSValue val)
681 {
682 jsValues_.push_back(val);
683 }
684
685 } // namespace OHOS::Ace::Framework
686