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