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