Index: src/wasm/wasm-objects.cc |
diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc |
index 3c0139242d23b48b495e91461bfb5b7cf1220227..2f0cdce23d598b006adebbc220681724572ea46e 100644 |
--- a/src/wasm/wasm-objects.cc |
+++ b/src/wasm/wasm-objects.cc |
@@ -5,6 +5,7 @@ |
#include "src/wasm/wasm-objects.h" |
#include "src/utils.h" |
+#include "src/base/iterator.h" |
#include "src/debug/debug-interface.h" |
#include "src/wasm/module-decoder.h" |
#include "src/wasm/wasm-module.h" |
@@ -37,6 +38,15 @@ using namespace v8::internal::wasm; |
return !getter(field)->IsUndefined(GetIsolate()); \ |
} |
+#define DEFINE_OPTIONAL_GETTER0(getter, Container, name, field, type) \ |
+ DEFINE_GETTER0(getter, Container, name, field, type) \ |
+ bool Container::has_##name() { \ |
+ return !getter(field)->IsUndefined(GetIsolate()); \ |
+ } |
+ |
+#define DEFINE_GETTER0(getter, Container, name, field, type) \ |
+ type* Container::name() { return type::cast(getter(field)); } |
+ |
#define DEFINE_OBJ_GETTER(Container, name, field, type) \ |
DEFINE_GETTER0(GetInternalField, Container, name, field, type) |
#define DEFINE_OBJ_ACCESSORS(Container, name, field, type) \ |
@@ -51,6 +61,8 @@ using namespace v8::internal::wasm; |
DEFINE_ACCESSORS0(get, set, Container, name, field, type) |
#define DEFINE_OPTIONAL_ARR_ACCESSORS(Container, name, field, type) \ |
DEFINE_OPTIONAL_ACCESSORS0(get, set, Container, name, field, type) |
+#define DEFINE_OPTIONAL_ARR_GETTER(Container, name, field, type) \ |
+ DEFINE_OPTIONAL_GETTER0(get, Container, name, field, type) |
namespace { |
@@ -78,6 +90,130 @@ int32_t SafeInt32(Object* value) { |
return static_cast<int32_t>(num->value()); |
} |
+// An iterator that returns first the module itself, then all modules linked via |
+// next, then all linked via prev. |
+class CompiledModulesIterator |
+ : public std::iterator<std::input_iterator_tag, |
+ Handle<WasmCompiledModule>> { |
+ public: |
+ CompiledModulesIterator(Isolate* isolate, |
+ Handle<WasmCompiledModule> start_module, bool at_end) |
+ : isolate_(isolate), |
+ start_module_(start_module), |
+ current_(at_end ? Handle<WasmCompiledModule>::null() : start_module) {} |
+ |
+ Handle<WasmCompiledModule> operator*() const { |
+ DCHECK(!current_.is_null()); |
+ return current_; |
+ } |
+ |
+ void operator++() { Advance(); } |
+ |
+ bool operator!=(const CompiledModulesIterator& other) { |
+ DCHECK(start_module_.is_identical_to(other.start_module_)); |
+ return !current_.is_identical_to(other.current_); |
+ } |
+ |
+ private: |
+ void Advance() { |
+ DCHECK(!current_.is_null()); |
+ if (!is_backwards_) { |
+ if (current_->has_weak_next_instance()) { |
+ WeakCell* weak_next = current_->ptr_to_weak_next_instance(); |
+ if (!weak_next->cleared()) { |
+ current_ = |
+ handle(WasmCompiledModule::cast(weak_next->value()), isolate_); |
+ return; |
+ } |
+ } |
+ // No more modules in next-links, now try the previous-links. |
+ is_backwards_ = true; |
+ current_ = start_module_; |
+ } |
+ if (current_->has_weak_prev_instance()) { |
+ WeakCell* weak_prev = current_->ptr_to_weak_prev_instance(); |
+ if (!weak_prev->cleared()) { |
+ current_ = |
+ handle(WasmCompiledModule::cast(weak_prev->value()), isolate_); |
+ return; |
+ } |
+ } |
+ current_ = Handle<WasmCompiledModule>::null(); |
+ } |
+ |
+ friend class CompiledModuleInstancesIterator; |
+ Isolate* isolate_; |
+ Handle<WasmCompiledModule> start_module_; |
+ Handle<WasmCompiledModule> current_; |
+ bool is_backwards_ = false; |
+}; |
+ |
+// An iterator based on the CompiledModulesIterator, but it returns all live |
+// instances, not the WasmCompiledModules itself. |
+class CompiledModuleInstancesIterator |
+ : public std::iterator<std::input_iterator_tag, |
+ Handle<WasmInstanceObject>> { |
+ public: |
+ CompiledModuleInstancesIterator(Isolate* isolate, |
+ Handle<WasmCompiledModule> start_module, |
+ bool at_end) |
+ : it(isolate, start_module, at_end) { |
+ while (NeedToAdvance()) ++it; |
+ } |
+ |
+ Handle<WasmInstanceObject> operator*() { |
+ return handle( |
+ WasmInstanceObject::cast((*it)->weak_owning_instance()->value()), |
+ it.isolate_); |
+ } |
+ |
+ void operator++() { |
+ do { |
+ ++it; |
+ } while (NeedToAdvance()); |
+ } |
+ |
+ bool operator!=(const CompiledModuleInstancesIterator& other) { |
+ return it != other.it; |
+ } |
+ |
+ private: |
+ bool NeedToAdvance() { |
+ return !it.current_.is_null() && |
+ (!it.current_->has_weak_owning_instance() || |
+ it.current_->ptr_to_weak_owning_instance()->cleared()); |
+ } |
+ CompiledModulesIterator it; |
+}; |
+ |
+v8::base::iterator_range<CompiledModuleInstancesIterator> |
+iterate_compiled_module_instance_chain( |
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module) { |
+ return {CompiledModuleInstancesIterator(isolate, compiled_module, false), |
+ CompiledModuleInstancesIterator(isolate, compiled_module, true)}; |
+} |
+ |
+#ifdef DEBUG |
+bool IsBreakablePosition(Handle<WasmCompiledModule> compiled_module, |
+ int func_index, int offset_in_func) { |
+ DisallowHeapAllocation no_gc; |
+ AccountingAllocator alloc; |
+ Zone tmp(&alloc, ZONE_NAME); |
+ BodyLocalDecls locals(&tmp); |
+ const byte* module_start = compiled_module->module_bytes()->GetChars(); |
+ WasmFunction& func = compiled_module->module()->functions[func_index]; |
+ BytecodeIterator iterator(module_start + func.code_start_offset, |
+ module_start + func.code_end_offset, &locals); |
+ DCHECK_LT(0, locals.encoded_size); |
+ uint32_t found_position = 0; |
+ for (uint32_t offset : iterator.offsets()) { |
+ if (offset > static_cast<uint32_t>(offset_in_func)) break; |
+ if (offset == static_cast<uint32_t>(offset_in_func)) return true; |
+ } |
+ return false; |
+} |
+#endif // DEBUG |
+ |
} // namespace |
Handle<WasmModuleObject> WasmModuleObject::New( |
@@ -383,6 +519,9 @@ bool WasmSharedModuleData::IsWasmSharedModuleData(Object* object) { |
if (!arr->get(kAsmJsOffsetTable)->IsUndefined(isolate) && |
!arr->get(kAsmJsOffsetTable)->IsByteArray()) |
return false; |
+ if (!arr->get(kBreakPointInfos)->IsUndefined(isolate) && |
+ !arr->get(kBreakPointInfos)->IsFixedArray()) |
+ return false; |
return true; |
} |
@@ -400,6 +539,8 @@ DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, module_bytes, kModuleBytes, |
DEFINE_ARR_GETTER(WasmSharedModuleData, script, kScript, Script); |
DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, asm_js_offset_table, |
kAsmJsOffsetTable, ByteArray); |
+DEFINE_OPTIONAL_ARR_GETTER(WasmSharedModuleData, breakpoint_infos, |
+ kBreakPointInfos, FixedArray); |
Handle<WasmSharedModuleData> WasmSharedModuleData::New( |
Isolate* isolate, Handle<Foreign> module_wrapper, |
@@ -459,6 +600,126 @@ void WasmSharedModuleData::RecreateModuleWrapper( |
DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared)); |
} |
+namespace { |
+ |
+int GetBreakpointPos(Isolate* isolate, Object* break_point_info_or_undef) { |
+ if (break_point_info_or_undef->IsUndefined(isolate)) return kMaxInt; |
+ return BreakPointInfo::cast(break_point_info_or_undef)->source_position(); |
+} |
+ |
+int FindBreakpointInfoInsertPos(Isolate* isolate, |
+ Handle<FixedArray> breakpoint_infos, |
+ int position) { |
+ // Find insert location via binary search, taking care of undefined values on |
+ // the right. Position is always greater than zero. |
+ DCHECK_LT(0, position); |
+ |
+ int left = 0; // inclusive |
+ int right = breakpoint_infos->length(); // exclusive |
+ while (right - left > 1) { |
+ int mid = left + (right - left) / 2; |
+ Object* mid_obj = breakpoint_infos->get(mid); |
+ if (GetBreakpointPos(isolate, mid_obj) <= position) { |
+ left = mid; |
+ } else { |
+ right = mid; |
+ } |
+ } |
+ |
+ int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left)); |
+ return left_pos < position ? left + 1 : left; |
+} |
+ |
+} // namespace |
+ |
+void WasmSharedModuleData::AddBreakpoint(Handle<WasmSharedModuleData> shared, |
+ int position, |
+ Handle<Object> break_point_object) { |
+ Isolate* isolate = shared->GetIsolate(); |
+ Handle<FixedArray> breakpoint_infos; |
+ if (shared->has_breakpoint_infos()) { |
+ breakpoint_infos = handle(shared->breakpoint_infos(), isolate); |
+ } else { |
+ breakpoint_infos = isolate->factory()->NewFixedArray(4, TENURED); |
+ shared->set(kBreakPointInfos, *breakpoint_infos); |
+ } |
+ |
+ int insert_pos = |
+ FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position); |
+ |
+ // If a BreakPointInfo object already exists for this position, add the new |
+ // breakpoint object and return. |
+ if (insert_pos < breakpoint_infos->length() && |
+ GetBreakpointPos(isolate, breakpoint_infos->get(insert_pos)) == |
+ position) { |
+ Handle<BreakPointInfo> old_info( |
+ BreakPointInfo::cast(breakpoint_infos->get(insert_pos)), isolate); |
+ BreakPointInfo::SetBreakPoint(old_info, break_point_object); |
+ return; |
+ } |
+ |
+ // Enlarge break positions array if necessary. |
+ bool need_realloc = !breakpoint_infos->get(breakpoint_infos->length() - 1) |
+ ->IsUndefined(isolate); |
+ Handle<FixedArray> new_breakpoint_infos = breakpoint_infos; |
+ if (need_realloc) { |
+ new_breakpoint_infos = isolate->factory()->NewFixedArray( |
+ 2 * breakpoint_infos->length(), TENURED); |
+ shared->set(kBreakPointInfos, *new_breakpoint_infos); |
+ // Copy over the entries [0, insert_pos). |
+ for (int i = 0; i < insert_pos; ++i) |
+ new_breakpoint_infos->set(i, breakpoint_infos->get(i)); |
+ } |
+ |
+ // Move elements [insert_pos+1, ...] up by one. |
+ for (int i = insert_pos + 1; i < breakpoint_infos->length(); ++i) { |
+ Object* entry = breakpoint_infos->get(i); |
+ if (entry->IsUndefined(isolate)) break; |
+ new_breakpoint_infos->set(i + 1, entry); |
+ } |
+ |
+ // Generate new BreakpointInfo. |
+ Handle<BreakPointInfo> breakpoint_info = |
+ isolate->factory()->NewBreakPointInfo(position); |
+ BreakPointInfo::SetBreakPoint(breakpoint_info, break_point_object); |
+ |
+ // Now insert new position at insert_pos. |
+ new_breakpoint_infos->set(insert_pos, *breakpoint_info); |
+} |
+ |
+void WasmSharedModuleData::SetBreakpointsOnNewInstance( |
+ Handle<WasmSharedModuleData> shared, Handle<WasmInstanceObject> instance) { |
+ if (!shared->has_breakpoint_infos()) return; |
+ Isolate* isolate = shared->GetIsolate(); |
+ Handle<WasmCompiledModule> compiled_module(instance->compiled_module(), |
+ isolate); |
+ Handle<WasmDebugInfo> debug_info = |
+ WasmInstanceObject::GetOrCreateDebugInfo(instance); |
+ |
+ Handle<FixedArray> breakpoint_infos(shared->breakpoint_infos(), isolate); |
+ // If the array exists, it should not be empty. |
+ DCHECK_LT(0, breakpoint_infos->length()); |
+ |
+ for (int i = 0, e = breakpoint_infos->length(); i < e; ++i) { |
+ Handle<Object> obj(breakpoint_infos->get(i), isolate); |
+ if (obj->IsUndefined(isolate)) { |
+ for (; i < e; ++i) { |
+ DCHECK(breakpoint_infos->get(i)->IsUndefined(isolate)); |
+ } |
+ break; |
+ } |
+ Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj); |
+ int position = breakpoint_info->source_position(); |
+ |
+ // Find the function for this breakpoint, and set the breakpoint. |
+ int func_index = compiled_module->GetContainingFunction(position); |
+ DCHECK_LE(0, func_index); |
+ WasmFunction& func = compiled_module->module()->functions[func_index]; |
+ int offset_in_func = position - func.code_start_offset; |
+ WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func); |
+ } |
+} |
+ |
Handle<WasmCompiledModule> WasmCompiledModule::New( |
Isolate* isolate, Handle<WasmSharedModuleData> shared) { |
Handle<FixedArray> ret = |
@@ -832,6 +1093,37 @@ bool WasmCompiledModule::GetPossibleBreakpoints( |
return true; |
} |
+bool WasmCompiledModule::SetBreakPoint( |
+ Handle<WasmCompiledModule> compiled_module, int* position, |
+ Handle<Object> break_point_object) { |
+ Isolate* isolate = compiled_module->GetIsolate(); |
+ |
+ // Find the function for this breakpoint. |
+ int func_index = compiled_module->GetContainingFunction(*position); |
+ if (func_index < 0) return false; |
+ WasmFunction& func = compiled_module->module()->functions[func_index]; |
+ int offset_in_func = *position - func.code_start_offset; |
+ |
+ // According to the current design, we should only be called with valid |
+ // breakable positions. |
+ DCHECK(IsBreakablePosition(compiled_module, func_index, offset_in_func)); |
+ |
+ // Insert new break point into break_positions of shared module data. |
+ WasmSharedModuleData::AddBreakpoint(compiled_module->shared(), *position, |
+ break_point_object); |
+ |
+ // Iterate over all instances of this module and tell them to set this new |
+ // breakpoint. |
+ for (Handle<WasmInstanceObject> instance : |
+ iterate_compiled_module_instance_chain(isolate, compiled_module)) { |
+ Handle<WasmDebugInfo> debug_info = |
+ WasmInstanceObject::GetOrCreateDebugInfo(instance); |
+ WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func); |
+ } |
+ |
+ return true; |
+} |
+ |
Handle<WasmInstanceWrapper> WasmInstanceWrapper::New( |
Isolate* isolate, Handle<WasmInstanceObject> instance) { |
Handle<FixedArray> array = |