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