• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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