1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/wasm/wasm-module-sourcemap.h"
6
7 #include <algorithm>
8
9 #include "include/v8.h"
10 #include "src/api/api.h"
11 #include "src/base/vlq-base64.h"
12
13 namespace v8 {
14 namespace internal {
15 namespace wasm {
16
WasmModuleSourceMap(v8::Isolate * v8_isolate,v8::Local<v8::String> src_map_str)17 WasmModuleSourceMap::WasmModuleSourceMap(v8::Isolate* v8_isolate,
18 v8::Local<v8::String> src_map_str) {
19 v8::HandleScope scope(v8_isolate);
20 v8::Local<v8::Context> context = v8::Context::New(v8_isolate);
21
22 v8::Local<v8::Value> src_map_value;
23 if (!v8::JSON::Parse(context, src_map_str).ToLocal(&src_map_value)) return;
24 v8::Local<v8::Object> src_map_obj =
25 v8::Local<v8::Object>::Cast(src_map_value);
26
27 v8::Local<v8::Value> version_value, sources_value, mappings_value;
28 bool has_valid_version =
29 src_map_obj
30 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "version"))
31 .ToLocal(&version_value) &&
32 version_value->IsUint32();
33 uint32_t version = 0;
34 if (!has_valid_version || !version_value->Uint32Value(context).To(&version) ||
35 version != 3u)
36 return;
37
38 bool has_valid_sources =
39 src_map_obj
40 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "sources"))
41 .ToLocal(&sources_value) &&
42 sources_value->IsArray();
43 if (!has_valid_sources) return;
44
45 v8::Local<v8::Object> sources_arr =
46 v8::Local<v8::Object>::Cast(sources_value);
47 v8::Local<v8::Value> sources_len_value;
48 if (!sources_arr
49 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "length"))
50 .ToLocal(&sources_len_value))
51 return;
52 uint32_t sources_len = 0;
53 if (!sources_len_value->Uint32Value(context).To(&sources_len)) return;
54
55 for (uint32_t i = 0; i < sources_len; ++i) {
56 v8::Local<v8::Value> file_name_value;
57 if (!sources_arr->Get(context, i).ToLocal(&file_name_value) ||
58 !file_name_value->IsString())
59 return;
60 v8::Local<v8::String> file_name =
61 v8::Local<v8::String>::Cast(file_name_value);
62 auto file_name_sz = file_name->Utf8Length(v8_isolate);
63 std::unique_ptr<char[]> file_name_buf(new char[file_name_sz + 1]);
64 file_name->WriteUtf8(v8_isolate, file_name_buf.get());
65 file_name_buf.get()[file_name_sz] = '\0';
66 filenames.emplace_back(file_name_buf.get());
67 }
68
69 bool has_valid_mappings =
70 src_map_obj
71 ->Get(context, v8::String::NewFromUtf8Literal(v8_isolate, "mappings"))
72 .ToLocal(&mappings_value) &&
73 mappings_value->IsString();
74 if (!has_valid_mappings) return;
75
76 v8::Local<v8::String> mappings = v8::Local<v8::String>::Cast(mappings_value);
77 int mappings_sz = mappings->Utf8Length(v8_isolate);
78 std::unique_ptr<char[]> mappings_buf(new char[mappings_sz + 1]);
79 mappings->WriteUtf8(v8_isolate, mappings_buf.get());
80 mappings_buf.get()[mappings_sz] = '\0';
81
82 valid_ = DecodeMapping(mappings_buf.get());
83 }
84
GetSourceLine(size_t wasm_offset) const85 size_t WasmModuleSourceMap::GetSourceLine(size_t wasm_offset) const {
86 std::vector<std::size_t>::const_iterator up =
87 std::upper_bound(offsets.begin(), offsets.end(), wasm_offset);
88 CHECK_NE(offsets.begin(), up);
89 size_t source_idx = up - offsets.begin() - 1;
90 return source_row[source_idx];
91 }
92
GetFilename(size_t wasm_offset) const93 std::string WasmModuleSourceMap::GetFilename(size_t wasm_offset) const {
94 std::vector<size_t>::const_iterator up =
95 std::upper_bound(offsets.begin(), offsets.end(), wasm_offset);
96 CHECK_NE(offsets.begin(), up);
97 size_t offset_idx = up - offsets.begin() - 1;
98 size_t filename_idx = file_idxs[offset_idx];
99 return filenames[filename_idx];
100 }
101
HasSource(size_t start,size_t end) const102 bool WasmModuleSourceMap::HasSource(size_t start, size_t end) const {
103 return start <= *(offsets.end() - 1) && end > *offsets.begin();
104 }
105
HasValidEntry(size_t start,size_t addr) const106 bool WasmModuleSourceMap::HasValidEntry(size_t start, size_t addr) const {
107 std::vector<size_t>::const_iterator up =
108 std::upper_bound(offsets.begin(), offsets.end(), addr);
109 if (up == offsets.begin()) return false;
110 size_t offset_idx = up - offsets.begin() - 1;
111 size_t entry_offset = offsets[offset_idx];
112 if (entry_offset < start) return false;
113 return true;
114 }
115
DecodeMapping(const std::string & s)116 bool WasmModuleSourceMap::DecodeMapping(const std::string& s) {
117 size_t pos = 0, gen_col = 0, file_idx = 0, ori_line = 0;
118 int32_t qnt = 0;
119
120 while (pos < s.size()) {
121 // Skip redundant commas.
122 if (s[pos] == ',') {
123 ++pos;
124 continue;
125 }
126 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) ==
127 std::numeric_limits<int32_t>::min())
128 return false;
129 gen_col += qnt;
130 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) ==
131 std::numeric_limits<int32_t>::min())
132 return false;
133 file_idx += qnt;
134 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) ==
135 std::numeric_limits<int32_t>::min())
136 return false;
137 ori_line += qnt;
138 // Column number in source file is always 0 in source map generated by
139 // Emscripten. We just decode this value without further usage of it.
140 if ((qnt = base::VLQBase64Decode(s.c_str(), s.size(), &pos)) ==
141 std::numeric_limits<int32_t>::min())
142 return false;
143
144 if (pos < s.size() && s[pos] != ',') return false;
145 pos++;
146
147 file_idxs.push_back(file_idx);
148 source_row.push_back(ori_line);
149 offsets.push_back(gen_col);
150 }
151 return true;
152 }
153
154 } // namespace wasm
155 } // namespace internal
156 } // namespace v8
157