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,const std::string & uniqueId)568 void JsiBaseUtils::ReportJsErrorEvent(
569 std::shared_ptr<JsValue> error, std::shared_ptr<JsRuntime> runtime, const std::string& uniqueId)
570 {
571 if (!runtime) {
572 LOGI("ReportJsErrorEvent: jsi engine has been destroyed");
573 return;
574 }
575
576 auto arkJSRuntime = std::static_pointer_cast<ArkJSRuntime>(runtime);
577 if (arkJSRuntime && arkJSRuntime->GetErrorEventHandler()) {
578 std::string msg = GenerateErrorMsg(error, runtime);
579 LOGI("Handle error event, errMsg: \n%{public}s", msg.c_str());
580 arkJSRuntime->GetErrorEventHandler()(JS_CRASH_CODE, msg);
581 return;
582 }
583 auto errorInfo = GenerateJsErrorObject(error, runtime);
584
585 std::string summaryBody = GenerateSummaryBody(error, runtime);
586 LOGE("summaryBody: \n%{public}s", summaryBody.c_str());
587 EventReport::JsErrReport(AceApplicationInfo::GetInstance().GetPackageName(), errorInfo.name, summaryBody, uniqueId);
588 #if !defined(ANDROID_PLATFORM) && !defined(IOS_PLATFORM)
589 auto container = Container::Current();
590 auto isStageModel = container ? container->IsUseStageModel() : false;
591 ExceptionHandler::HandleJsException(summaryBody, errorInfo, isStageModel);
592 #endif
593 }
594
ParseLogContent(const std::vector<std::string> & params)595 std::string ParseLogContent(const std::vector<std::string>& params)
596 {
597 std::string ret;
598 int32_t flag = 0;
599 if (params.empty()) {
600 return ret;
601 }
602 std::string formatStr = params[0];
603 auto size = static_cast<int32_t>(params.size());
604 auto len = static_cast<int32_t>(formatStr.size());
605 int32_t pos = 0;
606 int32_t count = 1;
607 for (; pos < len; ++pos) {
608 if (count >= size) {
609 break;
610 }
611 if (formatStr[pos] == '%') {
612 flag = 1;
613 if (pos + 1 >= len) {
614 break;
615 }
616 switch (formatStr[pos + 1]) {
617 case 's':
618 case 'j':
619 case 'd':
620 case 'O':
621 case 'o':
622 case 'i':
623 case 'f':
624 case 'c':
625 ret += params[count++];
626 ++pos;
627 break;
628 case '%':
629 ret += formatStr[pos];
630 ++pos;
631 break;
632 default:
633 ret += formatStr[pos];
634 break;
635 }
636 } else {
637 ret += formatStr[pos];
638 }
639 }
640 if (pos < len) {
641 ret += formatStr.substr(pos, len - pos);
642 }
643 switch (flag) {
644 case 0:
645 ret += " ";
646 for (int32_t i = 1; i < size; ++i) {
647 ret += params[i];
648 if (i != size - 1) {
649 ret += " ";
650 }
651 }
652 break;
653 case 1:
654 for (int32_t i = 2; i < size; ++i) {
655 ret += params[i];
656 }
657 break;
658 default:
659 break;
660 }
661 return ret;
662 }
663
GetLogContent(const shared_ptr<JsRuntime> & runtime,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)664 std::string GetLogContent(
665 const shared_ptr<JsRuntime>& runtime, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
666 {
667 if (argc == 1) {
668 return argv[0]->ToString(runtime);
669 }
670 std::vector<std::string> params;
671 params.reserve(argc);
672 for (int32_t i = 0; i < argc; ++i) {
673 params.emplace_back(argv[i]->ToString(runtime));
674 }
675 return ParseLogContent(params);
676 }
677
678 // 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)679 std::string GetLogContentFromStartIndex(
680 const shared_ptr<JsRuntime>& runtime, const std::vector<shared_ptr<JsValue>>& argv,
681 int32_t argc, int32_t startIndex)
682 {
683 if (argc < startIndex + 1) {
684 return "";
685 } else if (argc == startIndex + 1) {
686 return argv[startIndex]->ToString(runtime);
687 }
688 std::vector<std::string> params;
689 params.reserve(argc);
690 for (int32_t i = startIndex; i < argc; ++i) {
691 params.emplace_back(argv[i]->ToString(runtime));
692 }
693 return ParseLogContent(params);
694 }
695
696 // 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)697 bool GetLogTag(
698 const shared_ptr<JsRuntime>& runtime, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc, AceLogTag& tag)
699 {
700 if (argc < 1) {
701 return false;
702 }
703 auto tagNum = argv[0]->ToInt32(runtime);
704 switch (tagNum) {
705 case 0:
706 tag = AceLogTag::ACE_STATE_MGMT;
707 break;
708 case 1:
709 tag = AceLogTag::ACE_ARK_COMPONENT;
710 break;
711 default:
712 tag = AceLogTag::ACE_DEFAULT_DOMAIN;
713 break;
714 }
715 return true;
716 }
717
AppLogPrint(const shared_ptr<JsRuntime> & runtime,JsLogLevel level,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)718 shared_ptr<JsValue> AppLogPrint(
719 const shared_ptr<JsRuntime>& runtime, JsLogLevel level, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
720 {
721 // Should have at least 1 parameters.
722 if (argc == 0) {
723 return runtime->NewUndefined();
724 }
725 std::string content = GetLogContent(runtime, argv, argc);
726 switch (level) {
727 case JsLogLevel::DEBUG:
728 APP_LOGD("%{public}s", content.c_str());
729 break;
730 case JsLogLevel::INFO:
731 APP_LOGI("%{public}s", content.c_str());
732 break;
733 case JsLogLevel::WARNING:
734 APP_LOGW("%{public}s", content.c_str());
735 break;
736 case JsLogLevel::ERROR:
737 APP_LOGE("%{public}s", content.c_str());
738 break;
739 }
740
741 return runtime->NewUndefined();
742 }
743
744 // 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)745 shared_ptr<JsValue> JsiBaseUtils::AppDebugLogPrint(const shared_ptr<JsRuntime>& runtime,
746 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
747 {
748 return AppLogPrint(runtime, JsLogLevel::DEBUG, argv, argc);
749 }
750
751 // 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)752 shared_ptr<JsValue> JsiBaseUtils::AppInfoLogPrint(const shared_ptr<JsRuntime>& runtime,
753 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
754 {
755 return AppLogPrint(runtime, JsLogLevel::INFO, argv, argc);
756 }
757
758 // 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)759 shared_ptr<JsValue> JsiBaseUtils::AppWarnLogPrint(const shared_ptr<JsRuntime>& runtime,
760 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
761 {
762 return AppLogPrint(runtime, JsLogLevel::WARNING, argv, argc);
763 }
764
765 // 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)766 shared_ptr<JsValue> JsiBaseUtils::AppErrorLogPrint(const shared_ptr<JsRuntime>& runtime,
767 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
768 {
769 return AppLogPrint(runtime, JsLogLevel::ERROR, argv, argc);
770 }
771
JsLogPrint(const shared_ptr<JsRuntime> & runtime,JsLogLevel level,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)772 shared_ptr<JsValue> JsLogPrint(
773 const shared_ptr<JsRuntime>& runtime, JsLogLevel level, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
774 {
775 // Should have 1 parameters.
776 if (argc == 0) {
777 return runtime->NewUndefined();
778 }
779
780 AceLogTag tag;
781 std::string content;
782 auto getTagSuccess = GetLogTag(runtime, argv, argc, tag);
783 if (getTagSuccess) {
784 content = GetLogContentFromStartIndex(runtime, argv, argc, 1);
785 }
786 switch (level) {
787 case JsLogLevel::DEBUG:
788 TAG_LOGD(tag, "%{public}s", content.c_str());
789 break;
790 case JsLogLevel::INFO:
791 TAG_LOGI(tag, "%{public}s", content.c_str());
792 break;
793 case JsLogLevel::WARNING:
794 TAG_LOGW(tag, "%{public}s", content.c_str());
795 break;
796 case JsLogLevel::ERROR:
797 TAG_LOGE(tag, "%{public}s", content.c_str());
798 break;
799 }
800
801 shared_ptr<JsValue> ret = runtime->NewUndefined();
802 return ret;
803 }
804
PrintLog(int id,int level,const char * tag,const char * fmt,const char * message)805 int PrintLog(int id, int level, const char* tag, const char* fmt, const char* message)
806 {
807 switch (JsLogLevel(level - 3)) {
808 case JsLogLevel::INFO:
809 LOGI("%{public}s::%{public}s", tag, message);
810 break;
811 case JsLogLevel::WARNING:
812 LOGW("%{public}s::%{public}s", tag, message);
813 break;
814 case JsLogLevel::ERROR:
815 LOGE("%{public}s::%{public}s", tag, message);
816 break;
817 case JsLogLevel::DEBUG:
818 LOGD("%{public}s::%{public}s", tag, message);
819 break;
820 default:
821 LOGF("%{public}s::%{public}s", tag, message);
822 break;
823 }
824 return 0;
825 }
826
JsDebugLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)827 shared_ptr<JsValue> JsiBaseUtils::JsDebugLogPrint(const shared_ptr<JsRuntime>& runtime,
828 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
829 {
830 return JsLogPrint(runtime, JsLogLevel::DEBUG, argv, argc);
831 }
832
JsInfoLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)833 shared_ptr<JsValue> JsiBaseUtils::JsInfoLogPrint(const shared_ptr<JsRuntime>& runtime,
834 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
835 {
836 return JsLogPrint(runtime, JsLogLevel::INFO, argv, argc);
837 }
838
JsWarnLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)839 shared_ptr<JsValue> JsiBaseUtils::JsWarnLogPrint(const shared_ptr<JsRuntime>& runtime,
840 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
841 {
842 return JsLogPrint(runtime, JsLogLevel::WARNING, argv, argc);
843 }
844
JsErrorLogPrint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)845 shared_ptr<JsValue> JsiBaseUtils::JsErrorLogPrint(const shared_ptr<JsRuntime>& runtime,
846 const shared_ptr<JsValue>& thisObj, const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
847 {
848 return JsLogPrint(runtime, JsLogLevel::ERROR, argv, argc);
849 }
850
851 thread_local std::stack<std::unique_ptr<AceScopedTrace>> JsiBaseUtils::aceScopedTrace_;
852
JsTraceBegin(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)853 shared_ptr<JsValue> JsiBaseUtils::JsTraceBegin(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
854 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
855 {
856 if (SystemProperties::GetDebugEnabled()) {
857 std::string traceName = GetLogContent(runtime, argv, argc);
858 aceScopedTrace_.emplace(std::make_unique<AceScopedTrace>(traceName.c_str()));
859 }
860 return runtime->NewUndefined();
861 }
862
JsTraceEnd(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)863 shared_ptr<JsValue> JsiBaseUtils::JsTraceEnd(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
864 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
865 {
866 if (!aceScopedTrace_.empty() && SystemProperties::GetDebugEnabled()) {
867 aceScopedTrace_.pop();
868 }
869 return runtime->NewUndefined();
870 }
871
GetLogContent(napi_env env,napi_callback_info info)872 std::string GetLogContent(napi_env env, napi_callback_info info)
873 {
874 size_t argc = 0;
875 napi_value* argv = nullptr;
876 napi_value thisVar = nullptr;
877 void* data = nullptr;
878 napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
879 if (argc > 0) {
880 argv = new napi_value[argc];
881 }
882 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
883
884 std::string content;
885 napi_valuetype valueType = napi_undefined;
886 for (size_t i = 0; i < argc; ++i) {
887 napi_typeof(env, argv[i], &valueType);
888 if (valueType != napi_string) {
889 continue;
890 }
891 size_t buffSize = 0;
892 napi_status status = napi_get_value_string_utf8(env, argv[i], nullptr, 0, &buffSize);
893 if (status != napi_ok || buffSize == 0) {
894 continue;
895 }
896 std::unique_ptr<char[]> paramsChar = std::make_unique<char[]>(buffSize + 1);
897 size_t ret = 0;
898 napi_get_value_string_utf8(env, argv[i], paramsChar.get(), buffSize + 1, &ret);
899 content.append(paramsChar.get());
900 }
901 delete[] argv;
902 return content;
903 }
904
AppLogPrint(napi_env env,napi_callback_info info,JsLogLevel level)905 napi_value AppLogPrint(napi_env env, napi_callback_info info, JsLogLevel level)
906 {
907 // Should have at least 1 parameters.
908 napi_value result = nullptr;
909 std::string content = GetLogContent(env, info);
910 switch (level) {
911 case JsLogLevel::DEBUG:
912 APP_LOGD("%{public}s", content.c_str());
913 break;
914 case JsLogLevel::INFO:
915 APP_LOGI("%{public}s", content.c_str());
916 break;
917 case JsLogLevel::WARNING:
918 APP_LOGW("%{public}s", content.c_str());
919 break;
920 case JsLogLevel::ERROR:
921 APP_LOGE("%{public}s", content.c_str());
922 break;
923 }
924
925 return result;
926 }
927
AppDebugLogPrint(napi_env env,napi_callback_info info)928 napi_value AppDebugLogPrint(napi_env env, napi_callback_info info)
929 {
930 return AppLogPrint(env, info, JsLogLevel::DEBUG);
931 }
932
AppInfoLogPrint(napi_env env,napi_callback_info info)933 napi_value AppInfoLogPrint(napi_env env, napi_callback_info info)
934 {
935 return AppLogPrint(env, info, JsLogLevel::INFO);
936 }
937
AppWarnLogPrint(napi_env env,napi_callback_info info)938 napi_value AppWarnLogPrint(napi_env env, napi_callback_info info)
939 {
940 return AppLogPrint(env, info, JsLogLevel::WARNING);
941 }
942
AppErrorLogPrint(napi_env env,napi_callback_info info)943 napi_value AppErrorLogPrint(napi_env env, napi_callback_info info)
944 {
945 return AppLogPrint(env, info, JsLogLevel::ERROR);
946 }
947
GetStageSourceMap(const AceType * data,std::unordered_map<std::string,RefPtr<Framework::RevSourceMap>> & sourceMaps)948 void JsiBaseUtils::GetStageSourceMap(
949 const AceType* data, std::unordered_map<std::string, RefPtr<Framework::RevSourceMap>>& sourceMaps)
950 {
951 auto delegate = GetDelegate(data);
952 std::string maps;
953 if (delegate != nullptr && delegate->GetAssetContent(MERGE_SOURCEMAPS_PATH, maps)) {
954 auto SourceMap = AceType::MakeRefPtr<RevSourceMap>();
955 SourceMap->StageModeSourceMapSplit(maps, sourceMaps);
956 } else {
957 LOGW("GetRunningPage SourceMap load failed!");
958 }
959 }
960 } // namespace OHOS::Ace::Framework
961