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