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