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