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