• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "source_map.h"
17 
18 #include <cerrno>
19 #include <climits>
20 #include <cstdlib>
21 #include <fstream>
22 #include <vector>
23 #include <unistd.h>
24 
25 #include "hilog_wrapper.h"
26 namespace OHOS {
27 namespace AbilityRuntime {
28 
29 constexpr char SOURCES[] = "sources";
30 constexpr char NAMES[] = "names";
31 constexpr char MAPPINGS[] = "mappings";
32 constexpr char FILE[] = "file";
33 constexpr char SOURCE_CONTENT[] = "sourceContent";
34 constexpr char SOURCE_ROOT[] = "sourceRoot";
35 constexpr char DELIMITER_COMMA = ',';
36 constexpr char DELIMITER_SEMICOLON = ';';
37 constexpr char DOUBLE_SLASH = '\\';
38 constexpr char WEBPACK[] = "webpack:///";
39 const std::string REALPATH_FLAG = "/temprary/";
40 const std::string ABILITYPATH_FLAG = "/entry/ets/";
41 const std::string NOT_FOUNDMAP = "Cannot get SourceMap info, dump raw stack:\n";
42 constexpr int64_t ASSET_FILE_MAX_SIZE = 20 * (1 << 20);
43 
ReadSourceMapData(const std::string & filePath,std::string & content)44 bool ModSourceMap::ReadSourceMapData(const std::string& filePath, std::string& content)
45 {
46     char path[PATH_MAX] ;
47     if (realpath(filePath.c_str(), path) == nullptr) {
48         HILOG_ERROR("ModSourceMap::ReadSourceMapData realpath(%{public}s) failed, errno = %{public}d",
49                     filePath.c_str(), errno);
50         return false;
51     }
52 
53     std::ifstream stream(path, std::ios::binary | std::ios::ate);
54     if (!stream.is_open()) {
55         HILOG_ERROR("ModSourceMap::ReadSourceMapData failed to open file %{public}s", path);
56         return false;
57     }
58 
59     int64_t fileLen = stream.tellg();
60     if (fileLen > ASSET_FILE_MAX_SIZE) {
61         return false;
62     }
63 
64     char buffer[fileLen];
65     buffer[fileLen - 1] = '\0';
66     stream.seekg(0);
67     stream.read(buffer, fileLen);
68     content = buffer;
69 
70     return true;
71 }
72 
Find(int32_t row,int32_t col,const SourceMapData & targetMap,const std::string & key)73 MappingInfo ModSourceMap::Find(int32_t row, int32_t col, const SourceMapData& targetMap, const std::string& key)
74 {
75     if (row < 1 || col < 1) {
76         HILOG_ERROR("the input pos is wrong");
77         return MappingInfo {};
78     }
79     row--;
80     col--;
81     // binary search
82     int32_t left = 0;
83     int32_t right = static_cast<int32_t>(targetMap.afterPos_.size()) - 1;
84     int32_t res = 0;
85     if (row > targetMap.afterPos_[targetMap.afterPos_.size() - 1].afterRow) {
86         return MappingInfo { row + 1, col + 1, targetMap.files_[0] };
87     }
88     while (right - left >= 0) {
89         int32_t mid = (right + left) / 2;
90         if ((targetMap.afterPos_[mid].afterRow == row && targetMap.afterPos_[mid].afterColumn > col) ||
91              targetMap.afterPos_[mid].afterRow > row) {
92             right = mid - 1;
93         } else {
94             res = mid;
95             left = mid + 1;
96         }
97     }
98     std::string sources = key;
99     auto pos = sources.find(WEBPACK);
100     if (pos != std::string::npos) {
101         sources.replace(pos, sizeof(WEBPACK) - 1, "");
102     }
103 
104     return MappingInfo {
105         .row = targetMap.afterPos_[res].beforeRow + 1,
106         .col = targetMap.afterPos_[res].beforeColumn + 1,
107         .sources = sources,
108     };
109 }
110 
ExtractKeyInfo(const std::string & sourceMap,std::vector<std::string> & sourceKeyInfo)111 void ModSourceMap::ExtractKeyInfo(const std::string& sourceMap, std::vector<std::string>& sourceKeyInfo)
112 {
113 
114     uint32_t cnt = 0;
115     std::string tempStr;
116     for (uint32_t i = 0; i < sourceMap.size(); i++) {
117         // reslove json file
118         if (sourceMap[i] == DOUBLE_SLASH) {
119             i++;
120             tempStr += sourceMap[i];
121             continue;
122         }
123         // cnt is used to represent a pair of double quotation marks: ""
124         if (sourceMap[i] == '"') {
125             cnt++;
126         }
127         if (cnt == 2) {
128             sourceKeyInfo.push_back(tempStr);
129             tempStr = "";
130             cnt = 0;
131         } else if (cnt == 1) {
132             if (sourceMap[i] != '"') {
133                 tempStr += sourceMap[i];
134             }
135         }
136     }
137 }
138 
GetPosInfo(const std::string & temp,int32_t start,std::string & line,std::string & column)139 void ModSourceMap::GetPosInfo(const std::string& temp, int32_t start, std::string& line, std::string& column)
140 {
141     // 0 for colum, 1 for row
142     int32_t flag = 0;
143     // find line, column
144     for (int32_t i = start - 1; i > 0; i--) {
145         if (temp[i] == ':') {
146             flag += 1;
147             continue;
148         }
149         if (flag == 0) {
150             column = temp[i] + column;
151         } else if (flag == 1) {
152             line = temp[i] + line;
153         } else {
154             break;
155         }
156     }
157 }
158 
StringToInt(const std::string & value)159 int32_t ModSourceMap::StringToInt(const std::string& value)
160 {
161     errno = 0;
162     char* pEnd = nullptr;
163     int64_t result = std::strtol(value.c_str(), &pEnd, 10);
164     if (pEnd == value.c_str() || (result < INT_MIN || result > INT_MAX) || errno == ERANGE) {
165         return 0;
166     } else {
167         return result;
168     }
169 }
170 
GetRelativePath(const std::string & sources)171 std::string ModSourceMap::GetRelativePath(const std::string& sources)
172 {
173     std::string temp = sources;
174     std::size_t splitPos = std::string::npos;
175     const static int pathLevel = 3;
176     int i = 0;
177     while (i < pathLevel) {
178         splitPos = temp.find_last_of("/\\");
179         if (splitPos != std::string::npos) {
180             temp = temp.substr(0, splitPos - 1);
181         } else {
182             break;
183         }
184         i++;
185     }
186     if (i == pathLevel) {
187         return sources.substr(splitPos);
188     }
189     return sources;
190 }
191 
Init(const std::string & sourceMap,SourceMapData & curMapData)192 void ModSourceMap::Init(const std::string& sourceMap, SourceMapData& curMapData)
193 {
194     std::vector<std::string> sourceKeyInfo;
195     std::string mark = "";
196 
197     ExtractKeyInfo(sourceMap, sourceKeyInfo);
198 
199     // first: find the key info and record the temp key info
200     // second: add the detail into the keyinfo
201     for (auto keyInfo : sourceKeyInfo) {
202         if (keyInfo == SOURCES || keyInfo == NAMES || keyInfo == MAPPINGS || keyInfo == FILE ||
203             keyInfo == SOURCE_CONTENT ||  keyInfo == SOURCE_ROOT) {
204             // record the temp key info
205             mark = keyInfo;
206         } else if (mark == SOURCES) {
207             curMapData.sources_.push_back(keyInfo);
208         } else if (mark == NAMES) {
209             curMapData.names_.push_back(keyInfo);
210         } else if (mark == MAPPINGS) {
211             curMapData.mappings_.push_back(keyInfo);
212         } else if (mark == FILE) {
213             curMapData.files_.push_back(keyInfo);
214         } else {
215             continue;
216         }
217     }
218 
219     // transform to vector for mapping easily
220     curMapData.mappings_ = HandleMappings(curMapData.mappings_[0]);
221 
222     // the first bit: the column after transferring.
223     // the second bit: the source file.
224     // the third bit: the row before transferring.
225     // the fourth bit: the column before transferring.
226     // the fifth bit: the variable name.
227     for (const auto& mapping : curMapData.mappings_) {
228         if (mapping == ";") {
229             // plus a line for each semicolon
230             curMapData.nowPos_.afterRow++,
231             curMapData.nowPos_.afterColumn = 0;
232             continue;
233         }
234         // decode each mapping ";QAABC"
235         std::vector<int32_t> ans;
236         if (!VlqRevCode(mapping, ans)) {
237             HILOG_ERROR("decode code fail");
238             return;
239         }
240         if (ans.size() == 0) {
241             HILOG_ERROR("decode sourcemap fail, mapping: %{public}s", mapping.c_str());
242             break;
243         }
244         if (ans.size() == 1) {
245             curMapData.nowPos_.afterColumn += ans[0];
246             continue;
247         }
248         // after decode, assgin each value to the position
249         curMapData.nowPos_.afterColumn += ans[0];
250         curMapData.nowPos_.sourcesVal += ans[1];
251         curMapData.nowPos_.beforeRow += ans[2];
252         curMapData.nowPos_.beforeColumn += ans[3];
253         if (ans.size() == 5) {
254             curMapData.nowPos_.namesVal += ans[4];
255         }
256         curMapData.afterPos_.push_back({
257             curMapData.nowPos_.beforeRow,
258             curMapData.nowPos_.beforeColumn,
259             curMapData.nowPos_.afterRow,
260             curMapData.nowPos_.afterColumn,
261             curMapData.nowPos_.sourcesVal,
262             curMapData.nowPos_.namesVal
263         });
264     }
265     curMapData.mappings_.clear();
266     curMapData.mappings_.shrink_to_fit();
267     sourceKeyInfo.clear();
268     sourceKeyInfo.shrink_to_fit();
269 };
270 
HandleMappings(const std::string & mapping)271 std::vector<std::string> ModSourceMap::HandleMappings(const std::string& mapping)
272 {
273     std::vector<std::string> keyInfo;
274     std::string tempStr;
275     for (uint32_t i = 0; i < mapping.size(); i++) {
276         if (mapping[i] == DELIMITER_COMMA) {
277             keyInfo.push_back(tempStr);
278             tempStr = "";
279         } else if (mapping[i] == DELIMITER_SEMICOLON) {
280             if (tempStr != "") {
281                 keyInfo.push_back(tempStr);
282             }
283             tempStr = "";
284             keyInfo.push_back(";");
285         } else {
286             tempStr += mapping[i];
287         }
288     }
289     if (tempStr != "") {
290         keyInfo.push_back(tempStr);
291     }
292     return keyInfo;
293 };
294 
Base64CharToInt(char charCode)295 uint32_t ModSourceMap::Base64CharToInt(char charCode)
296 {
297     if ('A' <= charCode && charCode <= 'Z') {
298         // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
299         return charCode - 'A';
300     } else if ('a' <= charCode && charCode <= 'z') {
301         // 26 - 51: abcdefghijklmnopqrstuvwxyz
302         return charCode - 'a' + 26;
303     } else if ('0' <= charCode && charCode <= '9') {
304         // 52 - 61: 0123456789
305         return charCode - '0' + 52;
306     } else if (charCode == '+') {
307         // 62: +
308         return 62;
309     } else if (charCode == '/') {
310         // 63: /
311         return 63;
312     }
313     return 64;
314 };
315 
VlqRevCode(const std::string & vStr,std::vector<int32_t> & ans)316 bool ModSourceMap::VlqRevCode(const std::string& vStr, std::vector<int32_t>& ans)
317 {
318     const int32_t VLQ_BASE_SHIFT = 5;
319     // binary: 100000
320     uint32_t VLQ_BASE = 1 << VLQ_BASE_SHIFT;
321     // binary: 011111
322     uint32_t VLQ_BASE_MASK = VLQ_BASE - 1;
323     // binary: 100000
324     uint32_t VLQ_CONTINUATION_BIT = VLQ_BASE;
325     uint32_t result = 0;
326     uint32_t shift = 0;
327     bool continuation = 0;
328     for (uint32_t i = 0; i < vStr.size(); i++) {
329         uint32_t digit = Base64CharToInt(vStr[i]);
330         if (digit == 64) {
331             return false;
332         }
333         continuation = digit & VLQ_CONTINUATION_BIT;
334         digit &= VLQ_BASE_MASK;
335         result += digit << shift;
336         if (continuation) {
337             shift += VLQ_BASE_SHIFT;
338         } else {
339             bool isNegate = result & 1;
340             result >>= 1;
341             ans.push_back(isNegate ? -result : result);
342             result = 0;
343             shift = 0;
344         }
345     }
346     if (continuation) {
347         return false;
348     }
349     return true;
350 };
351 
GetSourceMapData(ModSourceMap & bindSourceMaps,const std::string & temp,SourceMapData & curMapData)352 bool ModSourceMap::GetSourceMapData(ModSourceMap& bindSourceMaps, const std::string& temp, SourceMapData& curMapData)
353 {
354     // get the file path of the .map file
355     int32_t startPos = static_cast<int32_t>(temp.find(REALPATH_FLAG));
356     if (startPos == -1) {
357         HILOG_ERROR("ModSourceMap::TranslateBySourceMap Get /temprary/ pos error.");
358         return false;
359     }
360     int32_t endPos = static_cast<int32_t>(temp.size() - 1);
361     std::string mapFilePath = bindSourceMaps.bundleCodeDir_ + ABILITYPATH_FLAG +
362                               temp.substr(startPos + REALPATH_FLAG.size(), endPos - startPos) + ".map";
363 
364     // parse file and cache
365     auto iter = bindSourceMaps.sourceMaps_.find(mapFilePath);
366     if (iter == bindSourceMaps.sourceMaps_.end()) {
367         std::string curSourceMap;
368         if (!ReadSourceMapData(mapFilePath, curSourceMap)) {
369             return false;
370         }
371 
372         Init(curSourceMap, curMapData);
373         bindSourceMaps.sourceMaps_.insert(std::pair<std::string, SourceMapData>(mapFilePath, curMapData));
374     } else {
375         curMapData = iter->second;
376     }
377     return true;
378 }
379 
TranslateBySourceMap(const std::string & stackStr,ModSourceMap & bindSourceMaps,const std::string & BundleCodeDir)380 std::string ModSourceMap::TranslateBySourceMap(const std::string& stackStr, ModSourceMap& bindSourceMaps, const std::string& BundleCodeDir)
381 {
382     const std::string closeBrace = ")";
383     const std::string openBrace = "(";
384     std::string ans = "";
385     std::string tempStack = stackStr;
386 
387     // find per line of stack
388     std::vector<std::string> res;
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     if (!tempStr.empty()) {
399         res.push_back(tempStr);
400     }
401 
402     // collect error info first
403     bool needGetErrorPos = false;
404     int32_t errorPos = 0;
405     uint32_t i = 0;
406     std::string codeStart = "SourceCode (";
407     std::string sourceCode = "";
408     if (res.size() >= 1) {
409         std::string fristLine = res[0];
410         uint32_t codeStartLen = codeStart.length();
411         if (fristLine.substr(0, codeStartLen).compare(codeStart) == 0) {
412             sourceCode = fristLine.substr(codeStartLen, fristLine.length() - codeStartLen - 1);
413             i = 1;  // 1 means Convert from the second line
414             needGetErrorPos = true;
415     }
416     }
417     std::string curSourceMap;
418     std::string filePath = BundleCodeDir + ABILITYPATH_FLAG + "sourceMaps.map";
419     if (!ReadSourceMapData(filePath, curSourceMap)) {
420         HILOG_ERROR("ReadSourceMapData fail");
421         return stackStr;
422     }
423     std::size_t s = 0;
424     std::size_t j = 0;
425     std::string value;
426     std::string key;
427     std::map<std::string, std::string> MapData;
428     while((s = curSourceMap.find(": {", j)) != std::string::npos)
429     {
430         j = curSourceMap.find("}," , s);
431         uint32_t q = s;
432         uint32_t jj = j;
433         value = curSourceMap.substr(q + 1, jj - q+2);
434         uint32_t sources = value.find("\"sources\": [");
435         uint32_t names = value.find("],");
436         key = value.substr(sources + 20 , names - sources - 26);
437         MapData.insert(std::pair<std::string, std::string>(key, value));
438     }
439 
440     for (; i < res.size(); i++) {
441         std::string temp = res[i];
442         uint32_t start = temp.find("(");
443         uint32_t end  = temp.find(":");
444         std::string key = temp.substr(start + 1 , end - start - 1);
445         int32_t closeBracePos = static_cast<int32_t>(temp.find(closeBrace));
446         int32_t openBracePos = static_cast<int32_t>(temp.find(openBrace));
447 
448         std::string line = "";
449         std::string column = "";
450         GetPosInfo(temp, closeBracePos, line, column);
451         if (needGetErrorPos) {
452             errorPos = StringToInt(column);
453             needGetErrorPos = false;
454         }
455         if (line.empty() || column.empty()) {
456             break;
457         }
458         static SourceMapData curMapData;
459         if(!bindSourceMaps.isStageModel) {
460             if (i == 1) {   // The non module scenario initializes curmapdata only at the first traversal
461                 if (!bindSourceMaps.nonModularMap_) {
462                     return NOT_FOUNDMAP + stackStr;
463                 }
464                 curMapData = *bindSourceMaps.nonModularMap_;
465             }
466         } else {
467             auto iter = MapData.find(key);
468             if (iter != MapData.end()) {
469                 Init(iter->second, curMapData);
470             } else {
471                 ans += NOT_FOUNDMAP + temp + "\n";
472                 continue;
473             }
474         }
475         const std::string sourceInfo = GetSourceInfo(line, column, curMapData, key);
476         if (sourceInfo.empty()) {
477             break;
478         }
479         temp.replace(openBracePos, closeBracePos - openBracePos + 1, sourceInfo);
480         ans += temp + "\n";
481     }
482 
483     if (ans.empty()) {
484         return tempStack;
485     }
486     return ans;
487 }
488 
GetSourceInfo(const std::string & line,const std::string & column,const SourceMapData & targetMap,const std::string & key)489 std::string ModSourceMap::GetSourceInfo(const std::string& line, const std::string& column, const SourceMapData& targetMap, const std::string& key)
490 {
491     int32_t offSet = 0;
492     std::string sourceInfo;
493     MappingInfo mapInfo;
494 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
495         mapInfo = Find(StringToInt(line) - offSet + OFFSET_PREVIEW, StringToInt(column), targetMap, key);
496 #else
497         mapInfo = Find(StringToInt(line) - offSet, StringToInt(column), targetMap, key);
498 #endif
499     if (mapInfo.row == 0 || mapInfo.col == 0) {
500         return "";
501     }
502     std::string sources = GetRelativePath(mapInfo.sources);
503     sourceInfo = "(" + sources + ":" + std::to_string(mapInfo.row) + ":" + std::to_string(mapInfo.col) + ")";
504     return sourceInfo;
505 }
506 
NonModularLoadSourceMap(ModSourceMap & targetMaps,const std::string & targetMap)507 void ModSourceMap::NonModularLoadSourceMap(ModSourceMap& targetMaps, const std::string& targetMap) {
508     if (!targetMaps.nonModularMap_) {
509         targetMaps.nonModularMap_ = std::make_shared<SourceMapData>();
510     }
511     Init(targetMap, *targetMaps.nonModularMap_);
512 }
513 
GetOriginalNames(std::shared_ptr<SourceMapData> targetMapData,const std::string & sourceCode,uint32_t & errorPos)514 std::string ModSourceMap::GetOriginalNames(std::shared_ptr<SourceMapData> targetMapData, const std::string& sourceCode, uint32_t& errorPos)
515 {
516     if (sourceCode.empty() || sourceCode.find("SourceCode:\n") == std::string::npos) {
517         return sourceCode;
518     }
519     std::vector<std::string> names = targetMapData->names_;
520     if (names.size() % 2 != 0) {
521         HILOG_ERROR("Names in sourcemap is wrong.");
522         return sourceCode;
523     }
524 
525     std::string jsCode = sourceCode;
526     int32_t posDiff = 0;
527     for (uint32_t i = 0; i < names.size(); i += 2) {
528         auto found = jsCode.find(names[i]);
529         while (found != std::string::npos) {
530             // names_[i + 1] is the original name of names_[i]
531             jsCode.replace(found, names[i].length(), names[i + 1]);
532             if (static_cast<uint32_t>(found) < errorPos) {
533                 // sum the errorPos differences to adjust position of ^
534                 posDiff += static_cast<int32_t>(names[i + 1].length()) - static_cast<int32_t>(names[i].length());
535             }
536             // In case there are other variable names not replaced.
537             // example:var e = process.a.b + _ohos_process_1.a.b;
538             found = jsCode.find(names[i], found + names[i + 1].length());
539         }
540     }
541     auto lineBreakPos = jsCode.rfind('\n', jsCode.length() - 2);
542     if (lineBreakPos == std::string::npos) {
543         HILOG_WARN("There is something wrong in source code of summaryBody.");
544         return jsCode;
545     }
546     // adjust position of ^ in dump file
547     if (posDiff < 0) {
548         int32_t flagPos = static_cast<int32_t>(lineBreakPos) + static_cast<int32_t>(errorPos);
549         if (lineBreakPos > 0 && errorPos > 0 && flagPos < 0) {
550             HILOG_WARN("Add overflow of sourceCode.");
551             return jsCode;
552         }
553         if (flagPos < static_cast<int32_t>(jsCode.length()) && jsCode[flagPos] == '^' && flagPos + posDiff - 1 > 0) {
554             jsCode.erase(flagPos + posDiff - 1, -posDiff);
555         }
556     } else if (posDiff > 0) {
557         if (lineBreakPos + 1 < jsCode.length() - 1) {
558             jsCode.insert(lineBreakPos + 1, posDiff, ' ');
559         }
560     }
561     return jsCode;
562 }
563 
GetErrorPos(const std::string & rawStack)564 ErrorPos ModSourceMap::GetErrorPos(const std::string& rawStack)
565 {
566     size_t findLineEnd = rawStack.find("\n");
567     if (findLineEnd == std::string::npos) {
568         return std::make_pair(0, 0);
569     }
570     int32_t lineEnd = findLineEnd - 1;
571     if (lineEnd < 1 || rawStack[lineEnd - 1] == '?') {
572         return std::make_pair(0, 0);
573     }
574 
575     uint32_t secondPos = rawStack.rfind(':', lineEnd);
576     uint32_t fristPos = rawStack.rfind(':', secondPos - 1);
577 
578     std::string lineStr = rawStack.substr(fristPos + 1, secondPos - 1 - fristPos);
579     std::string columnStr = rawStack.substr(secondPos + 1, lineEnd - 1 - secondPos);
580 
581     return std::make_pair(StringToInt(lineStr), StringToInt(columnStr));
582 }
583 }   // namespace AbilityRuntime
584 }   // namespace OHOS
585