• 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 "source_map.h"
17 
18 
19 #include "ecmascript/base/string_helper.h"
20 #include "ecmascript/extractortool/src/extractor.h"
21 
22 namespace panda {
23 namespace ecmascript {
24 namespace {
25 static constexpr char DELIMITER_COMMA = ',';
26 static constexpr char DELIMITER_SEMICOLON = ';';
27 static constexpr char DOUBLE_SLASH = '\\';
28 
29 static constexpr int32_t INDEX_ONE = 1;
30 static constexpr int32_t INDEX_TWO = 2;
31 static constexpr int32_t INDEX_THREE = 3;
32 static constexpr int32_t INDEX_FOUR = 4;
33 static constexpr int32_t ANS_MAP_SIZE = 5;
34 static constexpr int32_t DIGIT_NUM = 64;
35 
36 const std::string MEGER_SOURCE_MAP_PATH = "ets/sourceMaps.map";
37 static const CString FLAG_SOURCES = "    \"sources\":";
38 static const CString FLAG_MAPPINGS = "    \"mappings\": \"";
39 static const CString FLAG_ENTRY_PACKAGE_INFO = "    \"entry-package-info\": \"";
40 static const CString FLAG_PACKAGE_INFO = "    \"package-info\": \"";
41 static const CString FLAG_END = "  }";
42 
43 static constexpr size_t FLAG_MAPPINGS_LEN = 17;
44 static constexpr size_t REAL_SOURCE_SIZE = 7;
45 static constexpr size_t REAL_URL_INDEX = 3;
46 static constexpr size_t FLAG_ENTRY_PACKAGE_INFO_SIZE = 27;
47 static constexpr size_t FLAG_PACKAGE_INFO_SIZE = 21;
48 } // namespace
49 
50 #if defined(PANDA_TARGET_OHOS)
ReadSourceMapData(const std::string & hapPath,std::string & content)51 bool SourceMap::ReadSourceMapData(const std::string& hapPath, std::string& content)
52 {
53     if (hapPath.empty()) {
54         return false;
55     }
56     bool newCreate = false;
57     std::shared_ptr<Extractor> extractor = ExtractorUtil::GetExtractor(
58         ExtractorUtil::GetLoadFilePath(hapPath), newCreate);
59     if (extractor == nullptr) {
60         return false;
61     }
62     std::unique_ptr<uint8_t[]> dataPtr = nullptr;
63     size_t len = 0;
64     if (!extractor->ExtractToBufByName(MEGER_SOURCE_MAP_PATH, dataPtr, len)) {
65         return false;
66     }
67     content.assign(dataPtr.get(), dataPtr.get() + len);
68     return true;
69 }
70 #endif
71 
Base64CharToInt(char charCode)72 uint32_t SourceMap::Base64CharToInt(char charCode)
73 {
74     if ('A' <= charCode && charCode <= 'Z') {
75         // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
76         return charCode - 'A';
77     } else if ('a' <= charCode && charCode <= 'z') {
78         // 26 - 51: abcdefghijklmnopqrstuvwxyz
79         return charCode - 'a' + 26;
80     } else if ('0' <= charCode && charCode <= '9') {
81         // 52 - 61: 0123456789
82         return charCode - '0' + 52;
83     } else if (charCode == '+') {
84         // 62: +
85         return 62;
86     } else if (charCode == '/') {
87         // 63: /
88         return 63;
89     }
90     return DIGIT_NUM;
91 };
92 
93 #if defined(PANDA_TARGET_OHOS)
Init(const std::string & hapPath)94 void SourceMap::Init(const std::string& hapPath)
95 {
96     auto start = Clock::now();
97     std::string sourceMapData;
98     if (ReadSourceMapData(hapPath, sourceMapData)) {
99         SplitSourceMap(sourceMapData);
100     }
101     auto end = Clock::now();
102     auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
103     LOG_ECMA(DEBUG) << "Init sourcemap time: " << duration.count() << "ms";
104 }
105 #endif
106 
Init(uint8_t * data,size_t dataSize)107 void SourceMap::Init(uint8_t *data, size_t dataSize)
108 {
109     std::string content;
110     content.assign(data, data + dataSize);
111     SplitSourceMap(content);
112 }
113 
SplitSourceMap(const std::string & sourceMapData)114 void SourceMap::SplitSourceMap(const std::string& sourceMapData)
115 {
116     std::stringstream ss(sourceMapData);
117     std::string tmp;
118     std::string url;
119 
120     std::getline(ss, tmp);
121     bool isUrl = true;
122     while (std::getline(ss, tmp)) {
123         if (isUrl && tmp.size() > REAL_SOURCE_SIZE) {
124             url = tmp.substr(REAL_URL_INDEX, tmp.size() - REAL_SOURCE_SIZE);
125             isUrl = false;
126             continue;
127         }
128 
129         if (base::StringHelper::StringStartWith(tmp.c_str(), FLAG_SOURCES)) {
130             std::getline(ss, tmp);
131             sources_.emplace(url, tmp);
132             continue;
133         }
134 
135         if (base::StringHelper::StringStartWith(tmp.c_str(), FLAG_MAPPINGS)) {
136             mappings_.emplace(url, tmp);
137             continue;
138         }
139 
140         if (base::StringHelper::StringStartWith(tmp.c_str(), FLAG_ENTRY_PACKAGE_INFO)) {
141             entryPackageInfo_.emplace(url, tmp);
142             continue;
143         }
144 
145         if (base::StringHelper::StringStartWith(tmp.c_str(), FLAG_PACKAGE_INFO)) {
146             packageInfo_.emplace(url, tmp);
147             continue;
148         }
149 
150         if (base::StringHelper::StringStartWith(tmp.c_str(), FLAG_END)) {
151             isUrl = true;
152         }
153     }
154 }
155 
ExtractSourceMapData(const std::string & allmappings,std::shared_ptr<SourceMapData> & curMapData)156 void SourceMap::ExtractSourceMapData(const std::string& allmappings, std::shared_ptr<SourceMapData>& curMapData)
157 {
158     curMapData->mappings_ = HandleMappings(allmappings);
159 
160     // the first bit: the column after transferring.
161     // the second bit: the source file.
162     // the third bit: the row before transferring.
163     // the fourth bit: the column before transferring.
164     // the fifth bit: the variable name.
165     for (const auto& mapping : curMapData->mappings_) {
166         if (mapping == ";") {
167             // plus a line for each semicolon
168             curMapData->nowPos_.afterRow++,
169             curMapData->nowPos_.afterColumn = 0;
170             continue;
171         }
172         std::vector<int32_t> ans;
173 
174         if (!VlqRevCode(mapping, ans)) {
175             return;
176         }
177         if (ans.empty()) {
178             break;
179         }
180         if (ans.size() == 1) {
181             curMapData->nowPos_.afterColumn += ans[0];
182             continue;
183         }
184         // after decode, assgin each value to the position
185         curMapData->nowPos_.afterColumn += ans[0];
186         curMapData->nowPos_.sourcesVal += ans[INDEX_ONE];
187         curMapData->nowPos_.beforeRow += ans[INDEX_TWO];
188         curMapData->nowPos_.beforeColumn += ans[INDEX_THREE];
189         if (ans.size() == ANS_MAP_SIZE) {
190             curMapData->nowPos_.namesVal += ans[INDEX_FOUR];
191         }
192         curMapData->afterPos_.push_back({
193             curMapData->nowPos_.beforeRow,
194             curMapData->nowPos_.beforeColumn,
195             curMapData->nowPos_.afterRow,
196             curMapData->nowPos_.afterColumn,
197             curMapData->nowPos_.sourcesVal,
198             curMapData->nowPos_.namesVal
199         });
200     }
201     curMapData->mappings_.clear();
202     curMapData->mappings_.shrink_to_fit();
203 }
204 
Find(int32_t row,int32_t col,const SourceMapData & targetMap,bool & isReplaces)205 MappingInfo SourceMap::Find(int32_t row, int32_t col, const SourceMapData& targetMap, bool& isReplaces)
206 {
207     if (row < 1 || col < 1) {
208         LOG_ECMA(ERROR) << "SourceMap find failed, line: " << row << ", column: " << col;
209         return MappingInfo { 0, 0 };
210     } else if (targetMap.afterPos_.empty()) {
211         LOG_ECMA(ERROR) << "Target map can't find after pos.";
212         return MappingInfo { 0, 0 };
213     }
214     row--;
215     col--;
216     // binary search
217     int32_t left = 0;
218     int32_t right = static_cast<int32_t>(targetMap.afterPos_.size()) - 1;
219     int32_t res = 0;
220     if (row > targetMap.afterPos_[targetMap.afterPos_.size() - 1].afterRow) {
221         isReplaces = false;
222         return MappingInfo { row + 1, col + 1};
223     }
224     while (right - left >= 0) {
225         int32_t mid = (right + left) / 2;
226         if ((targetMap.afterPos_[mid].afterRow == row && targetMap.afterPos_[mid].afterColumn > col) ||
227              targetMap.afterPos_[mid].afterRow > row) {
228             right = mid - 1;
229         } else {
230             res = mid;
231             left = mid + 1;
232         }
233     }
234     return MappingInfo { targetMap.afterPos_[res].beforeRow + 1, targetMap.afterPos_[res].beforeColumn + 1 };
235 }
236 
ExtractKeyInfo(const std::string & sourceMap,std::vector<std::string> & sourceKeyInfo)237 void SourceMap::ExtractKeyInfo(const std::string& sourceMap, std::vector<std::string>& sourceKeyInfo)
238 {
239     uint32_t cnt = 0;
240     std::string tempStr;
241     for (uint32_t i = 0; i < sourceMap.size(); i++) {
242         // reslove json file
243         if (sourceMap[i] == DOUBLE_SLASH) {
244             i++;
245             tempStr += sourceMap[i];
246             continue;
247         }
248         // cnt is used to represent a pair of double quotation marks: ""
249         if (sourceMap[i] == '"') {
250             cnt++;
251         }
252         if (cnt == INDEX_TWO) {
253             sourceKeyInfo.push_back(tempStr);
254             tempStr = "";
255             cnt = 0;
256         } else if (cnt == 1) {
257             if (sourceMap[i] != '"') {
258                 tempStr += sourceMap[i];
259             }
260         }
261     }
262 }
263 
GetPosInfo(const std::string & temp,int32_t start,std::string & line,std::string & column)264 void SourceMap::GetPosInfo(const std::string& temp, int32_t start, std::string& line, std::string& column)
265 {
266     // 0 for colum, 1 for row
267     int32_t flag = 0;
268     // find line, column
269     for (int32_t i = start - 1; i > 0; i--) {
270         if (temp[i] == ':') {
271             flag += 1;
272             continue;
273         }
274         if (flag == 0) {
275             column = temp[i] + column;
276         } else if (flag == 1) {
277             line = temp[i] + line;
278         } else {
279             break;
280         }
281     }
282 }
283 
HandleMappings(const std::string & mapping)284 std::vector<std::string> SourceMap::HandleMappings(const std::string& mapping)
285 {
286     std::vector<std::string> keyInfo;
287     std::string tempStr;
288     for (uint32_t i = 0; i < mapping.size(); i++) {
289         if (mapping[i] == DELIMITER_COMMA) {
290             keyInfo.push_back(tempStr);
291             tempStr = "";
292         } else if (mapping[i] == DELIMITER_SEMICOLON) {
293             if (tempStr != "") {
294                 keyInfo.push_back(tempStr);
295             }
296             tempStr = "";
297             keyInfo.push_back(";");
298         } else {
299             tempStr += mapping[i];
300         }
301     }
302     if (tempStr != "") {
303         keyInfo.push_back(tempStr);
304     }
305     return keyInfo;
306 };
307 
VlqRevCode(const std::string & vStr,std::vector<int32_t> & ans)308 bool SourceMap::VlqRevCode(const std::string& vStr, std::vector<int32_t>& ans)
309 {
310     const int32_t VLQ_BASE_SHIFT = 5;
311     // binary: 100000
312     uint32_t VLQ_BASE = 1 << VLQ_BASE_SHIFT;
313     // binary: 011111
314     uint32_t VLQ_BASE_MASK = VLQ_BASE - 1;
315     // binary: 100000
316     uint32_t VLQ_CONTINUATION_BIT = VLQ_BASE;
317     uint32_t result = 0;
318     uint32_t shift = 0;
319     bool continuation = 0;
320     for (uint32_t i = 0; i < vStr.size(); i++) {
321         uint32_t digit = Base64CharToInt(vStr[i]);
322         if (digit == DIGIT_NUM) {
323             return false;
324         }
325         continuation = digit & VLQ_CONTINUATION_BIT;
326         digit &= VLQ_BASE_MASK;
327         result += digit << shift;
328         if (continuation) {
329             shift += VLQ_BASE_SHIFT;
330         } else {
331             bool isNegate = result & 1;
332             result >>= 1;
333             ans.push_back(isNegate ? -result : result);
334             result = 0;
335             shift = 0;
336         }
337     }
338     if (continuation) {
339         return false;
340     }
341     return true;
342 };
343 
GetPackageName(std::string & url,std::string & packageName)344 void SourceMap::GetPackageName(std::string& url, std::string& packageName)
345 {
346     auto iterData = packageName_.find(url);
347     if (iterData != packageName_.end()) {
348         packageName = iterData->second;
349         return;
350     }
351     std::string entryPackageInfo = entryPackageInfo_[url];
352     std::string packageInfo = packageInfo_[url];
353     if (!packageInfo.empty()) {
354         auto last = packageInfo.rfind('|');
355         if (last == std::string::npos) {
356             LOG_ECMA(ERROR) << "packageInfo can't find fisrt |";
357             return;
358         }
359         packageName = packageInfo.substr(FLAG_PACKAGE_INFO_SIZE, last - FLAG_PACKAGE_INFO_SIZE);
360         packageName_.emplace(url, packageName);
361         return;
362     }
363     if (!entryPackageInfo.empty()) {
364         auto last = entryPackageInfo.rfind('|');
365         if (last == std::string::npos) {
366             LOG_ECMA(ERROR) << "entryPackageInfo can't find fisrt |";
367             return;
368         }
369         packageName = entryPackageInfo.substr(FLAG_ENTRY_PACKAGE_INFO_SIZE, last - FLAG_ENTRY_PACKAGE_INFO_SIZE);
370         packageName_.emplace(url, packageName);
371     }
372 }
373 
TranslateUrlPositionBySourceMap(std::string & url,int & line,int & column,std::string & packageName)374 bool SourceMap::TranslateUrlPositionBySourceMap(std::string& url, int& line, int& column, std::string& packageName)
375 {
376     std::string tmp = sources_[url];
377     if (tmp.empty() || tmp.size() < REAL_SOURCE_SIZE + 1) {
378         LOG_ECMA(ERROR) << "Translate failed, url: " << url;
379         return false;
380     }
381     tmp = tmp.substr(REAL_SOURCE_SIZE, tmp.size() - REAL_SOURCE_SIZE - 1);
382     if (url.rfind(".js") != std::string::npos) {
383         url = tmp;
384         return true;
385     }
386     bool isReplaces = true;
387     bool ret = false;
388     GetPackageName(url, packageName);
389     auto iterData = sourceMaps_.find(url);
390     if (iterData != sourceMaps_.end()) {
391         if (iterData->second == nullptr) {
392             LOG_ECMA(ERROR) << "Extract mappings failed, url: " << url;
393             return false;
394         }
395         ret = GetLineAndColumnNumbers(line, column, *(iterData->second), isReplaces);
396         if (isReplaces) {
397             url = tmp;
398         }
399         return ret;
400     }
401     auto iter = mappings_.find(url);
402     if (iter != mappings_.end()) {
403         std::string mappings = mappings_[url];
404         if (mappings.size() < FLAG_MAPPINGS_LEN + 1) {
405             LOG_ECMA(ERROR) << "Translate failed, url: " << url << ", mappings: " << mappings;
406             return false;
407         }
408         std::shared_ptr<SourceMapData> modularMap = std::make_shared<SourceMapData>();
409         if (modularMap == nullptr) {
410             LOG_ECMA(ERROR) << "New SourceMapData failed";
411             return false;
412         }
413         ExtractSourceMapData(mappings.substr(FLAG_MAPPINGS_LEN, mappings.size() - FLAG_MAPPINGS_LEN - 1), modularMap);
414         sourceMaps_.emplace(url, modularMap);
415         ret = GetLineAndColumnNumbers(line, column, *(modularMap), isReplaces);
416         if (isReplaces) {
417             url = tmp;
418         }
419         return ret;
420     }
421     return false;
422 }
423 
GetLineAndColumnNumbers(int & line,int & column,SourceMapData & targetMap,bool & isReplaces)424 bool SourceMap::GetLineAndColumnNumbers(int& line, int& column, SourceMapData& targetMap, bool& isReplaces)
425 {
426     int32_t offSet = 0;
427     MappingInfo mapInfo;
428 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
429     mapInfo = Find(line - offSet + OFFSET_PREVIEW, column, targetMap, isReplaces);
430 #else
431     mapInfo = Find(line - offSet, column, targetMap, isReplaces);
432 #endif
433     if (mapInfo.row == 0 || mapInfo.col == 0) {
434         return false;
435     } else {
436         line = mapInfo.row;
437         column = mapInfo.col;
438         return true;
439     }
440 }
441 }   // namespace panda
442 }   // namespace ecmascript
443