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