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