aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/wasm-objects.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/wasm-objects.cc')
-rw-r--r--deps/v8/src/wasm/wasm-objects.cc678
1 files changed, 592 insertions, 86 deletions
diff --git a/deps/v8/src/wasm/wasm-objects.cc b/deps/v8/src/wasm/wasm-objects.cc
index 68f66d246d..3f694c579f 100644
--- a/deps/v8/src/wasm/wasm-objects.cc
+++ b/deps/v8/src/wasm/wasm-objects.cc
@@ -3,7 +3,12 @@
// found in the LICENSE file.
#include "src/wasm/wasm-objects.h"
+#include "src/utils.h"
+
+#include "src/debug/debug-interface.h"
+#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module.h"
+#include "src/wasm/wasm-text.h"
#define TRACE(...) \
do { \
@@ -18,29 +23,38 @@
using namespace v8::internal;
using namespace v8::internal::wasm;
-#define DEFINE_ACCESSORS(Container, name, field, type) \
- type* Container::get_##name() { \
- return type::cast(GetInternalField(field)); \
- } \
- void Container::set_##name(type* value) { \
- return SetInternalField(field, value); \
- }
+#define DEFINE_GETTER0(getter, Container, name, field, type) \
+ type* Container::name() { return type::cast(getter(field)); }
-#define DEFINE_OPTIONAL_ACCESSORS(Container, name, field, type) \
- bool Container::has_##name() { \
- return !GetInternalField(field)->IsUndefined(GetIsolate()); \
- } \
- type* Container::get_##name() { \
- return type::cast(GetInternalField(field)); \
- } \
- void Container::set_##name(type* value) { \
- return SetInternalField(field, value); \
- }
+#define DEFINE_ACCESSORS0(getter, setter, Container, name, field, type) \
+ DEFINE_GETTER0(getter, Container, name, field, type) \
+ void Container::set_##name(type* value) { return setter(field, value); }
-#define DEFINE_GETTER(Container, name, field, type) \
- type* Container::get_##name() { return type::cast(GetInternalField(field)); }
+#define DEFINE_OPTIONAL_ACCESSORS0(getter, setter, Container, name, field, \
+ type) \
+ DEFINE_ACCESSORS0(getter, setter, Container, name, field, type) \
+ bool Container::has_##name() { \
+ return !getter(field)->IsUndefined(GetIsolate()); \
+ }
-static uint32_t SafeUint32(Object* value) {
+#define DEFINE_OBJ_GETTER(Container, name, field, type) \
+ DEFINE_GETTER0(GetInternalField, Container, name, field, type)
+#define DEFINE_OBJ_ACCESSORS(Container, name, field, type) \
+ DEFINE_ACCESSORS0(GetInternalField, SetInternalField, Container, name, \
+ field, type)
+#define DEFINE_OPTIONAL_OBJ_ACCESSORS(Container, name, field, type) \
+ DEFINE_OPTIONAL_ACCESSORS0(GetInternalField, SetInternalField, Container, \
+ name, field, type)
+#define DEFINE_ARR_GETTER(Container, name, field, type) \
+ DEFINE_GETTER0(get, Container, name, field, type)
+#define DEFINE_ARR_ACCESSORS(Container, name, field, type) \
+ 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)
+
+namespace {
+
+uint32_t SafeUint32(Object* value) {
if (value->IsSmi()) {
int32_t val = Smi::cast(value)->value();
CHECK_GE(val, 0);
@@ -49,21 +63,23 @@ static uint32_t SafeUint32(Object* value) {
DCHECK(value->IsHeapNumber());
HeapNumber* num = HeapNumber::cast(value);
CHECK_GE(num->value(), 0.0);
- CHECK_LE(num->value(), static_cast<double>(kMaxUInt32));
+ CHECK_LE(num->value(), kMaxUInt32);
return static_cast<uint32_t>(num->value());
}
-static int32_t SafeInt32(Object* value) {
+int32_t SafeInt32(Object* value) {
if (value->IsSmi()) {
return Smi::cast(value)->value();
}
DCHECK(value->IsHeapNumber());
HeapNumber* num = HeapNumber::cast(value);
- CHECK_GE(num->value(), static_cast<double>(Smi::kMinValue));
- CHECK_LE(num->value(), static_cast<double>(Smi::kMaxValue));
+ CHECK_GE(num->value(), Smi::kMinValue);
+ CHECK_LE(num->value(), Smi::kMaxValue);
return static_cast<int32_t>(num->value());
}
+} // namespace
+
Handle<WasmModuleObject> WasmModuleObject::New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
ModuleOrigin origin = compiled_module->module()->origin;
@@ -97,8 +113,16 @@ WasmModuleObject* WasmModuleObject::cast(Object* object) {
return reinterpret_cast<WasmModuleObject*>(object);
}
+bool WasmModuleObject::IsWasmModuleObject(Object* object) {
+ return object->IsJSObject() &&
+ JSObject::cast(object)->GetInternalFieldCount() == kFieldCount;
+}
+
+DEFINE_OBJ_GETTER(WasmModuleObject, compiled_module, kCompiledModule,
+ WasmCompiledModule)
+
Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
- uint32_t maximum,
+ int64_t maximum,
Handle<FixedArray>* js_functions) {
Handle<JSFunction> table_ctor(
isolate->native_context()->wasm_table_constructor());
@@ -109,8 +133,8 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
(*js_functions)->set(i, null);
}
table_obj->SetInternalField(kFunctions, *(*js_functions));
- table_obj->SetInternalField(kMaximum,
- static_cast<Object*>(Smi::FromInt(maximum)));
+ Handle<Object> max = isolate->factory()->NewNumber(maximum);
+ table_obj->SetInternalField(kMaximum, *max);
Handle<FixedArray> dispatch_tables = isolate->factory()->NewFixedArray(0);
table_obj->SetInternalField(kDispatchTables, *dispatch_tables);
@@ -119,27 +143,28 @@ Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
return Handle<WasmTableObject>::cast(table_obj);
}
-DEFINE_GETTER(WasmTableObject, dispatch_tables, kDispatchTables, FixedArray)
+DEFINE_OBJ_GETTER(WasmTableObject, dispatch_tables, kDispatchTables, FixedArray)
Handle<FixedArray> WasmTableObject::AddDispatchTable(
Isolate* isolate, Handle<WasmTableObject> table_obj,
Handle<WasmInstanceObject> instance, int table_index,
- Handle<FixedArray> dispatch_table) {
+ Handle<FixedArray> function_table, Handle<FixedArray> signature_table) {
Handle<FixedArray> dispatch_tables(
FixedArray::cast(table_obj->GetInternalField(kDispatchTables)), isolate);
- DCHECK_EQ(0, dispatch_tables->length() % 3);
+ DCHECK_EQ(0, dispatch_tables->length() % 4);
if (instance.is_null()) return dispatch_tables;
// TODO(titzer): use weak cells here to avoid leaking instances.
// Grow the dispatch table and add a new triple at the end.
Handle<FixedArray> new_dispatch_tables =
- isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 3);
+ isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 4);
new_dispatch_tables->set(dispatch_tables->length() + 0, *instance);
new_dispatch_tables->set(dispatch_tables->length() + 1,
Smi::FromInt(table_index));
- new_dispatch_tables->set(dispatch_tables->length() + 2, *dispatch_table);
+ new_dispatch_tables->set(dispatch_tables->length() + 2, *function_table);
+ new_dispatch_tables->set(dispatch_tables->length() + 3, *signature_table);
table_obj->SetInternalField(WasmTableObject::kDispatchTables,
*new_dispatch_tables);
@@ -147,12 +172,16 @@ Handle<FixedArray> WasmTableObject::AddDispatchTable(
return new_dispatch_tables;
}
-DEFINE_ACCESSORS(WasmTableObject, functions, kFunctions, FixedArray)
+DEFINE_OBJ_ACCESSORS(WasmTableObject, functions, kFunctions, FixedArray)
+
+uint32_t WasmTableObject::current_length() { return functions()->length(); }
-uint32_t WasmTableObject::current_length() { return get_functions()->length(); }
+bool WasmTableObject::has_maximum_length() {
+ return GetInternalField(kMaximum)->Number() >= 0;
+}
-uint32_t WasmTableObject::maximum_length() {
- return SafeUint32(GetInternalField(kMaximum));
+int64_t WasmTableObject::maximum_length() {
+ return static_cast<int64_t>(GetInternalField(kMaximum)->Number());
}
WasmTableObject* WasmTableObject::cast(Object* object) {
@@ -161,28 +190,42 @@ WasmTableObject* WasmTableObject::cast(Object* object) {
return reinterpret_cast<WasmTableObject*>(object);
}
+void WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
+ uint32_t count) {
+ Handle<FixedArray> dispatch_tables(table->dispatch_tables());
+ wasm::GrowDispatchTables(isolate, dispatch_tables,
+ table->functions()->length(), count);
+}
+
Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
Handle<JSArrayBuffer> buffer,
- int maximum) {
+ int32_t maximum) {
Handle<JSFunction> memory_ctor(
isolate->native_context()->wasm_memory_constructor());
- Handle<JSObject> memory_obj = isolate->factory()->NewJSObject(memory_ctor);
+ Handle<JSObject> memory_obj =
+ isolate->factory()->NewJSObject(memory_ctor, TENURED);
memory_obj->SetInternalField(kArrayBuffer, *buffer);
- memory_obj->SetInternalField(kMaximum,
- static_cast<Object*>(Smi::FromInt(maximum)));
+ Handle<Object> max = isolate->factory()->NewNumber(maximum);
+ memory_obj->SetInternalField(kMaximum, *max);
Handle<Symbol> memory_sym(isolate->native_context()->wasm_memory_sym());
Object::SetProperty(memory_obj, memory_sym, memory_obj, STRICT).Check();
return Handle<WasmMemoryObject>::cast(memory_obj);
}
-DEFINE_ACCESSORS(WasmMemoryObject, buffer, kArrayBuffer, JSArrayBuffer)
+DEFINE_OBJ_ACCESSORS(WasmMemoryObject, buffer, kArrayBuffer, JSArrayBuffer)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmMemoryObject, instances_link, kInstancesLink,
+ WasmInstanceWrapper)
uint32_t WasmMemoryObject::current_pages() {
- return SafeUint32(get_buffer()->byte_length()) / wasm::WasmModule::kPageSize;
+ return SafeUint32(buffer()->byte_length()) / wasm::WasmModule::kPageSize;
+}
+
+bool WasmMemoryObject::has_maximum_pages() {
+ return GetInternalField(kMaximum)->Number() >= 0;
}
int32_t WasmMemoryObject::maximum_pages() {
- return SafeInt32(GetInternalField(kMaximum));
+ return static_cast<int32_t>(GetInternalField(kMaximum)->Number());
}
WasmMemoryObject* WasmMemoryObject::cast(Object* object) {
@@ -191,31 +234,50 @@ WasmMemoryObject* WasmMemoryObject::cast(Object* object) {
return reinterpret_cast<WasmMemoryObject*>(object);
}
-void WasmMemoryObject::AddInstance(WasmInstanceObject* instance) {
- // TODO(gdeepti): This should be a weak list of instance objects
- // for instances that share memory.
- SetInternalField(kInstance, instance);
+void WasmMemoryObject::AddInstance(Isolate* isolate,
+ Handle<WasmInstanceObject> instance) {
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ handle(instance->instance_wrapper());
+ if (has_instances_link()) {
+ Handle<WasmInstanceWrapper> current_wrapper(instances_link());
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*current_wrapper));
+ DCHECK(!current_wrapper->has_previous());
+ instance_wrapper->set_next_wrapper(*current_wrapper);
+ current_wrapper->set_previous_wrapper(*instance_wrapper);
+ }
+ set_instances_link(*instance_wrapper);
+}
+
+void WasmMemoryObject::ResetInstancesLink(Isolate* isolate) {
+ Handle<Object> undefined = isolate->factory()->undefined_value();
+ SetInternalField(kInstancesLink, *undefined);
}
-DEFINE_ACCESSORS(WasmInstanceObject, compiled_module, kCompiledModule,
- WasmCompiledModule)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, globals_buffer,
- kGlobalsArrayBuffer, JSArrayBuffer)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, memory_buffer, kMemoryArrayBuffer,
- JSArrayBuffer)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, memory_object, kMemoryObject,
- WasmMemoryObject)
-DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, kDebugInfo,
- WasmDebugInfo)
+DEFINE_OBJ_ACCESSORS(WasmInstanceObject, compiled_module, kCompiledModule,
+ WasmCompiledModule)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, globals_buffer,
+ kGlobalsArrayBuffer, JSArrayBuffer)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, memory_buffer,
+ kMemoryArrayBuffer, JSArrayBuffer)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, memory_object, kMemoryObject,
+ WasmMemoryObject)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, debug_info, kDebugInfo,
+ WasmDebugInfo)
+DEFINE_OPTIONAL_OBJ_ACCESSORS(WasmInstanceObject, instance_wrapper,
+ kWasmMemInstanceWrapper, WasmInstanceWrapper)
WasmModuleObject* WasmInstanceObject::module_object() {
- return WasmModuleObject::cast(*get_compiled_module()->wasm_module());
+ return *compiled_module()->wasm_module();
}
-WasmModule* WasmInstanceObject::module() {
- return reinterpret_cast<WasmModuleWrapper*>(
- *get_compiled_module()->module_wrapper())
- ->get();
+WasmModule* WasmInstanceObject::module() { return compiled_module()->module(); }
+
+Handle<WasmDebugInfo> WasmInstanceObject::GetOrCreateDebugInfo(
+ Handle<WasmInstanceObject> instance) {
+ if (instance->has_debug_info()) return handle(instance->debug_info());
+ Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(instance);
+ instance->set_debug_info(*new_info);
+ return new_info;
}
WasmInstanceObject* WasmInstanceObject::cast(Object* object) {
@@ -224,7 +286,6 @@ WasmInstanceObject* WasmInstanceObject::cast(Object* object) {
}
bool WasmInstanceObject::IsWasmInstanceObject(Object* object) {
- if (!object->IsObject()) return false;
if (!object->IsJSObject()) return false;
JSObject* obj = JSObject::cast(object);
@@ -246,15 +307,21 @@ bool WasmInstanceObject::IsWasmInstanceObject(Object* object) {
Handle<WasmInstanceObject> WasmInstanceObject::New(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
- Handle<Map> map = isolate->factory()->NewMap(
- JS_OBJECT_TYPE, JSObject::kHeaderSize + kFieldCount * kPointerSize);
+ Handle<JSFunction> instance_cons(
+ isolate->native_context()->wasm_instance_constructor());
+ Handle<JSObject> instance_object =
+ isolate->factory()->NewJSObject(instance_cons, TENURED);
+ Handle<Symbol> instance_sym(isolate->native_context()->wasm_instance_sym());
+ Object::SetProperty(instance_object, instance_sym, instance_object, STRICT)
+ .Check();
Handle<WasmInstanceObject> instance(
- reinterpret_cast<WasmInstanceObject*>(
- *isolate->factory()->NewJSObjectFromMap(map, TENURED)),
- isolate);
+ reinterpret_cast<WasmInstanceObject*>(*instance_object), isolate);
instance->SetInternalField(kCompiledModule, *compiled_module);
instance->SetInternalField(kMemoryObject, isolate->heap()->undefined_value());
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ WasmInstanceWrapper::New(isolate, instance);
+ instance->SetInternalField(kWasmMemInstanceWrapper, *instance_wrapper);
return instance;
}
@@ -275,8 +342,20 @@ WasmExportedFunction* WasmExportedFunction::cast(Object* object) {
}
Handle<WasmExportedFunction> WasmExportedFunction::New(
- Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<String> name,
- Handle<Code> export_wrapper, int arity, int func_index) {
+ Isolate* isolate, Handle<WasmInstanceObject> instance,
+ MaybeHandle<String> maybe_name, int func_index, int arity,
+ Handle<Code> export_wrapper) {
+ Handle<String> name;
+ if (maybe_name.is_null()) {
+ EmbeddedVector<char, 16> buffer;
+ int length = SNPrintF(buffer, "%d", func_index);
+ name = isolate->factory()
+ ->NewStringFromAscii(
+ Vector<const char>::cast(buffer.SubVector(0, length)))
+ .ToHandleChecked();
+ } else {
+ name = maybe_name.ToHandleChecked();
+ }
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
Handle<SharedFunctionInfo> shared =
isolate->factory()->NewSharedFunctionInfo(name, export_wrapper, false);
@@ -291,22 +370,109 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
return Handle<WasmExportedFunction>::cast(function);
}
+bool WasmSharedModuleData::IsWasmSharedModuleData(Object* object) {
+ if (!object->IsFixedArray()) return false;
+ FixedArray* arr = FixedArray::cast(object);
+ if (arr->length() != kFieldCount) return false;
+ Isolate* isolate = arr->GetIsolate();
+ if (!arr->get(kModuleWrapper)->IsForeign()) return false;
+ if (!arr->get(kModuleBytes)->IsUndefined(isolate) &&
+ !arr->get(kModuleBytes)->IsSeqOneByteString())
+ return false;
+ if (!arr->get(kScript)->IsScript()) return false;
+ if (!arr->get(kAsmJsOffsetTable)->IsUndefined(isolate) &&
+ !arr->get(kAsmJsOffsetTable)->IsByteArray())
+ return false;
+ return true;
+}
+
+WasmSharedModuleData* WasmSharedModuleData::cast(Object* object) {
+ DCHECK(IsWasmSharedModuleData(object));
+ return reinterpret_cast<WasmSharedModuleData*>(object);
+}
+
+wasm::WasmModule* WasmSharedModuleData::module() {
+ return reinterpret_cast<WasmModuleWrapper*>(get(kModuleWrapper))->get();
+}
+
+DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, module_bytes, kModuleBytes,
+ SeqOneByteString);
+DEFINE_ARR_GETTER(WasmSharedModuleData, script, kScript, Script);
+DEFINE_OPTIONAL_ARR_ACCESSORS(WasmSharedModuleData, asm_js_offset_table,
+ kAsmJsOffsetTable, ByteArray);
+
+Handle<WasmSharedModuleData> WasmSharedModuleData::New(
+ Isolate* isolate, Handle<Foreign> module_wrapper,
+ Handle<SeqOneByteString> module_bytes, Handle<Script> script,
+ Handle<ByteArray> asm_js_offset_table) {
+ Handle<FixedArray> arr =
+ isolate->factory()->NewFixedArray(kFieldCount, TENURED);
+
+ arr->set(kModuleWrapper, *module_wrapper);
+ if (!module_bytes.is_null()) {
+ arr->set(kModuleBytes, *module_bytes);
+ }
+ if (!script.is_null()) {
+ arr->set(kScript, *script);
+ }
+ if (!asm_js_offset_table.is_null()) {
+ arr->set(kAsmJsOffsetTable, *asm_js_offset_table);
+ }
+
+ DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*arr));
+ return Handle<WasmSharedModuleData>::cast(arr);
+}
+
+bool WasmSharedModuleData::is_asm_js() {
+ bool asm_js = module()->origin == wasm::ModuleOrigin::kAsmJsOrigin;
+ DCHECK_EQ(asm_js, script()->type() == Script::TYPE_NORMAL);
+ DCHECK_EQ(asm_js, has_asm_js_offset_table());
+ return asm_js;
+}
+
+void WasmSharedModuleData::RecreateModuleWrapper(
+ Isolate* isolate, Handle<WasmSharedModuleData> shared) {
+ DCHECK(shared->get(kModuleWrapper)->IsUndefined(isolate));
+
+ WasmModule* module = nullptr;
+ {
+ // We parse the module again directly from the module bytes, so
+ // the underlying storage must not be moved meanwhile.
+ DisallowHeapAllocation no_allocation;
+ SeqOneByteString* module_bytes = shared->module_bytes();
+ const byte* start =
+ reinterpret_cast<const byte*>(module_bytes->GetCharsAddress());
+ const byte* end = start + module_bytes->length();
+ // TODO(titzer): remember the module origin in the compiled_module
+ // For now, we assume serialized modules did not originate from asm.js.
+ ModuleResult result =
+ DecodeWasmModule(isolate, start, end, false, kWasmOrigin);
+ CHECK(result.ok());
+ CHECK_NOT_NULL(result.val);
+ module = const_cast<WasmModule*>(result.val);
+ }
+
+ Handle<WasmModuleWrapper> module_wrapper =
+ WasmModuleWrapper::New(isolate, module);
+
+ shared->set(kModuleWrapper, *module_wrapper);
+ DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared));
+}
+
Handle<WasmCompiledModule> WasmCompiledModule::New(
- Isolate* isolate, Handle<WasmModuleWrapper> module_wrapper) {
+ Isolate* isolate, Handle<WasmSharedModuleData> shared) {
Handle<FixedArray> ret =
isolate->factory()->NewFixedArray(PropertyIndices::Count, TENURED);
- // WasmCompiledModule::cast would fail since module bytes are not set yet.
+ // WasmCompiledModule::cast would fail since fields are not set yet.
Handle<WasmCompiledModule> compiled_module(
reinterpret_cast<WasmCompiledModule*>(*ret), isolate);
compiled_module->InitId();
- compiled_module->set_module_wrapper(module_wrapper);
+ compiled_module->set_num_imported_functions(0);
+ compiled_module->set_shared(shared);
+ compiled_module->set_native_context(isolate->native_context());
return compiled_module;
}
-wasm::WasmModule* WasmCompiledModule::module() const {
- return reinterpret_cast<WasmModuleWrapper*>(*module_wrapper())->get();
-}
-
void WasmCompiledModule::InitId() {
#if DEBUG
static uint32_t instance_id_counter = 0;
@@ -315,19 +481,39 @@ void WasmCompiledModule::InitId() {
#endif
}
+MaybeHandle<String> WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t offset, uint32_t size) {
+ // TODO(wasm): cache strings from modules if it's a performance win.
+ Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(),
+ isolate);
+ DCHECK_GE(module_bytes->length(), offset);
+ DCHECK_GE(module_bytes->length() - offset, size);
+ Address raw = module_bytes->GetCharsAddress() + offset;
+ if (!unibrow::Utf8::Validate(reinterpret_cast<const byte*>(raw), size))
+ return {}; // UTF8 decoding error for name.
+ DCHECK_GE(kMaxInt, offset);
+ DCHECK_GE(kMaxInt, size);
+ return isolate->factory()->NewStringFromUtf8SubString(
+ module_bytes, static_cast<int>(offset), static_cast<int>(size));
+}
+
bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
if (!obj->IsFixedArray()) return false;
FixedArray* arr = FixedArray::cast(obj);
if (arr->length() != PropertyIndices::Count) return false;
Isolate* isolate = arr->GetIsolate();
-#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) \
- if (!arr->get(kID_##NAME)->IsSmi()) return false;
-#define WCM_CHECK_OBJECT_OR_WEAK(TYPE, NAME) \
- if (!arr->get(kID_##NAME)->IsUndefined(isolate) && \
- !arr->get(kID_##NAME)->Is##TYPE()) \
- return false;
-#define WCM_CHECK_OBJECT(TYPE, NAME) WCM_CHECK_OBJECT_OR_WEAK(TYPE, NAME)
-#define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT_OR_WEAK(WeakCell, NAME)
+#define WCM_CHECK_TYPE(NAME, TYPE_CHECK) \
+ do { \
+ Object* obj = arr->get(kID_##NAME); \
+ if (!(TYPE_CHECK)) return false; \
+ } while (false);
+#define WCM_CHECK_OBJECT(TYPE, NAME) \
+ WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->Is##TYPE())
+#define WCM_CHECK_WASM_OBJECT(TYPE, NAME) \
+ WCM_CHECK_TYPE(NAME, TYPE::Is##TYPE(obj))
+#define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT(WeakCell, NAME)
+#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) WCM_CHECK_TYPE(NAME, obj->IsSmi())
#define WCM_CHECK(KIND, TYPE, NAME) WCM_CHECK_##KIND(TYPE, NAME)
WCM_PROPERTY_TABLE(WCM_CHECK)
#undef WCM_CHECK
@@ -341,7 +527,7 @@ void WasmCompiledModule::PrintInstancesChain() {
if (!FLAG_trace_wasm_instances) return;
for (WasmCompiledModule* current = this; current != nullptr;) {
PrintF("->%d", current->instance_id());
- if (current->ptr_to_weak_next_instance() == nullptr) break;
+ if (!current->has_weak_next_instance()) break;
CHECK(!current->ptr_to_weak_next_instance()->cleared());
current =
WasmCompiledModule::cast(current->ptr_to_weak_next_instance()->value());
@@ -350,6 +536,19 @@ void WasmCompiledModule::PrintInstancesChain() {
#endif
}
+void WasmCompiledModule::RecreateModuleWrapper(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
+ // This method must only be called immediately after deserialization.
+ // At this point, no module wrapper exists, so the shared module data is
+ // incomplete.
+ Handle<WasmSharedModuleData> shared(
+ static_cast<WasmSharedModuleData*>(compiled_module->get(kID_shared)),
+ isolate);
+ DCHECK(!WasmSharedModuleData::IsWasmSharedModuleData(*shared));
+ WasmSharedModuleData::RecreateModuleWrapper(isolate, shared);
+ DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared));
+}
+
uint32_t WasmCompiledModule::mem_size() const {
return has_memory() ? memory()->byte_length()->Number() : default_mem_size();
}
@@ -357,3 +556,310 @@ uint32_t WasmCompiledModule::mem_size() const {
uint32_t WasmCompiledModule::default_mem_size() const {
return min_mem_pages() * WasmModule::kPageSize;
}
+
+MaybeHandle<String> WasmCompiledModule::GetFunctionNameOrNull(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t func_index) {
+ DCHECK_LT(func_index, compiled_module->module()->functions.size());
+ WasmFunction& function = compiled_module->module()->functions[func_index];
+ return WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, function.name_offset, function.name_length);
+}
+
+Handle<String> WasmCompiledModule::GetFunctionName(
+ Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
+ uint32_t func_index) {
+ MaybeHandle<String> name =
+ GetFunctionNameOrNull(isolate, compiled_module, func_index);
+ if (!name.is_null()) return name.ToHandleChecked();
+ return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
+}
+
+Vector<const uint8_t> WasmCompiledModule::GetRawFunctionName(
+ uint32_t func_index) {
+ DCHECK_GT(module()->functions.size(), func_index);
+ WasmFunction& function = module()->functions[func_index];
+ SeqOneByteString* bytes = module_bytes();
+ DCHECK_GE(bytes->length(), function.name_offset);
+ DCHECK_GE(bytes->length() - function.name_offset, function.name_length);
+ return Vector<const uint8_t>(bytes->GetCharsAddress() + function.name_offset,
+ function.name_length);
+}
+
+int WasmCompiledModule::GetFunctionOffset(uint32_t func_index) {
+ std::vector<WasmFunction>& functions = module()->functions;
+ if (static_cast<uint32_t>(func_index) >= functions.size()) return -1;
+ DCHECK_GE(kMaxInt, functions[func_index].code_start_offset);
+ return static_cast<int>(functions[func_index].code_start_offset);
+}
+
+int WasmCompiledModule::GetContainingFunction(uint32_t byte_offset) {
+ std::vector<WasmFunction>& functions = module()->functions;
+
+ // Binary search for a function containing the given position.
+ int left = 0; // inclusive
+ int right = static_cast<int>(functions.size()); // exclusive
+ if (right == 0) return false;
+ while (right - left > 1) {
+ int mid = left + (right - left) / 2;
+ if (functions[mid].code_start_offset <= byte_offset) {
+ left = mid;
+ } else {
+ right = mid;
+ }
+ }
+ // If the found function does not contains the given position, return -1.
+ WasmFunction& func = functions[left];
+ if (byte_offset < func.code_start_offset ||
+ byte_offset >= func.code_end_offset) {
+ return -1;
+ }
+
+ return left;
+}
+
+bool WasmCompiledModule::GetPositionInfo(uint32_t position,
+ Script::PositionInfo* info) {
+ int func_index = GetContainingFunction(position);
+ if (func_index < 0) return false;
+
+ WasmFunction& function = module()->functions[func_index];
+
+ info->line = func_index;
+ info->column = position - function.code_start_offset;
+ info->line_start = function.code_start_offset;
+ info->line_end = function.code_end_offset;
+ return true;
+}
+
+namespace {
+
+enum AsmJsOffsetTableEntryLayout {
+ kOTEByteOffset,
+ kOTECallPosition,
+ kOTENumberConvPosition,
+ kOTESize
+};
+
+Handle<ByteArray> GetDecodedAsmJsOffsetTable(
+ Handle<WasmCompiledModule> compiled_module, Isolate* isolate) {
+ DCHECK(compiled_module->is_asm_js());
+ Handle<ByteArray> offset_table(
+ compiled_module->shared()->asm_js_offset_table(), isolate);
+
+ // The last byte in the asm_js_offset_tables ByteArray tells whether it is
+ // still encoded (0) or decoded (1).
+ enum AsmJsTableType : int { Encoded = 0, Decoded = 1 };
+ int table_type = offset_table->get(offset_table->length() - 1);
+ DCHECK(table_type == Encoded || table_type == Decoded);
+ if (table_type == Decoded) return offset_table;
+
+ AsmJsOffsetsResult asm_offsets;
+ {
+ DisallowHeapAllocation no_gc;
+ const byte* bytes_start = offset_table->GetDataStartAddress();
+ const byte* bytes_end = bytes_start + offset_table->length() - 1;
+ asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end);
+ }
+ // Wasm bytes must be valid and must contain asm.js offset table.
+ DCHECK(asm_offsets.ok());
+ DCHECK_GE(kMaxInt, asm_offsets.val.size());
+ int num_functions = static_cast<int>(asm_offsets.val.size());
+ int num_imported_functions =
+ static_cast<int>(compiled_module->module()->num_imported_functions);
+ DCHECK_EQ(compiled_module->module()->functions.size(),
+ static_cast<size_t>(num_functions) + num_imported_functions);
+ int num_entries = 0;
+ for (int func = 0; func < num_functions; ++func) {
+ size_t new_size = asm_offsets.val[func].size();
+ DCHECK_LE(new_size, static_cast<size_t>(kMaxInt) - num_entries);
+ num_entries += static_cast<int>(new_size);
+ }
+ // One byte to encode that this is a decoded table.
+ DCHECK_GE(kMaxInt,
+ 1 + static_cast<uint64_t>(num_entries) * kOTESize * kIntSize);
+ int total_size = 1 + num_entries * kOTESize * kIntSize;
+ Handle<ByteArray> decoded_table =
+ isolate->factory()->NewByteArray(total_size, TENURED);
+ decoded_table->set(total_size - 1, AsmJsTableType::Decoded);
+ compiled_module->shared()->set_asm_js_offset_table(*decoded_table);
+
+ int idx = 0;
+ std::vector<WasmFunction>& wasm_funs = compiled_module->module()->functions;
+ for (int func = 0; func < num_functions; ++func) {
+ std::vector<AsmJsOffsetEntry>& func_asm_offsets = asm_offsets.val[func];
+ if (func_asm_offsets.empty()) continue;
+ int func_offset =
+ wasm_funs[num_imported_functions + func].code_start_offset;
+ for (AsmJsOffsetEntry& e : func_asm_offsets) {
+ // Byte offsets must be strictly monotonously increasing:
+ DCHECK_IMPLIES(idx > 0, func_offset + e.byte_offset >
+ decoded_table->get_int(idx - kOTESize));
+ decoded_table->set_int(idx + kOTEByteOffset, func_offset + e.byte_offset);
+ decoded_table->set_int(idx + kOTECallPosition, e.source_position_call);
+ decoded_table->set_int(idx + kOTENumberConvPosition,
+ e.source_position_number_conversion);
+ idx += kOTESize;
+ }
+ }
+ DCHECK_EQ(total_size, idx * kIntSize + 1);
+ return decoded_table;
+}
+
+} // namespace
+
+int WasmCompiledModule::GetAsmJsSourcePosition(
+ Handle<WasmCompiledModule> compiled_module, uint32_t func_index,
+ uint32_t byte_offset, bool is_at_number_conversion) {
+ Isolate* isolate = compiled_module->GetIsolate();
+ Handle<ByteArray> offset_table =
+ GetDecodedAsmJsOffsetTable(compiled_module, isolate);
+
+ DCHECK_LT(func_index, compiled_module->module()->functions.size());
+ uint32_t func_code_offset =
+ compiled_module->module()->functions[func_index].code_start_offset;
+ uint32_t total_offset = func_code_offset + byte_offset;
+
+ // Binary search for the total byte offset.
+ int left = 0; // inclusive
+ int right = offset_table->length() / kIntSize / kOTESize; // exclusive
+ DCHECK_LT(left, right);
+ while (right - left > 1) {
+ int mid = left + (right - left) / 2;
+ int mid_entry = offset_table->get_int(kOTESize * mid);
+ DCHECK_GE(kMaxInt, mid_entry);
+ if (static_cast<uint32_t>(mid_entry) <= total_offset) {
+ left = mid;
+ } else {
+ right = mid;
+ }
+ }
+ // There should be an entry for each position that could show up on the stack
+ // trace:
+ DCHECK_EQ(total_offset, offset_table->get_int(kOTESize * left));
+ int idx = is_at_number_conversion ? kOTENumberConvPosition : kOTECallPosition;
+ return offset_table->get_int(kOTESize * left + idx);
+}
+
+v8::debug::WasmDisassembly WasmCompiledModule::DisassembleFunction(
+ int func_index) {
+ DisallowHeapAllocation no_gc;
+
+ if (func_index < 0 ||
+ static_cast<uint32_t>(func_index) >= module()->functions.size())
+ return {};
+
+ SeqOneByteString* module_bytes_str = module_bytes();
+ Vector<const byte> module_bytes(module_bytes_str->GetChars(),
+ module_bytes_str->length());
+
+ std::ostringstream disassembly_os;
+ v8::debug::WasmDisassembly::OffsetTable offset_table;
+
+ PrintWasmText(module(), module_bytes, static_cast<uint32_t>(func_index),
+ disassembly_os, &offset_table);
+
+ return {disassembly_os.str(), std::move(offset_table)};
+}
+
+bool WasmCompiledModule::GetPossibleBreakpoints(
+ const v8::debug::Location& start, const v8::debug::Location& end,
+ std::vector<v8::debug::Location>* locations) {
+ DisallowHeapAllocation no_gc;
+
+ std::vector<WasmFunction>& functions = module()->functions;
+ if (start.GetLineNumber() < 0 || start.GetColumnNumber() < 0 ||
+ (!end.IsEmpty() &&
+ (end.GetLineNumber() < 0 || end.GetColumnNumber() < 0)))
+ return false;
+
+ // start_func_index, start_offset and end_func_index is inclusive.
+ // end_offset is exclusive.
+ // start_offset and end_offset are module-relative byte offsets.
+ uint32_t start_func_index = start.GetLineNumber();
+ if (start_func_index >= functions.size()) return false;
+ int start_func_len = functions[start_func_index].code_end_offset -
+ functions[start_func_index].code_start_offset;
+ if (start.GetColumnNumber() > start_func_len) return false;
+ uint32_t start_offset =
+ functions[start_func_index].code_start_offset + start.GetColumnNumber();
+ uint32_t end_func_index;
+ uint32_t end_offset;
+ if (end.IsEmpty()) {
+ // Default: everything till the end of the Script.
+ end_func_index = static_cast<uint32_t>(functions.size() - 1);
+ end_offset = functions[end_func_index].code_end_offset;
+ } else {
+ // If end is specified: Use it and check for valid input.
+ end_func_index = static_cast<uint32_t>(end.GetLineNumber());
+
+ // Special case: Stop before the start of the next function. Change to: Stop
+ // at the end of the function before, such that we don't disassemble the
+ // next function also.
+ if (end.GetColumnNumber() == 0 && end_func_index > 0) {
+ --end_func_index;
+ end_offset = functions[end_func_index].code_end_offset;
+ } else {
+ if (end_func_index >= functions.size()) return false;
+ end_offset =
+ functions[end_func_index].code_start_offset + end.GetColumnNumber();
+ if (end_offset > functions[end_func_index].code_end_offset) return false;
+ }
+ }
+
+ AccountingAllocator alloc;
+ Zone tmp(&alloc, ZONE_NAME);
+ const byte* module_start = module_bytes()->GetChars();
+
+ for (uint32_t func_idx = start_func_index; func_idx <= end_func_index;
+ ++func_idx) {
+ WasmFunction& func = functions[func_idx];
+ if (func.code_start_offset == func.code_end_offset) continue;
+
+ BodyLocalDecls locals(&tmp);
+ BytecodeIterator iterator(module_start + func.code_start_offset,
+ module_start + func.code_end_offset, &locals);
+ DCHECK_LT(0u, locals.encoded_size);
+ for (uint32_t offset : iterator.offsets()) {
+ uint32_t total_offset = func.code_start_offset + offset;
+ if (total_offset >= end_offset) {
+ DCHECK_EQ(end_func_index, func_idx);
+ break;
+ }
+ if (total_offset < start_offset) continue;
+ locations->push_back(v8::debug::Location(func_idx, offset));
+ }
+ }
+ return true;
+}
+
+Handle<WasmInstanceWrapper> WasmInstanceWrapper::New(
+ Isolate* isolate, Handle<WasmInstanceObject> instance) {
+ Handle<FixedArray> array =
+ isolate->factory()->NewFixedArray(kWrapperPropertyCount, TENURED);
+ Handle<WasmInstanceWrapper> instance_wrapper(
+ reinterpret_cast<WasmInstanceWrapper*>(*array), isolate);
+ instance_wrapper->set_instance_object(instance, isolate);
+ return instance_wrapper;
+}
+
+bool WasmInstanceWrapper::IsWasmInstanceWrapper(Object* obj) {
+ if (!obj->IsFixedArray()) return false;
+ Handle<FixedArray> array = handle(FixedArray::cast(obj));
+ if (array->length() != kWrapperPropertyCount) return false;
+ if (!array->get(kWrapperInstanceObject)->IsWeakCell()) return false;
+ Isolate* isolate = array->GetIsolate();
+ if (!array->get(kNextInstanceWrapper)->IsUndefined(isolate) &&
+ !array->get(kNextInstanceWrapper)->IsFixedArray())
+ return false;
+ if (!array->get(kPreviousInstanceWrapper)->IsUndefined(isolate) &&
+ !array->get(kPreviousInstanceWrapper)->IsFixedArray())
+ return false;
+ return true;
+}
+
+void WasmInstanceWrapper::set_instance_object(Handle<JSObject> instance,
+ Isolate* isolate) {
+ Handle<WeakCell> cell = isolate->factory()->NewWeakCell(instance);
+ set(kWrapperInstanceObject, *cell);
+}