Home | History | Annotate | Download | only in wasm
      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 
     39 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 
     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.
     60 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.
     68 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.
     74 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.
     82 WasmName ModuleWireBytes::GetNameOrNull(const WasmFunction* function,
     83                                         const WasmModule* module) const {
     84   return GetNameOrNull(module->LookupFunctionName(*this, function->func_index));
     85 }
     86 
     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 
    100 WasmModule::WasmModule(std::unique_ptr<Zone> owned)
    101     : signature_zone(std::move(owned)) {}
    102 
    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 
    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 
    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 
    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 
    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>
    325 inline size_t VectorSize(const std::vector<T>& vector) {
    326   return sizeof(T) * vector.size();
    327 }
    328 }  // namespace
    329 
    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