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