1 // Copyright 2015 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 <functional>
6 #include <memory>
7
8 #include "src/api-inl.h"
9 #include "src/assembler-inl.h"
10 #include "src/compiler/wasm-compiler.h"
11 #include "src/debug/interface-types.h"
12 #include "src/frames-inl.h"
13 #include "src/objects.h"
14 #include "src/objects/js-array-inl.h"
15 #include "src/property-descriptor.h"
16 #include "src/simulator.h"
17 #include "src/snapshot/snapshot.h"
18 #include "src/v8.h"
19 #include "src/wasm/module-decoder.h"
20 #include "src/wasm/wasm-code-manager.h"
21 #include "src/wasm/wasm-js.h"
22 #include "src/wasm/wasm-module.h"
23 #include "src/wasm/wasm-objects-inl.h"
24 #include "src/wasm/wasm-result.h"
25
26 namespace v8 {
27 namespace internal {
28 namespace wasm {
29
30 // static
31 const WasmExceptionSig WasmException::empty_sig_(0, 0, nullptr);
32
33 // static
34 constexpr const char* WasmException::kRuntimeIdStr;
35
36 // static
37 constexpr const char* WasmException::kRuntimeValuesStr;
38
LookupFunctionName(const ModuleWireBytes & wire_bytes,uint32_t function_index) const39 WireBytesRef WasmModule::LookupFunctionName(const ModuleWireBytes& wire_bytes,
40 uint32_t function_index) const {
41 if (!function_names) {
42 function_names.reset(new std::unordered_map<uint32_t, WireBytesRef>());
43 DecodeFunctionNames(wire_bytes.start(), wire_bytes.end(),
44 function_names.get());
45 }
46 auto it = function_names->find(function_index);
47 if (it == function_names->end()) return WireBytesRef();
48 return it->second;
49 }
50
AddFunctionNameForTesting(int function_index,WireBytesRef name)51 void WasmModule::AddFunctionNameForTesting(int function_index,
52 WireBytesRef name) {
53 if (!function_names) {
54 function_names.reset(new std::unordered_map<uint32_t, WireBytesRef>());
55 }
56 function_names->insert(std::make_pair(function_index, name));
57 }
58
59 // Get a string stored in the module bytes representing a name.
GetName(WireBytesRef ref) const60 WasmName ModuleWireBytes::GetName(WireBytesRef ref) const {
61 if (ref.is_empty()) return {"<?>", 3}; // no name.
62 CHECK(BoundsCheck(ref.offset(), ref.length()));
63 return WasmName::cast(
64 module_bytes_.SubVector(ref.offset(), ref.end_offset()));
65 }
66
67 // Get a string stored in the module bytes representing a function name.
GetName(const WasmFunction * function,const WasmModule * module) const68 WasmName ModuleWireBytes::GetName(const WasmFunction* function,
69 const WasmModule* module) const {
70 return GetName(module->LookupFunctionName(*this, function->func_index));
71 }
72
73 // Get a string stored in the module bytes representing a name.
GetNameOrNull(WireBytesRef ref) const74 WasmName ModuleWireBytes::GetNameOrNull(WireBytesRef ref) const {
75 if (!ref.is_set()) return {nullptr, 0}; // no name.
76 CHECK(BoundsCheck(ref.offset(), ref.length()));
77 return WasmName::cast(
78 module_bytes_.SubVector(ref.offset(), ref.end_offset()));
79 }
80
81 // Get a string stored in the module bytes representing a function name.
GetNameOrNull(const WasmFunction * function,const WasmModule * module) const82 WasmName ModuleWireBytes::GetNameOrNull(const WasmFunction* function,
83 const WasmModule* module) const {
84 return GetNameOrNull(module->LookupFunctionName(*this, function->func_index));
85 }
86
operator <<(std::ostream & os,const WasmFunctionName & name)87 std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name) {
88 os << "#" << name.function_->func_index;
89 if (!name.name_.is_empty()) {
90 if (name.name_.start()) {
91 os << ":";
92 os.write(name.name_.start(), name.name_.length());
93 }
94 } else {
95 os << "?";
96 }
97 return os;
98 }
99
WasmModule(std::unique_ptr<Zone> owned)100 WasmModule::WasmModule(std::unique_ptr<Zone> owned)
101 : signature_zone(std::move(owned)) {}
102
IsWasmCodegenAllowed(Isolate * isolate,Handle<Context> context)103 bool IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) {
104 // TODO(wasm): Once wasm has its own CSP policy, we should introduce a
105 // separate callback that includes information about the module about to be
106 // compiled. For the time being, pass an empty string as placeholder for the
107 // sources.
108 if (auto wasm_codegen_callback = isolate->allow_wasm_code_gen_callback()) {
109 return wasm_codegen_callback(
110 v8::Utils::ToLocal(context),
111 v8::Utils::ToLocal(isolate->factory()->empty_string()));
112 }
113 auto codegen_callback = isolate->allow_code_gen_callback();
114 return codegen_callback == nullptr ||
115 codegen_callback(
116 v8::Utils::ToLocal(context),
117 v8::Utils::ToLocal(isolate->factory()->empty_string()));
118 }
119
GetImports(Isolate * isolate,Handle<WasmModuleObject> module_object)120 Handle<JSArray> GetImports(Isolate* isolate,
121 Handle<WasmModuleObject> module_object) {
122 Factory* factory = isolate->factory();
123
124 Handle<String> module_string = factory->InternalizeUtf8String("module");
125 Handle<String> name_string = factory->InternalizeUtf8String("name");
126 Handle<String> kind_string = factory->InternalizeUtf8String("kind");
127
128 Handle<String> function_string = factory->InternalizeUtf8String("function");
129 Handle<String> table_string = factory->InternalizeUtf8String("table");
130 Handle<String> memory_string = factory->InternalizeUtf8String("memory");
131 Handle<String> global_string = factory->InternalizeUtf8String("global");
132
133 // Create the result array.
134 const WasmModule* module = module_object->module();
135 int num_imports = static_cast<int>(module->import_table.size());
136 Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
137 Handle<FixedArray> storage = factory->NewFixedArray(num_imports);
138 JSArray::SetContent(array_object, storage);
139 array_object->set_length(Smi::FromInt(num_imports));
140
141 Handle<JSFunction> object_function =
142 Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
143
144 // Populate the result array.
145 for (int index = 0; index < num_imports; ++index) {
146 const WasmImport& import = module->import_table[index];
147
148 Handle<JSObject> entry = factory->NewJSObject(object_function);
149
150 Handle<String> import_kind;
151 switch (import.kind) {
152 case kExternalFunction:
153 import_kind = function_string;
154 break;
155 case kExternalTable:
156 import_kind = table_string;
157 break;
158 case kExternalMemory:
159 import_kind = memory_string;
160 break;
161 case kExternalGlobal:
162 import_kind = global_string;
163 break;
164 default:
165 UNREACHABLE();
166 }
167
168 MaybeHandle<String> import_module =
169 WasmModuleObject::ExtractUtf8StringFromModuleBytes(
170 isolate, module_object, import.module_name);
171
172 MaybeHandle<String> import_name =
173 WasmModuleObject::ExtractUtf8StringFromModuleBytes(
174 isolate, module_object, import.field_name);
175
176 JSObject::AddProperty(isolate, entry, module_string,
177 import_module.ToHandleChecked(), NONE);
178 JSObject::AddProperty(isolate, entry, name_string,
179 import_name.ToHandleChecked(), NONE);
180 JSObject::AddProperty(isolate, entry, kind_string, import_kind, NONE);
181
182 storage->set(index, *entry);
183 }
184
185 return array_object;
186 }
187
GetExports(Isolate * isolate,Handle<WasmModuleObject> module_object)188 Handle<JSArray> GetExports(Isolate* isolate,
189 Handle<WasmModuleObject> module_object) {
190 Factory* factory = isolate->factory();
191
192 Handle<String> name_string = factory->InternalizeUtf8String("name");
193 Handle<String> kind_string = factory->InternalizeUtf8String("kind");
194
195 Handle<String> function_string = factory->InternalizeUtf8String("function");
196 Handle<String> table_string = factory->InternalizeUtf8String("table");
197 Handle<String> memory_string = factory->InternalizeUtf8String("memory");
198 Handle<String> global_string = factory->InternalizeUtf8String("global");
199
200 // Create the result array.
201 const WasmModule* module = module_object->module();
202 int num_exports = static_cast<int>(module->export_table.size());
203 Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
204 Handle<FixedArray> storage = factory->NewFixedArray(num_exports);
205 JSArray::SetContent(array_object, storage);
206 array_object->set_length(Smi::FromInt(num_exports));
207
208 Handle<JSFunction> object_function =
209 Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
210
211 // Populate the result array.
212 for (int index = 0; index < num_exports; ++index) {
213 const WasmExport& exp = module->export_table[index];
214
215 Handle<String> export_kind;
216 switch (exp.kind) {
217 case kExternalFunction:
218 export_kind = function_string;
219 break;
220 case kExternalTable:
221 export_kind = table_string;
222 break;
223 case kExternalMemory:
224 export_kind = memory_string;
225 break;
226 case kExternalGlobal:
227 export_kind = global_string;
228 break;
229 default:
230 UNREACHABLE();
231 }
232
233 Handle<JSObject> entry = factory->NewJSObject(object_function);
234
235 MaybeHandle<String> export_name =
236 WasmModuleObject::ExtractUtf8StringFromModuleBytes(
237 isolate, module_object, exp.name);
238
239 JSObject::AddProperty(isolate, entry, name_string,
240 export_name.ToHandleChecked(), NONE);
241 JSObject::AddProperty(isolate, entry, kind_string, export_kind, NONE);
242
243 storage->set(index, *entry);
244 }
245
246 return array_object;
247 }
248
GetCustomSections(Isolate * isolate,Handle<WasmModuleObject> module_object,Handle<String> name,ErrorThrower * thrower)249 Handle<JSArray> GetCustomSections(Isolate* isolate,
250 Handle<WasmModuleObject> module_object,
251 Handle<String> name, ErrorThrower* thrower) {
252 Factory* factory = isolate->factory();
253
254 Vector<const uint8_t> wire_bytes =
255 module_object->native_module()->wire_bytes();
256 std::vector<CustomSectionOffset> custom_sections =
257 DecodeCustomSections(wire_bytes.start(), wire_bytes.end());
258
259 std::vector<Handle<Object>> matching_sections;
260
261 // Gather matching sections.
262 for (auto& section : custom_sections) {
263 MaybeHandle<String> section_name =
264 WasmModuleObject::ExtractUtf8StringFromModuleBytes(
265 isolate, module_object, section.name);
266
267 if (!name->Equals(*section_name.ToHandleChecked())) continue;
268
269 // Make a copy of the payload data in the section.
270 size_t size = section.payload.length();
271 void* memory =
272 size == 0 ? nullptr : isolate->array_buffer_allocator()->Allocate(size);
273
274 if (size && !memory) {
275 thrower->RangeError("out of memory allocating custom section data");
276 return Handle<JSArray>();
277 }
278 Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
279 constexpr bool is_external = false;
280 JSArrayBuffer::Setup(buffer, isolate, is_external, memory, size);
281 memcpy(memory, wire_bytes.start() + section.payload.offset(),
282 section.payload.length());
283
284 matching_sections.push_back(buffer);
285 }
286
287 int num_custom_sections = static_cast<int>(matching_sections.size());
288 Handle<JSArray> array_object = factory->NewJSArray(PACKED_ELEMENTS, 0, 0);
289 Handle<FixedArray> storage = factory->NewFixedArray(num_custom_sections);
290 JSArray::SetContent(array_object, storage);
291 array_object->set_length(Smi::FromInt(num_custom_sections));
292
293 for (int i = 0; i < num_custom_sections; i++) {
294 storage->set(i, *matching_sections[i]);
295 }
296
297 return array_object;
298 }
299
DecodeLocalNames(Isolate * isolate,Handle<WasmModuleObject> module_object)300 Handle<FixedArray> DecodeLocalNames(Isolate* isolate,
301 Handle<WasmModuleObject> module_object) {
302 Vector<const uint8_t> wire_bytes =
303 module_object->native_module()->wire_bytes();
304 LocalNames decoded_locals;
305 DecodeLocalNames(wire_bytes.start(), wire_bytes.end(), &decoded_locals);
306 Handle<FixedArray> locals_names =
307 isolate->factory()->NewFixedArray(decoded_locals.max_function_index + 1);
308 for (LocalNamesPerFunction& func : decoded_locals.names) {
309 Handle<FixedArray> func_locals_names =
310 isolate->factory()->NewFixedArray(func.max_local_index + 1);
311 locals_names->set(func.function_index, *func_locals_names);
312 for (LocalName& name : func.names) {
313 Handle<String> name_str =
314 WasmModuleObject::ExtractUtf8StringFromModuleBytes(
315 isolate, module_object, name.name)
316 .ToHandleChecked();
317 func_locals_names->set(name.local_index, *name_str);
318 }
319 }
320 return locals_names;
321 }
322
323 namespace {
324 template <typename T>
VectorSize(const std::vector<T> & vector)325 inline size_t VectorSize(const std::vector<T>& vector) {
326 return sizeof(T) * vector.size();
327 }
328 } // namespace
329
EstimateWasmModuleSize(const WasmModule * module)330 size_t EstimateWasmModuleSize(const WasmModule* module) {
331 size_t estimate =
332 sizeof(WasmModule) + VectorSize(module->signatures) +
333 VectorSize(module->signature_ids) + VectorSize(module->functions) +
334 VectorSize(module->data_segments) + VectorSize(module->tables) +
335 VectorSize(module->import_table) + VectorSize(module->export_table) +
336 VectorSize(module->exceptions) + VectorSize(module->table_inits);
337 // TODO(wasm): include names table and wire bytes in size estimate
338 return estimate;
339 }
340 } // namespace wasm
341 } // namespace internal
342 } // namespace v8
343