• 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 "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