• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 "ecmascript/extractortool/src/extractor.h"
17 #include "source_map.h"
18 
19 #include <cerrno>
20 #include <climits>
21 #include <cstdlib>
22 #include <fstream>
23 #include <vector>
24 #include <unistd.h>
25 
26 namespace panda {
27 namespace ecmascript {
28 namespace {
29 constexpr char SOURCES[] = "sources";
30 constexpr char NAMES[] = "names";
31 constexpr char MAPPINGS[] = "mappings";
32 constexpr char FILE[] = "file";
33 constexpr char SOURCE_CONTENT[] = "sourceContent";
34 constexpr char SOURCE_ROOT[] = "sourceRoot";
35 constexpr char DELIMITER_COMMA = ',';
36 constexpr char DELIMITER_SEMICOLON = ';';
37 constexpr char DOUBLE_SLASH = '\\';
38 constexpr char WEBPACK[] = "webpack:///";
39 constexpr int32_t INDEX_ONE = 1;
40 constexpr int32_t INDEX_TWO = 2;
41 constexpr int32_t INDEX_THREE = 3;
42 constexpr int32_t INDEX_FOUR = 4;
43 constexpr int32_t ANS_MAP_SIZE = 5;
44 constexpr int32_t NUM_TWENTY = 20;
45 constexpr int32_t NUM_TWENTYSIX = 26;
46 constexpr int32_t DIGIT_NUM = 64;
47 const std::string MEGER_SOURCE_MAP_PATH = "ets/sourceMaps.map";
48 } // namespace
49 
ReadSourceMapData(const std::string & hapPath,std::string & content)50 bool SourceMap::ReadSourceMapData(const std::string& hapPath, std::string& content)
51 {
52     if (hapPath.empty()) {
53         return false;
54     }
55     bool newCreate = false;
56     std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(
57         ExtractorUtil::GetLoadFilePath(hapPath), newCreate);
58     if (extractor == nullptr) {
59         return false;
60     }
61     std::unique_ptr<uint8_t[]> dataPtr = nullptr;
62     size_t len = 0;
63     if (!extractor->ExtractToBufByName(MEGER_SOURCE_MAP_PATH, dataPtr, len)) {
64         return false;
65     }
66     content.assign(dataPtr.get(), dataPtr.get() + len);
67     return true;
68 }
69 
StringToInt(const std::string & value)70 int32_t StringToInt(const std::string& value)
71 {
72     errno = 0;
73     char* pEnd = nullptr;
74     int64_t result = std::strtol(value.c_str(), &pEnd, 10);
75     if (pEnd == value.c_str() || (result < INT_MIN || result > INT_MAX) || errno == ERANGE) {
76         return 0;
77     } else {
78         return result;
79     }
80 }
81 
Base64CharToInt(char charCode)82 uint32_t Base64CharToInt(char charCode)
83 {
84     if ('A' <= charCode && charCode <= 'Z') {
85         // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
86         return charCode - 'A';
87     } else if ('a' <= charCode && charCode <= 'z') {
88         // 26 - 51: abcdefghijklmnopqrstuvwxyz
89         return charCode - 'a' + 26;
90     } else if ('0' <= charCode && charCode <= '9') {
91         // 52 - 61: 0123456789
92         return charCode - '0' + 52;
93     } else if (charCode == '+') {
94         // 62: +
95         return 62;
96     } else if (charCode == '/') {
97         // 63: /
98         return 63;
99     }
100     return DIGIT_NUM;
101 };
102 
Init(const std::string & url,const std::string & hapPath)103 void SourceMap::Init(const std::string& url, const std::string& hapPath)
104 {
105     std::string sourceMapData;
106     if (ReadSourceMapData(hapPath, sourceMapData)) {
107         SplitSourceMap(url, sourceMapData);
108     }
109 }
110 
SplitSourceMap(const std::string & url,const std::string & sourceMapData)111 void SourceMap::SplitSourceMap(const std::string& url, const std::string& sourceMapData)
112 {
113     size_t leftBracket = 0;
114     size_t rightBracket = 0;
115     std::string value;
116     while ((leftBracket = sourceMapData.find(": {", rightBracket)) != std::string::npos) {
117         rightBracket = sourceMapData.find("},", leftBracket);
118         value = sourceMapData.substr(leftBracket, rightBracket);
119         std::size_t sources = value.find("\"sources\": [");
120         if (sources == std::string::npos) {
121             continue;
122         }
123         std::size_t names = value.find("],", sources);
124         if (names == std::string::npos) {
125             continue;
126         }
127         // Intercept the sourcemap file path as the key
128         std::string key = value.substr(sources + NUM_TWENTY, names - sources - NUM_TWENTYSIX);
129         if (key == url) {
130             auto iter = sourceMaps_.find(key);
131             if (iter != sourceMaps_.end()) {
132                 continue;
133             }
134             std::shared_ptr<SourceMapData> modularMap = std::make_shared<SourceMapData>();
135             ExtractSourceMapData(value, modularMap);
136             sourceMaps_.emplace(key, modularMap);
137         }
138     }
139 }
140 
ExtractSourceMapData(const std::string & sourceMapData,std::shared_ptr<SourceMapData> & curMapData)141 void SourceMap::ExtractSourceMapData(const std::string& sourceMapData, std::shared_ptr<SourceMapData>& curMapData)
142 {
143     std::vector<std::string> sourceKey;
144     ExtractKeyInfo(sourceMapData, sourceKey);
145 
146     std::string mark = "";
147     for (auto sourceKeyInfo : sourceKey) {
148         if (sourceKeyInfo == SOURCES || sourceKeyInfo == NAMES ||
149             sourceKeyInfo == MAPPINGS || sourceKeyInfo == FILE ||
150             sourceKeyInfo == SOURCE_CONTENT ||  sourceKeyInfo == SOURCE_ROOT) {
151             mark = sourceKeyInfo;
152         } else if (mark == SOURCES) {
153             curMapData->sources_.push_back(sourceKeyInfo);
154         } else if (mark == NAMES) {
155             curMapData->names_.push_back(sourceKeyInfo);
156         } else if (mark == MAPPINGS) {
157             curMapData->mappings_.push_back(sourceKeyInfo);
158         } else if (mark == FILE) {
159             curMapData->files_.push_back(sourceKeyInfo);
160         } else {
161             continue;
162         }
163     }
164 
165     if (curMapData->mappings_.empty()) {
166         return;
167     }
168 
169     // transform to vector for mapping easily
170     curMapData->mappings_ = HandleMappings(curMapData->mappings_[0]);
171 
172     // the first bit: the column after transferring.
173     // the second bit: the source file.
174     // the third bit: the row before transferring.
175     // the fourth bit: the column before transferring.
176     // the fifth bit: the variable name.
177     for (const auto& mapping : curMapData->mappings_) {
178         if (mapping == ";") {
179             // plus a line for each semicolon
180             curMapData->nowPos_.afterRow++,
181             curMapData->nowPos_.afterColumn = 0;
182             continue;
183         }
184         std::vector<int32_t> ans;
185 
186         if (!VlqRevCode(mapping, ans)) {
187             return;
188         }
189         if (ans.empty()) {
190             break;
191         }
192         if (ans.size() == 1) {
193             curMapData->nowPos_.afterColumn += ans[0];
194             continue;
195         }
196         // after decode, assgin each value to the position
197         curMapData->nowPos_.afterColumn += ans[0];
198         curMapData->nowPos_.sourcesVal += ans[INDEX_ONE];
199         curMapData->nowPos_.beforeRow += ans[INDEX_TWO];
200         curMapData->nowPos_.beforeColumn += ans[INDEX_THREE];
201         if (ans.size() == ANS_MAP_SIZE) {
202             curMapData->nowPos_.namesVal += ans[INDEX_FOUR];
203         }
204         curMapData->afterPos_.push_back({
205             curMapData->nowPos_.beforeRow,
206             curMapData->nowPos_.beforeColumn,
207             curMapData->nowPos_.afterRow,
208             curMapData->nowPos_.afterColumn,
209             curMapData->nowPos_.sourcesVal,
210             curMapData->nowPos_.namesVal
211         });
212     }
213     curMapData->mappings_.clear();
214     curMapData->mappings_.shrink_to_fit();
215 }
216 
Find(int32_t row,int32_t col,const SourceMapData & targetMap,const std::string & key)217 MappingInfo SourceMap::Find(int32_t row, int32_t col, const SourceMapData& targetMap, const std::string& key)
218 {
219     if (row < 1 || col < 1 || targetMap.afterPos_.empty()) {
220         return MappingInfo {0, 0, ""};
221     }
222     row--;
223     col--;
224     // binary search
225     int32_t left = 0;
226     int32_t right = static_cast<int32_t>(targetMap.afterPos_.size()) - 1;
227     int32_t res = 0;
228     if (row > targetMap.afterPos_[targetMap.afterPos_.size() - 1].afterRow) {
229         return MappingInfo { row + 1, col + 1, targetMap.files_[0] };
230     }
231     while (right - left >= 0) {
232         int32_t mid = (right + left) / 2;
233         if ((targetMap.afterPos_[mid].afterRow == row && targetMap.afterPos_[mid].afterColumn > col) ||
234              targetMap.afterPos_[mid].afterRow > row) {
235             right = mid - 1;
236         } else {
237             res = mid;
238             left = mid + 1;
239         }
240     }
241     std::string sources = key;
242     auto pos = sources.find(WEBPACK);
243     if (pos != std::string::npos) {
244         sources.replace(pos, sizeof(WEBPACK) - 1, "");
245     }
246 
247     return MappingInfo {
248         .row = targetMap.afterPos_[res].beforeRow + 1,
249         .col = targetMap.afterPos_[res].beforeColumn + 1,
250         .sources = sources,
251     };
252 }
253 
ExtractKeyInfo(const std::string & sourceMap,std::vector<std::string> & sourceKeyInfo)254 void SourceMap::ExtractKeyInfo(const std::string& sourceMap, std::vector<std::string>& sourceKeyInfo)
255 {
256     uint32_t cnt = 0;
257     std::string tempStr;
258     for (uint32_t i = 0; i < sourceMap.size(); i++) {
259         // reslove json file
260         if (sourceMap[i] == DOUBLE_SLASH) {
261             i++;
262             tempStr += sourceMap[i];
263             continue;
264         }
265         // cnt is used to represent a pair of double quotation marks: ""
266         if (sourceMap[i] == '"') {
267             cnt++;
268         }
269         if (cnt == INDEX_TWO) {
270             sourceKeyInfo.push_back(tempStr);
271             tempStr = "";
272             cnt = 0;
273         } else if (cnt == 1) {
274             if (sourceMap[i] != '"') {
275                 tempStr += sourceMap[i];
276             }
277         }
278     }
279 }
280 
GetPosInfo(const std::string & temp,int32_t start,std::string & line,std::string & column)281 void SourceMap::GetPosInfo(const std::string& temp, int32_t start, std::string& line, std::string& column)
282 {
283     // 0 for colum, 1 for row
284     int32_t flag = 0;
285     // find line, column
286     for (int32_t i = start - 1; i > 0; i--) {
287         if (temp[i] == ':') {
288             flag += 1;
289             continue;
290         }
291         if (flag == 0) {
292             column = temp[i] + column;
293         } else if (flag == 1) {
294             line = temp[i] + line;
295         } else {
296             break;
297         }
298     }
299 }
300 
HandleMappings(const std::string & mapping)301 std::vector<std::string> SourceMap::HandleMappings(const std::string& mapping)
302 {
303     std::vector<std::string> keyInfo;
304     std::string tempStr;
305     for (uint32_t i = 0; i < mapping.size(); i++) {
306         if (mapping[i] == DELIMITER_COMMA) {
307             keyInfo.push_back(tempStr);
308             tempStr = "";
309         } else if (mapping[i] == DELIMITER_SEMICOLON) {
310             if (tempStr != "") {
311                 keyInfo.push_back(tempStr);
312             }
313             tempStr = "";
314             keyInfo.push_back(";");
315         } else {
316             tempStr += mapping[i];
317         }
318     }
319     if (tempStr != "") {
320         keyInfo.push_back(tempStr);
321     }
322     return keyInfo;
323 };
324 
VlqRevCode(const std::string & vStr,std::vector<int32_t> & ans)325 bool SourceMap::VlqRevCode(const std::string& vStr, std::vector<int32_t>& ans)
326 {
327     const int32_t VLQ_BASE_SHIFT = 5;
328     // binary: 100000
329     uint32_t VLQ_BASE = 1 << VLQ_BASE_SHIFT;
330     // binary: 011111
331     uint32_t VLQ_BASE_MASK = VLQ_BASE - 1;
332     // binary: 100000
333     uint32_t VLQ_CONTINUATION_BIT = VLQ_BASE;
334     uint32_t result = 0;
335     uint32_t shift = 0;
336     bool continuation = 0;
337     for (uint32_t i = 0; i < vStr.size(); i++) {
338         uint32_t digit = Base64CharToInt(vStr[i]);
339         if (digit == DIGIT_NUM) {
340             return false;
341         }
342         continuation = digit & VLQ_CONTINUATION_BIT;
343         digit &= VLQ_BASE_MASK;
344         result += digit << shift;
345         if (continuation) {
346             shift += VLQ_BASE_SHIFT;
347         } else {
348             bool isNegate = result & 1;
349             result >>= 1;
350             ans.push_back(isNegate ? -result : result);
351             result = 0;
352             shift = 0;
353         }
354     }
355     if (continuation) {
356         return false;
357     }
358     return true;
359 };
360 
TranslateUrlPositionBySourceMap(std::string & url,int & line,int & column)361 bool SourceMap::TranslateUrlPositionBySourceMap(std::string& url, int& line, int& column)
362 {
363     auto iter = sourceMaps_.find(url);
364     if (iter != sourceMaps_.end()) {
365         return GetLineAndColumnNumbers(line, column, *(iter->second), url);
366     }
367     return false;
368 }
369 
GetLineAndColumnNumbers(int & line,int & column,SourceMapData & targetMap,std::string & key)370 bool SourceMap::GetLineAndColumnNumbers(int& line, int& column, SourceMapData& targetMap, std::string& key)
371 {
372     int32_t offSet = 0;
373     MappingInfo mapInfo;
374 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
375         mapInfo = Find(line - offSet + OFFSET_PREVIEW, column, targetMap, key);
376 #else
377         mapInfo = Find(line - offSet, column, targetMap, key);
378 #endif
379     if (mapInfo.row == 0 || mapInfo.col == 0) {
380         return false;
381     } else {
382         line = mapInfo.row;
383         column = mapInfo.col;
384         return true;
385     }
386 }
387 }   // namespace JsEnv
388 }   // namespace OHOS
389