summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/wasm-module.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/wasm-module.cc')
-rw-r--r--deps/v8/src/wasm/wasm-module.cc1913
1 files changed, 1249 insertions, 664 deletions
diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc
index 79b99fe04d..60dda925fa 100644
--- a/deps/v8/src/wasm/wasm-module.cc
+++ b/deps/v8/src/wasm/wasm-module.cc
@@ -4,25 +4,26 @@
#include <memory>
+#include "src/assembler-inl.h"
+#include "src/base/adapters.h"
#include "src/base/atomic-utils.h"
#include "src/code-stubs.h"
-
-#include "src/macro-assembler.h"
+#include "src/compiler/wasm-compiler.h"
+#include "src/debug/interface-types.h"
#include "src/objects.h"
#include "src/property-descriptor.h"
#include "src/simulator.h"
#include "src/snapshot/snapshot.h"
#include "src/v8.h"
-#include "src/wasm/ast-decoder.h"
+#include "src/wasm/function-body-decoder.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-js.h"
+#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
-#include "src/compiler/wasm-compiler.h"
-
using namespace v8::internal;
using namespace v8::internal::wasm;
namespace base = v8::base;
@@ -40,26 +41,11 @@ namespace base = v8::base;
namespace {
static const int kInvalidSigIndex = -1;
-static const int kPlaceholderMarker = 1000000000;
byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
}
-MaybeHandle<String> ExtractStringFromModuleBytes(
- 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();
- DCHECK_GE(static_cast<size_t>(module_bytes->length()), offset);
- DCHECK_GE(static_cast<size_t>(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.
- return isolate->factory()->NewStringFromUtf8SubString(
- module_bytes, static_cast<int>(offset), static_cast<int>(size));
-}
-
void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
Handle<Object> new_ref) {
for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done();
@@ -70,34 +56,74 @@ void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
}
}
-Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) {
- if (size > (WasmModule::kV8MaxPages * WasmModule::kPageSize)) {
- // TODO(titzer): lift restriction on maximum memory allocated here.
- return Handle<JSArrayBuffer>::null();
- }
- void* memory = isolate->array_buffer_allocator()->Allocate(size);
- if (memory == nullptr) {
- return Handle<JSArrayBuffer>::null();
- }
+static void MemoryFinalizer(const v8::WeakCallbackInfo<void>& data) {
+ DisallowHeapAllocation no_gc;
+ JSArrayBuffer** p = reinterpret_cast<JSArrayBuffer**>(data.GetParameter());
+ JSArrayBuffer* buffer = *p;
-#if DEBUG
- // Double check the API allocator actually zero-initialized the memory.
- const byte* bytes = reinterpret_cast<const byte*>(memory);
- for (size_t i = 0; i < size; ++i) {
- DCHECK_EQ(0, bytes[i]);
- }
+ void* memory = buffer->backing_store();
+ base::OS::Free(memory,
+ RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize()));
+
+ data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
+ -buffer->byte_length()->Number());
+
+ GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
+}
+
+#if V8_TARGET_ARCH_64_BIT
+const bool kGuardRegionsSupported = true;
+#else
+const bool kGuardRegionsSupported = false;
#endif
- Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
- JSArrayBuffer::Setup(buffer, isolate, false, memory, static_cast<int>(size));
- buffer->set_is_neuterable(false);
- return buffer;
+bool EnableGuardRegions() {
+ return FLAG_wasm_guard_pages && kGuardRegionsSupported;
+}
+
+void* TryAllocateBackingStore(Isolate* isolate, size_t size,
+ bool enable_guard_regions, bool& is_external) {
+ is_external = false;
+ // TODO(eholk): Right now enable_guard_regions has no effect on 32-bit
+ // systems. It may be safer to fail instead, given that other code might do
+ // things that would be unsafe if they expected guard pages where there
+ // weren't any.
+ if (enable_guard_regions && kGuardRegionsSupported) {
+ // TODO(eholk): On Windows we want to make sure we don't commit the guard
+ // pages yet.
+
+ // We always allocate the largest possible offset into the heap, so the
+ // addressable memory after the guard page can be made inaccessible.
+ const size_t alloc_size =
+ RoundUp(kWasmMaxHeapOffset, base::OS::CommitPageSize());
+ DCHECK_EQ(0, size % base::OS::CommitPageSize());
+
+ // AllocateGuarded makes the whole region inaccessible by default.
+ void* memory = base::OS::AllocateGuarded(alloc_size);
+ if (memory == nullptr) {
+ return nullptr;
+ }
+
+ // Make the part we care about accessible.
+ base::OS::Unprotect(memory, size);
+
+ reinterpret_cast<v8::Isolate*>(isolate)
+ ->AdjustAmountOfExternalAllocatedMemory(size);
+
+ is_external = true;
+ return memory;
+ } else {
+ void* memory = isolate->array_buffer_allocator()->Allocate(size);
+ return memory;
+ }
}
void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table,
+ uint32_t num_imported_functions,
Address old_start, Address start,
uint32_t prev_size, uint32_t new_size) {
- for (int i = 0; i < code_table->length(); ++i) {
+ for (int i = static_cast<int>(num_imported_functions);
+ i < code_table->length(); ++i) {
DCHECK(code_table->get(i)->IsCode());
Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i)));
AllowDeferredHandleDereference embedding_raw_address;
@@ -123,52 +149,25 @@ void RelocateGlobals(Handle<FixedArray> code_table, Address old_start,
}
}
-Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index,
- Code::Kind kind) {
- // Create a placeholder code object and encode the corresponding index in
- // the {constant_pool_offset} field of the code object.
- // TODO(titzer): instead of placeholders, use a reloc_info mode.
- static byte buffer[] = {0, 0, 0, 0}; // fake instructions.
- static CodeDesc desc = {
- buffer, arraysize(buffer), arraysize(buffer), 0, 0, nullptr, 0, nullptr};
- Handle<Code> code = factory->NewCode(desc, Code::KindField::encode(kind),
- Handle<Object>::null());
- code->set_constant_pool_offset(static_cast<int>(index) + kPlaceholderMarker);
- return code;
-}
-
-bool LinkFunction(Handle<Code> unlinked,
- std::vector<Handle<Code>>& code_table) {
- bool modified = false;
- int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
- AllowDeferredHandleDereference embedding_raw_address;
- for (RelocIterator it(*unlinked, mode_mask); !it.done(); it.next()) {
- RelocInfo::Mode mode = it.rinfo()->rmode();
- if (RelocInfo::IsCodeTarget(mode)) {
- Code* target =
- Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (target->constant_pool_offset() < kPlaceholderMarker) continue;
- switch (target->kind()) {
- case Code::WASM_FUNCTION: // fall through
- case Code::WASM_TO_JS_FUNCTION: // fall through
- case Code::JS_TO_WASM_FUNCTION: {
- // Patch direct calls to placeholder code objects.
- uint32_t index = target->constant_pool_offset() - kPlaceholderMarker;
- Handle<Code> new_target = code_table[index];
- if (target != *new_target) {
- it.rinfo()->set_target_address(new_target->instruction_start(),
- UPDATE_WRITE_BARRIER,
- SKIP_ICACHE_FLUSH);
- modified = true;
- }
- break;
- }
- default:
- break;
- }
+void RelocateTableSizeReferences(Handle<FixedArray> code_table,
+ uint32_t old_size, uint32_t new_size) {
+ for (int i = 0; i < code_table->length(); ++i) {
+ DCHECK(code_table->get(i)->IsCode());
+ Handle<Code> code = Handle<Code>(Code::cast(code_table->get(i)));
+ AllowDeferredHandleDereference embedding_raw_address;
+ int mask = 1 << RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE;
+ for (RelocIterator it(*code, mask); !it.done(); it.next()) {
+ it.rinfo()->update_wasm_function_table_size_reference(old_size, new_size);
}
}
- return modified;
+}
+
+Handle<Code> CreatePlaceholder(Factory* factory, Code::Kind kind) {
+ byte buffer[] = {0, 0, 0, 0}; // fake instructions.
+ CodeDesc desc = {
+ buffer, arraysize(buffer), arraysize(buffer), 0, 0, nullptr, 0, nullptr};
+ return factory->NewCode(desc, Code::KindField::encode(kind),
+ Handle<Object>::null());
}
void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) {
@@ -257,7 +256,7 @@ Address GetGlobalStartAddressFromCodeTemplate(Object* undefined,
Address old_address = nullptr;
if (instance->has_globals_buffer()) {
old_address =
- static_cast<Address>(instance->get_globals_buffer()->backing_store());
+ static_cast<Address>(instance->globals_buffer()->backing_store());
}
return old_address;
}
@@ -265,7 +264,7 @@ Address GetGlobalStartAddressFromCodeTemplate(Object* undefined,
void InitializeParallelCompilation(
Isolate* isolate, const std::vector<WasmFunction>& functions,
std::vector<compiler::WasmCompilationUnit*>& compilation_units,
- ModuleEnv& module_env, ErrorThrower* thrower) {
+ ModuleBytesEnv& module_env, ErrorThrower* thrower) {
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); ++i) {
const WasmFunction* func = &functions[i];
compilation_units[i] =
@@ -329,9 +328,10 @@ void FinishCompilationUnits(
}
}
-void CompileInParallel(Isolate* isolate, const WasmModule* module,
+void CompileInParallel(Isolate* isolate, ModuleBytesEnv* module_env,
std::vector<Handle<Code>>& functions,
- ErrorThrower* thrower, ModuleEnv* module_env) {
+ ErrorThrower* thrower) {
+ const WasmModule* module = module_env->module;
// Data structures for the parallel compilation.
std::vector<compiler::WasmCompilationUnit*> compilation_units(
module->functions.size());
@@ -393,22 +393,23 @@ void CompileInParallel(Isolate* isolate, const WasmModule* module,
FinishCompilationUnits(executed_units, functions, result_mutex);
}
-void CompileSequentially(Isolate* isolate, const WasmModule* module,
+void CompileSequentially(Isolate* isolate, ModuleBytesEnv* module_env,
std::vector<Handle<Code>>& functions,
- ErrorThrower* thrower, ModuleEnv* module_env) {
+ ErrorThrower* thrower) {
DCHECK(!thrower->error());
+ const WasmModule* module = module_env->module;
for (uint32_t i = FLAG_skip_compiling_wasm_funcs;
i < module->functions.size(); ++i) {
const WasmFunction& func = module->functions[i];
if (func.imported) continue; // Imports are compiled at instantiation time.
- WasmName str = module->GetName(func.name_offset, func.name_length);
Handle<Code> code = Handle<Code>::null();
// Compile the function.
code = compiler::WasmCompilationUnit::CompileWasmFunction(
thrower, isolate, module_env, &func);
if (code.is_null()) {
+ WasmName str = module_env->GetName(&func);
thrower->CompileError("Compilation of #%d:%.*s failed.", i, str.length(),
str.start());
break;
@@ -418,36 +419,120 @@ void CompileSequentially(Isolate* isolate, const WasmModule* module,
}
}
-void PatchDirectCalls(Handle<FixedArray> old_functions,
- Handle<FixedArray> new_functions, int start) {
- DCHECK_EQ(new_functions->length(), old_functions->length());
+int ExtractDirectCallIndex(wasm::Decoder& decoder, const byte* pc) {
+ DCHECK_EQ(static_cast<int>(kExprCallFunction), static_cast<int>(*pc));
+ decoder.Reset(pc + 1, pc + 6);
+ uint32_t call_idx = decoder.consume_u32v("call index");
+ DCHECK(decoder.ok());
+ DCHECK_GE(kMaxInt, call_idx);
+ return static_cast<int>(call_idx);
+}
+
+int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator,
+ size_t offset_l) {
+ DCHECK_GE(kMaxInt, offset_l);
+ int offset = static_cast<int>(offset_l);
+ DCHECK(!iterator.done());
+ int byte_pos;
+ do {
+ byte_pos = iterator.source_position().ScriptOffset();
+ iterator.Advance();
+ } while (!iterator.done() && iterator.code_offset() <= offset);
+ return byte_pos;
+}
+void PatchContext(RelocIterator& it, Context* context) {
+ Object* old = it.rinfo()->target_object();
+ // The only context we use is the native context.
+ DCHECK_IMPLIES(old->IsContext(), old->IsNativeContext());
+ if (!old->IsNativeContext()) return;
+ it.rinfo()->set_target_object(context, UPDATE_WRITE_BARRIER,
+ SKIP_ICACHE_FLUSH);
+}
+
+void PatchDirectCallsAndContext(Handle<FixedArray> new_functions,
+ Handle<WasmCompiledModule> compiled_module,
+ WasmModule* module, int start) {
DisallowHeapAllocation no_gc;
- std::map<Code*, Code*> old_to_new_code;
- for (int i = 0; i < new_functions->length(); ++i) {
- old_to_new_code.insert(std::make_pair(Code::cast(old_functions->get(i)),
- Code::cast(new_functions->get(i))));
- }
- int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
AllowDeferredHandleDereference embedding_raw_address;
- for (int i = start; i < new_functions->length(); ++i) {
- Code* wasm_function = Code::cast(new_functions->get(i));
+ SeqOneByteString* module_bytes = compiled_module->module_bytes();
+ std::vector<WasmFunction>* wasm_functions =
+ &compiled_module->module()->functions;
+ DCHECK_EQ(wasm_functions->size() +
+ compiled_module->module()->num_exported_functions,
+ new_functions->length());
+ DCHECK_EQ(start, compiled_module->module()->num_imported_functions);
+ Context* context = compiled_module->ptr_to_native_context();
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
+ RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+
+ // Allocate decoder outside of the loop and reuse it to decode all function
+ // indexes.
+ wasm::Decoder decoder(nullptr, nullptr);
+ int num_wasm_functions = static_cast<int>(wasm_functions->size());
+ int func_index = start;
+ // Patch all wasm functions.
+ for (; func_index < num_wasm_functions; ++func_index) {
+ Code* wasm_function = Code::cast(new_functions->get(func_index));
+ DCHECK(wasm_function->kind() == Code::WASM_FUNCTION);
+ // Iterate simultaneously over the relocation information and the source
+ // position table. For each call in the reloc info, move the source position
+ // iterator forward to that position to find the byte offset of the
+ // respective call. Then extract the call index from the module wire bytes
+ // to find the new compiled function.
+ SourcePositionTableIterator source_pos_iterator(
+ wasm_function->source_position_table());
+ const byte* func_bytes =
+ module_bytes->GetChars() +
+ compiled_module->module()->functions[func_index].code_start_offset;
for (RelocIterator it(wasm_function, mode_mask); !it.done(); it.next()) {
- Code* old_code =
- Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
- if (old_code->kind() == Code::WASM_TO_JS_FUNCTION ||
- old_code->kind() == Code::WASM_FUNCTION) {
- auto found = old_to_new_code.find(old_code);
- DCHECK(found != old_to_new_code.end());
- Code* new_code = found->second;
- if (new_code != old_code) {
- it.rinfo()->set_target_address(new_code->instruction_start(),
- UPDATE_WRITE_BARRIER,
- SKIP_ICACHE_FLUSH);
- }
+ if (RelocInfo::IsEmbeddedObject(it.rinfo()->rmode())) {
+ PatchContext(it, context);
+ continue;
}
+ DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
+ Code::Kind kind =
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address())->kind();
+ if (kind != Code::WASM_FUNCTION && kind != Code::WASM_TO_JS_FUNCTION)
+ continue;
+ size_t offset = it.rinfo()->pc() - wasm_function->instruction_start();
+ int byte_pos =
+ AdvanceSourcePositionTableIterator(source_pos_iterator, offset);
+ int called_func_index =
+ ExtractDirectCallIndex(decoder, func_bytes + byte_pos);
+ Code* new_code = Code::cast(new_functions->get(called_func_index));
+ it.rinfo()->set_target_address(new_code->instruction_start(),
+ UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
}
}
+ // Patch all exported functions.
+ for (auto exp : module->export_table) {
+ if (exp.kind != kExternalFunction) continue;
+ Code* export_wrapper = Code::cast(new_functions->get(func_index));
+ DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
+ // There must be exactly one call to WASM_FUNCTION or WASM_TO_JS_FUNCTION.
+ int num_wasm_calls = 0;
+ for (RelocIterator it(export_wrapper, mode_mask); !it.done(); it.next()) {
+ if (RelocInfo::IsEmbeddedObject(it.rinfo()->rmode())) {
+ PatchContext(it, context);
+ continue;
+ }
+ DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
+ Code::Kind kind =
+ Code::GetCodeFromTargetAddress(it.rinfo()->target_address())->kind();
+ if (kind != Code::WASM_FUNCTION && kind != Code::WASM_TO_JS_FUNCTION)
+ continue;
+ ++num_wasm_calls;
+ Code* new_code = Code::cast(new_functions->get(exp.index));
+ DCHECK(new_code->kind() == Code::WASM_FUNCTION ||
+ new_code->kind() == Code::WASM_TO_JS_FUNCTION);
+ it.rinfo()->set_target_address(new_code->instruction_start(),
+ UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
+ }
+ DCHECK_EQ(1, num_wasm_calls);
+ func_index++;
+ }
+ DCHECK_EQ(new_functions->length(), func_index);
}
static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
@@ -456,7 +541,7 @@ static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
Object* undefined = *isolate->factory()->undefined_value();
uint32_t old_mem_size = compiled_module->mem_size();
uint32_t default_mem_size = compiled_module->default_mem_size();
- Object* mem_start = compiled_module->ptr_to_memory();
+ Object* mem_start = compiled_module->maybe_ptr_to_memory();
Address old_mem_address = nullptr;
Address globals_start =
GetGlobalStartAddressFromCodeTemplate(undefined, owner);
@@ -486,7 +571,8 @@ static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
if (fct_obj != nullptr && fct_obj != undefined &&
(old_mem_size > 0 || globals_start != nullptr || function_tables)) {
FixedArray* functions = FixedArray::cast(fct_obj);
- for (int i = 0; i < functions->length(); ++i) {
+ for (int i = compiled_module->num_imported_functions();
+ i < functions->length(); ++i) {
Code* code = Code::cast(functions->get(i));
bool changed = false;
for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
@@ -518,12 +604,58 @@ static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
compiled_module->reset_memory();
}
+static void MemoryInstanceFinalizer(Isolate* isolate,
+ WasmInstanceObject* instance) {
+ DisallowHeapAllocation no_gc;
+ // If the memory object is destroyed, nothing needs to be done here.
+ if (!instance->has_memory_object()) return;
+ Handle<WasmInstanceWrapper> instance_wrapper =
+ handle(instance->instance_wrapper());
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ DCHECK(instance_wrapper->has_instance());
+ bool has_prev = instance_wrapper->has_previous();
+ bool has_next = instance_wrapper->has_next();
+ Handle<WasmMemoryObject> memory_object(instance->memory_object());
+
+ if (!has_prev && !has_next) {
+ memory_object->ResetInstancesLink(isolate);
+ return;
+ } else {
+ Handle<WasmInstanceWrapper> next_wrapper, prev_wrapper;
+ if (!has_prev) {
+ Handle<WasmInstanceWrapper> next_wrapper =
+ instance_wrapper->next_wrapper();
+ next_wrapper->reset_previous_wrapper();
+ // As this is the first link in the memory object, destroying
+ // without updating memory object would corrupt the instance chain in
+ // the memory object.
+ memory_object->set_instances_link(*next_wrapper);
+ } else if (!has_next) {
+ instance_wrapper->previous_wrapper()->reset_next_wrapper();
+ } else {
+ DCHECK(has_next && has_prev);
+ Handle<WasmInstanceWrapper> prev_wrapper =
+ instance_wrapper->previous_wrapper();
+ Handle<WasmInstanceWrapper> next_wrapper =
+ instance_wrapper->next_wrapper();
+ prev_wrapper->set_next_wrapper(*next_wrapper);
+ next_wrapper->set_previous_wrapper(*prev_wrapper);
+ }
+ // Reset to avoid dangling pointers
+ instance_wrapper->reset();
+ }
+}
+
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
+ DisallowHeapAllocation no_gc;
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
WasmInstanceObject* owner = reinterpret_cast<WasmInstanceObject*>(*p);
- WasmCompiledModule* compiled_module = owner->get_compiled_module();
- TRACE("Finalizing %d {\n", compiled_module->instance_id());
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
+ // If a link to shared memory instances exists, update the list of memory
+ // instances before the instance is destroyed.
+ if (owner->has_instance_wrapper()) MemoryInstanceFinalizer(isolate, owner);
+ WasmCompiledModule* compiled_module = owner->compiled_module();
+ TRACE("Finalizing %d {\n", compiled_module->instance_id());
DCHECK(compiled_module->has_weak_wasm_module());
WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
@@ -540,8 +672,8 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
TRACE("}\n");
DCHECK(!current_template->has_weak_prev_instance());
- WeakCell* next = compiled_module->ptr_to_weak_next_instance();
- WeakCell* prev = compiled_module->ptr_to_weak_prev_instance();
+ WeakCell* next = compiled_module->maybe_ptr_to_weak_next_instance();
+ WeakCell* prev = compiled_module->maybe_ptr_to_weak_prev_instance();
if (current_template == compiled_module) {
if (next == nullptr) {
@@ -597,8 +729,81 @@ std::pair<int, int> GetFunctionOffsetAndLength(
static_cast<int>(func.code_end_offset - func.code_start_offset)};
}
+Handle<Script> CreateWasmScript(Isolate* isolate,
+ const ModuleWireBytes& wire_bytes) {
+ Handle<Script> script =
+ isolate->factory()->NewScript(isolate->factory()->empty_string());
+ script->set_type(Script::TYPE_WASM);
+
+ int hash = StringHasher::HashSequentialString(
+ reinterpret_cast<const char*>(wire_bytes.module_bytes.start()),
+ wire_bytes.module_bytes.length(), kZeroHashSeed);
+
+ const int kBufferSize = 50;
+ char buffer[kBufferSize];
+ int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
+ DCHECK(url_chars >= 0 && url_chars < kBufferSize);
+ MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
+ Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
+ TENURED);
+ script->set_source_url(*url_str.ToHandleChecked());
+
+ int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
+ DCHECK(name_chars >= 0 && name_chars < kBufferSize);
+ MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
+ Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
+ TENURED);
+ script->set_name(*name_str.ToHandleChecked());
+
+ return script;
+}
} // namespace
+Handle<JSArrayBuffer> wasm::NewArrayBuffer(Isolate* isolate, size_t size,
+ bool enable_guard_regions) {
+ if (size > (kV8MaxWasmMemoryPages * WasmModule::kPageSize)) {
+ // TODO(titzer): lift restriction on maximum memory allocated here.
+ return Handle<JSArrayBuffer>::null();
+ }
+
+ enable_guard_regions = enable_guard_regions && kGuardRegionsSupported;
+
+ bool is_external; // Set by TryAllocateBackingStore
+ void* memory =
+ TryAllocateBackingStore(isolate, size, enable_guard_regions, is_external);
+
+ if (memory == nullptr) {
+ return Handle<JSArrayBuffer>::null();
+ }
+
+#if DEBUG
+ // Double check the API allocator actually zero-initialized the memory.
+ const byte* bytes = reinterpret_cast<const byte*>(memory);
+ for (size_t i = 0; i < size; ++i) {
+ DCHECK_EQ(0, bytes[i]);
+ }
+#endif
+
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
+ JSArrayBuffer::Setup(buffer, isolate, is_external, memory,
+ static_cast<int>(size));
+ buffer->set_is_neuterable(false);
+ buffer->set_has_guard_region(enable_guard_regions);
+
+ if (is_external) {
+ // We mark the buffer as external if we allocated it here with guard
+ // pages. That means we need to arrange for it to be freed.
+
+ // TODO(eholk): Finalizers may not run when the main thread is shutting
+ // down, which means we may leak memory here.
+ Handle<Object> global_handle = isolate->global_handles()->Create(*buffer);
+ GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(),
+ &MemoryFinalizer, v8::WeakCallbackType::kFinalizer);
+ }
+
+ return buffer;
+}
+
const char* wasm::SectionName(WasmSectionCode code) {
switch (code) {
case kUnknownSectionCode:
@@ -650,15 +855,12 @@ std::ostream& wasm::operator<<(std::ostream& os, const WasmFunction& function) {
return os;
}
-std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& pair) {
- os << "#" << pair.function_->func_index << ":";
- if (pair.function_->name_offset > 0) {
- if (pair.module_) {
- WasmName name = pair.module_->GetName(pair.function_->name_offset,
- pair.function_->name_length);
- os.write(name.start(), name.length());
- } else {
- os << "+" << pair.function_->func_index;
+std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& name) {
+ os << "#" << name.function_->func_index;
+ if (name.function_->name_offset > 0) {
+ if (name.name_.start()) {
+ os << ":";
+ os.write(name.name_.start(), name.name_.length());
}
} else {
os << "?";
@@ -666,16 +868,17 @@ std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& pair) {
return os;
}
-Object* wasm::GetOwningWasmInstance(Code* code) {
+WasmInstanceObject* wasm::GetOwningWasmInstance(Code* code) {
DCHECK(code->kind() == Code::WASM_FUNCTION);
DisallowHeapAllocation no_gc;
FixedArray* deopt_data = code->deoptimization_data();
DCHECK_NOT_NULL(deopt_data);
- DCHECK(deopt_data->length() == 2);
+ DCHECK_EQ(2, deopt_data->length());
Object* weak_link = deopt_data->get(0);
- if (!weak_link->IsWeakCell()) return nullptr;
+ DCHECK(weak_link->IsWeakCell());
WeakCell* cell = WeakCell::cast(weak_link);
- return cell->value();
+ if (!cell->value()) return nullptr;
+ return WasmInstanceObject::cast(cell->value());
}
int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
@@ -683,69 +886,41 @@ int wasm::GetFunctionCodeOffset(Handle<WasmCompiledModule> compiled_module,
return GetFunctionOffsetAndLength(compiled_module, func_index).first;
}
-bool wasm::GetPositionInfo(Handle<WasmCompiledModule> compiled_module,
- uint32_t position, Script::PositionInfo* info) {
- std::vector<WasmFunction>& functions = compiled_module->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 <= position) {
- left = mid;
- } else {
- right = mid;
- }
- }
- // If the found entry does not contains the given position, return false.
- WasmFunction& func = functions[left];
- if (position < func.code_start_offset || position >= func.code_end_offset) {
- return false;
- }
-
- info->line = left;
- info->column = position - func.code_start_offset;
- info->line_start = func.code_start_offset;
- info->line_end = func.code_end_offset;
- return true;
-}
-
-WasmModule::WasmModule(Zone* owned, const byte* module_start)
- : owned_zone(owned),
- module_start(module_start),
- pending_tasks(new base::Semaphore(0)) {}
+WasmModule::WasmModule(Zone* owned)
+ : owned_zone(owned), pending_tasks(new base::Semaphore(0)) {}
MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
Isolate* isolate, Handle<WasmModuleWrapper> module_wrapper,
- ErrorThrower* thrower) const {
+ ErrorThrower* thrower, const ModuleWireBytes& wire_bytes,
+ Handle<Script> asm_js_script,
+ Vector<const byte> asm_js_offset_table_bytes) const {
Factory* factory = isolate->factory();
MaybeHandle<WasmCompiledModule> nothing;
WasmInstance temp_instance(this);
temp_instance.context = isolate->native_context();
- temp_instance.mem_size = WasmModule::kPageSize * this->min_mem_pages;
+ temp_instance.mem_size = WasmModule::kPageSize * min_mem_pages;
temp_instance.mem_start = nullptr;
temp_instance.globals_start = nullptr;
// Initialize the indirect tables with placeholders.
- int function_table_count = static_cast<int>(this->function_tables.size());
+ int function_table_count = static_cast<int>(function_tables.size());
Handle<FixedArray> function_tables =
- factory->NewFixedArray(function_table_count);
+ factory->NewFixedArray(function_table_count, TENURED);
+ Handle<FixedArray> signature_tables =
+ factory->NewFixedArray(function_table_count, TENURED);
for (int i = 0; i < function_table_count; ++i) {
- temp_instance.function_tables[i] = factory->NewFixedArray(0);
+ temp_instance.function_tables[i] = factory->NewFixedArray(1, TENURED);
+ temp_instance.signature_tables[i] = factory->NewFixedArray(1, TENURED);
function_tables->set(i, *temp_instance.function_tables[i]);
+ signature_tables->set(i, *temp_instance.signature_tables[i]);
}
HistogramTimerScope wasm_compile_module_time_scope(
isolate->counters()->wasm_compile_module_time());
- ModuleEnv module_env;
- module_env.module = this;
- module_env.instance = &temp_instance;
- module_env.origin = origin;
+ ModuleBytesEnv module_env(this, &temp_instance, wire_bytes);
// The {code_table} array contains import wrappers and functions (which
// are both included in {functions.size()}, and export wrappers.
@@ -755,12 +930,11 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
factory->NewFixedArray(static_cast<int>(code_table_size), TENURED);
// Initialize the code table with placeholders.
+ Handle<Code> code_placeholder =
+ CreatePlaceholder(factory, Code::WASM_FUNCTION);
for (uint32_t i = 0; i < functions.size(); ++i) {
- Code::Kind kind = Code::WASM_FUNCTION;
- if (i < num_imported_functions) kind = Code::WASM_TO_JS_FUNCTION;
- Handle<Code> placeholder = CreatePlaceholder(factory, i, kind);
- code_table->set(static_cast<int>(i), *placeholder);
- temp_instance.function_code[i] = placeholder;
+ code_table->set(static_cast<int>(i), *code_placeholder);
+ temp_instance.function_code[i] = code_placeholder;
}
isolate->counters()->wasm_functions_per_module()->AddSample(
@@ -772,14 +946,14 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
for (size_t i = 0; i < temp_instance.function_code.size(); ++i) {
results.push_back(temp_instance.function_code[i]);
}
- CompileInParallel(isolate, this, results, thrower, &module_env);
+ CompileInParallel(isolate, &module_env, results, thrower);
for (size_t i = 0; i < results.size(); ++i) {
temp_instance.function_code[i] = results[i];
}
} else {
- CompileSequentially(isolate, this, temp_instance.function_code, thrower,
- &module_env);
+ CompileSequentially(isolate, &module_env, temp_instance.function_code,
+ thrower);
}
if (thrower->error()) return nothing;
@@ -788,61 +962,74 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
i < temp_instance.function_code.size(); ++i) {
Code* code = *temp_instance.function_code[i];
code_table->set(static_cast<int>(i), code);
+ RecordStats(isolate, code);
}
- // Link the functions in the module.
- for (size_t i = FLAG_skip_compiling_wasm_funcs;
- i < temp_instance.function_code.size(); ++i) {
- Handle<Code> code = temp_instance.function_code[i];
- bool modified = LinkFunction(code, temp_instance.function_code);
- if (modified) {
- // TODO(mtrofin): do we need to flush the cache here?
- Assembler::FlushICache(isolate, code->instruction_start(),
- code->instruction_size());
- }
- }
+ // Create heap objects for script, module bytes and asm.js offset table to be
+ // stored in the shared module data.
+ Handle<Script> script;
+ Handle<ByteArray> asm_js_offset_table;
+ if (asm_js_script.is_null()) {
+ script = CreateWasmScript(isolate, wire_bytes);
+ } else {
+ script = asm_js_script;
+ asm_js_offset_table =
+ isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length());
+ asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(),
+ asm_js_offset_table_bytes.length());
+ }
+ // TODO(wasm): only save the sections necessary to deserialize a
+ // {WasmModule}. E.g. function bodies could be omitted.
+ Handle<String> module_bytes =
+ factory->NewStringFromOneByte(wire_bytes.module_bytes, TENURED)
+ .ToHandleChecked();
+ DCHECK(module_bytes->IsSeqOneByteString());
+
+ // Create the shared module data.
+ // TODO(clemensh): For the same module (same bytes / same hash), we should
+ // only have one WasmSharedModuleData. Otherwise, we might only set
+ // breakpoints on a (potentially empty) subset of the instances.
+
+ Handle<WasmSharedModuleData> shared = WasmSharedModuleData::New(
+ isolate, module_wrapper, Handle<SeqOneByteString>::cast(module_bytes),
+ script, asm_js_offset_table);
// Create the compiled module object, and populate with compiled functions
// and information needed at instantiation time. This object needs to be
// serializable. Instantiation may occur off a deserialized version of this
// object.
- Handle<WasmCompiledModule> ret =
- WasmCompiledModule::New(isolate, module_wrapper);
+ Handle<WasmCompiledModule> ret = WasmCompiledModule::New(isolate, shared);
+ ret->set_num_imported_functions(num_imported_functions);
ret->set_code_table(code_table);
ret->set_min_mem_pages(min_mem_pages);
ret->set_max_mem_pages(max_mem_pages);
if (function_table_count > 0) {
ret->set_function_tables(function_tables);
+ ret->set_signature_tables(signature_tables);
ret->set_empty_function_tables(function_tables);
}
+ // If we created a wasm script, finish it now and make it public to the
+ // debugger.
+ if (asm_js_script.is_null()) {
+ script->set_wasm_compiled_module(*ret);
+ isolate->debug()->OnAfterCompile(script);
+ }
+
// Compile JS->WASM wrappers for exported functions.
int func_index = 0;
for (auto exp : export_table) {
if (exp.kind != kExternalFunction) continue;
Handle<Code> wasm_code =
code_table->GetValueChecked<Code>(isolate, exp.index);
- Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
- isolate, &module_env, wasm_code, exp.index);
+ Handle<Code> wrapper_code =
+ compiler::CompileJSToWasmWrapper(isolate, this, wasm_code, exp.index);
int export_index = static_cast<int>(functions.size() + func_index);
code_table->set(export_index, *wrapper_code);
+ RecordStats(isolate, *wrapper_code);
func_index++;
}
- {
- // TODO(wasm): only save the sections necessary to deserialize a
- // {WasmModule}. E.g. function bodies could be omitted.
- size_t module_bytes_len = module_end - module_start;
- DCHECK_LE(module_bytes_len, static_cast<size_t>(kMaxInt));
- Vector<const uint8_t> module_bytes_vec(module_start,
- static_cast<int>(module_bytes_len));
- Handle<String> module_bytes_string =
- factory->NewStringFromOneByte(module_bytes_vec, TENURED)
- .ToHandleChecked();
- DCHECK(module_bytes_string->IsSeqOneByteString());
- ret->set_module_bytes(Handle<SeqOneByteString>::cast(module_bytes_string));
- }
-
return ret;
}
@@ -876,7 +1063,7 @@ static Handle<Code> UnwrapImportWrapper(Handle<Object> target) {
code = handle(target);
}
}
- DCHECK(found == 1);
+ DCHECK_EQ(1, found);
return code;
}
@@ -884,8 +1071,8 @@ static Handle<Code> CompileImportWrapper(Isolate* isolate, int index,
FunctionSig* sig,
Handle<JSReceiver> target,
Handle<String> module_name,
- MaybeHandle<String> import_name) {
- Handle<Code> code;
+ MaybeHandle<String> import_name,
+ ModuleOrigin origin) {
WasmFunction* other_func = GetWasmFunctionForImportWrapper(isolate, target);
if (other_func) {
if (sig->Equals(other_func->sig)) {
@@ -898,7 +1085,7 @@ static Handle<Code> CompileImportWrapper(Isolate* isolate, int index,
} else {
// Signature mismatch. Compile a new wrapper for the new signature.
return compiler::CompileWasmToJSWrapper(isolate, target, sig, index,
- module_name, import_name);
+ module_name, import_name, origin);
}
}
@@ -906,11 +1093,13 @@ static void UpdateDispatchTablesInternal(Isolate* isolate,
Handle<FixedArray> dispatch_tables,
int index, WasmFunction* function,
Handle<Code> code) {
- DCHECK_EQ(0, dispatch_tables->length() % 3);
- for (int i = 0; i < dispatch_tables->length(); i += 3) {
+ DCHECK_EQ(0, dispatch_tables->length() % 4);
+ for (int i = 0; i < dispatch_tables->length(); i += 4) {
int table_index = Smi::cast(dispatch_tables->get(i + 1))->value();
- Handle<FixedArray> dispatch_table(
+ Handle<FixedArray> function_table(
FixedArray::cast(dispatch_tables->get(i + 2)), isolate);
+ Handle<FixedArray> signature_table(
+ FixedArray::cast(dispatch_tables->get(i + 3)), isolate);
if (function) {
// TODO(titzer): the signature might need to be copied to avoid
// a dangling pointer in the signature map.
@@ -919,12 +1108,12 @@ static void UpdateDispatchTablesInternal(Isolate* isolate,
int sig_index = static_cast<int>(
instance->module()->function_tables[table_index].map.FindOrInsert(
function->sig));
- dispatch_table->set(index, Smi::FromInt(sig_index));
- dispatch_table->set(index + (dispatch_table->length() / 2), *code);
+ signature_table->set(index, Smi::FromInt(sig_index));
+ function_table->set(index, *code);
} else {
Code* code = nullptr;
- dispatch_table->set(index, Smi::FromInt(-1));
- dispatch_table->set(index + (dispatch_table->length() / 2), code);
+ signature_table->set(index, Smi::FromInt(-1));
+ function_table->set(index, code);
}
}
}
@@ -949,17 +1138,27 @@ void wasm::UpdateDispatchTables(Isolate* isolate,
class WasmInstanceBuilder {
public:
WasmInstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
- Handle<JSObject> module_object, Handle<JSReceiver> ffi,
- Handle<JSArrayBuffer> memory)
+ Handle<WasmModuleObject> module_object,
+ Handle<JSReceiver> ffi, Handle<JSArrayBuffer> memory)
: isolate_(isolate),
+ module_(module_object->compiled_module()->module()),
thrower_(thrower),
module_object_(module_object),
ffi_(ffi),
memory_(memory) {}
// Build an instance, in all of its glory.
- MaybeHandle<JSObject> Build() {
- MaybeHandle<JSObject> nothing;
+ MaybeHandle<WasmInstanceObject> Build() {
+ MaybeHandle<WasmInstanceObject> nothing;
+
+ // Check that an imports argument was provided, if the module requires it.
+ // No point in continuing otherwise.
+ if (!module_->import_table.empty() && ffi_.is_null()) {
+ thrower_->TypeError(
+ "Imports argument must be present and must be an object");
+ return nothing;
+ }
+
HistogramTimerScope wasm_instantiate_module_time_scope(
isolate_->counters()->wasm_instantiate_module_time());
Factory* factory = isolate_->factory();
@@ -982,8 +1181,7 @@ class WasmInstanceBuilder {
Handle<WasmCompiledModule> original;
{
DisallowHeapAllocation no_gc;
- original = handle(
- WasmCompiledModule::cast(module_object_->GetInternalField(0)));
+ original = handle(module_object_->compiled_module());
if (original->has_weak_owning_instance()) {
owner = handle(WasmInstanceObject::cast(
original->weak_owning_instance()->value()));
@@ -1032,10 +1230,8 @@ class WasmInstanceBuilder {
compiled_module_->instance_id());
}
compiled_module_->set_code_table(code_table);
+ compiled_module_->set_native_context(isolate_->native_context());
}
- module_ = reinterpret_cast<WasmModuleWrapper*>(
- *compiled_module_->module_wrapper())
- ->get();
//--------------------------------------------------------------------------
// Allocate the instance object.
@@ -1049,8 +1245,9 @@ class WasmInstanceBuilder {
MaybeHandle<JSArrayBuffer> old_globals;
uint32_t globals_size = module_->globals_size;
if (globals_size > 0) {
+ const bool enable_guard_regions = false;
Handle<JSArrayBuffer> global_buffer =
- NewArrayBuffer(isolate_, globals_size);
+ NewArrayBuffer(isolate_, globals_size, enable_guard_regions);
globals_ = global_buffer;
if (globals_.is_null()) {
thrower_->RangeError("Out of memory: wasm globals");
@@ -1072,9 +1269,9 @@ class WasmInstanceBuilder {
static_cast<int>(module_->function_tables.size());
table_instances_.reserve(module_->function_tables.size());
for (int index = 0; index < function_table_count; ++index) {
- table_instances_.push_back({Handle<WasmTableObject>::null(),
- Handle<FixedArray>::null(),
- Handle<FixedArray>::null()});
+ table_instances_.push_back(
+ {Handle<WasmTableObject>::null(), Handle<FixedArray>::null(),
+ Handle<FixedArray>::null(), Handle<FixedArray>::null()});
}
//--------------------------------------------------------------------------
@@ -1089,6 +1286,11 @@ class WasmInstanceBuilder {
InitGlobals();
//--------------------------------------------------------------------------
+ // Set up the indirect function tables for the new instance.
+ //--------------------------------------------------------------------------
+ if (function_table_count > 0) InitializeTables(code_table, instance);
+
+ //--------------------------------------------------------------------------
// Set up the memory for the new instance.
//--------------------------------------------------------------------------
MaybeHandle<JSArrayBuffer> old_memory;
@@ -1099,17 +1301,51 @@ class WasmInstanceBuilder {
if (!memory_.is_null()) {
// Set externally passed ArrayBuffer non neuterable.
memory_->set_is_neuterable(false);
+
+ DCHECK_IMPLIES(EnableGuardRegions(), module_->origin == kAsmJsOrigin ||
+ memory_->has_guard_region());
} else if (min_mem_pages > 0) {
memory_ = AllocateMemory(min_mem_pages);
if (memory_.is_null()) return nothing; // failed to allocate memory
}
+ //--------------------------------------------------------------------------
+ // Check that indirect function table segments are within bounds.
+ //--------------------------------------------------------------------------
+ for (WasmTableInit& table_init : module_->table_inits) {
+ DCHECK(table_init.table_index < table_instances_.size());
+ uint32_t base = EvalUint32InitExpr(table_init.offset);
+ uint32_t table_size =
+ table_instances_[table_init.table_index].function_table->length();
+ if (!in_bounds(base, static_cast<uint32_t>(table_init.entries.size()),
+ table_size)) {
+ thrower_->LinkError("table initializer is out of bounds");
+ return nothing;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Check that memory segments are within bounds.
+ //--------------------------------------------------------------------------
+ for (WasmDataSegment& seg : module_->data_segments) {
+ uint32_t base = EvalUint32InitExpr(seg.dest_addr);
+ uint32_t mem_size = memory_.is_null()
+ ? 0 : static_cast<uint32_t>(memory_->byte_length()->Number());
+ if (!in_bounds(base, seg.source_size, mem_size)) {
+ thrower_->LinkError("data segment is out of bounds");
+ return nothing;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Initialize memory.
+ //--------------------------------------------------------------------------
if (!memory_.is_null()) {
instance->set_memory_buffer(*memory_);
Address mem_start = static_cast<Address>(memory_->backing_store());
uint32_t mem_size =
static_cast<uint32_t>(memory_->byte_length()->Number());
- LoadDataSegments(mem_start, mem_size);
+ if (!LoadDataSegments(mem_start, mem_size)) return nothing;
uint32_t old_mem_size = compiled_module_->mem_size();
Address old_mem_start =
@@ -1117,11 +1353,10 @@ class WasmInstanceBuilder {
? static_cast<Address>(
compiled_module_->memory()->backing_store())
: nullptr;
- RelocateMemoryReferencesInCode(code_table, old_mem_start, mem_start,
- old_mem_size, mem_size);
+ RelocateMemoryReferencesInCode(
+ code_table, module_->num_imported_functions, old_mem_start, mem_start,
+ old_mem_size, mem_size);
compiled_module_->set_memory(memory_);
- } else {
- LoadDataSegments(nullptr, 0);
}
//--------------------------------------------------------------------------
@@ -1144,21 +1379,61 @@ class WasmInstanceBuilder {
//--------------------------------------------------------------------------
// Set up the exports object for the new instance.
//--------------------------------------------------------------------------
- ProcessExports(code_table, instance);
+ ProcessExports(code_table, instance, compiled_module_);
//--------------------------------------------------------------------------
- // Set up the indirect function tables for the new instance.
+ // Add instance to Memory object
//--------------------------------------------------------------------------
- if (function_table_count > 0) InitializeTables(code_table, instance);
-
- if (num_imported_functions > 0 || !owner.is_null()) {
- // If the code was cloned, or new imports were compiled, patch.
- PatchDirectCalls(old_code_table, code_table, num_imported_functions);
+ DCHECK(wasm::IsWasmInstance(*instance));
+ if (instance->has_memory_object()) {
+ instance->memory_object()->AddInstance(isolate_, instance);
}
+ //--------------------------------------------------------------------------
+ // Initialize the indirect function tables.
+ //--------------------------------------------------------------------------
+ if (function_table_count > 0) LoadTableSegments(code_table, instance);
+
+ // Patch new call sites and the context.
+ PatchDirectCallsAndContext(code_table, compiled_module_, module_,
+ num_imported_functions);
+
FlushICache(isolate_, code_table);
//--------------------------------------------------------------------------
+ // Unpack and notify signal handler of protected instructions.
+ //--------------------------------------------------------------------------
+ {
+ for (int i = 0; i < code_table->length(); ++i) {
+ Handle<Code> code = code_table->GetValueChecked<Code>(isolate_, i);
+
+ if (code->kind() != Code::WASM_FUNCTION) {
+ continue;
+ }
+
+ FixedArray* protected_instructions = code->protected_instructions();
+ DCHECK(protected_instructions != nullptr);
+ Zone zone(isolate_->allocator(), "Wasm Module");
+ ZoneVector<trap_handler::ProtectedInstructionData> unpacked(&zone);
+ for (int i = 0; i < protected_instructions->length();
+ i += Code::kTrapDataSize) {
+ trap_handler::ProtectedInstructionData data;
+ data.instr_offset =
+ protected_instructions
+ ->GetValueChecked<Smi>(isolate_, i + Code::kTrapCodeOffset)
+ ->value();
+ data.landing_offset =
+ protected_instructions
+ ->GetValueChecked<Smi>(isolate_, i + Code::kTrapLandingOffset)
+ ->value();
+ unpacked.emplace_back(data);
+ }
+ // TODO(eholk): Register the protected instruction information once the
+ // trap handler is in place.
+ }
+ }
+
+ //--------------------------------------------------------------------------
// Set up and link the new instance.
//--------------------------------------------------------------------------
{
@@ -1174,7 +1449,7 @@ class WasmInstanceBuilder {
// we want all the publishing to happen free from GC interruptions, and
// so we do it in
// one GC-free scope afterwards.
- original = handle(owner.ToHandleChecked()->get_compiled_module());
+ original = handle(owner.ToHandleChecked()->compiled_module());
link_to_original = factory->NewWeakCell(original.ToHandleChecked());
}
// Publish the new instance to the instances chain.
@@ -1194,30 +1469,20 @@ class WasmInstanceBuilder {
v8::WeakCallbackType::kFinalizer);
}
}
-
- DCHECK(wasm::IsWasmInstance(*instance));
- if (instance->has_memory_object()) {
- instance->get_memory_object()->AddInstance(*instance);
- }
-
//--------------------------------------------------------------------------
// Run the start function if one was specified.
//--------------------------------------------------------------------------
if (module_->start_function_index >= 0) {
HandleScope scope(isolate_);
- ModuleEnv module_env;
- module_env.module = module_;
- module_env.instance = nullptr;
- module_env.origin = module_->origin;
int start_index = module_->start_function_index;
Handle<Code> startup_code =
code_table->GetValueChecked<Code>(isolate_, start_index);
FunctionSig* sig = module_->functions[start_index].sig;
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
- isolate_, &module_env, startup_code, start_index);
+ isolate_, module_, startup_code, start_index);
Handle<WasmExportedFunction> startup_fct = WasmExportedFunction::New(
- isolate_, instance, factory->InternalizeUtf8String("start"),
- wrapper_code, static_cast<int>(sig->parameter_count()), start_index);
+ isolate_, instance, MaybeHandle<String>(), start_index,
+ static_cast<int>(sig->parameter_count()), wrapper_code);
RecordStats(isolate_, *startup_code);
// Call the JS function.
Handle<Object> undefined = factory->undefined_value();
@@ -1237,7 +1502,7 @@ class WasmInstanceBuilder {
DCHECK(!isolate_->has_pending_exception());
TRACE("Finishing instance %d\n", compiled_module_->instance_id());
- TRACE_CHAIN(WasmCompiledModule::cast(module_object_->GetInternalField(0)));
+ TRACE_CHAIN(module_object_->compiled_module());
return instance;
}
@@ -1246,13 +1511,14 @@ class WasmInstanceBuilder {
struct TableInstance {
Handle<WasmTableObject> table_object; // WebAssembly.Table instance
Handle<FixedArray> js_wrappers; // JSFunctions exported
- Handle<FixedArray> dispatch_table; // internal (code, sig) pairs
+ Handle<FixedArray> function_table; // internal code array
+ Handle<FixedArray> signature_table; // internal sig array
};
Isolate* isolate_;
- WasmModule* module_;
+ WasmModule* const module_;
ErrorThrower* thrower_;
- Handle<JSObject> module_object_;
+ Handle<WasmModuleObject> module_object_;
Handle<JSReceiver> ffi_;
Handle<JSArrayBuffer> memory_;
Handle<JSArrayBuffer> globals_;
@@ -1260,58 +1526,49 @@ class WasmInstanceBuilder {
std::vector<TableInstance> table_instances_;
std::vector<Handle<JSFunction>> js_wrappers_;
- // Helper routine to print out errors with imports (FFI).
- MaybeHandle<JSFunction> ReportFFIError(const char* error, uint32_t index,
- Handle<String> module_name,
- MaybeHandle<String> function_name) {
- Handle<String> function_name_handle;
- if (function_name.ToHandle(&function_name_handle)) {
- thrower_->TypeError(
- "Import #%d module=\"%.*s\" function=\"%.*s\" error: %s", index,
- module_name->length(), module_name->ToCString().get(),
- function_name_handle->length(),
- function_name_handle->ToCString().get(), error);
- } else {
- thrower_->TypeError("Import #%d module=\"%.*s\" error: %s", index,
- module_name->length(), module_name->ToCString().get(),
- error);
- }
- thrower_->TypeError("Import ");
- return MaybeHandle<JSFunction>();
+ // Helper routines to print out errors with imports.
+ void ReportLinkError(const char* error, uint32_t index,
+ Handle<String> module_name, Handle<String> import_name) {
+ thrower_->LinkError(
+ "Import #%d module=\"%.*s\" function=\"%.*s\" error: %s", index,
+ module_name->length(), module_name->ToCString().get(),
+ import_name->length(), import_name->ToCString().get(), error);
+ }
+
+ MaybeHandle<Object> ReportLinkError(const char* error, uint32_t index,
+ Handle<String> module_name) {
+ thrower_->LinkError("Import #%d module=\"%.*s\" error: %s", index,
+ module_name->length(), module_name->ToCString().get(),
+ error);
+ return MaybeHandle<Object>();
}
// Look up an import value in the {ffi_} object.
MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
- MaybeHandle<String> import_name) {
- if (ffi_.is_null()) {
- return ReportFFIError("FFI is not an object", index, module_name,
- import_name);
- }
+ Handle<String> import_name) {
+ // We pre-validated in the js-api layer that the ffi object is present, and
+ // a JSObject, if the module has imports.
+ DCHECK(!ffi_.is_null());
// Look up the module first.
- MaybeHandle<Object> result = Object::GetProperty(ffi_, module_name);
+ MaybeHandle<Object> result =
+ Object::GetPropertyOrElement(ffi_, module_name);
if (result.is_null()) {
- return ReportFFIError("module not found", index, module_name,
- import_name);
+ return ReportLinkError("module not found", index, module_name);
}
Handle<Object> module = result.ToHandleChecked();
- if (!import_name.is_null()) {
- // Look up the value in the module.
- if (!module->IsJSReceiver()) {
- return ReportFFIError("module is not an object or function", index,
- module_name, import_name);
- }
+ // Look up the value in the module.
+ if (!module->IsJSReceiver()) {
+ return ReportLinkError("module is not an object or function", index,
+ module_name);
+ }
- result = Object::GetProperty(module, import_name.ToHandleChecked());
- if (result.is_null()) {
- return ReportFFIError("import not found", index, module_name,
- import_name);
- }
- } else {
- // No function specified. Use the "default export".
- result = module;
+ result = Object::GetPropertyOrElement(module, import_name);
+ if (result.is_null()) {
+ ReportLinkError("import not found", index, module_name, import_name);
+ return MaybeHandle<JSFunction>();
}
return result;
@@ -1331,26 +1588,27 @@ class WasmInstanceBuilder {
}
}
+ bool in_bounds(uint32_t offset, uint32_t size, uint32_t upper) {
+ return offset + size <= upper && offset + size >= offset;
+ }
+
// Load data segments into the memory.
- void LoadDataSegments(Address mem_addr, size_t mem_size) {
- Handle<SeqOneByteString> module_bytes = compiled_module_->module_bytes();
+ bool LoadDataSegments(Address mem_addr, size_t mem_size) {
+ Handle<SeqOneByteString> module_bytes(compiled_module_->module_bytes(),
+ isolate_);
for (const WasmDataSegment& segment : module_->data_segments) {
uint32_t source_size = segment.source_size;
// Segments of size == 0 are just nops.
if (source_size == 0) continue;
uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr);
- if (dest_offset >= mem_size || source_size >= mem_size ||
- dest_offset > (mem_size - source_size)) {
- thrower_->TypeError("data segment (start = %" PRIu32 ", size = %" PRIu32
- ") does not fit into memory (size = %" PRIuS ")",
- dest_offset, source_size, mem_size);
- return;
- }
+ DCHECK(in_bounds(dest_offset, source_size,
+ static_cast<uint32_t>(mem_size)));
byte* dest = mem_addr + dest_offset;
const byte* src = reinterpret_cast<const byte*>(
module_bytes->GetCharsAddress() + segment.source_offset);
memcpy(dest, src, source_size);
}
+ return true;
}
void WriteGlobalValue(WasmGlobal& global, Handle<Object> value) {
@@ -1365,17 +1623,17 @@ class WasmInstanceBuilder {
TRACE("init [globals+%u] = %lf, type = %s\n", global.offset, num,
WasmOpcodes::TypeName(global.type));
switch (global.type) {
- case kAstI32:
+ case kWasmI32:
*GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num);
break;
- case kAstI64:
+ case kWasmI64:
// TODO(titzer): initialization of imported i64 globals.
UNREACHABLE();
break;
- case kAstF32:
+ case kWasmF32:
*GetRawGlobalPtr<float>(global) = static_cast<float>(num);
break;
- case kAstF64:
+ case kWasmF64:
*GetRawGlobalPtr<double>(global) = static_cast<double>(num);
break;
default:
@@ -1393,39 +1651,43 @@ class WasmInstanceBuilder {
for (int index = 0; index < static_cast<int>(module_->import_table.size());
++index) {
WasmImport& import = module_->import_table[index];
- Handle<String> module_name =
- ExtractStringFromModuleBytes(isolate_, compiled_module_,
- import.module_name_offset,
- import.module_name_length)
- .ToHandleChecked();
- Handle<String> function_name = Handle<String>::null();
- if (import.field_name_length > 0) {
- function_name = ExtractStringFromModuleBytes(isolate_, compiled_module_,
- import.field_name_offset,
- import.field_name_length)
- .ToHandleChecked();
- }
+
+ Handle<String> module_name;
+ MaybeHandle<String> maybe_module_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, import.module_name_offset,
+ import.module_name_length);
+ if (!maybe_module_name.ToHandle(&module_name)) return -1;
+
+ Handle<String> import_name;
+ MaybeHandle<String> maybe_import_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, import.field_name_offset,
+ import.field_name_length);
+ if (!maybe_import_name.ToHandle(&import_name)) return -1;
MaybeHandle<Object> result =
- LookupImport(index, module_name, function_name);
+ LookupImport(index, module_name, import_name);
if (thrower_->error()) return -1;
+ Handle<Object> value = result.ToHandleChecked();
switch (import.kind) {
case kExternalFunction: {
// Function imports must be callable.
- Handle<Object> function = result.ToHandleChecked();
- if (!function->IsCallable()) {
- ReportFFIError("function import requires a callable", index,
- module_name, function_name);
+ if (!value->IsCallable()) {
+ ReportLinkError("function import requires a callable", index,
+ module_name, import_name);
return -1;
}
Handle<Code> import_wrapper = CompileImportWrapper(
isolate_, index, module_->functions[import.index].sig,
- Handle<JSReceiver>::cast(function), module_name, function_name);
+ Handle<JSReceiver>::cast(value), module_name, import_name,
+ module_->origin);
if (import_wrapper.is_null()) {
- ReportFFIError("imported function does not match the expected type",
- index, module_name, function_name);
+ ReportLinkError(
+ "imported function does not match the expected type", index,
+ module_name, import_name);
return -1;
}
code_table->set(num_imported_functions, *import_wrapper);
@@ -1434,10 +1696,9 @@ class WasmInstanceBuilder {
break;
}
case kExternalTable: {
- Handle<Object> value = result.ToHandleChecked();
if (!WasmJs::IsWasmTableObject(isolate_, value)) {
- ReportFFIError("table import requires a WebAssembly.Table", index,
- module_name, function_name);
+ ReportLinkError("table import requires a WebAssembly.Table", index,
+ module_name, import_name);
return -1;
}
WasmIndirectFunctionTable& table =
@@ -1445,23 +1706,43 @@ class WasmInstanceBuilder {
TableInstance& table_instance = table_instances_[num_imported_tables];
table_instance.table_object = Handle<WasmTableObject>::cast(value);
table_instance.js_wrappers = Handle<FixedArray>(
- table_instance.table_object->get_functions(), isolate_);
-
- // TODO(titzer): import table size must match exactly for now.
- int table_size = table_instance.js_wrappers->length();
- if (table_size != static_cast<int>(table.min_size)) {
- thrower_->TypeError(
- "table import %d is wrong size (%d), expected %u", index,
- table_size, table.min_size);
+ table_instance.table_object->functions(), isolate_);
+
+ int imported_cur_size = table_instance.js_wrappers->length();
+ if (imported_cur_size < static_cast<int>(table.min_size)) {
+ thrower_->LinkError(
+ "table import %d is smaller than minimum %d, got %u", index,
+ table.min_size, imported_cur_size);
return -1;
}
- // Allocate a new dispatch table.
- table_instance.dispatch_table =
- isolate_->factory()->NewFixedArray(table_size * 2);
- for (int i = 0; i < table_size * 2; ++i) {
- table_instance.dispatch_table->set(i,
- Smi::FromInt(kInvalidSigIndex));
+ if (table.has_max) {
+ int64_t imported_max_size =
+ table_instance.table_object->maximum_length();
+ if (imported_max_size < 0) {
+ thrower_->LinkError(
+ "table import %d has no maximum length, expected %d", index,
+ table.max_size);
+ return -1;
+ }
+ if (imported_max_size > table.max_size) {
+ thrower_->LinkError(
+ "table import %d has maximum larger than maximum %d, "
+ "got %" PRIx64,
+ index, table.max_size, imported_max_size);
+ return -1;
+ }
+ }
+
+ // Allocate a new dispatch table and signature table.
+ int table_size = imported_cur_size;
+ table_instance.function_table =
+ isolate_->factory()->NewFixedArray(table_size);
+ table_instance.signature_table =
+ isolate_->factory()->NewFixedArray(table_size);
+ for (int i = 0; i < table_size; ++i) {
+ table_instance.signature_table->set(i,
+ Smi::FromInt(kInvalidSigIndex));
}
// Initialize the dispatch table with the (foreign) JS functions
// that are already in the table.
@@ -1471,43 +1752,70 @@ class WasmInstanceBuilder {
WasmFunction* function =
GetWasmFunctionForImportWrapper(isolate_, val);
if (function == nullptr) {
- thrower_->TypeError("table import %d[%d] is not a WASM function",
+ thrower_->LinkError("table import %d[%d] is not a WASM function",
index, i);
return -1;
}
int sig_index = table.map.FindOrInsert(function->sig);
- table_instance.dispatch_table->set(i, Smi::FromInt(sig_index));
- table_instance.dispatch_table->set(i + table_size,
- *UnwrapImportWrapper(val));
+ table_instance.signature_table->set(i, Smi::FromInt(sig_index));
+ table_instance.function_table->set(i, *UnwrapImportWrapper(val));
}
num_imported_tables++;
break;
}
case kExternalMemory: {
- Handle<Object> object = result.ToHandleChecked();
- if (!WasmJs::IsWasmMemoryObject(isolate_, object)) {
- ReportFFIError("memory import must be a WebAssembly.Memory object",
- index, module_name, function_name);
+ // Validation should have failed if more than one memory object was
+ // provided.
+ DCHECK(!instance->has_memory_object());
+ if (!WasmJs::IsWasmMemoryObject(isolate_, value)) {
+ ReportLinkError("memory import must be a WebAssembly.Memory object",
+ index, module_name, import_name);
return -1;
}
- auto memory = Handle<WasmMemoryObject>::cast(object);
+ auto memory = Handle<WasmMemoryObject>::cast(value);
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory));
instance->set_memory_object(*memory);
- memory_ = Handle<JSArrayBuffer>(memory->get_buffer(), isolate_);
+ memory_ = Handle<JSArrayBuffer>(memory->buffer(), isolate_);
+ uint32_t imported_cur_pages = static_cast<uint32_t>(
+ memory_->byte_length()->Number() / WasmModule::kPageSize);
+ if (imported_cur_pages < module_->min_mem_pages) {
+ thrower_->LinkError(
+ "memory import %d is smaller than maximum %u, got %u", index,
+ module_->min_mem_pages, imported_cur_pages);
+ }
+ int32_t imported_max_pages = memory->maximum_pages();
+ if (module_->has_max_mem) {
+ if (imported_max_pages < 0) {
+ thrower_->LinkError(
+ "memory import %d has no maximum limit, expected at most %u",
+ index, imported_max_pages);
+ return -1;
+ }
+ if (static_cast<uint32_t>(imported_max_pages) >
+ module_->max_mem_pages) {
+ thrower_->LinkError(
+ "memory import %d has larger maximum than maximum %u, got %d",
+ index, module_->max_mem_pages, imported_max_pages);
+ return -1;
+ }
+ }
break;
}
case kExternalGlobal: {
// Global imports are converted to numbers and written into the
// {globals_} array buffer.
- Handle<Object> object = result.ToHandleChecked();
- MaybeHandle<Object> number = Object::ToNumber(object);
- if (number.is_null()) {
- ReportFFIError("global import could not be converted to number",
- index, module_name, function_name);
+ if (module_->globals[import.index].type == kWasmI64) {
+ ReportLinkError("global import cannot have type i64", index,
+ module_name, import_name);
+ return -1;
+ }
+ if (!value->IsNumber()) {
+ ReportLinkError("global import must be a number", index,
+ module_name, import_name);
return -1;
}
- Handle<Object> val = number.ToHandleChecked();
- WriteGlobalValue(module_->globals[import.index], val);
+ WriteGlobalValue(module_->globals[import.index], value);
break;
}
default:
@@ -1546,7 +1854,7 @@ class WasmInstanceBuilder {
module_->globals[global.init.val.global_index].offset;
TRACE("init [globals+%u] = [globals+%d]\n", global.offset,
old_offset);
- size_t size = (global.type == kAstI64 || global.type == kAstF64)
+ size_t size = (global.type == kWasmI64 || global.type == kWasmF64)
? sizeof(double)
: sizeof(int32_t);
memcpy(raw_buffer_ptr(globals_, new_offset),
@@ -1565,12 +1873,13 @@ class WasmInstanceBuilder {
// Allocate memory for a module instance as a new JSArrayBuffer.
Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) {
- if (min_mem_pages > WasmModule::kV8MaxPages) {
+ if (min_mem_pages > kV8MaxWasmMemoryPages) {
thrower_->RangeError("Out of memory: wasm memory too large");
return Handle<JSArrayBuffer>::null();
}
- Handle<JSArrayBuffer> mem_buffer =
- NewArrayBuffer(isolate_, min_mem_pages * WasmModule::kPageSize);
+ const bool enable_guard_regions = EnableGuardRegions();
+ Handle<JSArrayBuffer> mem_buffer = NewArrayBuffer(
+ isolate_, min_mem_pages * WasmModule::kPageSize, enable_guard_regions);
if (mem_buffer.is_null()) {
thrower_->RangeError("Out of memory: wasm memory");
@@ -1578,31 +1887,30 @@ class WasmInstanceBuilder {
return mem_buffer;
}
- // Process the exports, creating wrappers for functions, tables, memories,
- // and globals.
- void ProcessExports(Handle<FixedArray> code_table,
- Handle<WasmInstanceObject> instance) {
- bool needs_wrappers = module_->num_exported_functions > 0;
+ bool NeedsWrappers() {
+ if (module_->num_exported_functions > 0) return true;
for (auto table_instance : table_instances_) {
- if (!table_instance.js_wrappers.is_null()) {
- needs_wrappers = true;
- break;
- }
+ if (!table_instance.js_wrappers.is_null()) return true;
}
for (auto table : module_->function_tables) {
- if (table.exported) {
- needs_wrappers = true;
- break;
- }
+ if (table.exported) return true;
}
- if (needs_wrappers) {
+ return false;
+ }
+
+ // Process the exports, creating wrappers for functions, tables, memories,
+ // and globals.
+ void ProcessExports(Handle<FixedArray> code_table,
+ Handle<WasmInstanceObject> instance,
+ Handle<WasmCompiledModule> compiled_module) {
+ if (NeedsWrappers()) {
// Fill the table to cache the exported JSFunction wrappers.
js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(),
Handle<JSFunction>::null());
}
Handle<JSObject> exports_object = instance;
- if (module_->export_table.size() > 0 && module_->origin == kWasmOrigin) {
+ if (module_->origin == kWasmOrigin) {
// Create the "exports" object.
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate_->native_context()->object_function(), isolate_);
@@ -1610,38 +1918,68 @@ class WasmInstanceBuilder {
isolate_->factory()->NewJSObject(object_function, TENURED);
Handle<String> exports_name =
isolate_->factory()->InternalizeUtf8String("exports");
- JSObject::AddProperty(instance, exports_name, exports_object, READ_ONLY);
+ JSObject::AddProperty(instance, exports_name, exports_object, NONE);
}
PropertyDescriptor desc;
desc.set_writable(false);
+ desc.set_enumerable(true);
- // Process each export in the export table.
+ // Count up export indexes.
int export_index = 0;
for (auto exp : module_->export_table) {
+ if (exp.kind == kExternalFunction) {
+ ++export_index;
+ }
+ }
+
+ // Store weak references to all exported functions.
+ Handle<FixedArray> weak_exported_functions;
+ if (compiled_module->has_weak_exported_functions()) {
+ weak_exported_functions = compiled_module->weak_exported_functions();
+ } else {
+ weak_exported_functions =
+ isolate_->factory()->NewFixedArray(export_index);
+ compiled_module->set_weak_exported_functions(weak_exported_functions);
+ }
+ DCHECK_EQ(export_index, weak_exported_functions->length());
+
+ // Process each export in the export table (go in reverse so asm.js
+ // can skip duplicates).
+ for (auto exp : base::Reversed(module_->export_table)) {
Handle<String> name =
- ExtractStringFromModuleBytes(isolate_, compiled_module_,
- exp.name_offset, exp.name_length)
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, exp.name_offset, exp.name_length)
.ToHandleChecked();
switch (exp.kind) {
case kExternalFunction: {
// Wrap and export the code as a JSFunction.
WasmFunction& function = module_->functions[exp.index];
int func_index =
- static_cast<int>(module_->functions.size() + export_index);
+ static_cast<int>(module_->functions.size() + --export_index);
Handle<JSFunction> js_function = js_wrappers_[exp.index];
if (js_function.is_null()) {
// Wrap the exported code as a JSFunction.
Handle<Code> export_code =
code_table->GetValueChecked<Code>(isolate_, func_index);
+ MaybeHandle<String> func_name;
+ if (module_->origin == kAsmJsOrigin) {
+ // For modules arising from asm.js, honor the names section.
+ func_name = WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, function.name_offset,
+ function.name_length)
+ .ToHandleChecked();
+ }
js_function = WasmExportedFunction::New(
- isolate_, instance, name, export_code,
- static_cast<int>(function.sig->parameter_count()),
- function.func_index);
+ isolate_, instance, func_name, function.func_index,
+ static_cast<int>(function.sig->parameter_count()), export_code);
js_wrappers_[exp.index] = js_function;
}
desc.set_value(js_function);
- export_index++;
+ Handle<WeakCell> weak_export =
+ isolate_->factory()->NewWeakCell(js_function);
+ DCHECK_GT(weak_exported_functions->length(), export_index);
+ weak_exported_functions->set(export_index, *weak_export);
break;
}
case kExternalTable: {
@@ -1651,7 +1989,7 @@ class WasmInstanceBuilder {
module_->function_tables[exp.index];
if (table_instance.table_object.is_null()) {
uint32_t maximum =
- table.has_max ? table.max_size : WasmModule::kV8MaxTableSize;
+ table.has_max ? table.max_size : kV8MaxWasmTableSize;
table_instance.table_object = WasmTableObject::New(
isolate_, table.min_size, maximum, &table_instance.js_wrappers);
}
@@ -1663,15 +2001,16 @@ class WasmInstanceBuilder {
Handle<WasmMemoryObject> memory_object;
if (!instance->has_memory_object()) {
// If there was no imported WebAssembly.Memory object, create one.
- Handle<JSArrayBuffer> buffer(instance->get_memory_buffer(),
- isolate_);
+ Handle<JSArrayBuffer> buffer(instance->memory_buffer(), isolate_);
memory_object = WasmMemoryObject::New(
isolate_, buffer,
(module_->max_mem_pages != 0) ? module_->max_mem_pages : -1);
instance->set_memory_object(*memory_object);
} else {
- memory_object = Handle<WasmMemoryObject>(
- instance->get_memory_object(), isolate_);
+ memory_object =
+ Handle<WasmMemoryObject>(instance->memory_object(), isolate_);
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate_, memory_object));
+ memory_object->ResetInstancesLink(isolate_);
}
desc.set_value(memory_object);
@@ -1682,15 +2021,19 @@ class WasmInstanceBuilder {
WasmGlobal& global = module_->globals[exp.index];
double num = 0;
switch (global.type) {
- case kAstI32:
+ case kWasmI32:
num = *GetRawGlobalPtr<int32_t>(global);
break;
- case kAstF32:
+ case kWasmF32:
num = *GetRawGlobalPtr<float>(global);
break;
- case kAstF64:
+ case kWasmF64:
num = *GetRawGlobalPtr<double>(global);
break;
+ case kWasmI64:
+ thrower_->LinkError(
+ "export of globals of type I64 is not allowed.");
+ break;
default:
UNREACHABLE();
}
@@ -1702,42 +2045,99 @@ class WasmInstanceBuilder {
break;
}
+ // Skip duplicates for asm.js.
+ if (module_->origin == kAsmJsOrigin) {
+ v8::Maybe<bool> status =
+ JSReceiver::HasOwnProperty(exports_object, name);
+ if (status.FromMaybe(false)) {
+ continue;
+ }
+ }
v8::Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate_, exports_object, name, &desc, Object::THROW_ON_ERROR);
if (!status.IsJust()) {
- thrower_->TypeError("export of %.*s failed.", name->length(),
+ thrower_->LinkError("export of %.*s failed.", name->length(),
name->ToCString().get());
return;
}
}
+
+ if (module_->origin == kWasmOrigin) {
+ v8::Maybe<bool> success = JSReceiver::SetIntegrityLevel(
+ exports_object, FROZEN, Object::DONT_THROW);
+ DCHECK(success.FromMaybe(false));
+ USE(success);
+ }
}
void InitializeTables(Handle<FixedArray> code_table,
Handle<WasmInstanceObject> instance) {
- Handle<FixedArray> old_function_tables =
- compiled_module_->function_tables();
int function_table_count =
static_cast<int>(module_->function_tables.size());
Handle<FixedArray> new_function_tables =
isolate_->factory()->NewFixedArray(function_table_count);
+ Handle<FixedArray> new_signature_tables =
+ isolate_->factory()->NewFixedArray(function_table_count);
for (int index = 0; index < function_table_count; ++index) {
WasmIndirectFunctionTable& table = module_->function_tables[index];
TableInstance& table_instance = table_instances_[index];
int table_size = static_cast<int>(table.min_size);
- if (table_instance.dispatch_table.is_null()) {
+ if (table_instance.function_table.is_null()) {
// Create a new dispatch table if necessary.
- table_instance.dispatch_table =
- isolate_->factory()->NewFixedArray(table_size * 2);
+ table_instance.function_table =
+ isolate_->factory()->NewFixedArray(table_size);
+ table_instance.signature_table =
+ isolate_->factory()->NewFixedArray(table_size);
for (int i = 0; i < table_size; ++i) {
// Fill the table with invalid signature indexes so that
// uninitialized entries will always fail the signature check.
- table_instance.dispatch_table->set(i, Smi::FromInt(kInvalidSigIndex));
+ table_instance.signature_table->set(i,
+ Smi::FromInt(kInvalidSigIndex));
+ }
+ } else {
+ // Table is imported, patch table bounds check
+ DCHECK(table_size <= table_instance.function_table->length());
+ if (table_size < table_instance.function_table->length()) {
+ RelocateTableSizeReferences(code_table, table_size,
+ table_instance.function_table->length());
}
}
new_function_tables->set(static_cast<int>(index),
- *table_instance.dispatch_table);
+ *table_instance.function_table);
+ new_signature_tables->set(static_cast<int>(index),
+ *table_instance.signature_table);
+ }
+
+ // Patch all code that has references to the old indirect tables.
+ Handle<FixedArray> old_function_tables =
+ compiled_module_->function_tables();
+ Handle<FixedArray> old_signature_tables =
+ compiled_module_->signature_tables();
+ for (int i = 0; i < code_table->length(); ++i) {
+ if (!code_table->get(i)->IsCode()) continue;
+ Handle<Code> code(Code::cast(code_table->get(i)), isolate_);
+ for (int j = 0; j < function_table_count; ++j) {
+ ReplaceReferenceInCode(
+ code, Handle<Object>(old_function_tables->get(j), isolate_),
+ Handle<Object>(new_function_tables->get(j), isolate_));
+ ReplaceReferenceInCode(
+ code, Handle<Object>(old_signature_tables->get(j), isolate_),
+ Handle<Object>(new_signature_tables->get(j), isolate_));
+ }
+ }
+ compiled_module_->set_function_tables(new_function_tables);
+ compiled_module_->set_signature_tables(new_signature_tables);
+ }
+
+ void LoadTableSegments(Handle<FixedArray> code_table,
+ Handle<WasmInstanceObject> instance) {
+ int function_table_count =
+ static_cast<int>(module_->function_tables.size());
+ for (int index = 0; index < function_table_count; ++index) {
+ WasmIndirectFunctionTable& table = module_->function_tables[index];
+ TableInstance& table_instance = table_instances_[index];
Handle<FixedArray> all_dispatch_tables;
if (!table_instance.table_object.is_null()) {
@@ -1745,28 +2145,24 @@ class WasmInstanceBuilder {
all_dispatch_tables = WasmTableObject::AddDispatchTable(
isolate_, table_instance.table_object,
Handle<WasmInstanceObject>::null(), index,
- Handle<FixedArray>::null());
+ Handle<FixedArray>::null(), Handle<FixedArray>::null());
}
// TODO(titzer): this does redundant work if there are multiple tables,
// since initializations are not sorted by table index.
for (auto table_init : module_->table_inits) {
uint32_t base = EvalUint32InitExpr(table_init.offset);
- if (base > static_cast<uint32_t>(table_size) ||
- (base + table_init.entries.size() >
- static_cast<uint32_t>(table_size))) {
- thrower_->CompileError("table initializer is out of bounds");
- continue;
- }
+ DCHECK(in_bounds(base, static_cast<uint32_t>(table_init.entries.size()),
+ table_instance.function_table->length()));
for (int i = 0; i < static_cast<int>(table_init.entries.size()); ++i) {
uint32_t func_index = table_init.entries[i];
WasmFunction* function = &module_->functions[func_index];
int table_index = static_cast<int>(i + base);
int32_t sig_index = table.map.Find(function->sig);
DCHECK_GE(sig_index, 0);
- table_instance.dispatch_table->set(table_index,
- Smi::FromInt(sig_index));
- table_instance.dispatch_table->set(table_index + table_size,
+ table_instance.signature_table->set(table_index,
+ Smi::FromInt(sig_index));
+ table_instance.function_table->set(table_index,
code_table->get(func_index));
if (!all_dispatch_tables.is_null()) {
@@ -1783,19 +2179,22 @@ class WasmInstanceBuilder {
temp_instance.mem_start = nullptr;
temp_instance.globals_start = nullptr;
- ModuleEnv module_env;
- module_env.module = module_;
- module_env.instance = &temp_instance;
- module_env.origin = module_->origin;
-
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
- isolate_, &module_env, wasm_code, func_index);
+ isolate_, module_, wasm_code, func_index);
+ MaybeHandle<String> func_name;
+ if (module_->origin == kAsmJsOrigin) {
+ // For modules arising from asm.js, honor the names section.
+ func_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate_, compiled_module_, function->name_offset,
+ function->name_length)
+ .ToHandleChecked();
+ }
Handle<WasmExportedFunction> js_function =
WasmExportedFunction::New(
- isolate_, instance, isolate_->factory()->empty_string(),
- wrapper_code,
+ isolate_, instance, func_name, func_index,
static_cast<int>(function->sig->parameter_count()),
- func_index);
+ wrapper_code);
js_wrappers_[func_index] = js_function;
}
table_instance.js_wrappers->set(table_index,
@@ -1814,115 +2213,57 @@ class WasmInstanceBuilder {
// Add the new dispatch table to the WebAssembly.Table object.
all_dispatch_tables = WasmTableObject::AddDispatchTable(
isolate_, table_instance.table_object, instance, index,
- table_instance.dispatch_table);
+ table_instance.function_table, table_instance.signature_table);
}
}
- // Patch all code that has references to the old indirect tables.
- for (int i = 0; i < code_table->length(); ++i) {
- if (!code_table->get(i)->IsCode()) continue;
- Handle<Code> code(Code::cast(code_table->get(i)), isolate_);
- for (int j = 0; j < function_table_count; ++j) {
- ReplaceReferenceInCode(
- code, Handle<Object>(old_function_tables->get(j), isolate_),
- Handle<Object>(new_function_tables->get(j), isolate_));
- }
- }
- compiled_module_->set_function_tables(new_function_tables);
}
};
// Instantiates a WASM module, creating a WebAssembly.Instance from a
// WebAssembly.Module.
-MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
- ErrorThrower* thrower,
- Handle<JSObject> wasm_module,
- Handle<JSReceiver> ffi,
- Handle<JSArrayBuffer> memory) {
+MaybeHandle<WasmInstanceObject> WasmModule::Instantiate(
+ Isolate* isolate, ErrorThrower* thrower,
+ Handle<WasmModuleObject> wasm_module, Handle<JSReceiver> ffi,
+ Handle<JSArrayBuffer> memory) {
WasmInstanceBuilder builder(isolate, thrower, wasm_module, ffi, memory);
return builder.Build();
}
-Handle<String> wasm::GetWasmFunctionName(Isolate* isolate,
- Handle<Object> instance_or_undef,
- uint32_t func_index) {
- if (!instance_or_undef->IsUndefined(isolate)) {
- Handle<WasmCompiledModule> compiled_module(
- Handle<WasmInstanceObject>::cast(instance_or_undef)
- ->get_compiled_module());
- MaybeHandle<String> maybe_name =
- WasmCompiledModule::GetFunctionName(compiled_module, func_index);
- if (!maybe_name.is_null()) return maybe_name.ToHandleChecked();
- }
- return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
-}
-
bool wasm::IsWasmInstance(Object* object) {
return WasmInstanceObject::IsWasmInstanceObject(object);
}
-WasmCompiledModule* wasm::GetCompiledModule(Object* object) {
- return WasmInstanceObject::cast(object)->get_compiled_module();
-}
-
-bool wasm::WasmIsAsmJs(Object* instance, Isolate* isolate) {
- if (instance->IsUndefined(isolate)) return false;
- DCHECK(IsWasmInstance(instance));
- WasmCompiledModule* compiled_module =
- GetCompiledModule(JSObject::cast(instance));
- DCHECK_EQ(compiled_module->has_asm_js_offset_tables(),
- compiled_module->script()->type() == Script::TYPE_NORMAL);
- return compiled_module->has_asm_js_offset_tables();
-}
-
Handle<Script> wasm::GetScript(Handle<JSObject> instance) {
- DCHECK(IsWasmInstance(*instance));
- WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
- DCHECK(compiled_module->has_script());
- return compiled_module->script();
-}
-
-int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
- int byte_offset) {
- return WasmDebugInfo::GetAsmJsSourcePosition(GetDebugInfo(instance),
- func_index, byte_offset);
-}
-
-Handle<SeqOneByteString> wasm::GetWasmBytes(Handle<JSObject> object) {
- return Handle<WasmInstanceObject>::cast(object)
- ->get_compiled_module()
- ->module_bytes();
-}
-
-Handle<WasmDebugInfo> wasm::GetDebugInfo(Handle<JSObject> object) {
- auto instance = Handle<WasmInstanceObject>::cast(object);
- if (instance->has_debug_info()) {
- Handle<WasmDebugInfo> info(instance->get_debug_info(),
- instance->GetIsolate());
- return info;
- }
- Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(instance);
- instance->set_debug_info(*new_info);
- return new_info;
+ WasmCompiledModule* compiled_module =
+ WasmInstanceObject::cast(*instance)->compiled_module();
+ return handle(compiled_module->script());
}
-int wasm::GetNumberOfFunctions(Handle<JSObject> object) {
- return static_cast<int>(
- Handle<WasmInstanceObject>::cast(object)->module()->functions.size());
+bool wasm::IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) {
+ return isolate->allow_code_gen_callback() == nullptr ||
+ isolate->allow_code_gen_callback()(v8::Utils::ToLocal(context));
}
// TODO(clemensh): origin can be inferred from asm_js_script; remove it.
MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
ModuleOrigin origin, Handle<Script> asm_js_script,
- const byte* asm_js_offset_tables_start,
- const byte* asm_js_offset_tables_end) {
+ Vector<const byte> asm_js_offset_table_bytes) {
MaybeHandle<WasmModuleObject> nothing;
+
+ if (origin != kAsmJsOrigin &&
+ !IsWasmCodegenAllowed(isolate, isolate->native_context())) {
+ thrower->CompileError("Wasm code generation disallowed in this context");
+ return nothing;
+ }
+
ModuleResult result = DecodeWasmModule(isolate, start, end, false, origin);
if (result.failed()) {
if (result.val) delete result.val;
thrower->CompileFailed("Wasm decoding failed", result);
return nothing;
}
+
// The {module_wrapper} will take ownership of the {WasmModule} object,
// and it will be destroyed when the GC reclaims the wrapper object.
Handle<WasmModuleWrapper> module_wrapper =
@@ -1930,61 +2271,15 @@ MaybeHandle<WasmModuleObject> wasm::CreateModuleObjectFromBytes(
// Compile the functions of the module, producing a compiled module.
MaybeHandle<WasmCompiledModule> maybe_compiled_module =
- result.val->CompileFunctions(isolate, module_wrapper, thrower);
+ result.val->CompileFunctions(isolate, module_wrapper, thrower,
+ ModuleWireBytes(start, end), asm_js_script,
+ asm_js_offset_table_bytes);
if (maybe_compiled_module.is_null()) return nothing;
Handle<WasmCompiledModule> compiled_module =
maybe_compiled_module.ToHandleChecked();
- DCHECK_EQ(origin == kAsmJsOrigin, !asm_js_script.is_null());
- DCHECK(!compiled_module->has_script());
- DCHECK(!compiled_module->has_asm_js_offset_tables());
- if (origin == kAsmJsOrigin) {
- // Set script for the asm.js source, and the offset table mapping wasm byte
- // offsets to source positions.
- compiled_module->set_script(asm_js_script);
- size_t offset_tables_len =
- asm_js_offset_tables_end - asm_js_offset_tables_start;
- DCHECK_GE(static_cast<size_t>(kMaxInt), offset_tables_len);
- Handle<ByteArray> offset_tables =
- isolate->factory()->NewByteArray(static_cast<int>(offset_tables_len));
- memcpy(offset_tables->GetDataStartAddress(), asm_js_offset_tables_start,
- offset_tables_len);
- compiled_module->set_asm_js_offset_tables(offset_tables);
- } else {
- // Create a new Script object representing this wasm module, store it in the
- // compiled wasm module, and register it at the debugger.
- Handle<Script> script =
- isolate->factory()->NewScript(isolate->factory()->empty_string());
- script->set_type(Script::TYPE_WASM);
-
- DCHECK_GE(kMaxInt, end - start);
- int hash = StringHasher::HashSequentialString(
- reinterpret_cast<const char*>(start), static_cast<int>(end - start),
- kZeroHashSeed);
-
- const int kBufferSize = 50;
- char buffer[kBufferSize];
- int url_chars = SNPrintF(ArrayVector(buffer), "wasm://wasm/%08x", hash);
- DCHECK(url_chars >= 0 && url_chars < kBufferSize);
- MaybeHandle<String> url_str = isolate->factory()->NewStringFromOneByte(
- Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), url_chars),
- TENURED);
- script->set_source_url(*url_str.ToHandleChecked());
-
- int name_chars = SNPrintF(ArrayVector(buffer), "wasm-%08x", hash);
- DCHECK(name_chars >= 0 && name_chars < kBufferSize);
- MaybeHandle<String> name_str = isolate->factory()->NewStringFromOneByte(
- Vector<const uint8_t>(reinterpret_cast<uint8_t*>(buffer), name_chars),
- TENURED);
- script->set_name(*name_str.ToHandleChecked());
-
- script->set_wasm_compiled_module(*compiled_module);
- compiled_module->set_script(script);
- isolate->debug()->OnAfterCompile(script);
- }
-
return WasmModuleObject::New(isolate, compiled_module);
}
@@ -2000,24 +2295,25 @@ bool wasm::ValidateModuleBytes(Isolate* isolate, const byte* start,
return result.ok();
}
-MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(Isolate* isolate,
- Handle<JSObject> object) {
+MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(
+ Isolate* isolate, Handle<WasmInstanceObject> object) {
auto instance = Handle<WasmInstanceObject>::cast(object);
if (instance->has_memory_buffer()) {
- return Handle<JSArrayBuffer>(instance->get_memory_buffer(), isolate);
+ return Handle<JSArrayBuffer>(instance->memory_buffer(), isolate);
}
return MaybeHandle<JSArrayBuffer>();
}
-void SetInstanceMemory(Handle<JSObject> object, JSArrayBuffer* buffer) {
+void SetInstanceMemory(Handle<WasmInstanceObject> instance,
+ JSArrayBuffer* buffer) {
DisallowHeapAllocation no_gc;
- auto instance = Handle<WasmInstanceObject>::cast(object);
instance->set_memory_buffer(buffer);
- instance->get_compiled_module()->set_ptr_to_memory(buffer);
+ instance->compiled_module()->set_ptr_to_memory(buffer);
}
int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
- Handle<JSObject> instance) {
+ Handle<WasmInstanceObject> instance) {
+ DCHECK(IsWasmInstance(*instance));
MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
GetInstanceMemory(isolate, instance);
Handle<JSArrayBuffer> buffer;
@@ -2028,92 +2324,222 @@ int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
}
}
-uint32_t GetMaxInstanceMemorySize(Isolate* isolate,
- Handle<WasmInstanceObject> instance) {
+uint32_t GetMaxInstanceMemoryPages(Isolate* isolate,
+ Handle<WasmInstanceObject> instance) {
if (instance->has_memory_object()) {
- Handle<WasmMemoryObject> memory_object(instance->get_memory_object(),
- isolate);
-
- int maximum = memory_object->maximum_pages();
- if (maximum > 0) return static_cast<uint32_t>(maximum);
+ Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate);
+ if (memory_object->has_maximum_pages()) {
+ uint32_t maximum = static_cast<uint32_t>(memory_object->maximum_pages());
+ if (maximum < kV8MaxWasmMemoryPages) return maximum;
+ }
}
- uint32_t compiled_max_pages =
- instance->get_compiled_module()->max_mem_pages();
+ uint32_t compiled_max_pages = instance->compiled_module()->max_mem_pages();
isolate->counters()->wasm_max_mem_pages_count()->AddSample(
compiled_max_pages);
if (compiled_max_pages != 0) return compiled_max_pages;
- return WasmModule::kV8MaxPages;
+ return kV8MaxWasmMemoryPages;
}
-int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> object,
- uint32_t pages) {
- if (!IsWasmInstance(*object)) return -1;
- auto instance = Handle<WasmInstanceObject>::cast(object);
- if (pages == 0) return GetInstanceMemorySize(isolate, instance);
- uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
-
- Address old_mem_start = nullptr;
- uint32_t old_size = 0, new_size = 0;
-
- MaybeHandle<JSArrayBuffer> maybe_mem_buffer =
- GetInstanceMemory(isolate, instance);
+Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate,
+ MaybeHandle<JSArrayBuffer> buffer,
+ uint32_t pages, uint32_t max_pages) {
Handle<JSArrayBuffer> old_buffer;
- if (!maybe_mem_buffer.ToHandle(&old_buffer) ||
- old_buffer->backing_store() == nullptr) {
- // If module object does not have linear memory associated with it,
- // Allocate new array buffer of given size.
- new_size = pages * WasmModule::kPageSize;
- if (max_pages < pages) return -1;
- } else {
+ Address old_mem_start = nullptr;
+ uint32_t old_size = 0;
+ if (buffer.ToHandle(&old_buffer) && old_buffer->backing_store() != nullptr) {
old_mem_start = static_cast<Address>(old_buffer->backing_store());
- old_size = old_buffer->byte_length()->Number();
- // If the old memory was zero-sized, we should have been in the
- // "undefined" case above.
DCHECK_NOT_NULL(old_mem_start);
- DCHECK(old_size + pages * WasmModule::kPageSize <=
- std::numeric_limits<uint32_t>::max());
- new_size = old_size + pages * WasmModule::kPageSize;
+ old_size = old_buffer->byte_length()->Number();
}
-
+ DCHECK(old_size + pages * WasmModule::kPageSize <=
+ std::numeric_limits<uint32_t>::max());
+ uint32_t new_size = old_size + pages * WasmModule::kPageSize;
if (new_size <= old_size || max_pages * WasmModule::kPageSize < new_size ||
- WasmModule::kV8MaxPages * WasmModule::kPageSize < new_size) {
- return -1;
- }
- Handle<JSArrayBuffer> buffer = NewArrayBuffer(isolate, new_size);
- if (buffer.is_null()) return -1;
- Address new_mem_start = static_cast<Address>(buffer->backing_store());
- if (old_size != 0) {
- memcpy(new_mem_start, old_mem_start, old_size);
- }
- SetInstanceMemory(instance, *buffer);
- Handle<FixedArray> code_table = instance->get_compiled_module()->code_table();
- RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
- old_size, new_size);
- if (instance->has_memory_object()) {
- instance->get_memory_object()->set_buffer(*buffer);
+ kV8MaxWasmMemoryPages * WasmModule::kPageSize < new_size) {
+ return Handle<JSArrayBuffer>::null();
}
+ Handle<JSArrayBuffer> new_buffer;
+ if (!old_buffer.is_null() && old_buffer->has_guard_region()) {
+ // We don't move the backing store, we simply change the protection to make
+ // more of it accessible.
+ base::OS::Unprotect(old_buffer->backing_store(), new_size);
+ reinterpret_cast<v8::Isolate*>(isolate)
+ ->AdjustAmountOfExternalAllocatedMemory(pages * WasmModule::kPageSize);
+ Handle<Object> new_size_object =
+ isolate->factory()->NewNumberFromSize(new_size);
+ old_buffer->set_byte_length(*new_size_object);
+ new_buffer = old_buffer;
+ } else {
+ const bool enable_guard_regions = false;
+ new_buffer = NewArrayBuffer(isolate, new_size, enable_guard_regions);
+ if (new_buffer.is_null()) return new_buffer;
+ Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
+ if (old_size != 0) {
+ memcpy(new_mem_start, old_mem_start, old_size);
+ }
+ }
+ return new_buffer;
+}
+
+void UncheckedUpdateInstanceMemory(Isolate* isolate,
+ Handle<WasmInstanceObject> instance,
+ Address old_mem_start, uint32_t old_size) {
+ DCHECK(instance->has_memory_buffer());
+ Handle<JSArrayBuffer> new_buffer(instance->memory_buffer());
+ uint32_t new_size = new_buffer->byte_length()->Number();
+ DCHECK(new_size <= std::numeric_limits<uint32_t>::max());
+ Address new_mem_start = static_cast<Address>(new_buffer->backing_store());
+ DCHECK_NOT_NULL(new_mem_start);
+ Handle<FixedArray> code_table = instance->compiled_module()->code_table();
+ RelocateMemoryReferencesInCode(
+ code_table, instance->compiled_module()->module()->num_imported_functions,
+ old_mem_start, new_mem_start, old_size, new_size);
+}
+
+int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate,
+ Handle<WasmMemoryObject> receiver,
+ uint32_t pages) {
+ DCHECK(WasmJs::IsWasmMemoryObject(isolate, receiver));
+ Handle<WasmMemoryObject> memory_object =
+ handle(WasmMemoryObject::cast(*receiver));
+ MaybeHandle<JSArrayBuffer> memory_buffer = handle(memory_object->buffer());
+ Handle<JSArrayBuffer> old_buffer;
+ uint32_t old_size = 0;
+ Address old_mem_start = nullptr;
+ if (memory_buffer.ToHandle(&old_buffer) &&
+ old_buffer->backing_store() != nullptr) {
+ old_size = old_buffer->byte_length()->Number();
+ old_mem_start = static_cast<Address>(old_buffer->backing_store());
+ }
+ // Return current size if grow by 0
+ if (pages == 0) {
+ DCHECK(old_size % WasmModule::kPageSize == 0);
+ return (old_size / WasmModule::kPageSize);
+ }
+ Handle<JSArrayBuffer> new_buffer;
+ if (!memory_object->has_instances_link()) {
+ // Memory object does not have an instance associated with it, just grow
+ uint32_t max_pages;
+ if (memory_object->has_maximum_pages()) {
+ max_pages = static_cast<uint32_t>(memory_object->maximum_pages());
+ if (kV8MaxWasmMemoryPages < max_pages) return -1;
+ } else {
+ max_pages = kV8MaxWasmMemoryPages;
+ }
+ new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
+ if (new_buffer.is_null()) return -1;
+ } else {
+ Handle<WasmInstanceWrapper> instance_wrapper(
+ memory_object->instances_link());
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ DCHECK(instance_wrapper->has_instance());
+ Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
+ DCHECK(IsWasmInstance(*instance));
+ uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance);
+
+ // Grow memory object buffer and update instances associated with it.
+ new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
+ if (new_buffer.is_null()) return -1;
+ DCHECK(!instance_wrapper->has_previous());
+ SetInstanceMemory(instance, *new_buffer);
+ UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
+ while (instance_wrapper->has_next()) {
+ instance_wrapper = instance_wrapper->next_wrapper();
+ DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
+ Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
+ DCHECK(IsWasmInstance(*instance));
+ SetInstanceMemory(instance, *new_buffer);
+ UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
+ }
+ }
+ memory_object->set_buffer(*new_buffer);
DCHECK(old_size % WasmModule::kPageSize == 0);
return (old_size / WasmModule::kPageSize);
}
+int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
+ uint32_t pages) {
+ if (!IsWasmInstance(*instance)) return -1;
+ if (pages == 0) return GetInstanceMemorySize(isolate, instance);
+ Handle<WasmInstanceObject> instance_obj(WasmInstanceObject::cast(*instance));
+ if (!instance_obj->has_memory_object()) {
+ // No other instances to grow, grow just the one.
+ MaybeHandle<JSArrayBuffer> instance_buffer =
+ GetInstanceMemory(isolate, instance);
+ Handle<JSArrayBuffer> old_buffer;
+ uint32_t old_size = 0;
+ Address old_mem_start = nullptr;
+ if (instance_buffer.ToHandle(&old_buffer) &&
+ old_buffer->backing_store() != nullptr) {
+ old_size = old_buffer->byte_length()->Number();
+ old_mem_start = static_cast<Address>(old_buffer->backing_store());
+ }
+ uint32_t max_pages = GetMaxInstanceMemoryPages(isolate, instance_obj);
+ Handle<JSArrayBuffer> buffer =
+ GrowMemoryBuffer(isolate, instance_buffer, pages, max_pages);
+ if (buffer.is_null()) return -1;
+ SetInstanceMemory(instance, *buffer);
+ UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
+ DCHECK(old_size % WasmModule::kPageSize == 0);
+ return (old_size / WasmModule::kPageSize);
+ } else {
+ return GrowWebAssemblyMemory(isolate, handle(instance_obj->memory_object()),
+ pages);
+ }
+}
+
+void wasm::GrowDispatchTables(Isolate* isolate,
+ Handle<FixedArray> dispatch_tables,
+ uint32_t old_size, uint32_t count) {
+ DCHECK_EQ(0, dispatch_tables->length() % 4);
+ for (int i = 0; i < dispatch_tables->length(); i += 4) {
+ Handle<FixedArray> old_function_table(
+ FixedArray::cast(dispatch_tables->get(i + 2)));
+ Handle<FixedArray> old_signature_table(
+ FixedArray::cast(dispatch_tables->get(i + 3)));
+ Handle<FixedArray> new_function_table =
+ isolate->factory()->CopyFixedArrayAndGrow(old_function_table, count);
+ Handle<FixedArray> new_signature_table =
+ isolate->factory()->CopyFixedArrayAndGrow(old_signature_table, count);
+
+ // Get code table for the instance
+ Handle<WasmInstanceObject> instance(
+ WasmInstanceObject::cast(dispatch_tables->get(i)));
+ Handle<FixedArray> code_table(instance->compiled_module()->code_table());
+
+ // Relocate size references
+ RelocateTableSizeReferences(code_table, old_size, old_size + count);
+
+ // Replace references of old tables with new tables.
+ for (int j = 0; j < code_table->length(); ++j) {
+ if (!code_table->get(j)->IsCode()) continue;
+ Handle<Code> code = Handle<Code>(Code::cast(code_table->get(j)));
+ ReplaceReferenceInCode(code, old_function_table, new_function_table);
+ ReplaceReferenceInCode(code, old_signature_table, new_signature_table);
+ }
+
+ // Update dispatch tables with new function/signature tables
+ dispatch_tables->set(i + 2, *new_function_table);
+ dispatch_tables->set(i + 3, *new_signature_table);
+ }
+}
+
void testing::ValidateInstancesChain(Isolate* isolate,
- Handle<JSObject> wasm_module,
+ Handle<WasmModuleObject> module_obj,
int instance_count) {
CHECK_GE(instance_count, 0);
DisallowHeapAllocation no_gc;
- WasmCompiledModule* compiled_module =
- WasmCompiledModule::cast(wasm_module->GetInternalField(0));
+ WasmCompiledModule* compiled_module = module_obj->compiled_module();
CHECK_EQ(JSObject::cast(compiled_module->ptr_to_weak_wasm_module()->value()),
- *wasm_module);
+ *module_obj);
Object* prev = nullptr;
int found_instances = compiled_module->has_weak_owning_instance() ? 1 : 0;
WasmCompiledModule* current_instance = compiled_module;
while (current_instance->has_weak_next_instance()) {
CHECK((prev == nullptr && !current_instance->has_weak_prev_instance()) ||
current_instance->ptr_to_weak_prev_instance()->value() == prev);
- CHECK_EQ(current_instance->ptr_to_weak_wasm_module()->value(),
- *wasm_module);
+ CHECK_EQ(current_instance->ptr_to_weak_wasm_module()->value(), *module_obj);
CHECK(IsWasmInstance(
current_instance->ptr_to_weak_owning_instance()->value()));
prev = current_instance;
@@ -2126,63 +2552,222 @@ void testing::ValidateInstancesChain(Isolate* isolate,
}
void testing::ValidateModuleState(Isolate* isolate,
- Handle<JSObject> wasm_module) {
+ Handle<WasmModuleObject> module_obj) {
DisallowHeapAllocation no_gc;
- WasmCompiledModule* compiled_module =
- WasmCompiledModule::cast(wasm_module->GetInternalField(0));
+ WasmCompiledModule* compiled_module = module_obj->compiled_module();
CHECK(compiled_module->has_weak_wasm_module());
- CHECK_EQ(compiled_module->ptr_to_weak_wasm_module()->value(), *wasm_module);
+ CHECK_EQ(compiled_module->ptr_to_weak_wasm_module()->value(), *module_obj);
CHECK(!compiled_module->has_weak_prev_instance());
CHECK(!compiled_module->has_weak_next_instance());
CHECK(!compiled_module->has_weak_owning_instance());
}
void testing::ValidateOrphanedInstance(Isolate* isolate,
- Handle<JSObject> object) {
+ Handle<WasmInstanceObject> instance) {
DisallowHeapAllocation no_gc;
- WasmInstanceObject* instance = WasmInstanceObject::cast(*object);
- WasmCompiledModule* compiled_module = instance->get_compiled_module();
+ WasmCompiledModule* compiled_module = instance->compiled_module();
CHECK(compiled_module->has_weak_wasm_module());
CHECK(compiled_module->ptr_to_weak_wasm_module()->cleared());
}
-void WasmCompiledModule::RecreateModuleWrapper(Isolate* isolate,
- Handle<FixedArray> array) {
- Handle<WasmCompiledModule> compiled_module(
- reinterpret_cast<WasmCompiledModule*>(*array), isolate);
+Handle<JSArray> wasm::GetImports(Isolate* isolate,
+ Handle<WasmModuleObject> module_object) {
+ Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
+ isolate);
+ Factory* factory = isolate->factory();
- WasmModule* module = nullptr;
+ Handle<String> module_string = factory->InternalizeUtf8String("module");
+ Handle<String> name_string = factory->InternalizeUtf8String("name");
+ Handle<String> kind_string = factory->InternalizeUtf8String("kind");
+
+ Handle<String> function_string = factory->InternalizeUtf8String("function");
+ Handle<String> table_string = factory->InternalizeUtf8String("table");
+ Handle<String> memory_string = factory->InternalizeUtf8String("memory");
+ Handle<String> global_string = factory->InternalizeUtf8String("global");
+
+ // Create the result array.
+ WasmModule* module = compiled_module->module();
+ int num_imports = static_cast<int>(module->import_table.size());
+ Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0);
+ Handle<FixedArray> storage = factory->NewFixedArray(num_imports);
+ JSArray::SetContent(array_object, storage);
+ array_object->set_length(Smi::FromInt(num_imports));
+
+ Handle<JSFunction> object_function =
+ Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
+
+ // Populate the result array.
+ for (int index = 0; index < num_imports; ++index) {
+ WasmImport& import = module->import_table[index];
+
+ Handle<JSObject> entry = factory->NewJSObject(object_function);
+
+ Handle<String> import_kind;
+ switch (import.kind) {
+ case kExternalFunction:
+ import_kind = function_string;
+ break;
+ case kExternalTable:
+ import_kind = table_string;
+ break;
+ case kExternalMemory:
+ import_kind = memory_string;
+ break;
+ case kExternalGlobal:
+ import_kind = global_string;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ MaybeHandle<String> import_module =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, import.module_name_offset,
+ import.module_name_length);
+
+ MaybeHandle<String> import_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, import.field_name_offset,
+ import.field_name_length);
+
+ JSObject::AddProperty(entry, module_string, import_module.ToHandleChecked(),
+ NONE);
+ JSObject::AddProperty(entry, name_string, import_name.ToHandleChecked(),
+ NONE);
+ JSObject::AddProperty(entry, kind_string, import_kind, NONE);
+
+ storage->set(index, *entry);
+ }
+
+ return array_object;
+}
+
+Handle<JSArray> wasm::GetExports(Isolate* isolate,
+ Handle<WasmModuleObject> module_object) {
+ Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
+ isolate);
+ Factory* factory = isolate->factory();
+
+ Handle<String> name_string = factory->InternalizeUtf8String("name");
+ Handle<String> kind_string = factory->InternalizeUtf8String("kind");
+
+ Handle<String> function_string = factory->InternalizeUtf8String("function");
+ Handle<String> table_string = factory->InternalizeUtf8String("table");
+ Handle<String> memory_string = factory->InternalizeUtf8String("memory");
+ Handle<String> global_string = factory->InternalizeUtf8String("global");
+
+ // Create the result array.
+ WasmModule* module = compiled_module->module();
+ int num_exports = static_cast<int>(module->export_table.size());
+ Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0);
+ Handle<FixedArray> storage = factory->NewFixedArray(num_exports);
+ JSArray::SetContent(array_object, storage);
+ array_object->set_length(Smi::FromInt(num_exports));
+
+ Handle<JSFunction> object_function =
+ Handle<JSFunction>(isolate->native_context()->object_function(), isolate);
+
+ // Populate the result array.
+ for (int index = 0; index < num_exports; ++index) {
+ WasmExport& exp = module->export_table[index];
+
+ Handle<String> export_kind;
+ switch (exp.kind) {
+ case kExternalFunction:
+ export_kind = function_string;
+ break;
+ case kExternalTable:
+ export_kind = table_string;
+ break;
+ case kExternalMemory:
+ export_kind = memory_string;
+ break;
+ case kExternalGlobal:
+ export_kind = global_string;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ Handle<JSObject> entry = factory->NewJSObject(object_function);
+
+ MaybeHandle<String> export_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, exp.name_offset, exp.name_length);
+
+ JSObject::AddProperty(entry, name_string, export_name.ToHandleChecked(),
+ NONE);
+ JSObject::AddProperty(entry, kind_string, export_kind, NONE);
+
+ storage->set(index, *entry);
+ }
+
+ return array_object;
+}
+
+Handle<JSArray> wasm::GetCustomSections(Isolate* isolate,
+ Handle<WasmModuleObject> module_object,
+ Handle<String> name,
+ ErrorThrower* thrower) {
+ Handle<WasmCompiledModule> compiled_module(module_object->compiled_module(),
+ isolate);
+ Factory* factory = isolate->factory();
+
+ std::vector<CustomSectionOffset> custom_sections;
{
- Handle<SeqOneByteString> module_bytes = compiled_module->module_bytes();
- // We parse the module again directly from the module bytes, so
- // the underlying storage must not be moved meanwhile.
- DisallowHeapAllocation no_allocation;
+ DisallowHeapAllocation no_gc; // for raw access to string bytes.
+ Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(),
+ isolate);
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);
+ custom_sections = DecodeCustomSections(start, end);
+ }
+
+ std::vector<Handle<Object>> matching_sections;
+
+ // Gather matching sections.
+ for (auto section : custom_sections) {
+ MaybeHandle<String> section_name =
+ WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
+ isolate, compiled_module, section.name_offset, section.name_length);
+
+ if (!name->Equals(*section_name.ToHandleChecked())) continue;
+
+ // Make a copy of the payload data in the section.
+ bool is_external; // Set by TryAllocateBackingStore
+ void* memory = TryAllocateBackingStore(isolate, section.payload_length,
+ false, is_external);
+
+ Handle<Object> section_data = factory->undefined_value();
+ if (memory) {
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
+ JSArrayBuffer::Setup(buffer, isolate, is_external, memory,
+ static_cast<int>(section.payload_length));
+ DisallowHeapAllocation no_gc; // for raw access to string bytes.
+ Handle<SeqOneByteString> module_bytes(compiled_module->module_bytes(),
+ isolate);
+ const byte* start =
+ reinterpret_cast<const byte*>(module_bytes->GetCharsAddress());
+ memcpy(memory, start + section.payload_offset, section.payload_length);
+ section_data = buffer;
+ } else {
+ thrower->RangeError("out of memory allocating custom section data");
+ return Handle<JSArray>();
+ }
+
+ matching_sections.push_back(section_data);
}
- Handle<WasmModuleWrapper> module_wrapper =
- WasmModuleWrapper::New(isolate, module);
+ int num_custom_sections = static_cast<int>(matching_sections.size());
+ Handle<JSArray> array_object = factory->NewJSArray(FAST_ELEMENTS, 0, 0);
+ Handle<FixedArray> storage = factory->NewFixedArray(num_custom_sections);
+ JSArray::SetContent(array_object, storage);
+ array_object->set_length(Smi::FromInt(num_custom_sections));
- compiled_module->set_module_wrapper(module_wrapper);
- DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
-}
+ for (int i = 0; i < num_custom_sections; i++) {
+ storage->set(i, *matching_sections[i]);
+ }
-MaybeHandle<String> WasmCompiledModule::GetFunctionName(
- 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];
- Isolate* isolate = compiled_module->GetIsolate();
- MaybeHandle<String> string = ExtractStringFromModuleBytes(
- isolate, compiled_module, function.name_offset, function.name_length);
- if (!string.is_null()) return string.ToHandleChecked();
- return {};
+ return array_object;
}