• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "frameworks/bridge/common/utils/source_map.h"
17 
18 #include <cstdint>
19 #include <fstream>
20 
21 #include "base/log/log.h"
22 #include "base/log/log_wrapper.h"
23 
24 namespace OHOS::Ace::Framework {
25 
26 const char SOURCES[] = "sources";
27 const char NAMES[] = "names";
28 const char MAPPINGS[] = "mappings";
29 const char FILE[] = "file";
30 const char NAMEMAP[] = "nameMap";
31 const char SOURCE_CONTENT[] = "sourceContent";
32 const char SOURCE_ROOT[] = "sourceRoot";
33 const char DELIMITER_COMMA = ',';
34 const char DELIMITER_SEMICOLON = ';';
35 const char DOUBLE_SLASH = '\\';
36 const char WEBPACK[] = "webpack:///";
37 constexpr int32_t AFTER_COLUMN = 0;
38 constexpr int32_t SOURCES_VAL = 1;
39 constexpr int32_t BEFORE_ROW = 2;
40 constexpr int32_t BEFORE_COLUMN = 3;
41 constexpr int32_t NAMES_VAL = 4;
42 constexpr int32_t P0S_SPACE_LENGTH = 20;
43 constexpr int32_t SPACE_LEN = 26;
44 
Find(int32_t row,int32_t col,bool isColPrecise)45 MappingInfo RevSourceMap::Find(int32_t row, int32_t col, bool isColPrecise)
46 {
47     if (row < 1 || col < 1 || afterPos_.empty()) {
48         LOGE("the input pos is wrong");
49         return MappingInfo {};
50     }
51     row--;
52     col--;
53     // binary search
54     int32_t left = 0;
55     int32_t right = static_cast<int32_t>(afterPos_.size()) - 1;
56     int32_t res = 0;
57     bool isRightBig = false;
58     if (row > afterPos_[afterPos_.size() - 1].afterRow) {
59         return MappingInfo { row + 1, col + 1, files_[0] };
60     }
61     while (right - left >= 0) {
62         int32_t mid = (right + left) / 2;
63         if ((afterPos_[mid].afterRow == row && afterPos_[mid].afterColumn > col) || afterPos_[mid].afterRow > row) {
64             right = mid - 1;
65             isRightBig = true;
66         } else {
67             res = mid;
68             left = mid + 1;
69         }
70     }
71 
72     /*
73      * real:[56:7]->[250:21]
74      * [row:col]->[afterRow:afterColumn]
75      * 0:[53:14]->[237:77]
76      * 1:[53:14]->[237:78]
77      * 2:[56:7]->[250:39]
78      * 3:[56:14]->[250:40]
79     */
80     if (!isColPrecise && isRightBig && right > 0 && afterPos_[right].afterRow < row) {
81         res = right + 1;
82     }
83 
84     int32_t sourcesSize = static_cast<int32_t>(sources_.size());
85     if (afterPos_[res].sourcesVal < 0 || afterPos_[res].sourcesVal >= sourcesSize) {
86         LOGE("sourcesVal invalid");
87         return MappingInfo {};
88     }
89     std::string sources = sources_[afterPos_[res].sourcesVal];
90     auto pos = sources.find(WEBPACK);
91     if (pos != std::string::npos) {
92         sources.replace(pos, sizeof(WEBPACK) - 1, "");
93     }
94 
95     return MappingInfo {
96         .row = afterPos_[res].beforeRow + 1,
97         .col = afterPos_[res].beforeColumn + 1,
98         .sources = sources,
99     };
100 }
101 
GetOriginalNames(const std::string & sourceCode,uint32_t & errorPos) const102 std::string RevSourceMap::GetOriginalNames(const std::string& sourceCode, uint32_t& errorPos) const
103 {
104     if (sourceCode.empty() || sourceCode.find("SourceCode:\n") == std::string::npos) {
105         LOGE("SourceCode in stack is wrong.");
106         return sourceCode;
107     }
108     if (nameMap_.size() % 2 != 0) {
109         LOGE("NameMap in sourcemap is wrong.");
110         return sourceCode;
111     }
112     std::string jsCode = sourceCode;
113     int32_t posDiff = 0;
114     for (uint32_t i = 0; i < nameMap_.size(); i += 2) {
115         auto found = jsCode.find(nameMap_[i]);
116         while (found != std::string::npos) {
117             // nameMap_[i + 1] is the original name of nameMap_[i]
118             jsCode.replace(found, nameMap_[i].length(), nameMap_[i + 1]);
119             if (static_cast<uint32_t>(found) < errorPos) {
120                 // sum the errorPos differences to adjust position of ^
121                 posDiff += static_cast<int32_t>(nameMap_[i + 1].length()) - static_cast<int32_t>(nameMap_[i].length());
122             }
123             // In case there are other variable names not replaced.
124             // example:var e = process.a.b + _ohos_process_1.a.b;
125             found = jsCode.find(nameMap_[i], found + nameMap_[i + 1].length());
126         }
127     }
128     auto lineBreakPos = jsCode.rfind('\n', jsCode.length() - 2);
129     if (lineBreakPos == std::string::npos) {
130         LOGW("There is something wrong in source code of summaryBody.");
131         return jsCode;
132     }
133     // adjust position of ^ in dump file
134     if (posDiff < 0) {
135         int32_t flagPos = static_cast<int32_t>(lineBreakPos) + static_cast<int32_t>(errorPos);
136         if (lineBreakPos > 0 && errorPos > 0 && flagPos < 0) {
137             LOGW("Add overflow of sourceCode.");
138             return jsCode;
139         }
140         if (flagPos < static_cast<int32_t>(jsCode.length()) && jsCode[flagPos] == '^' && flagPos + posDiff - 1 > 0) {
141             jsCode.erase(flagPos + posDiff - 1, -posDiff);
142         }
143     } else if (posDiff > 0) {
144         if (lineBreakPos + 1 < jsCode.length() - 1) {
145             jsCode.insert(lineBreakPos + 1, posDiff, ' ');
146         }
147     }
148     return jsCode;
149 }
150 
ExtractKeyInfo(const std::string & sourceMap,std::vector<std::string> & sourceKeyInfo)151 void RevSourceMap::ExtractKeyInfo(const std::string& sourceMap, std::vector<std::string>& sourceKeyInfo)
152 {
153     uint32_t cnt = 0;
154     std::string tempStr;
155     for (uint32_t i = 0; i < sourceMap.size(); i++) {
156         // reslove json file
157         if (sourceMap[i] == DOUBLE_SLASH) {
158             i++;
159             tempStr += sourceMap[i];
160             continue;
161         }
162         // cnt is used to represent a pair of double quotation marks: ""
163         if (sourceMap[i] == '"') {
164             cnt++;
165         }
166         if (cnt == 2) {
167             sourceKeyInfo.push_back(tempStr);
168             tempStr = "";
169             cnt = 0;
170         } else if (cnt == 1) {
171             if (sourceMap[i] != '"') {
172                 tempStr += sourceMap[i];
173             }
174         }
175     }
176 }
177 
Init(const std::string & sourceMap)178 void RevSourceMap::Init(const std::string& sourceMap)
179 {
180     std::vector<std::string> sourceKeyInfo;
181     std::string mark = "";
182 
183     ExtractKeyInfo(sourceMap, sourceKeyInfo);
184 
185     // first: find the key info and record the temp key info
186     // second: add the detail into the keyinfo
187     for (auto keyInfo : sourceKeyInfo) {
188         if (keyInfo == SOURCES || keyInfo == NAMES || keyInfo == MAPPINGS || keyInfo == FILE ||
189             keyInfo == SOURCE_CONTENT || keyInfo == SOURCE_ROOT || keyInfo == NAMEMAP) {
190             // record the temp key info
191             mark = keyInfo;
192         } else if (mark == SOURCES) {
193             sources_.push_back(keyInfo);
194         } else if (mark == NAMES) {
195             names_.push_back(keyInfo);
196         } else if (mark == MAPPINGS) {
197             mappings_.push_back(keyInfo);
198         } else if (mark == FILE) {
199             files_.push_back(keyInfo);
200         } else if (mark == NAMEMAP) {
201             nameMap_.push_back(keyInfo);
202         } else {
203             continue;
204         }
205     }
206 
207     if (mappings_.empty()) {
208         LOGE("decode sourcemap fail, mapping: %{public}s", sourceMap.c_str());
209         return;
210     }
211 
212     // transform to vector for mapping easily
213     mappings_ = HandleMappings(mappings_[0]);
214 
215     // the first bit: the column after transferring.
216     // the second bit: the source file.
217     // the third bit: the row before transferring.
218     // the fourth bit: the column before transferring.
219     // the fifth bit: the variable name.
220     for (const auto& mapping : mappings_) {
221         if (mapping == ";") {
222             // plus a line for each semicolon
223             nowPos_.afterRow++, nowPos_.afterColumn = 0;
224             continue;
225         }
226         // decode each mapping ";QAABC"
227         std::vector<int32_t> ans;
228         if (!VlqRevCode(mapping, ans)) {
229             LOGE("decode code fail");
230             return;
231         }
232         if (ans.size() == 0) {
233             LOGE("decode sourcemap fail, mapping: %{public}s", mapping.c_str());
234             break;
235         }
236         if (ans.size() == 1) {
237             nowPos_.afterColumn += ans[AFTER_COLUMN];
238             continue;
239         }
240         // after decode, assgin each value to the position
241         nowPos_.afterColumn += ans[AFTER_COLUMN];
242         nowPos_.sourcesVal += ans[SOURCES_VAL];
243         nowPos_.beforeRow += ans[BEFORE_ROW];
244         nowPos_.beforeColumn += ans[BEFORE_COLUMN];
245         if (ans.size() == 5) {
246             nowPos_.namesVal += ans[NAMES_VAL];
247         }
248         afterPos_.push_back({ nowPos_.beforeRow, nowPos_.beforeColumn, nowPos_.afterRow, nowPos_.afterColumn,
249             nowPos_.sourcesVal, nowPos_.namesVal });
250     }
251     mappings_.clear();
252     mappings_.shrink_to_fit();
253     sourceKeyInfo.clear();
254     sourceKeyInfo.shrink_to_fit();
255 };
256 
MergeInit(const std::string & sourceMap,RefPtr<RevSourceMap> & curMapData)257 void RevSourceMap::MergeInit(const std::string& sourceMap,
258     RefPtr<RevSourceMap>& curMapData)
259 {
260     std::vector<std::string> sourceKey;
261     std::string mark = "";
262     ExtractKeyInfo(sourceMap, sourceKey);
263     for (auto sourceKeyInfo : sourceKey) {
264         if (sourceKeyInfo == SOURCES || sourceKeyInfo == NAMES ||
265             sourceKeyInfo == MAPPINGS || sourceKeyInfo == FILE ||
266             sourceKeyInfo == SOURCE_CONTENT ||  sourceKeyInfo == SOURCE_ROOT) {
267             mark = sourceKeyInfo;
268         } else if (mark == SOURCES) {
269             curMapData->sources_.push_back(sourceKeyInfo);
270         } else if (mark == NAMES) {
271             curMapData->names_.push_back(sourceKeyInfo);
272         } else if (mark == MAPPINGS) {
273             curMapData->mappings_.push_back(sourceKeyInfo);
274         } else if (mark == FILE) {
275             curMapData->files_.push_back(sourceKeyInfo);
276         } else {
277             continue;
278         }
279     }
280 
281     if (curMapData->mappings_.empty()) {
282         LOGE("MergeInit decode sourcemap fail, mapping: %{public}s", sourceMap.c_str());
283         return;
284     }
285 
286     // transform to vector for mapping easily
287     curMapData->mappings_ = HandleMappings(curMapData->mappings_[0]);
288 
289     // the first bit: the column after transferring.
290     // the second bit: the source file.
291     // the third bit: the row before transferring.
292     // the fourth bit: the column before transferring.
293     // the fifth bit: the variable name.
294     for (const auto& mapping : curMapData->mappings_) {
295         if (mapping == ";") {
296             // plus a line for each semicolon
297             curMapData->nowPos_.afterRow++,
298             curMapData->nowPos_.afterColumn = 0;
299             continue;
300         }
301         std::vector<int32_t> ans;
302 
303         if (!VlqRevCode(mapping, ans)) {
304             LOGE("decode code fail");
305             return;
306         }
307         if (ans.size() == 0) {
308             LOGE("decode sourcemap fail, mapping: %{public}s", mapping.c_str());
309             break;
310         }
311         if (ans.size() == 1) {
312             curMapData->nowPos_.afterColumn += ans[AFTER_COLUMN];
313             continue;
314         }
315         // after decode, assgin each value to the position
316         curMapData->nowPos_.afterColumn += ans[AFTER_COLUMN];
317         curMapData->nowPos_.sourcesVal += ans[SOURCES_VAL];
318         curMapData->nowPos_.beforeRow += ans[BEFORE_ROW];
319         curMapData->nowPos_.beforeColumn += ans[BEFORE_COLUMN];
320         if (ans.size() == 5) {
321             curMapData->nowPos_.namesVal += ans[NAMES_VAL];
322         }
323         curMapData->afterPos_.push_back({
324             curMapData->nowPos_.beforeRow,
325             curMapData->nowPos_.beforeColumn,
326             curMapData->nowPos_.afterRow,
327             curMapData->nowPos_.afterColumn,
328             curMapData->nowPos_.sourcesVal,
329             curMapData->nowPos_.namesVal
330         });
331     }
332     curMapData->mappings_.clear();
333     curMapData->mappings_.shrink_to_fit();
334     sourceKey.clear();
335     sourceKey.shrink_to_fit();
336 };
337 
338 
HandleMappings(const std::string & mapping)339 std::vector<std::string> RevSourceMap::HandleMappings(const std::string& mapping)
340 {
341     std::vector<std::string> keyInfo;
342     std::string tempStr;
343     for (uint32_t i = 0; i < mapping.size(); i++) {
344         if (mapping[i] == DELIMITER_COMMA) {
345             keyInfo.push_back(tempStr);
346             tempStr = "";
347         } else if (mapping[i] == DELIMITER_SEMICOLON) {
348             if (tempStr != "") {
349                 keyInfo.push_back(tempStr);
350             }
351             tempStr = "";
352             keyInfo.push_back(";");
353         } else {
354             tempStr += mapping[i];
355         }
356     }
357     if (tempStr != "") {
358         keyInfo.push_back(tempStr);
359     }
360     return keyInfo;
361 };
362 
Base64CharToInt(char charCode)363 uint32_t RevSourceMap::Base64CharToInt(char charCode)
364 {
365     if ('A' <= charCode && charCode <= 'Z') {
366         // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
367         return charCode - 'A';
368     } else if ('a' <= charCode && charCode <= 'z') {
369         // 26 - 51: abcdefghijklmnopqrstuvwxyz
370         return charCode - 'a' + 26;
371     } else if ('0' <= charCode && charCode <= '9') {
372         // 52 - 61: 0123456789
373         return charCode - '0' + 52;
374     } else if (charCode == '+') {
375         // 62: +
376         return 62;
377     } else if (charCode == '/') {
378         // 63: /
379         return 63;
380     }
381     return 64;
382 };
383 
VlqRevCode(const std::string & vStr,std::vector<int32_t> & ans)384 bool RevSourceMap::VlqRevCode(const std::string& vStr, std::vector<int32_t>& ans)
385 {
386     if (vStr.size() == 0) {
387         LOGE("VlqRevCode fail with empty string.");
388         return false;
389     }
390     const int32_t VLQ_BASE_SHIFT = 5;
391     // binary: 100000
392     uint32_t VLQ_BASE = 1 << VLQ_BASE_SHIFT;
393     // binary: 011111
394     uint32_t VLQ_BASE_MASK = VLQ_BASE - 1;
395     // binary: 100000
396     uint32_t VLQ_CONTINUATION_BIT = VLQ_BASE;
397     uint32_t result = 0;
398     uint32_t shift = 0;
399     bool continuation = 0;
400     for (uint32_t i = 0; i < vStr.size(); i++) {
401         uint32_t digit = Base64CharToInt(vStr[i]);
402         if (digit == 64) {
403             LOGE("the arg is error");
404             return false;
405         }
406         continuation = digit & VLQ_CONTINUATION_BIT;
407         digit &= VLQ_BASE_MASK;
408         result += digit << shift;
409         if (continuation) {
410             shift += VLQ_BASE_SHIFT;
411         } else {
412             bool isOdd = result & 1;
413             result >>= 1;
414             ans.push_back(isOdd ? -result : result);
415             result = 0;
416             shift = 0;
417         }
418     }
419     if (continuation) {
420         LOGE("the arg is error");
421         return false;
422     }
423     return true;
424 };
425 
StageModeSourceMapSplit(const std::string & sourceMap,std::unordered_map<std::string,RefPtr<RevSourceMap>> & sourceMaps)426 void RevSourceMap::StageModeSourceMapSplit(const std::string& sourceMap,
427     std::unordered_map<std::string, RefPtr<RevSourceMap>>& sourceMaps)
428 {
429     std::size_t leftBracket = 0;
430     std::size_t rightBracket = 0;
431     std::string value;
432     std::string key;
433     while ((leftBracket = sourceMap.find(": {", rightBracket)) != std::string::npos) {
434         rightBracket = sourceMap.find("}", leftBracket);
435         if (rightBracket == std::string::npos) {
436             return;
437         }
438         value = sourceMap.substr(leftBracket, rightBracket);
439         std::size_t  sources = value.find("\"sources\": [");
440         if (sources == std::string::npos) {
441             continue;
442         }
443         std::size_t  names = value.find("],");
444         if (names == std::string::npos) {
445             continue;
446         }
447         // Intercept the sourcemap file path as the key
448         key = value.substr(sources + P0S_SPACE_LENGTH, names - sources - SPACE_LEN);
449         RefPtr<RevSourceMap> curMapData = MakeRefPtr<RevSourceMap>();
450         MergeInit(value, curMapData);
451         sourceMaps.emplace(key, curMapData);
452     }
453 }
454 } // namespace OHOS::Ace::Framework
455