• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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