• 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 <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