• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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