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 "src/wasm/wasm-objects.h"
6 #include "src/utils.h"
7
8 #include "src/assembler-inl.h"
9 #include "src/base/iterator.h"
10 #include "src/code-factory.h"
11 #include "src/compiler/wasm-compiler.h"
12 #include "src/debug/debug-interface.h"
13 #include "src/objects-inl.h"
14 #include "src/objects/debug-objects-inl.h"
15 #include "src/objects/shared-function-info.h"
16 #include "src/trap-handler/trap-handler.h"
17 #include "src/wasm/jump-table-assembler.h"
18 #include "src/wasm/module-compiler.h"
19 #include "src/wasm/module-decoder.h"
20 #include "src/wasm/wasm-code-manager.h"
21 #include "src/wasm/wasm-engine.h"
22 #include "src/wasm/wasm-limits.h"
23 #include "src/wasm/wasm-memory.h"
24 #include "src/wasm/wasm-module.h"
25 #include "src/wasm/wasm-objects-inl.h"
26 #include "src/wasm/wasm-text.h"
27
28 #define TRACE(...) \
29 do { \
30 if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
31 } while (false)
32
33 #define TRACE_IFT(...) \
34 do { \
35 if (false) PrintF(__VA_ARGS__); \
36 } while (false)
37
38 namespace v8 {
39 namespace internal {
40
41 // Import a few often used types from the wasm namespace.
42 using WasmFunction = wasm::WasmFunction;
43 using WasmModule = wasm::WasmModule;
44
45 namespace {
46
47 // Manages the natively-allocated memory for a WasmInstanceObject. Since
48 // an instance finalizer is not guaranteed to run upon isolate shutdown,
49 // we must use a Managed<WasmInstanceNativeAllocations> to guarantee
50 // it is freed.
51 // Native allocations are the signature ids and targets for indirect call
52 // targets, as well as the call targets for imported functions.
53 class WasmInstanceNativeAllocations {
54 public:
55 // Helper macro to set an internal field and the corresponding field
56 // on an instance.
57 #define SET(instance, field, value) \
58 { \
59 auto v = value; \
60 this->field##_ = v; \
61 instance->set_##field(v); \
62 }
63
64 // Allocates initial native storage for a given instance.
WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance,size_t num_imported_functions,size_t num_imported_mutable_globals)65 WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance,
66 size_t num_imported_functions,
67 size_t num_imported_mutable_globals) {
68 SET(instance, imported_function_targets,
69 reinterpret_cast<Address*>(
70 calloc(num_imported_functions, sizeof(Address))));
71 SET(instance, imported_mutable_globals,
72 reinterpret_cast<Address*>(
73 calloc(num_imported_mutable_globals, sizeof(Address))));
74 }
~WasmInstanceNativeAllocations()75 ~WasmInstanceNativeAllocations() { free(); }
76 // Frees natively-allocated storage.
free()77 void free() {
78 ::free(indirect_function_table_sig_ids_);
79 ::free(indirect_function_table_targets_);
80 ::free(imported_function_targets_);
81 ::free(imported_mutable_globals_);
82 indirect_function_table_sig_ids_ = nullptr;
83 indirect_function_table_targets_ = nullptr;
84 imported_function_targets_ = nullptr;
85 imported_mutable_globals_ = nullptr;
86 }
87 // Resizes the indirect function table.
resize_indirect_function_table(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t new_size)88 void resize_indirect_function_table(Isolate* isolate,
89 Handle<WasmInstanceObject> instance,
90 uint32_t new_size) {
91 uint32_t old_size = instance->indirect_function_table_size();
92 void* new_sig_ids = nullptr;
93 void* new_targets = nullptr;
94 Handle<FixedArray> new_instances;
95 if (indirect_function_table_sig_ids_) {
96 // Reallocate the old storage.
97 new_sig_ids = realloc(indirect_function_table_sig_ids_,
98 new_size * sizeof(uint32_t));
99 new_targets =
100 realloc(indirect_function_table_targets_, new_size * sizeof(Address));
101
102 Handle<FixedArray> old(instance->indirect_function_table_instances(),
103 isolate);
104 new_instances = isolate->factory()->CopyFixedArrayAndGrow(
105 old, static_cast<int>(new_size - old_size));
106 } else {
107 // Allocate new storage.
108 new_sig_ids = malloc(new_size * sizeof(uint32_t));
109 new_targets = malloc(new_size * sizeof(Address));
110 new_instances =
111 isolate->factory()->NewFixedArray(static_cast<int>(new_size));
112 }
113 // Initialize new entries.
114 instance->set_indirect_function_table_size(new_size);
115 SET(instance, indirect_function_table_sig_ids,
116 reinterpret_cast<uint32_t*>(new_sig_ids));
117 SET(instance, indirect_function_table_targets,
118 reinterpret_cast<Address*>(new_targets));
119
120 instance->set_indirect_function_table_instances(*new_instances);
121 for (uint32_t j = old_size; j < new_size; j++) {
122 IndirectFunctionTableEntry(instance, static_cast<int>(j)).clear();
123 }
124 }
125 uint32_t* indirect_function_table_sig_ids_ = nullptr;
126 Address* indirect_function_table_targets_ = nullptr;
127 Address* imported_function_targets_ = nullptr;
128 Address* imported_mutable_globals_ = nullptr;
129 #undef SET
130 };
131
EstimateNativeAllocationsSize(const WasmModule * module)132 size_t EstimateNativeAllocationsSize(const WasmModule* module) {
133 size_t estimate = sizeof(WasmInstanceNativeAllocations) +
134 (1 * kPointerSize * module->num_imported_mutable_globals) +
135 (2 * kPointerSize * module->num_imported_functions);
136 for (auto& table : module->tables) {
137 estimate += 3 * kPointerSize * table.initial_size;
138 }
139 return estimate;
140 }
141
GetNativeAllocations(WasmInstanceObject * instance)142 WasmInstanceNativeAllocations* GetNativeAllocations(
143 WasmInstanceObject* instance) {
144 return reinterpret_cast<Managed<WasmInstanceNativeAllocations>*>(
145 instance->managed_native_allocations())
146 ->raw();
147 }
148
149 #ifdef DEBUG
IsBreakablePosition(wasm::NativeModule * native_module,int func_index,int offset_in_func)150 bool IsBreakablePosition(wasm::NativeModule* native_module, int func_index,
151 int offset_in_func) {
152 AccountingAllocator alloc;
153 Zone tmp(&alloc, ZONE_NAME);
154 wasm::BodyLocalDecls locals(&tmp);
155 const byte* module_start = native_module->wire_bytes().start();
156 const WasmFunction& func = native_module->module()->functions[func_index];
157 wasm::BytecodeIterator iterator(module_start + func.code.offset(),
158 module_start + func.code.end_offset(),
159 &locals);
160 DCHECK_LT(0, locals.encoded_size);
161 for (uint32_t offset : iterator.offsets()) {
162 if (offset > static_cast<uint32_t>(offset_in_func)) break;
163 if (offset == static_cast<uint32_t>(offset_in_func)) return true;
164 }
165 return false;
166 }
167 #endif // DEBUG
168
169 enum DispatchTableElements : int {
170 kDispatchTableInstanceOffset,
171 kDispatchTableIndexOffset,
172 kDispatchTableFunctionTableOffset,
173 // Marker:
174 kDispatchTableNumElements
175 };
176
177 } // namespace
178
179 // static
New(Isolate * isolate,const wasm::WasmFeatures & enabled,std::shared_ptr<const wasm::WasmModule> shared_module,wasm::ModuleEnv & env,OwnedVector<const uint8_t> wire_bytes,Handle<Script> script,Handle<ByteArray> asm_js_offset_table)180 Handle<WasmModuleObject> WasmModuleObject::New(
181 Isolate* isolate, const wasm::WasmFeatures& enabled,
182 std::shared_ptr<const wasm::WasmModule> shared_module, wasm::ModuleEnv& env,
183 OwnedVector<const uint8_t> wire_bytes, Handle<Script> script,
184 Handle<ByteArray> asm_js_offset_table) {
185 DCHECK_EQ(shared_module.get(), env.module);
186
187 // Create a new {NativeModule} first.
188 size_t native_memory_estimate =
189 isolate->wasm_engine()->code_manager()->EstimateNativeModuleSize(
190 env.module);
191 auto native_module = isolate->wasm_engine()->code_manager()->NewNativeModule(
192 isolate, enabled, native_memory_estimate,
193 wasm::NativeModule::kCanAllocateMoreMemory, std::move(shared_module),
194 env);
195 native_module->set_wire_bytes(std::move(wire_bytes));
196 native_module->SetRuntimeStubs(isolate);
197
198 // Delegate to the shared {WasmModuleObject::New} allocator.
199 Handle<WasmModuleObject> module_object =
200 New(isolate, std::move(native_module), script);
201 if (!asm_js_offset_table.is_null()) {
202 module_object->set_asm_js_offset_table(*asm_js_offset_table);
203 }
204 return module_object;
205 }
206
207 // static
New(Isolate * isolate,std::shared_ptr<wasm::NativeModule> native_module,Handle<Script> script)208 Handle<WasmModuleObject> WasmModuleObject::New(
209 Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module,
210 Handle<Script> script) {
211 int export_wrapper_size =
212 static_cast<int>(native_module->module()->num_exported_functions);
213 Handle<FixedArray> export_wrappers =
214 isolate->factory()->NewFixedArray(export_wrapper_size, TENURED);
215
216 // Use the given shared {NativeModule}, but increase its reference count by
217 // allocating a new {Managed<T>} that the {WasmModuleObject} references.
218 size_t native_memory_estimate =
219 isolate->wasm_engine()->code_manager()->EstimateNativeModuleSize(
220 native_module->module());
221 size_t memory_estimate =
222 EstimateWasmModuleSize(native_module->module()) + native_memory_estimate;
223 Handle<Managed<wasm::NativeModule>> managed_native_module =
224 Managed<wasm::NativeModule>::FromSharedPtr(isolate, memory_estimate,
225 std::move(native_module));
226
227 Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast(
228 isolate->factory()->NewJSObject(isolate->wasm_module_constructor()));
229 module_object->set_export_wrappers(*export_wrappers);
230 if (script->type() == Script::TYPE_WASM) {
231 script->set_wasm_module_object(*module_object);
232 }
233 module_object->set_script(*script);
234 module_object->set_weak_instance_list(
235 ReadOnlyRoots(isolate).empty_weak_array_list());
236 module_object->set_managed_native_module(*managed_native_module);
237 return module_object;
238 }
239
SetBreakPoint(Handle<WasmModuleObject> module_object,int * position,Handle<BreakPoint> break_point)240 bool WasmModuleObject::SetBreakPoint(Handle<WasmModuleObject> module_object,
241 int* position,
242 Handle<BreakPoint> break_point) {
243 Isolate* isolate = module_object->GetIsolate();
244
245 // Find the function for this breakpoint.
246 int func_index = module_object->GetContainingFunction(*position);
247 if (func_index < 0) return false;
248 const WasmFunction& func = module_object->module()->functions[func_index];
249 int offset_in_func = *position - func.code.offset();
250
251 // According to the current design, we should only be called with valid
252 // breakable positions.
253 DCHECK(IsBreakablePosition(module_object->native_module(), func_index,
254 offset_in_func));
255
256 // Insert new break point into break_positions of module object.
257 WasmModuleObject::AddBreakpoint(module_object, *position, break_point);
258
259 // Iterate over all instances of this module and tell them to set this new
260 // breakpoint. We do this using the weak list of all instances.
261 Handle<WeakArrayList> weak_instance_list(module_object->weak_instance_list(),
262 isolate);
263 for (int i = 0; i < weak_instance_list->length(); ++i) {
264 MaybeObject* maybe_instance = weak_instance_list->Get(i);
265 if (maybe_instance->IsWeakHeapObject()) {
266 Handle<WasmInstanceObject> instance(
267 WasmInstanceObject::cast(maybe_instance->ToWeakHeapObject()),
268 isolate);
269 Handle<WasmDebugInfo> debug_info =
270 WasmInstanceObject::GetOrCreateDebugInfo(instance);
271 WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func);
272 }
273 }
274
275 return true;
276 }
277
278 namespace {
279
GetBreakpointPos(Isolate * isolate,Object * break_point_info_or_undef)280 int GetBreakpointPos(Isolate* isolate, Object* break_point_info_or_undef) {
281 if (break_point_info_or_undef->IsUndefined(isolate)) return kMaxInt;
282 return BreakPointInfo::cast(break_point_info_or_undef)->source_position();
283 }
284
FindBreakpointInfoInsertPos(Isolate * isolate,Handle<FixedArray> breakpoint_infos,int position)285 int FindBreakpointInfoInsertPos(Isolate* isolate,
286 Handle<FixedArray> breakpoint_infos,
287 int position) {
288 // Find insert location via binary search, taking care of undefined values on
289 // the right. Position is always greater than zero.
290 DCHECK_LT(0, position);
291
292 int left = 0; // inclusive
293 int right = breakpoint_infos->length(); // exclusive
294 while (right - left > 1) {
295 int mid = left + (right - left) / 2;
296 Object* mid_obj = breakpoint_infos->get(mid);
297 if (GetBreakpointPos(isolate, mid_obj) <= position) {
298 left = mid;
299 } else {
300 right = mid;
301 }
302 }
303
304 int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left));
305 return left_pos < position ? left + 1 : left;
306 }
307
308 } // namespace
309
AddBreakpoint(Handle<WasmModuleObject> module_object,int position,Handle<BreakPoint> break_point)310 void WasmModuleObject::AddBreakpoint(Handle<WasmModuleObject> module_object,
311 int position,
312 Handle<BreakPoint> break_point) {
313 Isolate* isolate = module_object->GetIsolate();
314 Handle<FixedArray> breakpoint_infos;
315 if (module_object->has_breakpoint_infos()) {
316 breakpoint_infos = handle(module_object->breakpoint_infos(), isolate);
317 } else {
318 breakpoint_infos = isolate->factory()->NewFixedArray(4, TENURED);
319 module_object->set_breakpoint_infos(*breakpoint_infos);
320 }
321
322 int insert_pos =
323 FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
324
325 // If a BreakPointInfo object already exists for this position, add the new
326 // breakpoint object and return.
327 if (insert_pos < breakpoint_infos->length() &&
328 GetBreakpointPos(isolate, breakpoint_infos->get(insert_pos)) ==
329 position) {
330 Handle<BreakPointInfo> old_info(
331 BreakPointInfo::cast(breakpoint_infos->get(insert_pos)), isolate);
332 BreakPointInfo::SetBreakPoint(isolate, old_info, break_point);
333 return;
334 }
335
336 // Enlarge break positions array if necessary.
337 bool need_realloc = !breakpoint_infos->get(breakpoint_infos->length() - 1)
338 ->IsUndefined(isolate);
339 Handle<FixedArray> new_breakpoint_infos = breakpoint_infos;
340 if (need_realloc) {
341 new_breakpoint_infos = isolate->factory()->NewFixedArray(
342 2 * breakpoint_infos->length(), TENURED);
343 module_object->set_breakpoint_infos(*new_breakpoint_infos);
344 // Copy over the entries [0, insert_pos).
345 for (int i = 0; i < insert_pos; ++i)
346 new_breakpoint_infos->set(i, breakpoint_infos->get(i));
347 }
348
349 // Move elements [insert_pos, ...] up by one.
350 for (int i = breakpoint_infos->length() - 1; i >= insert_pos; --i) {
351 Object* entry = breakpoint_infos->get(i);
352 if (entry->IsUndefined(isolate)) continue;
353 new_breakpoint_infos->set(i + 1, entry);
354 }
355
356 // Generate new BreakpointInfo.
357 Handle<BreakPointInfo> breakpoint_info =
358 isolate->factory()->NewBreakPointInfo(position);
359 BreakPointInfo::SetBreakPoint(isolate, breakpoint_info, break_point);
360
361 // Now insert new position at insert_pos.
362 new_breakpoint_infos->set(insert_pos, *breakpoint_info);
363 }
364
SetBreakpointsOnNewInstance(Handle<WasmModuleObject> module_object,Handle<WasmInstanceObject> instance)365 void WasmModuleObject::SetBreakpointsOnNewInstance(
366 Handle<WasmModuleObject> module_object,
367 Handle<WasmInstanceObject> instance) {
368 if (!module_object->has_breakpoint_infos()) return;
369 Isolate* isolate = module_object->GetIsolate();
370 Handle<WasmDebugInfo> debug_info =
371 WasmInstanceObject::GetOrCreateDebugInfo(instance);
372
373 Handle<FixedArray> breakpoint_infos(module_object->breakpoint_infos(),
374 isolate);
375 // If the array exists, it should not be empty.
376 DCHECK_LT(0, breakpoint_infos->length());
377
378 for (int i = 0, e = breakpoint_infos->length(); i < e; ++i) {
379 Handle<Object> obj(breakpoint_infos->get(i), isolate);
380 if (obj->IsUndefined(isolate)) {
381 for (; i < e; ++i) {
382 DCHECK(breakpoint_infos->get(i)->IsUndefined(isolate));
383 }
384 break;
385 }
386 Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj);
387 int position = breakpoint_info->source_position();
388
389 // Find the function for this breakpoint, and set the breakpoint.
390 int func_index = module_object->GetContainingFunction(position);
391 DCHECK_LE(0, func_index);
392 const WasmFunction& func = module_object->module()->functions[func_index];
393 int offset_in_func = position - func.code.offset();
394 WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func);
395 }
396 }
397
398 namespace {
399
400 enum AsmJsOffsetTableEntryLayout {
401 kOTEByteOffset,
402 kOTECallPosition,
403 kOTENumberConvPosition,
404 kOTESize
405 };
406
GetDecodedAsmJsOffsetTable(Handle<WasmModuleObject> module_object,Isolate * isolate)407 Handle<ByteArray> GetDecodedAsmJsOffsetTable(
408 Handle<WasmModuleObject> module_object, Isolate* isolate) {
409 DCHECK(module_object->is_asm_js());
410 Handle<ByteArray> offset_table(module_object->asm_js_offset_table(), isolate);
411
412 // The last byte in the asm_js_offset_tables ByteArray tells whether it is
413 // still encoded (0) or decoded (1).
414 enum AsmJsTableType : int { Encoded = 0, Decoded = 1 };
415 int table_type = offset_table->get(offset_table->length() - 1);
416 DCHECK(table_type == Encoded || table_type == Decoded);
417 if (table_type == Decoded) return offset_table;
418
419 wasm::AsmJsOffsetsResult asm_offsets;
420 {
421 DisallowHeapAllocation no_gc;
422 byte* bytes_start = offset_table->GetDataStartAddress();
423 byte* bytes_end = reinterpret_cast<byte*>(
424 reinterpret_cast<Address>(bytes_start) + offset_table->length() - 1);
425 asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
426 }
427 // Wasm bytes must be valid and must contain asm.js offset table.
428 DCHECK(asm_offsets.ok());
429 DCHECK_GE(kMaxInt, asm_offsets.val.size());
430 int num_functions = static_cast<int>(asm_offsets.val.size());
431 int num_imported_functions =
432 static_cast<int>(module_object->module()->num_imported_functions);
433 DCHECK_EQ(module_object->module()->functions.size(),
434 static_cast<size_t>(num_functions) + num_imported_functions);
435 int num_entries = 0;
436 for (int func = 0; func < num_functions; ++func) {
437 size_t new_size = asm_offsets.val[func].size();
438 DCHECK_LE(new_size, static_cast<size_t>(kMaxInt) - num_entries);
439 num_entries += static_cast<int>(new_size);
440 }
441 // One byte to encode that this is a decoded table.
442 DCHECK_GE(kMaxInt,
443 1 + static_cast<uint64_t>(num_entries) * kOTESize * kIntSize);
444 int total_size = 1 + num_entries * kOTESize * kIntSize;
445 Handle<ByteArray> decoded_table =
446 isolate->factory()->NewByteArray(total_size, TENURED);
447 decoded_table->set(total_size - 1, AsmJsTableType::Decoded);
448 module_object->set_asm_js_offset_table(*decoded_table);
449
450 int idx = 0;
451 const std::vector<WasmFunction>& wasm_funs =
452 module_object->module()->functions;
453 for (int func = 0; func < num_functions; ++func) {
454 std::vector<wasm::AsmJsOffsetEntry>& func_asm_offsets =
455 asm_offsets.val[func];
456 if (func_asm_offsets.empty()) continue;
457 int func_offset = wasm_funs[num_imported_functions + func].code.offset();
458 for (wasm::AsmJsOffsetEntry& e : func_asm_offsets) {
459 // Byte offsets must be strictly monotonously increasing:
460 DCHECK_IMPLIES(idx > 0, func_offset + e.byte_offset >
461 decoded_table->get_int(idx - kOTESize));
462 decoded_table->set_int(idx + kOTEByteOffset, func_offset + e.byte_offset);
463 decoded_table->set_int(idx + kOTECallPosition, e.source_position_call);
464 decoded_table->set_int(idx + kOTENumberConvPosition,
465 e.source_position_number_conversion);
466 idx += kOTESize;
467 }
468 }
469 DCHECK_EQ(total_size, idx * kIntSize + 1);
470 return decoded_table;
471 }
472
473 } // namespace
474
GetSourcePosition(Handle<WasmModuleObject> module_object,uint32_t func_index,uint32_t byte_offset,bool is_at_number_conversion)475 int WasmModuleObject::GetSourcePosition(Handle<WasmModuleObject> module_object,
476 uint32_t func_index,
477 uint32_t byte_offset,
478 bool is_at_number_conversion) {
479 Isolate* isolate = module_object->GetIsolate();
480 const WasmModule* module = module_object->module();
481
482 if (module->origin != wasm::kAsmJsOrigin) {
483 // for non-asm.js modules, we just add the function's start offset
484 // to make a module-relative position.
485 return byte_offset + module_object->GetFunctionOffset(func_index);
486 }
487
488 // asm.js modules have an additional offset table that must be searched.
489 Handle<ByteArray> offset_table =
490 GetDecodedAsmJsOffsetTable(module_object, isolate);
491
492 DCHECK_LT(func_index, module->functions.size());
493 uint32_t func_code_offset = module->functions[func_index].code.offset();
494 uint32_t total_offset = func_code_offset + byte_offset;
495
496 // Binary search for the total byte offset.
497 int left = 0; // inclusive
498 int right = offset_table->length() / kIntSize / kOTESize; // exclusive
499 DCHECK_LT(left, right);
500 while (right - left > 1) {
501 int mid = left + (right - left) / 2;
502 int mid_entry = offset_table->get_int(kOTESize * mid);
503 DCHECK_GE(kMaxInt, mid_entry);
504 if (static_cast<uint32_t>(mid_entry) <= total_offset) {
505 left = mid;
506 } else {
507 right = mid;
508 }
509 }
510 // There should be an entry for each position that could show up on the stack
511 // trace:
512 DCHECK_EQ(total_offset, offset_table->get_int(kOTESize * left));
513 int idx = is_at_number_conversion ? kOTENumberConvPosition : kOTECallPosition;
514 return offset_table->get_int(kOTESize * left + idx);
515 }
516
DisassembleFunction(int func_index)517 v8::debug::WasmDisassembly WasmModuleObject::DisassembleFunction(
518 int func_index) {
519 DisallowHeapAllocation no_gc;
520
521 if (func_index < 0 ||
522 static_cast<uint32_t>(func_index) >= module()->functions.size())
523 return {};
524
525 Vector<const byte> wire_bytes = native_module()->wire_bytes();
526
527 std::ostringstream disassembly_os;
528 v8::debug::WasmDisassembly::OffsetTable offset_table;
529
530 PrintWasmText(module(), wire_bytes, static_cast<uint32_t>(func_index),
531 disassembly_os, &offset_table);
532
533 return {disassembly_os.str(), std::move(offset_table)};
534 }
535
GetPossibleBreakpoints(const v8::debug::Location & start,const v8::debug::Location & end,std::vector<v8::debug::BreakLocation> * locations)536 bool WasmModuleObject::GetPossibleBreakpoints(
537 const v8::debug::Location& start, const v8::debug::Location& end,
538 std::vector<v8::debug::BreakLocation>* locations) {
539 DisallowHeapAllocation no_gc;
540
541 const std::vector<WasmFunction>& functions = module()->functions;
542 if (start.GetLineNumber() < 0 || start.GetColumnNumber() < 0 ||
543 (!end.IsEmpty() &&
544 (end.GetLineNumber() < 0 || end.GetColumnNumber() < 0)))
545 return false;
546
547 // start_func_index, start_offset and end_func_index is inclusive.
548 // end_offset is exclusive.
549 // start_offset and end_offset are module-relative byte offsets.
550 uint32_t start_func_index = start.GetLineNumber();
551 if (start_func_index >= functions.size()) return false;
552 int start_func_len = functions[start_func_index].code.length();
553 if (start.GetColumnNumber() > start_func_len) return false;
554 uint32_t start_offset =
555 functions[start_func_index].code.offset() + start.GetColumnNumber();
556 uint32_t end_func_index;
557 uint32_t end_offset;
558 if (end.IsEmpty()) {
559 // Default: everything till the end of the Script.
560 end_func_index = static_cast<uint32_t>(functions.size() - 1);
561 end_offset = functions[end_func_index].code.end_offset();
562 } else {
563 // If end is specified: Use it and check for valid input.
564 end_func_index = static_cast<uint32_t>(end.GetLineNumber());
565
566 // Special case: Stop before the start of the next function. Change to: Stop
567 // at the end of the function before, such that we don't disassemble the
568 // next function also.
569 if (end.GetColumnNumber() == 0 && end_func_index > 0) {
570 --end_func_index;
571 end_offset = functions[end_func_index].code.end_offset();
572 } else {
573 if (end_func_index >= functions.size()) return false;
574 end_offset =
575 functions[end_func_index].code.offset() + end.GetColumnNumber();
576 if (end_offset > functions[end_func_index].code.end_offset())
577 return false;
578 }
579 }
580
581 AccountingAllocator alloc;
582 Zone tmp(&alloc, ZONE_NAME);
583 const byte* module_start = native_module()->wire_bytes().start();
584
585 for (uint32_t func_idx = start_func_index; func_idx <= end_func_index;
586 ++func_idx) {
587 const WasmFunction& func = functions[func_idx];
588 if (func.code.length() == 0) continue;
589
590 wasm::BodyLocalDecls locals(&tmp);
591 wasm::BytecodeIterator iterator(module_start + func.code.offset(),
592 module_start + func.code.end_offset(),
593 &locals);
594 DCHECK_LT(0u, locals.encoded_size);
595 for (uint32_t offset : iterator.offsets()) {
596 uint32_t total_offset = func.code.offset() + offset;
597 if (total_offset >= end_offset) {
598 DCHECK_EQ(end_func_index, func_idx);
599 break;
600 }
601 if (total_offset < start_offset) continue;
602 locations->emplace_back(func_idx, offset, debug::kCommonBreakLocation);
603 }
604 }
605 return true;
606 }
607
CheckBreakPoints(Isolate * isolate,Handle<WasmModuleObject> module_object,int position)608 MaybeHandle<FixedArray> WasmModuleObject::CheckBreakPoints(
609 Isolate* isolate, Handle<WasmModuleObject> module_object, int position) {
610 if (!module_object->has_breakpoint_infos()) return {};
611
612 Handle<FixedArray> breakpoint_infos(module_object->breakpoint_infos(),
613 isolate);
614 int insert_pos =
615 FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
616 if (insert_pos >= breakpoint_infos->length()) return {};
617
618 Handle<Object> maybe_breakpoint_info(breakpoint_infos->get(insert_pos),
619 isolate);
620 if (maybe_breakpoint_info->IsUndefined(isolate)) return {};
621 Handle<BreakPointInfo> breakpoint_info =
622 Handle<BreakPointInfo>::cast(maybe_breakpoint_info);
623 if (breakpoint_info->source_position() != position) return {};
624
625 // There is no support for conditional break points. Just assume that every
626 // break point always hits.
627 Handle<Object> break_points(breakpoint_info->break_points(), isolate);
628 if (break_points->IsFixedArray()) {
629 return Handle<FixedArray>::cast(break_points);
630 }
631 Handle<FixedArray> break_points_hit = isolate->factory()->NewFixedArray(1);
632 break_points_hit->set(0, *break_points);
633 return break_points_hit;
634 }
635
ExtractUtf8StringFromModuleBytes(Isolate * isolate,Handle<WasmModuleObject> module_object,wasm::WireBytesRef ref)636 MaybeHandle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes(
637 Isolate* isolate, Handle<WasmModuleObject> module_object,
638 wasm::WireBytesRef ref) {
639 // TODO(wasm): cache strings from modules if it's a performance win.
640 Vector<const uint8_t> wire_bytes =
641 module_object->native_module()->wire_bytes();
642 return ExtractUtf8StringFromModuleBytes(isolate, wire_bytes, ref);
643 }
644
ExtractUtf8StringFromModuleBytes(Isolate * isolate,Vector<const uint8_t> wire_bytes,wasm::WireBytesRef ref)645 MaybeHandle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes(
646 Isolate* isolate, Vector<const uint8_t> wire_bytes,
647 wasm::WireBytesRef ref) {
648 Vector<const uint8_t> name_vec = wire_bytes + ref.offset();
649 name_vec.Truncate(ref.length());
650 // UTF8 validation happens at decode time.
651 DCHECK(unibrow::Utf8::ValidateEncoding(name_vec.start(), name_vec.length()));
652 return isolate->factory()->NewStringFromUtf8(
653 Vector<const char>::cast(name_vec));
654 }
655
GetModuleNameOrNull(Isolate * isolate,Handle<WasmModuleObject> module_object)656 MaybeHandle<String> WasmModuleObject::GetModuleNameOrNull(
657 Isolate* isolate, Handle<WasmModuleObject> module_object) {
658 const WasmModule* module = module_object->module();
659 if (!module->name.is_set()) return {};
660 return ExtractUtf8StringFromModuleBytes(isolate, module_object, module->name);
661 }
662
GetFunctionNameOrNull(Isolate * isolate,Handle<WasmModuleObject> module_object,uint32_t func_index)663 MaybeHandle<String> WasmModuleObject::GetFunctionNameOrNull(
664 Isolate* isolate, Handle<WasmModuleObject> module_object,
665 uint32_t func_index) {
666 DCHECK_LT(func_index, module_object->module()->functions.size());
667 wasm::WireBytesRef name = module_object->module()->LookupFunctionName(
668 wasm::ModuleWireBytes(module_object->native_module()->wire_bytes()),
669 func_index);
670 if (!name.is_set()) return {};
671 return ExtractUtf8StringFromModuleBytes(isolate, module_object, name);
672 }
673
GetFunctionName(Isolate * isolate,Handle<WasmModuleObject> module_object,uint32_t func_index)674 Handle<String> WasmModuleObject::GetFunctionName(
675 Isolate* isolate, Handle<WasmModuleObject> module_object,
676 uint32_t func_index) {
677 MaybeHandle<String> name =
678 GetFunctionNameOrNull(isolate, module_object, func_index);
679 if (!name.is_null()) return name.ToHandleChecked();
680 EmbeddedVector<char, 32> buffer;
681 int length = SNPrintF(buffer, "wasm-function[%u]", func_index);
682 return isolate->factory()
683 ->NewStringFromOneByte(Vector<uint8_t>::cast(buffer.SubVector(0, length)))
684 .ToHandleChecked();
685 }
686
GetRawFunctionName(uint32_t func_index)687 Vector<const uint8_t> WasmModuleObject::GetRawFunctionName(
688 uint32_t func_index) {
689 DCHECK_GT(module()->functions.size(), func_index);
690 wasm::ModuleWireBytes wire_bytes(native_module()->wire_bytes());
691 wasm::WireBytesRef name_ref =
692 module()->LookupFunctionName(wire_bytes, func_index);
693 wasm::WasmName name = wire_bytes.GetName(name_ref);
694 return Vector<const uint8_t>::cast(name);
695 }
696
GetFunctionOffset(uint32_t func_index)697 int WasmModuleObject::GetFunctionOffset(uint32_t func_index) {
698 const std::vector<WasmFunction>& functions = module()->functions;
699 if (static_cast<uint32_t>(func_index) >= functions.size()) return -1;
700 DCHECK_GE(kMaxInt, functions[func_index].code.offset());
701 return static_cast<int>(functions[func_index].code.offset());
702 }
703
GetContainingFunction(uint32_t byte_offset)704 int WasmModuleObject::GetContainingFunction(uint32_t byte_offset) {
705 const std::vector<WasmFunction>& functions = module()->functions;
706
707 // Binary search for a function containing the given position.
708 int left = 0; // inclusive
709 int right = static_cast<int>(functions.size()); // exclusive
710 if (right == 0) return false;
711 while (right - left > 1) {
712 int mid = left + (right - left) / 2;
713 if (functions[mid].code.offset() <= byte_offset) {
714 left = mid;
715 } else {
716 right = mid;
717 }
718 }
719 // If the found function does not contains the given position, return -1.
720 const WasmFunction& func = functions[left];
721 if (byte_offset < func.code.offset() ||
722 byte_offset >= func.code.end_offset()) {
723 return -1;
724 }
725
726 return left;
727 }
728
GetPositionInfo(uint32_t position,Script::PositionInfo * info)729 bool WasmModuleObject::GetPositionInfo(uint32_t position,
730 Script::PositionInfo* info) {
731 int func_index = GetContainingFunction(position);
732 if (func_index < 0) return false;
733
734 const WasmFunction& function = module()->functions[func_index];
735
736 info->line = func_index;
737 info->column = position - function.code.offset();
738 info->line_start = function.code.offset();
739 info->line_end = function.code.end_offset();
740 return true;
741 }
742
New(Isolate * isolate,uint32_t initial,int64_t maximum,Handle<FixedArray> * js_functions)743 Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
744 int64_t maximum,
745 Handle<FixedArray>* js_functions) {
746 Handle<JSFunction> table_ctor(
747 isolate->native_context()->wasm_table_constructor(), isolate);
748 auto table_obj = Handle<WasmTableObject>::cast(
749 isolate->factory()->NewJSObject(table_ctor));
750
751 *js_functions = isolate->factory()->NewFixedArray(initial);
752 Object* null = ReadOnlyRoots(isolate).null_value();
753 for (int i = 0; i < static_cast<int>(initial); ++i) {
754 (*js_functions)->set(i, null);
755 }
756 table_obj->set_functions(**js_functions);
757 DCHECK_EQ(maximum, static_cast<int>(maximum));
758 Handle<Object> max = isolate->factory()->NewNumber(maximum);
759 table_obj->set_maximum_length(*max);
760
761 table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array());
762 return Handle<WasmTableObject>::cast(table_obj);
763 }
764
AddDispatchTable(Isolate * isolate,Handle<WasmTableObject> table_obj,Handle<WasmInstanceObject> instance,int table_index)765 void WasmTableObject::AddDispatchTable(Isolate* isolate,
766 Handle<WasmTableObject> table_obj,
767 Handle<WasmInstanceObject> instance,
768 int table_index) {
769 Handle<FixedArray> dispatch_tables(table_obj->dispatch_tables(), isolate);
770 int old_length = dispatch_tables->length();
771 DCHECK_EQ(0, old_length % kDispatchTableNumElements);
772
773 if (instance.is_null()) return;
774 // TODO(titzer): use weak cells here to avoid leaking instances.
775
776 // Grow the dispatch table and add a new entry at the end.
777 Handle<FixedArray> new_dispatch_tables =
778 isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables,
779 kDispatchTableNumElements);
780
781 new_dispatch_tables->set(old_length + kDispatchTableInstanceOffset,
782 *instance);
783 new_dispatch_tables->set(old_length + kDispatchTableIndexOffset,
784 Smi::FromInt(table_index));
785
786 table_obj->set_dispatch_tables(*new_dispatch_tables);
787 }
788
Grow(Isolate * isolate,uint32_t count)789 void WasmTableObject::Grow(Isolate* isolate, uint32_t count) {
790 if (count == 0) return; // Degenerate case: nothing to do.
791
792 Handle<FixedArray> dispatch_tables(this->dispatch_tables(), isolate);
793 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
794 uint32_t old_size = functions()->length();
795
796 // Tables are stored in the instance object, no code patching is
797 // necessary. We simply have to grow the raw tables in each instance
798 // that has imported this table.
799
800 // TODO(titzer): replace the dispatch table with a weak list of all
801 // the instances that import a given table.
802 for (int i = 0; i < dispatch_tables->length();
803 i += kDispatchTableNumElements) {
804 Handle<WasmInstanceObject> instance(
805 WasmInstanceObject::cast(dispatch_tables->get(i)), isolate);
806 DCHECK_EQ(old_size, instance->indirect_function_table_size());
807 uint32_t new_size = old_size + count;
808 WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(instance,
809 new_size);
810 }
811 }
812
Set(Isolate * isolate,Handle<WasmTableObject> table,int32_t table_index,Handle<JSFunction> function)813 void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table,
814 int32_t table_index, Handle<JSFunction> function) {
815 Handle<FixedArray> array(table->functions(), isolate);
816 if (function.is_null()) {
817 ClearDispatchTables(isolate, table, table_index); // Degenerate case.
818 array->set(table_index, ReadOnlyRoots(isolate).null_value());
819 return;
820 }
821
822 // TODO(titzer): Change this to MaybeHandle<WasmExportedFunction>
823 DCHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
824 auto exported_function = Handle<WasmExportedFunction>::cast(function);
825 Handle<WasmInstanceObject> other_instance(exported_function->instance(),
826 isolate);
827 int func_index = exported_function->function_index();
828 auto* wasm_function = &other_instance->module()->functions[func_index];
829 DCHECK_NOT_NULL(wasm_function);
830 DCHECK_NOT_NULL(wasm_function->sig);
831 Address call_target = exported_function->GetWasmCallTarget();
832 UpdateDispatchTables(isolate, table, table_index, wasm_function->sig,
833 handle(exported_function->instance(), isolate),
834 call_target);
835 array->set(table_index, *function);
836 }
837
UpdateDispatchTables(Isolate * isolate,Handle<WasmTableObject> table,int table_index,wasm::FunctionSig * sig,Handle<WasmInstanceObject> from_instance,Address call_target)838 void WasmTableObject::UpdateDispatchTables(
839 Isolate* isolate, Handle<WasmTableObject> table, int table_index,
840 wasm::FunctionSig* sig, Handle<WasmInstanceObject> from_instance,
841 Address call_target) {
842 // We simply need to update the IFTs for each instance that imports
843 // this table.
844 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
845 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
846
847 for (int i = 0; i < dispatch_tables->length();
848 i += kDispatchTableNumElements) {
849 Handle<WasmInstanceObject> to_instance(
850 WasmInstanceObject::cast(
851 dispatch_tables->get(i + kDispatchTableInstanceOffset)),
852 isolate);
853 // Note that {SignatureMap::Find} may return {-1} if the signature is
854 // not found; it will simply never match any check.
855 auto sig_id = to_instance->module()->signature_map.Find(*sig);
856 IndirectFunctionTableEntry(to_instance, table_index)
857 .set(sig_id, *from_instance, call_target);
858 }
859 }
860
ClearDispatchTables(Isolate * isolate,Handle<WasmTableObject> table,int index)861 void WasmTableObject::ClearDispatchTables(Isolate* isolate,
862 Handle<WasmTableObject> table,
863 int index) {
864 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate);
865 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements);
866 for (int i = 0; i < dispatch_tables->length();
867 i += kDispatchTableNumElements) {
868 Handle<WasmInstanceObject> target_instance(
869 WasmInstanceObject::cast(
870 dispatch_tables->get(i + kDispatchTableInstanceOffset)),
871 isolate);
872 DCHECK_LT(index, target_instance->indirect_function_table_size());
873 IndirectFunctionTableEntry(target_instance, index).clear();
874 }
875 }
876
877 namespace {
GrowMemoryBuffer(Isolate * isolate,Handle<JSArrayBuffer> old_buffer,uint32_t pages,uint32_t maximum_pages)878 MaybeHandle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate,
879 Handle<JSArrayBuffer> old_buffer,
880 uint32_t pages,
881 uint32_t maximum_pages) {
882 if (!old_buffer->is_growable()) return {};
883 void* old_mem_start = old_buffer->backing_store();
884 size_t old_size = old_buffer->byte_length()->Number();
885 CHECK_GE(wasm::kV8MaxWasmMemoryBytes, old_size);
886 CHECK_EQ(0, old_size % wasm::kWasmPageSize);
887 size_t old_pages = old_size / wasm::kWasmPageSize;
888 if (old_pages > maximum_pages || // already reached maximum
889 (pages > maximum_pages - old_pages) || // exceeds remaining
890 (pages > FLAG_wasm_max_mem_pages - old_pages)) { // exceeds limit
891 return {};
892 }
893 size_t new_size =
894 static_cast<size_t>(old_pages + pages) * wasm::kWasmPageSize;
895 CHECK_GE(wasm::kV8MaxWasmMemoryBytes, new_size);
896
897 // Reusing the backing store from externalized buffers causes problems with
898 // Blink's array buffers. The connection between the two is lost, which can
899 // lead to Blink not knowing about the other reference to the buffer and
900 // freeing it too early.
901 if (!old_buffer->is_external() &&
902 ((new_size < old_buffer->allocation_length()) || old_size == new_size)) {
903 if (old_size != new_size) {
904 DCHECK_NOT_NULL(old_buffer->backing_store());
905 // If adjusting permissions fails, propagate error back to return
906 // failure to grow.
907 if (!i::SetPermissions(old_mem_start, new_size,
908 PageAllocator::kReadWrite)) {
909 return {};
910 }
911 reinterpret_cast<v8::Isolate*>(isolate)
912 ->AdjustAmountOfExternalAllocatedMemory(pages * wasm::kWasmPageSize);
913 }
914 // NOTE: We must allocate a new array buffer here because the spec
915 // assumes that ArrayBuffers do not change size.
916 void* backing_store = old_buffer->backing_store();
917 bool is_external = old_buffer->is_external();
918 // Disconnect buffer early so GC won't free it.
919 i::wasm::DetachMemoryBuffer(isolate, old_buffer, false);
920 Handle<JSArrayBuffer> new_buffer =
921 wasm::SetupArrayBuffer(isolate, backing_store, new_size, is_external);
922 return new_buffer;
923 } else {
924 // We couldn't reuse the old backing store, so create a new one and copy the
925 // old contents in.
926 Handle<JSArrayBuffer> new_buffer;
927 if (!wasm::NewArrayBuffer(isolate, new_size).ToHandle(&new_buffer)) {
928 return {};
929 }
930 wasm::WasmMemoryTracker* const memory_tracker =
931 isolate->wasm_engine()->memory_tracker();
932 // If the old buffer had full guard regions, we can only safely use the new
933 // buffer if it also has full guard regions. Otherwise, we'd have to
934 // recompile all the instances using this memory to insert bounds checks.
935 if (memory_tracker->HasFullGuardRegions(old_mem_start) &&
936 !memory_tracker->HasFullGuardRegions(new_buffer->backing_store())) {
937 return {};
938 }
939 if (old_size == 0) return new_buffer;
940 memcpy(new_buffer->backing_store(), old_mem_start, old_size);
941 DCHECK(old_buffer.is_null() || !old_buffer->is_shared());
942 constexpr bool free_memory = true;
943 i::wasm::DetachMemoryBuffer(isolate, old_buffer, free_memory);
944 return new_buffer;
945 }
946 }
947
948 // May GC, because SetSpecializationMemInfoFrom may GC
SetInstanceMemory(Handle<WasmInstanceObject> instance,Handle<JSArrayBuffer> buffer)949 void SetInstanceMemory(Handle<WasmInstanceObject> instance,
950 Handle<JSArrayBuffer> buffer) {
951 instance->SetRawMemory(reinterpret_cast<byte*>(buffer->backing_store()),
952 buffer->byte_length()->Number());
953 #if DEBUG
954 if (!FLAG_mock_arraybuffer_allocator) {
955 // To flush out bugs earlier, in DEBUG mode, check that all pages of the
956 // memory are accessible by reading and writing one byte on each page.
957 // Don't do this if the mock ArrayBuffer allocator is enabled.
958 byte* mem_start = instance->memory_start();
959 size_t mem_size = instance->memory_size();
960 for (size_t offset = 0; offset < mem_size; offset += wasm::kWasmPageSize) {
961 byte val = mem_start[offset];
962 USE(val);
963 mem_start[offset] = val;
964 }
965 }
966 #endif
967 }
968
969 } // namespace
970
New(Isolate * isolate,MaybeHandle<JSArrayBuffer> maybe_buffer,int32_t maximum)971 Handle<WasmMemoryObject> WasmMemoryObject::New(
972 Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer,
973 int32_t maximum) {
974 // TODO(kschimpf): Do we need to add an argument that defines the
975 // style of memory the user prefers (with/without trap handling), so
976 // that the memory will match the style of the compiled wasm module.
977 // See issue v8:7143
978 Handle<JSFunction> memory_ctor(
979 isolate->native_context()->wasm_memory_constructor(), isolate);
980 auto memory_obj = Handle<WasmMemoryObject>::cast(
981 isolate->factory()->NewJSObject(memory_ctor, TENURED));
982
983 Handle<JSArrayBuffer> buffer;
984 if (!maybe_buffer.ToHandle(&buffer)) {
985 // If no buffer was provided, create a 0-length one.
986 buffer = wasm::SetupArrayBuffer(isolate, nullptr, 0, false);
987 }
988 memory_obj->set_array_buffer(*buffer);
989 memory_obj->set_maximum_pages(maximum);
990
991 return memory_obj;
992 }
993
current_pages()994 uint32_t WasmMemoryObject::current_pages() {
995 uint32_t byte_length;
996 CHECK(array_buffer()->byte_length()->ToUint32(&byte_length));
997 return byte_length / wasm::kWasmPageSize;
998 }
999
has_full_guard_region(Isolate * isolate)1000 bool WasmMemoryObject::has_full_guard_region(Isolate* isolate) {
1001 const wasm::WasmMemoryTracker::AllocationData* allocation =
1002 isolate->wasm_engine()->memory_tracker()->FindAllocationData(
1003 array_buffer()->backing_store());
1004 CHECK_NOT_NULL(allocation);
1005
1006 Address allocation_base =
1007 reinterpret_cast<Address>(allocation->allocation_base);
1008 Address buffer_start = reinterpret_cast<Address>(allocation->buffer_start);
1009
1010 // Return whether the allocation covers every possible Wasm heap index.
1011 //
1012 // We always have the following relationship:
1013 // allocation_base <= buffer_start <= buffer_start + memory_size <=
1014 // allocation_base + allocation_length
1015 // (in other words, the buffer fits within the allocation)
1016 //
1017 // The space between buffer_start + memory_size and allocation_base +
1018 // allocation_length is the guard region. Here we make sure the guard region
1019 // is large enough for any Wasm heap offset.
1020 return buffer_start + wasm::kWasmMaxHeapOffset <=
1021 allocation_base + allocation->allocation_length;
1022 }
1023
AddInstance(Isolate * isolate,Handle<WasmMemoryObject> memory,Handle<WasmInstanceObject> instance)1024 void WasmMemoryObject::AddInstance(Isolate* isolate,
1025 Handle<WasmMemoryObject> memory,
1026 Handle<WasmInstanceObject> instance) {
1027 Handle<WeakArrayList> old_instances =
1028 memory->has_instances()
1029 ? Handle<WeakArrayList>(memory->instances(), isolate)
1030 : handle(ReadOnlyRoots(isolate->heap()).empty_weak_array_list(),
1031 isolate);
1032 Handle<WeakArrayList> new_instances = WeakArrayList::AddToEnd(
1033 isolate, old_instances, MaybeObjectHandle::Weak(instance));
1034 memory->set_instances(*new_instances);
1035 Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate);
1036 SetInstanceMemory(instance, buffer);
1037 }
1038
RemoveInstance(Handle<WasmMemoryObject> memory,Handle<WasmInstanceObject> instance)1039 void WasmMemoryObject::RemoveInstance(Handle<WasmMemoryObject> memory,
1040 Handle<WasmInstanceObject> instance) {
1041 if (memory->has_instances()) {
1042 memory->instances()->RemoveOne(MaybeObjectHandle::Weak(instance));
1043 }
1044 }
1045
1046 // static
Grow(Isolate * isolate,Handle<WasmMemoryObject> memory_object,uint32_t pages)1047 int32_t WasmMemoryObject::Grow(Isolate* isolate,
1048 Handle<WasmMemoryObject> memory_object,
1049 uint32_t pages) {
1050 Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer(), isolate);
1051 if (!old_buffer->is_growable()) return -1;
1052 uint32_t old_size = 0;
1053 CHECK(old_buffer->byte_length()->ToUint32(&old_size));
1054 DCHECK_EQ(0, old_size % wasm::kWasmPageSize);
1055 Handle<JSArrayBuffer> new_buffer;
1056
1057 uint32_t maximum_pages = FLAG_wasm_max_mem_pages;
1058 if (memory_object->has_maximum_pages()) {
1059 maximum_pages = Min(FLAG_wasm_max_mem_pages,
1060 static_cast<uint32_t>(memory_object->maximum_pages()));
1061 }
1062 if (!GrowMemoryBuffer(isolate, old_buffer, pages, maximum_pages)
1063 .ToHandle(&new_buffer)) {
1064 return -1;
1065 }
1066
1067 if (memory_object->has_instances()) {
1068 Handle<WeakArrayList> instances(memory_object->instances(), isolate);
1069 for (int i = 0; i < instances->length(); i++) {
1070 MaybeObject* elem = instances->Get(i);
1071 HeapObject* heap_object;
1072 if (elem->ToWeakHeapObject(&heap_object)) {
1073 Handle<WasmInstanceObject> instance(
1074 WasmInstanceObject::cast(heap_object), isolate);
1075 SetInstanceMemory(instance, new_buffer);
1076 } else {
1077 DCHECK(elem->IsClearedWeakHeapObject());
1078 }
1079 }
1080 }
1081 memory_object->set_array_buffer(*new_buffer);
1082 return old_size / wasm::kWasmPageSize;
1083 }
1084
1085 // static
New(Isolate * isolate,MaybeHandle<JSArrayBuffer> maybe_buffer,wasm::ValueType type,int32_t offset,bool is_mutable)1086 MaybeHandle<WasmGlobalObject> WasmGlobalObject::New(
1087 Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer,
1088 wasm::ValueType type, int32_t offset, bool is_mutable) {
1089 Handle<JSFunction> global_ctor(
1090 isolate->native_context()->wasm_global_constructor(), isolate);
1091 auto global_obj = Handle<WasmGlobalObject>::cast(
1092 isolate->factory()->NewJSObject(global_ctor));
1093
1094 uint32_t type_size = wasm::ValueTypes::ElementSizeInBytes(type);
1095
1096 Handle<JSArrayBuffer> buffer;
1097 if (!maybe_buffer.ToHandle(&buffer)) {
1098 // If no buffer was provided, create one long enough for the given type.
1099 buffer =
1100 isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED);
1101
1102 const bool initialize = true;
1103 if (!JSArrayBuffer::SetupAllocatingData(buffer, isolate, type_size,
1104 initialize)) {
1105 return {};
1106 }
1107 }
1108
1109 // Check that the offset is in bounds.
1110 uint32_t buffer_size = 0;
1111 CHECK(buffer->byte_length()->ToUint32(&buffer_size));
1112 CHECK(offset + type_size <= buffer_size);
1113
1114 global_obj->set_array_buffer(*buffer);
1115 global_obj->set_flags(0);
1116 global_obj->set_type(type);
1117 global_obj->set_offset(offset);
1118 global_obj->set_is_mutable(is_mutable);
1119
1120 return global_obj;
1121 }
1122
clear()1123 void IndirectFunctionTableEntry::clear() {
1124 instance_->indirect_function_table_sig_ids()[index_] = -1;
1125 instance_->indirect_function_table_targets()[index_] = 0;
1126 instance_->indirect_function_table_instances()->set(
1127 index_, ReadOnlyRoots(instance_->GetIsolate()).undefined_value());
1128 }
1129
set(int sig_id,WasmInstanceObject * instance,Address call_target)1130 void IndirectFunctionTableEntry::set(int sig_id, WasmInstanceObject* instance,
1131 Address call_target) {
1132 TRACE_IFT("IFT entry %p[%d] = {sig_id=%d, instance=%p, target=%" PRIuPTR
1133 "}\n",
1134 *instance_, index_, sig_id, instance, call_target);
1135 instance_->indirect_function_table_sig_ids()[index_] = sig_id;
1136 instance_->indirect_function_table_targets()[index_] = call_target;
1137 instance_->indirect_function_table_instances()->set(index_, instance);
1138 }
1139
instance()1140 WasmInstanceObject* IndirectFunctionTableEntry::instance() {
1141 return WasmInstanceObject::cast(
1142 instance_->indirect_function_table_instances()->get(index_));
1143 }
1144
sig_id()1145 int IndirectFunctionTableEntry::sig_id() {
1146 return instance_->indirect_function_table_sig_ids()[index_];
1147 }
1148
target()1149 Address IndirectFunctionTableEntry::target() {
1150 return instance_->indirect_function_table_targets()[index_];
1151 }
1152
set_wasm_to_js(JSReceiver * callable,const wasm::WasmCode * wasm_to_js_wrapper)1153 void ImportedFunctionEntry::set_wasm_to_js(
1154 JSReceiver* callable, const wasm::WasmCode* wasm_to_js_wrapper) {
1155 TRACE_IFT("Import callable %p[%d] = {callable=%p, target=%p}\n", *instance_,
1156 index_, callable, wasm_to_js_wrapper->instructions().start());
1157 DCHECK_EQ(wasm::WasmCode::kWasmToJsWrapper, wasm_to_js_wrapper->kind());
1158 instance_->imported_function_instances()->set(index_, *instance_);
1159 instance_->imported_function_callables()->set(index_, callable);
1160 instance_->imported_function_targets()[index_] =
1161 wasm_to_js_wrapper->instruction_start();
1162 }
1163
set_wasm_to_wasm(WasmInstanceObject * instance,Address call_target)1164 void ImportedFunctionEntry::set_wasm_to_wasm(WasmInstanceObject* instance,
1165 Address call_target) {
1166 TRACE_IFT("Import WASM %p[%d] = {instance=%p, target=%" PRIuPTR "}\n",
1167 *instance_, index_, instance, call_target);
1168 instance_->imported_function_instances()->set(index_, instance);
1169 instance_->imported_function_callables()->set(
1170 index_, instance_->GetReadOnlyRoots().undefined_value());
1171 instance_->imported_function_targets()[index_] = call_target;
1172 }
1173
instance()1174 WasmInstanceObject* ImportedFunctionEntry::instance() {
1175 return WasmInstanceObject::cast(
1176 instance_->imported_function_instances()->get(index_));
1177 }
1178
callable()1179 JSReceiver* ImportedFunctionEntry::callable() {
1180 return JSReceiver::cast(
1181 instance_->imported_function_callables()->get(index_));
1182 }
1183
target()1184 Address ImportedFunctionEntry::target() {
1185 return instance_->imported_function_targets()[index_];
1186 }
1187
is_js_receiver_entry()1188 bool ImportedFunctionEntry::is_js_receiver_entry() {
1189 return instance_->imported_function_callables()->get(index_)->IsJSReceiver();
1190 }
1191
EnsureIndirectFunctionTableWithMinimumSize(Handle<WasmInstanceObject> instance,uint32_t minimum_size)1192 bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
1193 Handle<WasmInstanceObject> instance, uint32_t minimum_size) {
1194 uint32_t old_size = instance->indirect_function_table_size();
1195 if (old_size >= minimum_size) return false; // Nothing to do.
1196
1197 Isolate* isolate = instance->GetIsolate();
1198 HandleScope scope(isolate);
1199 auto native_allocations = GetNativeAllocations(*instance);
1200 native_allocations->resize_indirect_function_table(isolate, instance,
1201 minimum_size);
1202 return true;
1203 }
1204
SetRawMemory(byte * mem_start,size_t mem_size)1205 void WasmInstanceObject::SetRawMemory(byte* mem_start, size_t mem_size) {
1206 CHECK_LE(mem_size, wasm::kV8MaxWasmMemoryBytes);
1207 #if V8_HOST_ARCH_64_BIT
1208 uint64_t mem_mask64 = base::bits::RoundUpToPowerOfTwo64(mem_size) - 1;
1209 set_memory_start(mem_start);
1210 set_memory_size(mem_size);
1211 set_memory_mask(mem_mask64);
1212 #else
1213 // Must handle memory > 2GiB specially.
1214 CHECK_LE(mem_size, size_t{kMaxUInt32});
1215 uint32_t mem_mask32 =
1216 (mem_size > 2 * size_t{GB})
1217 ? 0xFFFFFFFFu
1218 : base::bits::RoundUpToPowerOfTwo32(static_cast<uint32_t>(mem_size)) -
1219 1;
1220 set_memory_start(mem_start);
1221 set_memory_size(mem_size);
1222 set_memory_mask(mem_mask32);
1223 #endif
1224 }
1225
module()1226 const WasmModule* WasmInstanceObject::module() {
1227 return module_object()->module();
1228 }
1229
GetOrCreateDebugInfo(Handle<WasmInstanceObject> instance)1230 Handle<WasmDebugInfo> WasmInstanceObject::GetOrCreateDebugInfo(
1231 Handle<WasmInstanceObject> instance) {
1232 if (instance->has_debug_info()) {
1233 return handle(instance->debug_info(), instance->GetIsolate());
1234 }
1235 Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(instance);
1236 DCHECK(instance->has_debug_info());
1237 return new_info;
1238 }
1239
New(Isolate * isolate,Handle<WasmModuleObject> module_object)1240 Handle<WasmInstanceObject> WasmInstanceObject::New(
1241 Isolate* isolate, Handle<WasmModuleObject> module_object) {
1242 Handle<JSFunction> instance_cons(
1243 isolate->native_context()->wasm_instance_constructor(), isolate);
1244 Handle<JSObject> instance_object =
1245 isolate->factory()->NewJSObject(instance_cons, TENURED);
1246
1247 Handle<WasmInstanceObject> instance(
1248 reinterpret_cast<WasmInstanceObject*>(*instance_object), isolate);
1249
1250 // Initialize the imported function arrays.
1251 auto module = module_object->module();
1252 auto num_imported_functions = module->num_imported_functions;
1253 auto num_imported_mutable_globals = module->num_imported_mutable_globals;
1254 size_t native_allocations_size = EstimateNativeAllocationsSize(module);
1255 auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate(
1256 isolate, native_allocations_size, instance, num_imported_functions,
1257 num_imported_mutable_globals);
1258 instance->set_managed_native_allocations(*native_allocations);
1259
1260 Handle<FixedArray> imported_function_instances =
1261 isolate->factory()->NewFixedArray(num_imported_functions);
1262 instance->set_imported_function_instances(*imported_function_instances);
1263
1264 Handle<FixedArray> imported_function_callables =
1265 isolate->factory()->NewFixedArray(num_imported_functions);
1266 instance->set_imported_function_callables(*imported_function_callables);
1267
1268 Handle<Code> centry_stub = CodeFactory::CEntry(isolate);
1269 instance->set_centry_stub(*centry_stub);
1270
1271 instance->SetRawMemory(nullptr, 0);
1272 instance->set_roots_array_address(
1273 reinterpret_cast<Address>(isolate->heap()->roots_array_start()));
1274 instance->set_stack_limit_address(
1275 isolate->stack_guard()->address_of_jslimit());
1276 instance->set_real_stack_limit_address(
1277 isolate->stack_guard()->address_of_real_jslimit());
1278 instance->set_globals_start(nullptr);
1279 instance->set_indirect_function_table_size(0);
1280 instance->set_indirect_function_table_sig_ids(nullptr);
1281 instance->set_indirect_function_table_targets(nullptr);
1282 instance->set_native_context(*isolate->native_context());
1283 instance->set_module_object(*module_object);
1284 instance->set_undefined_value(ReadOnlyRoots(isolate).undefined_value());
1285 instance->set_null_value(ReadOnlyRoots(isolate).null_value());
1286 instance->set_jump_table_start(
1287 module_object->native_module()->jump_table_start());
1288
1289 // Insert the new instance into the modules weak list of instances.
1290 // TODO(mstarzinger): Allow to reuse holes in the {WeakArrayList} below.
1291 Handle<WeakArrayList> weak_instance_list(module_object->weak_instance_list(),
1292 isolate);
1293 weak_instance_list = WeakArrayList::AddToEnd(
1294 isolate, weak_instance_list, MaybeObjectHandle::Weak(instance));
1295 module_object->set_weak_instance_list(*weak_instance_list);
1296
1297 return instance;
1298 }
1299
1300 namespace {
InstanceFinalizer(const v8::WeakCallbackInfo<void> & data)1301 void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
1302 DisallowHeapAllocation no_gc;
1303 JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
1304 WasmInstanceObject* instance = reinterpret_cast<WasmInstanceObject*>(*p);
1305 Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
1306 // If a link to shared memory instances exists, update the list of memory
1307 // instances before the instance is destroyed.
1308 TRACE("Finalizing instance of %p {\n",
1309 instance->module_object()->native_module());
1310
1311 // Since the order of finalizers is not guaranteed, it can be the case
1312 // that {instance->compiled_module()->module()}, which is a
1313 // {Managed<WasmModule>} has been collected earlier in this GC cycle.
1314 // Weak references to this instance won't be cleared until
1315 // the next GC cycle, so we need to manually break some links (such as
1316 // the weak references from {WasmMemoryObject::instances}.
1317 if (instance->has_memory_object()) {
1318 WasmMemoryObject::RemoveInstance(handle(instance->memory_object(), isolate),
1319 handle(instance, isolate));
1320 }
1321
1322 // Free raw C++ memory associated with the instance.
1323 GetNativeAllocations(instance)->free();
1324
1325 GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
1326 TRACE("}\n");
1327 }
1328
1329 } // namespace
1330
InstallFinalizer(Isolate * isolate,Handle<WasmInstanceObject> instance)1331 void WasmInstanceObject::InstallFinalizer(Isolate* isolate,
1332 Handle<WasmInstanceObject> instance) {
1333 Handle<Object> global_handle = isolate->global_handles()->Create(*instance);
1334 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
1335 InstanceFinalizer, v8::WeakCallbackType::kFinalizer);
1336 }
1337
GetCallTarget(uint32_t func_index)1338 Address WasmInstanceObject::GetCallTarget(uint32_t func_index) {
1339 wasm::NativeModule* native_module = module_object()->native_module();
1340 if (func_index < native_module->num_imported_functions()) {
1341 return imported_function_targets()[func_index];
1342 }
1343 return native_module->GetCallTargetForFunction(func_index);
1344 }
1345
IsWasmExportedFunction(Object * object)1346 bool WasmExportedFunction::IsWasmExportedFunction(Object* object) {
1347 if (!object->IsJSFunction()) return false;
1348 JSFunction* js_function = JSFunction::cast(object);
1349 if (Code::JS_TO_WASM_FUNCTION != js_function->code()->kind()) return false;
1350 DCHECK(js_function->shared()->HasWasmExportedFunctionData());
1351 return true;
1352 }
1353
cast(Object * object)1354 WasmExportedFunction* WasmExportedFunction::cast(Object* object) {
1355 DCHECK(IsWasmExportedFunction(object));
1356 return reinterpret_cast<WasmExportedFunction*>(object);
1357 }
1358
instance()1359 WasmInstanceObject* WasmExportedFunction::instance() {
1360 return shared()->wasm_exported_function_data()->instance();
1361 }
1362
function_index()1363 int WasmExportedFunction::function_index() {
1364 return shared()->wasm_exported_function_data()->function_index();
1365 }
1366
New(Isolate * isolate,Handle<WasmInstanceObject> instance,MaybeHandle<String> maybe_name,int func_index,int arity,Handle<Code> export_wrapper)1367 Handle<WasmExportedFunction> WasmExportedFunction::New(
1368 Isolate* isolate, Handle<WasmInstanceObject> instance,
1369 MaybeHandle<String> maybe_name, int func_index, int arity,
1370 Handle<Code> export_wrapper) {
1371 DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
1372 int num_imported_functions = instance->module()->num_imported_functions;
1373 int jump_table_offset = -1;
1374 if (func_index >= num_imported_functions) {
1375 ptrdiff_t jump_table_diff =
1376 instance->module_object()->native_module()->jump_table_offset(
1377 func_index);
1378 DCHECK(jump_table_diff >= 0 && jump_table_diff <= INT_MAX);
1379 jump_table_offset = static_cast<int>(jump_table_diff);
1380 }
1381 Handle<WasmExportedFunctionData> function_data =
1382 Handle<WasmExportedFunctionData>::cast(isolate->factory()->NewStruct(
1383 WASM_EXPORTED_FUNCTION_DATA_TYPE, TENURED));
1384 function_data->set_wrapper_code(*export_wrapper);
1385 function_data->set_instance(*instance);
1386 function_data->set_jump_table_offset(jump_table_offset);
1387 function_data->set_function_index(func_index);
1388 Handle<String> name;
1389 if (!maybe_name.ToHandle(&name)) {
1390 EmbeddedVector<char, 16> buffer;
1391 int length = SNPrintF(buffer, "%d", func_index);
1392 name = isolate->factory()
1393 ->NewStringFromOneByte(
1394 Vector<uint8_t>::cast(buffer.SubVector(0, length)))
1395 .ToHandleChecked();
1396 }
1397 NewFunctionArgs args = NewFunctionArgs::ForWasm(
1398 name, function_data, isolate->sloppy_function_without_prototype_map());
1399 Handle<JSFunction> js_function = isolate->factory()->NewFunction(args);
1400 // According to the spec, exported functions should not have a [[Construct]]
1401 // method.
1402 DCHECK(!js_function->IsConstructor());
1403 js_function->shared()->set_length(arity);
1404 js_function->shared()->set_internal_formal_parameter_count(arity);
1405 return Handle<WasmExportedFunction>::cast(js_function);
1406 }
1407
GetWasmCallTarget()1408 Address WasmExportedFunction::GetWasmCallTarget() {
1409 return instance()->GetCallTarget(function_index());
1410 }
1411
1412 #undef TRACE
1413 #undef TRACE_IFT
1414 } // namespace internal
1415 } // namespace v8
1416