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