1 /*
2  * Copyright (c) 2021-2023 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 "bridge/js_frontend/engine/jsi/jsi_base_utils.h"
17 
18 #include "bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
19 #include "bridge/js_frontend/engine/jsi/ark_js_value.h"
20 
21 namespace OHOS::Ace::Framework {
22 constexpr char JS_CRASH_CODE[] = "100001";
23 const std::string NAME = "name";
24 const std::string MESSAGE = "message";
25 const std::string STACK = "stack";
26 
GetLineOffset(const AceType * data)27 int32_t GetLineOffset(const AceType* data)
28 {
29 #ifndef PA_SUPPORT
30     if (data == nullptr) {
31         return 0;
32     }
33     if (AceType::InstanceOf<JsiDeclarativeEngineInstance>(data)) {
34         return 0;
35     }
36 #endif
37     const int32_t offset = 14;
38     return offset;
39 }
40 
GetMsgStr(const std::string & msg)41 std::string GetMsgStr(const std::string& msg)
42 {
43     auto pos = msg.find('\n');
44     if (pos == std::string::npos) {
45         return msg;
46     }
47 
48     return msg.substr(0, pos);
49 }
50 
GetRunningPage(const AceType * data)51 RefPtr<JsAcePage> GetRunningPage(const AceType* data)
52 {
53 #ifndef PA_SUPPORT
54     if (data == nullptr) {
55         return nullptr;
56     }
57     if (AceType::InstanceOf<JsiDeclarativeEngineInstance>(data)) {
58         auto instance = static_cast<const JsiDeclarativeEngineInstance*>(data);
59         return instance->GetRunningPage();
60     }
61 #ifndef NG_BUILD
62     else if (AceType::InstanceOf<JsiEngineInstance>(data)) {
63         auto instance = static_cast<const JsiEngineInstance*>(data);
64         return instance->GetRunningPage();
65     }
66 #endif
67 #endif
68     return nullptr;
69 }
70 
GetDelegate(const AceType * data)71 RefPtr<FrontendDelegate> GetDelegate(const AceType* data)
72 {
73 #ifndef PA_SUPPORT
74     if (data == nullptr) {
75         return nullptr;
76     }
77     if (AceType::InstanceOf<JsiDeclarativeEngineInstance>(data)) {
78         auto instance = static_cast<const JsiDeclarativeEngineInstance*>(data);
79         return instance->GetDelegate();
80     }
81 #ifndef NG_BUILD
82     else if (AceType::InstanceOf<JsiEngineInstance>(data)) {
83         auto instance = static_cast<const JsiEngineInstance*>(data);
84         return instance->GetDelegate();
85     }
86 #endif
87 #endif
88     return nullptr;
89 }
90 
GenerateErrorMsg(const std::shared_ptr<JsValue> & error,const std::shared_ptr<JsRuntime> & runtime)91 std::string JsiBaseUtils::GenerateErrorMsg(
92     const std::shared_ptr<JsValue>& error, const std::shared_ptr<JsRuntime>& runtime)
93 {
94     std::string errMsg;
95     if (!error) {
96         errMsg.append("error uncaught");
97         return errMsg;
98     }
99 
100     std::string messageStr;
101     std::string rawStack;
102     shared_ptr<JsValue> message = error->GetProperty(runtime, "message");
103     if (message) {
104         messageStr = message->ToString(runtime);
105     }
106 
107     shared_ptr<JsValue> stack = error->GetProperty(runtime, "stack");
108     if (stack) {
109         rawStack = stack->ToString(runtime);
110     }
111 
112     errMsg.append("{\"ErrMsg\":\"")
113         .append(messageStr)
114         .append("\", \"Stacktrace\": \"")
115         .append(GetMsgStr(rawStack))
116         .append("\"}");
117     return errMsg;
118 }
119 
GenerateJsErrorObject(const std::shared_ptr<JsValue> & error,const std::shared_ptr<JsRuntime> & runtime)120 JsErrorObject JsiBaseUtils::GenerateJsErrorObject(
121     const std::shared_ptr<JsValue>& error, const std::shared_ptr<JsRuntime>& runtime)
122 {
123     if (error == nullptr) {
124         return {};
125     }
126     JsErrorObject errInfo;
127     shared_ptr<JsValue> name = error->GetProperty(runtime, NAME);
128     if (name != nullptr) {
129         errInfo.name = name->ToString(runtime);
130     }
131     shared_ptr<JsValue> message = error->GetProperty(runtime, MESSAGE);
132     if (message != nullptr) {
133         errInfo.message = message->ToString(runtime);
134     }
135     shared_ptr<JsValue> stack = error->GetProperty(runtime, STACK);
136     if (stack != nullptr) {
137         errInfo.stack = stack->ToString(runtime);
138     }
139     return errInfo;
140 }
141 
GenerateSummaryBody(const std::shared_ptr<JsValue> & error,const std::shared_ptr<JsRuntime> & runtime)142 std::string JsiBaseUtils::GenerateSummaryBody(
143     const std::shared_ptr<JsValue>& error, const std::shared_ptr<JsRuntime>& runtime)
144 {
145     std::string summaryBody;
146     summaryBody.append("Lifetime: ")
147         .append(std::to_string(OHOS::Ace::AceApplicationInfo::GetInstance().GetLifeTime()))
148         .append("s")
149         .append("\n");
150 
151     summaryBody.append("Js-Engine: ark\n");
152 
153     if (!error) {
154         summaryBody.append("error uncaught: error is null");
155         return summaryBody;
156     }
157 
158     const AceType* data = static_cast<AceType*>(runtime->GetEmbedderData());
159     std::string pageUrl;
160     RefPtr<RevSourceMap> pageMap;
161     RefPtr<RevSourceMap> appMap;
162     std::unordered_map<std::string, RefPtr<RevSourceMap>> sourceMaps;
163     auto vm = const_cast<EcmaVM*>(std::static_pointer_cast<ArkJSRuntime>(runtime)->GetEcmaVm());
164     auto container = Container::Current();
165     if (container && container->IsUseNewPipeline()) {
166         auto frontEnd = container->GetFrontend();
167         if (frontEnd) {
168             pageUrl = frontEnd->GetCurrentPageUrl();
169             if (!JSNApi::IsBundle(vm)) {
170                 frontEnd->GetStageSourceMap(sourceMaps);
171             } else {
172                 pageMap = frontEnd->GetCurrentPageSourceMap();
173             }
174             appMap = frontEnd->GetFaAppSourceMap();
175         }
176     } else {
177         auto runningPage = GetRunningPage(data);
178         if (runningPage) {
179             pageUrl = runningPage->GetUrl();
180             appMap = runningPage->GetAppMap();
181             if (!JSNApi::IsBundle(vm)) {
182                 GetStageSourceMap(data, sourceMaps);
183             } else {
184                 pageMap = runningPage->GetPageMap();
185             }
186         }
187     }
188     if (!pageUrl.empty()) {
189         summaryBody.append("page: ").append(pageUrl).append("\n");
190     }
191     if (!error->IsObject(runtime) || error->IsNull(runtime)) {
192         std::string errorInfo = error->ToString(runtime);
193         summaryBody.append(errorInfo).append("\n");
194     }
195     shared_ptr<JsValue> message = error->GetProperty(runtime, "message");
196     std::string messageStr = message->ToString(runtime);
197     summaryBody.append("Error message: ");
198     summaryBody.append(messageStr).append("\n");
199 
200     if (error->HasProperty(runtime, "code")) {
201         shared_ptr<JsValue> code = error->GetProperty(runtime, "code");
202         std::string codeStr = code->ToString(runtime);
203         summaryBody.append("Error code: ");
204         summaryBody.append(codeStr).append("\n");
205     }
206 
207     shared_ptr<JsValue> stack = error->GetProperty(runtime, "stack");
208     std::string rawStack = stack->ToString(runtime);
209     if (rawStack.empty()) {
210         summaryBody.append("Stacktrace is empty!\n");
211         return summaryBody;
212     }
213 
214     shared_ptr<JsValue> errorFunc = error->GetProperty(runtime, "errorfunc");
215     auto errorPos = GetErrorPos(rawStack);
216     std::string sourceCodeInfo = GetSourceCodeInfo(runtime, errorFunc, errorPos);
217 
218     std::string stackHead = "Stacktrace:\n";
219     if (pageMap || appMap || !sourceMaps.empty()) {
220         std::string runningPageTag = "app_.js";
221         bool isAppPage = rawStack.find(runningPageTag, 1) != std::string::npos && appMap;
222         if (isAppPage) {
223             sourceCodeInfo = appMap->GetOriginalNames(sourceCodeInfo, errorPos.second);
224         } else if (pageMap) {
225             sourceCodeInfo = pageMap->GetOriginalNames(sourceCodeInfo, errorPos.second);
226         }
227         std::string showStack;
228         if (!JSNApi::IsBundle(vm)) {
229             showStack = TranslateBySourceMap(rawStack, pageUrl, sourceMaps, appMap, data);
230         } else {
231             showStack = TranslateStack(rawStack, pageUrl, pageMap, appMap, data);
232         }
233         summaryBody.append(sourceCodeInfo).append(stackHead).append(showStack);
234         // show raw stack for troubleshooting in the frame
235         LOGI("JS Stack:\n%{public}s", TranslateRawStack(rawStack).c_str());
236     } else {
237         summaryBody.append("Cannot get SourceMap info, dump raw stack:\n");
238         summaryBody.append(stackHead).append(rawStack);
239     }
240 
241 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
242     std::string summaryBodyInsertedWithTagStr = "";
243     size_t lastPosOfNextLine = -1;
244     size_t currPosOfNextLine = 0;
245     while (true) {
246         lastPosOfNextLine++; // Become the next position at which we start to find the target charactor.
247         currPosOfNextLine = summaryBody.find_first_of("\n", lastPosOfNextLine);
248         if (currPosOfNextLine == -1) {
249             break;
250         }
251         summaryBodyInsertedWithTagStr.append("[Engine Log]")
252             .append(summaryBody.substr(lastPosOfNextLine, (currPosOfNextLine - lastPosOfNextLine) + 1));
253         lastPosOfNextLine = currPosOfNextLine;
254     }
255     return summaryBodyInsertedWithTagStr;
256 #else
257     return summaryBody;
258 #endif
259 }
260 
GetErrorPos(const std::string & rawStack)261 ErrorPos JsiBaseUtils::GetErrorPos(const std::string& rawStack)
262 {
263     size_t findLineEnd = rawStack.find("\n");
264     if (findLineEnd == std::string::npos) {
265         return std::make_pair(0, 0);
266     }
267     size_t lineEnd = findLineEnd - 1;
268     if (lineEnd < 1 || rawStack[lineEnd - 1] == '?') {
269         return std::make_pair(0, 0);
270     }
271 
272     size_t secondPos = rawStack.rfind(':', lineEnd);
273     if (secondPos == std::string::npos) {
274         return std::make_pair(0, 0);
275     }
276 
277     size_t firstPos = rawStack.rfind(':', secondPos - 1);
278     if (firstPos == std::string::npos) {
279         return std::make_pair(0, 0);
280     }
281 
282     std::string lineStr = rawStack.substr(firstPos + 1, secondPos - 1 - firstPos);
283     std::string columnStr = rawStack.substr(secondPos + 1, lineEnd - 1 - secondPos);
284 
285     return std::make_pair(StringToInt(lineStr), StringToInt(columnStr));
286 }
287 
GetSourceCodeInfo(std::shared_ptr<JsRuntime> runtime,const shared_ptr<JsValue> & errorFunc,ErrorPos pos)288 std::string JsiBaseUtils::GetSourceCodeInfo(
289     std::shared_ptr<JsRuntime> runtime, const shared_ptr<JsValue>& errorFunc, ErrorPos pos)
290 {
291     if (pos.first == 0) {
292         return "";
293     }
294     shared_ptr<ArkJSRuntime> arkJsRuntime = std::static_pointer_cast<ArkJSRuntime>(runtime);
295     LocalScope scope(arkJsRuntime->GetEcmaVm());
296     uint32_t line = pos.first;
297     uint32_t column = pos.second;
298     Local<panda::FunctionRef> function(std::static_pointer_cast<ArkJSValue>(errorFunc)->GetValue(arkJsRuntime));
299     Local<panda::StringRef> sourceCode = function->GetSourceCode(arkJsRuntime->GetEcmaVm(), line);
300     std::string sourceCodeStr = sourceCode->ToString(arkJsRuntime->GetEcmaVm());
301     if (sourceCodeStr.empty()) {
302         return "";
303     }
304     std::string sourceCodeInfo = "SourceCode:\n";
305     sourceCodeInfo.append(sourceCodeStr).append("\n");
306     for (uint32_t k = 0; k < column - 1; k++) {
307         sourceCodeInfo.push_back(' ');
308     }
309     sourceCodeInfo.append("^\n");
310     return sourceCodeInfo;
311 }
312 
TransSourceStack(RefPtr<JsAcePage> runningPage,const std::string & rawStack)313 std::string JsiBaseUtils::TransSourceStack(RefPtr<JsAcePage> runningPage, const std::string& rawStack)
314 {
315     RefPtr<RevSourceMap> pageMap;
316     RefPtr<RevSourceMap> appMap;
317     std::string pageUrl;
318     auto container = Container::Current();
319     if (container && container->IsUseNewPipeline()) {
320         auto frontEnd = container->GetFrontend();
321         if (frontEnd) {
322             pageUrl = frontEnd->GetCurrentPageUrl();
323             pageMap = frontEnd->GetCurrentPageSourceMap();
324             appMap = frontEnd->GetFaAppSourceMap();
325         }
326     } else {
327         if (runningPage) {
328             pageUrl = runningPage->GetUrl();
329             pageMap = runningPage->GetPageMap();
330             appMap = runningPage->GetAppMap();
331         }
332     }
333 
334     if (!pageMap) {
335         return rawStack;
336     }
337 
338     std::string summaryBody;
339     summaryBody.append(" Page: ").append(pageUrl).append("\n");
340 
341     std::string stackHead = "Stacktrace:\n";
342     if (pageMap || appMap) {
343         std::string tempStack = JsiBaseUtils::TranslateStack(rawStack, pageUrl, pageMap, appMap);
344         summaryBody.append(stackHead).append(tempStack);
345     } else {
346         summaryBody.append("Cannot get SourceMap info, dump raw stack:\n");
347         summaryBody.append(stackHead).append(rawStack);
348     }
349 
350     return summaryBody;
351 }
352 
TranslateRawStack(const std::string & rawStackStr)353 std::string JsiBaseUtils::TranslateRawStack(const std::string& rawStackStr)
354 {
355     std::string ans;
356     std::string tempStack = rawStackStr;
357 
358     // find per line of stack
359     std::vector<std::string> res;
360     ExtractEachInfo(tempStack, res);
361 
362     // collect error info first
363     for (const auto& temp : res) {
364         const std::string sourceInfo = GetRelativePath(temp, "/");
365         ans = ans + sourceInfo + "\n";
366     }
367     if (ans.empty()) {
368         return tempStack;
369     }
370     return ans;
371 }
372 
TranslateStack(const std::string & stackStr,const std::string & pageUrl,const RefPtr<RevSourceMap> & pageMap,const RefPtr<RevSourceMap> & appMap,const AceType * data)373 std::string JsiBaseUtils::TranslateStack(const std::string& stackStr, const std::string& pageUrl,
374     const RefPtr<RevSourceMap>& pageMap, const RefPtr<RevSourceMap>& appMap, const AceType* data)
375 {
376     const std::string closeBrace = ")";
377     const std::string openBrace = "(";
378     std::string ans;
379     std::string tempStack = stackStr;
380     // find per line of stack
381     std::vector<std::string> res;
382     ExtractEachInfo(tempStack, res);
383 
384     std::string runningPageTag = "app_.js";
385     auto appFlag = static_cast<int32_t>(tempStack.find(runningPageTag));
386     bool isAppPage = appFlag > 0 && appMap;
387     if (!isAppPage) {
388         std::string tag = std::as_const(pageUrl);
389         std::string str = tag;
390         if (res[0].find('/') == std::string::npos) {
391             replace(str.begin(), str.end(), '/', '\\');
392         }
393         char* ch = strrchr((char*)str.c_str(), '.');
394         if (ch != nullptr) {
395             int index = ch - str.c_str();
396             str.insert(index, "_");
397         }
398         runningPageTag = str;
399     }
400 
401     // collect error info first
402     for (uint32_t i = 0; i < res.size(); i++) {
403         std::string temp = res[i];
404         if (temp.rfind(runningPageTag) == std::string::npos) {
405             continue;
406         }
407         auto closeBracePos = static_cast<int32_t>(temp.find(closeBrace));
408         auto openBracePos = static_cast<int32_t>(temp.find(openBrace));
409 
410         std::string line;
411         std::string column;
412         GetPosInfo(temp, closeBracePos, line, column);
413         if (line.empty() || column.empty()) {
414             break;
415         }
416 
417         const std::string sourceInfo = GetSourceInfo(line, column, pageMap, appMap, isAppPage, data);
418         if (sourceInfo.empty()) {
419             break;
420         }
421         temp.replace(openBracePos, closeBracePos - openBracePos + 1, sourceInfo);
422         replace(temp.begin(), temp.end(), '\\', '/');
423         ans = ans + temp + "\n";
424     }
425     if (ans.empty()) {
426         return tempStack;
427     }
428     return ans;
429 }
430 
TranslateBySourceMap(const std::string & stackStr,const std::string & pageUrl,const std::unordered_map<std::string,RefPtr<RevSourceMap>> & sourceMaps,const RefPtr<RevSourceMap> & appMap,const AceType * data)431 std::string JsiBaseUtils::TranslateBySourceMap(const std::string& stackStr, const std::string& pageUrl,
432     const std::unordered_map<std::string, RefPtr<RevSourceMap>>& sourceMaps, const RefPtr<RevSourceMap>& appMap,
433     const AceType* data)
434 {
435     const std::string closeBrace = ")";
436     const std::string openBrace = "(";
437     std::string ans;
438     std::string tempStack = stackStr;
439     std::string runningPageTag = "app_.js";
440     bool isAppPage = static_cast<int32_t>(tempStack.find(runningPageTag)) > 0 && appMap;
441     if (!isAppPage) {
442         std::string tag = std::as_const(pageUrl);
443         char* ch = strrchr((char*)tag.c_str(), '.');
444         if (ch != nullptr) {
445             tag.insert(static_cast<int>(ch - tag.c_str()), "_");
446         }
447         runningPageTag = tag;
448     }
449     // find per line of stack
450     std::vector<std::string> res;
451     ExtractEachInfo(tempStack, res);
452 
453     // collect error info first
454     for (uint32_t i = 0; i < res.size(); i++) {
455         std::string temp = res[i];
456         uint32_t start = temp.find(openBrace);
457         uint32_t end = temp.find(":");
458         if (temp.empty() || end < start + 1) {
459             break;
460         }
461         std::string key = temp.substr(start + 1, end - start - 1);
462         auto closeBracePos = static_cast<int32_t>(temp.find(closeBrace));
463         auto openBracePos = static_cast<int32_t>(temp.find(openBrace));
464         std::string line;
465         std::string column;
466         GetPosInfo(temp, closeBracePos, line, column);
467         if (line.empty() || column.empty()) {
468             break;
469         }
470         std::string sourceInfo;
471         auto iter = sourceMaps.find(key);
472         if (iter != sourceMaps.end()) {
473             sourceInfo = GetSourceInfo(line, column, iter->second, appMap, isAppPage, data, false);
474         }
475         if (sourceInfo.empty()) {
476             break;
477         }
478         temp.replace(openBracePos, closeBracePos - openBracePos + 1, sourceInfo);
479         replace(temp.begin(), temp.end(), '\\', '/');
480         ans = ans + temp + "\n";
481     }
482     if (ans.empty()) {
483         return tempStack;
484     }
485     return ans;
486 }
487 
ExtractEachInfo(const std::string & tempStack,std::vector<std::string> & res)488 void JsiBaseUtils::ExtractEachInfo(const std::string& tempStack, std::vector<std::string>& res)
489 {
490     std::string tempStr;
491     for (uint32_t i = 0; i < tempStack.length(); i++) {
492         if (tempStack[i] == '\n') {
493             res.push_back(tempStr);
494             tempStr = "";
495         } else {
496             tempStr += tempStack[i];
497         }
498     }
499     if (!tempStr.empty()) {
500         res.push_back(tempStr);
501     }
502 }
503 
GetPosInfo(const std::string & temp,int32_t start,std::string & line,std::string & column)504 void JsiBaseUtils::GetPosInfo(const std::string& temp, int32_t start, std::string& line, std::string& column)
505 {
506     // 0 for colum, 1 for row
507     int32_t flag = 0;
508     // find line, column
509     for (int32_t i = start - 1; i > 0; i--) {
510         if (temp[i] == ':') {
511             flag += 1;
512             continue;
513         }
514         if (flag == 0) {
515             column = temp[i] + column;
516         } else if (flag == 1) {
517             line = temp[i] + line;
518         } else {
519             break;
520         }
521     }
522 }
523 
GetSourceInfo(const std::string & line,const std::string & column,const RefPtr<RevSourceMap> & pageMap,const RefPtr<RevSourceMap> & appMap,bool isAppPage,const AceType * data,const bool isBundle)524 std::string JsiBaseUtils::GetSourceInfo(const std::string& line, const std::string& column,
525     const RefPtr<RevSourceMap>& pageMap, const RefPtr<RevSourceMap>& appMap, bool isAppPage, const AceType* data,
526     const bool isBundle)
527 {
528     int32_t offSet = GetLineOffset(data);
529     std::string sourceInfo;
530     MappingInfo mapInfo;
531     if (isAppPage) {
532         CHECK_NULL_RETURN(appMap, "");
533         mapInfo = appMap->Find(StringToInt(line) - offSet, StringToInt(column));
534     } else {
535         CHECK_NULL_RETURN(pageMap, "");
536         mapInfo = pageMap->Find(StringToInt(line) - offSet, StringToInt(column));
537     }
538     if (mapInfo.row == 0 || mapInfo.col == 0) {
539         return "";
540     }
541 
542     std::string sources = isBundle ? GetRelativePath(mapInfo.sources) : mapInfo.sources;
543     sourceInfo = "(" + sources + ":" + std::to_string(mapInfo.row) + ":" + std::to_string(mapInfo.col) + ")";
544     return sourceInfo;
545 }
546 
GetRelativePath(const std::string & sources,std::string splitStr)547 std::string JsiBaseUtils::GetRelativePath(const std::string& sources, std::string splitStr)
548 {
549     std::string temp = sources;
550     std::size_t splitPos = std::string::npos;
551     const static int pathLevel = 3;
552     int i = 0;
553     while (i < pathLevel) {
554         splitPos = temp.find_last_of(splitStr);
555         if (splitPos != std::string::npos) {
556             temp = temp.substr(0, splitPos - 1);
557         } else {
558             break;
559         }
560         i++;
561     }
562     if (i == pathLevel) {
563         return sources.substr(splitPos);
564     }
565     return sources;
566 }
567 
ReportJsErrorEvent(std::shared_ptr<JsValue> error,std::shared_ptr<JsRuntime> runtime)568 void JsiBaseUtils::ReportJsErrorEvent(std::shared_ptr<JsValue> error, std::shared_ptr<JsRuntime> runtime)
569 {
570     if (!runtime) {
571         LOGI("ReportJsErrorEvent: jsi engine has been destroyed");
572         return;
573     }
574 
575     auto arkJSRuntime = std::static_pointer_cast<ArkJSRuntime>(runtime);
576     if (arkJSRuntime && arkJSRuntime->GetErrorEventHandler()) {
577         std::string msg = GenerateErrorMsg(error, runtime);
578         LOGI("Handle error event, errMsg: \n%{public}s", msg.c_str());
579         arkJSRuntime->GetErrorEventHandler()(JS_CRASH_CODE, msg);
580         return;
581     }
582     auto errorInfo = GenerateJsErrorObject(error, runtime);
583 
584     std::string summaryBody = GenerateSummaryBody(error, runtime);
585     LOGE("summaryBody: \n%{public}s", summaryBody.c_str());
586     EventReport::JsErrReport(AceApplicationInfo::GetInstance().GetPackageName(), errorInfo.name, summaryBody);
587 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
588     ExceptionHandler::HandleJsException(summaryBody, errorInfo);
589 #endif
590 }
591 
ParseLogContent(const std::vector<std::string> & params)592 std::string ParseLogContent(const std::vector<std::string>& params)
593 {
594     std::string ret;
595     int32_t flag = 0;
596     if (params.empty()) {
597         return ret;
598     }
599     std::string formatStr = params[0];
600     auto size = static_cast<int32_t>(params.size());
601     auto len = static_cast<int32_t>(formatStr.size());
602     int32_t pos = 0;
603     int32_t count = 1;
604     for (; pos < len; ++pos) {
605         if (count >= size) {
606             break;
607         }
608         if (formatStr[pos] == '%') {
609             flag = 1;
610             if (pos + 1 >= len) {
611                 break;
612             }
613             switch (formatStr[pos + 1]) {
614                 case 's':
615                 case 'j':
616                 case 'd':
617                 case 'O':
618                 case 'o':
619                 case 'i':
620                 case 'f':
621                 case 'c':
622                     ret += params[count++];
623                     ++pos;
624                     break;
625                 case '%':
626                     ret += formatStr[pos];
627                     ++pos;
628                     break;
629                 default:
630                     ret += formatStr[pos];
631                     break;
632             }
633         } else {
634             ret += formatStr[pos];
635         }
636     }
637     if (pos < len) {
638         ret += formatStr.substr(pos, len - pos);
639     }
640     switch (flag) {
641         case 0:
642             ret += " ";
643             for (int32_t i = 1; i < size; ++i) {
644                 ret += params[i];
645                 if (i != size - 1) {
646                     ret += " ";
647                 }
648             }
649             break;
650         case 1:
651             for (int32_t i = 2; i < size; ++i) {
652                 ret += params[i];
653             }
654             break;
655         default:
656             break;
657     }
658     return ret;
659 }
660 
GetLogContent(const shared_ptr<JsRuntime> & runtime,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)661 std::string GetLogContent(
662     const shared_ptr<JsRuntime>& runtime, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
663 {
664     if (argc == 1) {
665         return argv[0]->ToString(runtime);
666     }
667     std::vector<std::string> params;
668     params.reserve(argc);
669     for (int32_t i = 0; i < argc; ++i) {
670         params.emplace_back(argv[i]->ToString(runtime));
671     }
672     return ParseLogContent(params);
673 }
674 
675 // parse log content from startIndex to end
GetLogContentFromStartIndex(const shared_ptr<JsRuntime> & runtime,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc,int32_t startIndex)676 std::string GetLogContentFromStartIndex(
677     const shared_ptr<JsRuntime>& runtime, const std::vector<shared_ptr<JsValue>>& argv,
678     int32_t argc, int32_t startIndex)
679 {
680     if (argc < startIndex + 1) {
681         return "";
682     } else if (argc == startIndex + 1) {
683         return argv[startIndex]->ToString(runtime);
684     }
685     std::vector<std::string> params;
686     params.reserve(argc);
687     for (int32_t i = startIndex; i < argc; ++i) {
688         params.emplace_back(argv[i]->ToString(runtime));
689     }
690     return ParseLogContent(params);
691 }
692 
693 // parse log tag when the first arg is tag
GetLogTag(const shared_ptr<JsRuntime> & runtime,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc,AceLogTag & tag)694 bool GetLogTag(
695     const shared_ptr<JsRuntime>& runtime, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc, AceLogTag& tag)
696 {
697     if (argc < 1) {
698         return false;
699     }
700     auto tagNum = argv[0]->ToInt32(runtime);
701     switch (tagNum) {
702         case 0:
703             tag = AceLogTag::ACE_STATE_MGMT;
704             break;
705         case 1:
706             tag = AceLogTag::ACE_ARK_COMPONENT;
707             break;
708         default:
709             tag = AceLogTag::ACE_DEFAULT_DOMAIN;
710             break;
711     }
712     return true;
713 }
714 
AppLogPrint(const shared_ptr<JsRuntime> & runtime,JsLogLevel level,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)715 shared_ptr<JsValue> AppLogPrint(
716     const shared_ptr<JsRuntime>& runtime, JsLogLevel level, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
717 {
718     // Should have at least 1 parameters.
719     if (argc == 0) {
720         return runtime->NewUndefined();
721     }
722     std::string content = GetLogContent(runtime, argv, argc);
723     switch (level) {
724         case JsLogLevel::DEBUG:
725             APP_LOGD("%{public}s", content.c_str());
726             break;
727         case JsLogLevel::INFO:
728             APP_LOGI("%{public}s", content.c_str());
729             break;
730         case JsLogLevel::WARNING:
731             APP_LOGW("%{public}s", content.c_str());
732             break;
733         case JsLogLevel::ERROR:
734             APP_LOGE("%{public}s", content.c_str());
735             break;
736     }
737 
738     return runtime->NewUndefined();
739 }
740 
741 // native implementation for js function: console.debug()
AppDebugLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)742 shared_ptr<JsValue> JsiBaseUtils::AppDebugLogPrint(const shared_ptr<JsRuntime>& runtime,
743     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
744 {
745     return AppLogPrint(runtime, JsLogLevel::DEBUG, argv, argc);
746 }
747 
748 // native implementation for js function: console.info()
AppInfoLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)749 shared_ptr<JsValue> JsiBaseUtils::AppInfoLogPrint(const shared_ptr<JsRuntime>& runtime,
750     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
751 {
752     return AppLogPrint(runtime, JsLogLevel::INFO, argv, argc);
753 }
754 
755 // native implementation for js function: console.warn()
AppWarnLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)756 shared_ptr<JsValue> JsiBaseUtils::AppWarnLogPrint(const shared_ptr<JsRuntime>& runtime,
757     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
758 {
759     return AppLogPrint(runtime, JsLogLevel::WARNING, argv, argc);
760 }
761 
762 // native implementation for js function: console.error()
AppErrorLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)763 shared_ptr<JsValue> JsiBaseUtils::AppErrorLogPrint(const shared_ptr<JsRuntime>& runtime,
764     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
765 {
766     return AppLogPrint(runtime, JsLogLevel::ERROR, argv, argc);
767 }
768 
JsLogPrint(const shared_ptr<JsRuntime> & runtime,JsLogLevel level,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)769 shared_ptr<JsValue> JsLogPrint(
770     const shared_ptr<JsRuntime>& runtime, JsLogLevel level, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
771 {
772     // Should have 1 parameters.
773     if (argc == 0) {
774         return runtime->NewUndefined();
775     }
776 
777     AceLogTag tag;
778     std::string content;
779     auto getTagSuccess = GetLogTag(runtime, argv, argc, tag);
780     if (getTagSuccess) {
781         content = GetLogContentFromStartIndex(runtime, argv, argc, 1);
782     }
783     switch (level) {
784         case JsLogLevel::DEBUG:
785             TAG_LOGD(tag, "%{public}s", content.c_str());
786             break;
787         case JsLogLevel::INFO:
788             TAG_LOGI(tag, "%{public}s", content.c_str());
789             break;
790         case JsLogLevel::WARNING:
791             TAG_LOGW(tag, "%{public}s", content.c_str());
792             break;
793         case JsLogLevel::ERROR:
794             TAG_LOGE(tag, "%{public}s", content.c_str());
795             break;
796     }
797 
798     shared_ptr<JsValue> ret = runtime->NewUndefined();
799     return ret;
800 }
801 
PrintLog(int id,int level,const char * tag,const char * fmt,const char * message)802 int PrintLog(int id, int level, const char* tag, const char* fmt, const char* message)
803 {
804     switch (JsLogLevel(level - 3)) {
805         case JsLogLevel::INFO:
806             LOGI("%{public}s::%{public}s", tag, message);
807             break;
808         case JsLogLevel::WARNING:
809             LOGW("%{public}s::%{public}s", tag, message);
810             break;
811         case JsLogLevel::ERROR:
812             LOGE("%{public}s::%{public}s", tag, message);
813             break;
814         case JsLogLevel::DEBUG:
815             LOGD("%{public}s::%{public}s", tag, message);
816             break;
817         default:
818             LOGF("%{public}s::%{public}s", tag, message);
819             break;
820     }
821     return 0;
822 }
823 
JsDebugLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)824 shared_ptr<JsValue> JsiBaseUtils::JsDebugLogPrint(const shared_ptr<JsRuntime>& runtime,
825     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
826 {
827     return JsLogPrint(runtime, JsLogLevel::DEBUG, argv, argc);
828 }
829 
JsInfoLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)830 shared_ptr<JsValue> JsiBaseUtils::JsInfoLogPrint(const shared_ptr<JsRuntime>& runtime,
831     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
832 {
833     return JsLogPrint(runtime, JsLogLevel::INFO, argv, argc);
834 }
835 
JsWarnLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)836 shared_ptr<JsValue> JsiBaseUtils::JsWarnLogPrint(const shared_ptr<JsRuntime>& runtime,
837     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
838 {
839     return JsLogPrint(runtime, JsLogLevel::WARNING, argv, argc);
840 }
841 
JsErrorLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)842 shared_ptr<JsValue> JsiBaseUtils::JsErrorLogPrint(const shared_ptr<JsRuntime>& runtime,
843     const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
844 {
845     return JsLogPrint(runtime, JsLogLevel::ERROR, argv, argc);
846 }
847 
848 thread_local std::stack<std::unique_ptr<AceScopedTrace>> JsiBaseUtils::aceScopedTrace_;
849 
JsTraceBegin(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)850 shared_ptr<JsValue> JsiBaseUtils::JsTraceBegin(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
851     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
852 {
853     if (SystemProperties::GetDebugEnabled()) {
854         std::string traceName = GetLogContent(runtime, argv, argc);
855         aceScopedTrace_.emplace(std::make_unique<AceScopedTrace>(traceName.c_str()));
856     }
857     return runtime->NewUndefined();
858 }
859 
JsTraceEnd(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)860 shared_ptr<JsValue> JsiBaseUtils::JsTraceEnd(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
861     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
862 {
863     if (!aceScopedTrace_.empty() && SystemProperties::GetDebugEnabled()) {
864         aceScopedTrace_.pop();
865     }
866     return runtime->NewUndefined();
867 }
868 
GetLogContent(napi_env env,napi_callback_info info)869 std::string GetLogContent(napi_env env, napi_callback_info info)
870 {
871     size_t argc = 0;
872     napi_value* argv = nullptr;
873     napi_value thisVar = nullptr;
874     void* data = nullptr;
875     napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
876     if (argc > 0) {
877         argv = new napi_value[argc];
878     }
879     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
880 
881     std::string content;
882     napi_valuetype valueType = napi_undefined;
883     for (size_t i = 0; i < argc; ++i) {
884         napi_typeof(env, argv[i], &valueType);
885         if (valueType != napi_string) {
886             continue;
887         }
888         size_t buffSize = 0;
889         napi_status status = napi_get_value_string_utf8(env, argv[i], nullptr, 0, &buffSize);
890         if (status != napi_ok || buffSize == 0) {
891             continue;
892         }
893         std::unique_ptr<char[]> paramsChar = std::make_unique<char[]>(buffSize + 1);
894         size_t ret = 0;
895         napi_get_value_string_utf8(env, argv[i], paramsChar.get(), buffSize + 1, &ret);
896         content.append(paramsChar.get());
897     }
898     delete[] argv;
899     return content;
900 }
901 
AppLogPrint(napi_env env,napi_callback_info info,JsLogLevel level)902 napi_value AppLogPrint(napi_env env, napi_callback_info info, JsLogLevel level)
903 {
904     // Should have at least 1 parameters.
905     napi_value result = nullptr;
906     std::string content = GetLogContent(env, info);
907     switch (level) {
908         case JsLogLevel::DEBUG:
909             APP_LOGD("%{public}s", content.c_str());
910             break;
911         case JsLogLevel::INFO:
912             APP_LOGI("%{public}s", content.c_str());
913             break;
914         case JsLogLevel::WARNING:
915             APP_LOGW("%{public}s", content.c_str());
916             break;
917         case JsLogLevel::ERROR:
918             APP_LOGE("%{public}s", content.c_str());
919             break;
920     }
921 
922     return result;
923 }
924 
AppDebugLogPrint(napi_env env,napi_callback_info info)925 napi_value AppDebugLogPrint(napi_env env, napi_callback_info info)
926 {
927     return AppLogPrint(env, info, JsLogLevel::DEBUG);
928 }
929 
AppInfoLogPrint(napi_env env,napi_callback_info info)930 napi_value AppInfoLogPrint(napi_env env, napi_callback_info info)
931 {
932     return AppLogPrint(env, info, JsLogLevel::INFO);
933 }
934 
AppWarnLogPrint(napi_env env,napi_callback_info info)935 napi_value AppWarnLogPrint(napi_env env, napi_callback_info info)
936 {
937     return AppLogPrint(env, info, JsLogLevel::WARNING);
938 }
939 
AppErrorLogPrint(napi_env env,napi_callback_info info)940 napi_value AppErrorLogPrint(napi_env env, napi_callback_info info)
941 {
942     return AppLogPrint(env, info, JsLogLevel::ERROR);
943 }
944 
GetStageSourceMap(const AceType * data,std::unordered_map<std::string,RefPtr<Framework::RevSourceMap>> & sourceMaps)945 void JsiBaseUtils::GetStageSourceMap(
946     const AceType* data, std::unordered_map<std::string, RefPtr<Framework::RevSourceMap>>& sourceMaps)
947 {
948     auto delegate = GetDelegate(data);
949     std::string maps;
950     if (delegate != nullptr && delegate->GetAssetContent(MERGE_SOURCEMAPS_PATH, maps)) {
951         auto SourceMap = AceType::MakeRefPtr<RevSourceMap>();
952         SourceMap->StageModeSourceMapSplit(maps, sourceMaps);
953     } else {
954         LOGW("GetRunningPage SourceMap load failed!");
955     }
956 }
957 } // namespace OHOS::Ace::Framework
958