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