Commit 418b239f authored by titzer's avatar titzer Committed by Commit bot

[wasm] Use a Managed<WasmModule> to hold metadata about modules.

This CL refactors the handling of metadata associated with WebAssembly
modules to reduce the duplicate marshalling of data from the C++ world
to the JavaScript world. It does this by wrapping the C++ WasmModule*
object in a Foreign that is rooted from the on-heap WasmCompiledModule
(which is itself just a FixedArray). Upon serialization, the C++ object
is ignored and the original WASM wire bytes are serialized. Upon
deserialization, the C++ object is reconstituted by reparsing the bytes.

This is motivated by increasing complications in implementing the JS
API, in particular WebAssembly.Table, which must perform signature
canonicalization across instances.

Additionally, this CL implements the proper base + offset initialization
behavior for tables.

R=rossberg@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org,yangguo@chromium.org
BUG=v8:5507, chromium:575167, chromium:657316

Review-Url: https://chromiumcodereview.appspot.com/2424623002
Cr-Commit-Position: refs/heads/master@{#40434}
parent db733efd
......@@ -7204,12 +7204,9 @@ WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
i::Handle<i::wasm::WasmCompiledModule> compiled_part =
i::handle(i::wasm::WasmCompiledModule::cast(obj->GetInternalField(0)));
i::Handle<i::SeqOneByteString> wire_bytes = compiled_part->module_bytes();
compiled_part->reset_module_bytes();
std::unique_ptr<i::ScriptData> script_data =
i::WasmCompiledModuleSerializer::SerializeWasmModule(obj->GetIsolate(),
compiled_part);
compiled_part->set_module_bytes(wire_bytes);
script_data->ReleaseDataOwnership();
size_t size = static_cast<size_t>(script_data->length());
......@@ -7238,7 +7235,7 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Deserialize(
i::Handle<i::wasm::WasmCompiledModule> compiled_module =
handle(i::wasm::WasmCompiledModule::cast(*compiled_part));
return Local<WasmCompiledModule>::Cast(
Utils::ToLocal(i::wasm::CreateCompiledModuleObject(
Utils::ToLocal(i::wasm::CreateWasmModuleObject(
i_isolate, compiled_module, i::wasm::ModuleOrigin::kWasmOrigin)));
}
......
......@@ -306,6 +306,44 @@ MaybeHandle<String> Factory::NewStringFromUtf8(Vector<const char> string,
return result;
}
MaybeHandle<String> Factory::NewStringFromUtf8SubString(
Handle<SeqOneByteString> str, int begin, int length,
PretenureFlag pretenure) {
// Check for ASCII first since this is the common case.
const char* start = reinterpret_cast<const char*>(str->GetChars() + begin);
int non_ascii_start = String::NonAsciiStart(start, length);
if (non_ascii_start >= length) {
// If the string is ASCII, we can just make a substring.
// TODO(v8): the pretenure flag is ignored in this case.
return NewSubString(str, begin, begin + length);
}
// Non-ASCII and we need to decode.
Access<UnicodeCache::Utf8Decoder> decoder(
isolate()->unicode_cache()->utf8_decoder());
decoder->Reset(start + non_ascii_start, length - non_ascii_start);
int utf16_length = static_cast<int>(decoder->Utf16Length());
DCHECK(utf16_length > 0);
// Allocate string.
Handle<SeqTwoByteString> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate(), result,
NewRawTwoByteString(non_ascii_start + utf16_length, pretenure), String);
// Reset the decoder, because the original {str} may have moved.
const char* ascii_data =
reinterpret_cast<const char*>(str->GetChars() + begin);
decoder->Reset(ascii_data + non_ascii_start, length - non_ascii_start);
// Copy ASCII portion.
uint16_t* data = result->GetChars();
for (int i = 0; i < non_ascii_start; i++) {
*data++ = *ascii_data++;
}
// Now write the remainder.
decoder->WriteUtf16(data, utf16_length);
return result;
}
MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string,
int length,
PretenureFlag pretenure) {
......
......@@ -183,6 +183,10 @@ class V8_EXPORT_PRIVATE Factory final {
MUST_USE_RESULT MaybeHandle<String> NewStringFromUtf8(
Vector<const char> str, PretenureFlag pretenure = NOT_TENURED);
MUST_USE_RESULT MaybeHandle<String> NewStringFromUtf8SubString(
Handle<SeqOneByteString> str, int begin, int end,
PretenureFlag pretenure = NOT_TENURED);
MUST_USE_RESULT MaybeHandle<String> NewStringFromTwoByte(
Vector<const uc16> str, PretenureFlag pretenure = NOT_TENURED);
......
......@@ -767,7 +767,7 @@ RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
if (!maybe_compiled_module.ToHandle(&compiled_module)) {
return isolate->heap()->undefined_value();
}
return *wasm::CreateCompiledModuleObject(
return *wasm::CreateWasmModuleObject(
isolate, Handle<wasm::WasmCompiledModule>::cast(compiled_module),
wasm::kWasmOrigin);
}
......
......@@ -32,7 +32,16 @@ class Signature : public ZoneObject {
return reps_[index];
}
const T* raw_data() const { return reps_; }
bool Equals(Signature* that) {
if (this == that) return true;
if (this->parameter_count() != that->parameter_count()) return false;
if (this->return_count() != that->return_count()) return false;
size_t size = this->return_count() + this->parameter_count();
for (size_t i = 0; i < size; i++) {
if (this->reps_[i] != that->reps_[i]) return false;
}
return true;
}
// For incrementally building signatures.
class Builder {
......
......@@ -12,6 +12,7 @@
#include "src/snapshot/deserializer.h"
#include "src/snapshot/snapshot.h"
#include "src/version.h"
#include "src/wasm/wasm-module.h"
namespace v8 {
namespace internal {
......@@ -217,7 +218,9 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
}
std::unique_ptr<ScriptData> WasmCompiledModuleSerializer::SerializeWasmModule(
Isolate* isolate, Handle<FixedArray> compiled_module) {
Isolate* isolate, Handle<FixedArray> input) {
Handle<wasm::WasmCompiledModule> compiled_module =
Handle<wasm::WasmCompiledModule>::cast(input);
WasmCompiledModuleSerializer wasm_cs(isolate, 0);
wasm_cs.reference_map()->AddAttachedReference(*isolate->native_context());
ScriptData* data = wasm_cs.Serialize(compiled_module);
......@@ -247,7 +250,10 @@ MaybeHandle<FixedArray> WasmCompiledModuleSerializer::DeserializeWasmModule(
MaybeHandle<HeapObject> obj = deserializer.DeserializeObject(isolate);
if (obj.is_null() || !obj.ToHandleChecked()->IsFixedArray()) return nothing;
return Handle<FixedArray>::cast(obj.ToHandleChecked());
Handle<FixedArray> compiled_module =
Handle<FixedArray>::cast(obj.ToHandleChecked());
wasm::WasmCompiledModule::RecreateModuleWrapper(isolate, compiled_module);
return compiled_module;
}
class Checksum {
......
......@@ -74,7 +74,9 @@ class WasmCompiledModuleSerializer : public CodeSerializer {
}
}
bool ElideObject(Object* obj) override { return obj->IsWeakCell(); };
bool ElideObject(Object* obj) override {
return obj->IsWeakCell() || obj->IsForeign();
};
private:
WasmCompiledModuleSerializer(Isolate* isolate, uint32_t source_hash)
......
......@@ -46,6 +46,7 @@ class Managed : public Foreign {
Managed<CppType>** p =
reinterpret_cast<Managed<CppType>**>(data.GetParameter());
delete (*p)->get();
(*p)->set_foreign_address(0);
GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
}
};
......
......@@ -1077,10 +1077,9 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
} // namespace
ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
const byte* module_start, const byte* module_end,
bool verify_functions, ModuleOrigin origin) {
size_t decode_memory_start = zone->allocation_size();
ModuleResult DecodeWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end, bool verify_functions,
ModuleOrigin origin) {
HistogramTimerScope wasm_decode_module_time_scope(
isolate->counters()->wasm_decode_module_time());
size_t size = module_end - module_start;
......@@ -1089,12 +1088,18 @@ ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
// TODO(bradnelson): Improve histogram handling of size_t.
isolate->counters()->wasm_module_size_bytes()->AddSample(
static_cast<int>(size));
WasmModule* module = new WasmModule();
// Signatures are stored in zone memory, which have the same lifetime
// as the {module}.
Zone* zone = new Zone(isolate->allocator(), ZONE_NAME);
WasmModule* module = new WasmModule(zone, module_start);
ModuleDecoder decoder(zone, module_start, module_end, origin);
ModuleResult result = decoder.DecodeModule(module, verify_functions);
// TODO(bradnelson): Improve histogram handling of size_t.
// TODO(titzer): this isn't accurate, since it doesn't count the data
// allocated on the C++ heap.
// https://bugs.chromium.org/p/chromium/issues/detail?id=657320
isolate->counters()->wasm_decode_module_peak_memory_bytes()->AddSample(
static_cast<int>(zone->allocation_size() - decode_memory_start));
static_cast<int>(zone->allocation_size()));
return result;
}
......
......@@ -22,7 +22,7 @@ typedef std::vector<std::vector<std::pair<int, int>>> AsmJsOffsets;
typedef Result<AsmJsOffsets> AsmJsOffsetsResult;
// Decodes the bytes of a WASM module between {module_start} and {module_end}.
V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(Isolate* isolate,
const byte* module_start,
const byte* module_end,
bool verify_functions,
......
......@@ -104,10 +104,8 @@ void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
RawBuffer buffer = GetRawBufferSource(args[0], &thrower);
if (thrower.error()) return;
i::Zone zone(isolate->allocator(), ZONE_NAME);
internal::wasm::ModuleResult result =
internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end,
true, internal::wasm::kWasmOrigin);
internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
isolate, buffer.start, buffer.end, true, internal::wasm::kWasmOrigin);
if (result.failed()) {
thrower.CompileFailed("", result);
......
......@@ -44,62 +44,17 @@ static const int kPlaceholderMarker = 1000000000;
enum JSFunctionExportInternalField {
kInternalModuleInstance,
kInternalArity,
kInternalSignature
kInternalFunctionIndex
};
// Internal constants for the layout of the module object.
enum WasmInstanceObjectFields {
kWasmCompiledModule = 0,
kWasmModuleFunctionTable,
kWasmModuleCodeTable,
kWasmMemObject,
kWasmMemArrayBuffer,
kWasmGlobalsArrayBuffer,
kWasmDebugInfo,
kWasmModuleInternalFieldCount
};
enum WasmImportData {
kImportKind, // Smi. an ExternalKind
kImportGlobalType, // Smi. Type for globals.
kImportIndex, // Smi. index for the import.
kModuleName, // String
kFunctionName, // maybe String
kOutputCount, // Smi. an uint32_t
kSignature, // ByteArray. A copy of the data in FunctionSig
kWasmImportDataSize // Sentinel value.
};
enum WasmExportData {
kExportKind, // Smi. an ExternalKind
kExportGlobalType, // Smi. Type for globals.
kExportName, // String
kExportArity, // Smi, an int
kExportIndex, // Smi, an uint32_t
kExportedSignature, // ByteArray. A copy of the data in FunctionSig
kWasmExportDataSize // Sentinel value.
};
enum WasmGlobalInitData {
kGlobalInitKind, // 0 = constant, 1 = global index
kGlobalInitType, // Smi. Type for globals.
kGlobalInitIndex, // Smi, an uint32_t
kGlobalInitValue, // Number.
kWasmGlobalInitDataSize
};
enum WasmSegmentInfo {
kDestAddrKind, // 0 = constant, 1 = global index
kDestAddrValue, // Smi. an uint32_t
kSourceSize, // Smi. an uint32_t
kWasmSegmentInfoSize // Sentinel value.
};
enum WasmIndirectFunctionTableData {
kSize, // Smi. an uint32_t
kTable, // FixedArray of indirect function table
kWasmIndirectFunctionTableDataSize // Sentinel value.
kWasmInstanceInternalFieldCount
};
byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
......@@ -110,53 +65,24 @@ uint32_t GetMinModuleMemSize(const WasmModule* module) {
return WasmModule::kPageSize * module->min_mem_pages;
}
void SaveDataSegmentInfo(Factory* factory, const WasmModule* module,
Handle<WasmCompiledModule> compiled_module) {
Handle<FixedArray> segments = factory->NewFixedArray(
static_cast<int>(module->data_segments.size()), TENURED);
uint32_t data_size = 0;
for (const WasmDataSegment& segment : module->data_segments) {
if (segment.source_size == 0) continue;
data_size += segment.source_size;
}
Handle<ByteArray> data = factory->NewByteArray(data_size, TENURED);
uint32_t last_insertion_pos = 0;
for (uint32_t i = 0; i < module->data_segments.size(); ++i) {
const WasmDataSegment& segment = module->data_segments[i];
if (segment.source_size == 0) continue;
Handle<ByteArray> js_segment =
factory->NewByteArray(kWasmSegmentInfoSize * sizeof(uint32_t), TENURED);
if (segment.dest_addr.kind == WasmInitExpr::kGlobalIndex) {
// The destination address is the value of a global variable.
js_segment->set_int(kDestAddrKind, 1);
uint32_t offset =
module->globals[segment.dest_addr.val.global_index].offset;
js_segment->set_int(kDestAddrValue, static_cast<int>(offset));
} else {
// The destination address is a constant.
CHECK_EQ(WasmInitExpr::kI32Const, segment.dest_addr.kind);
js_segment->set_int(kDestAddrKind, 0);
js_segment->set_int(kDestAddrValue, segment.dest_addr.val.i32_const);
}
js_segment->set_int(kSourceSize, segment.source_size);
segments->set(i, *js_segment);
data->copy_in(last_insertion_pos,
module->module_start + segment.source_offset,
segment.source_size);
last_insertion_pos += segment.source_size;
}
compiled_module->set_data_segments_info(segments);
compiled_module->set_data_segments(data);
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();
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 PatchFunctionTable(Handle<Code> code,
Handle<FixedArray> old_indirect_table,
Handle<FixedArray> new_indirect_table) {
void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
Handle<Object> new_ref) {
for (RelocIterator it(*code, 1 << RelocInfo::EMBEDDED_OBJECT); !it.done();
it.next()) {
if (it.rinfo()->target_object() == *old_indirect_table) {
it.rinfo()->set_target_object(*new_indirect_table);
if (it.rinfo()->target_object() == *old_ref) {
it.rinfo()->set_target_object(*new_ref);
}
}
}
......@@ -185,33 +111,30 @@ Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) {
return buffer;
}
void RelocateInstanceCode(Handle<JSObject> instance, Address old_start,
Address start, uint32_t prev_size,
uint32_t new_size) {
Handle<FixedArray> functions = Handle<FixedArray>(
FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
for (int i = 0; i < functions->length(); ++i) {
Handle<Code> function = Handle<Code>(Code::cast(functions->get(i)));
void RelocateMemoryReferencesInCode(Handle<FixedArray> code_table,
Address old_start, Address start,
uint32_t prev_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_MEMORY_REFERENCE) |
(1 << RelocInfo::WASM_MEMORY_SIZE_REFERENCE);
for (RelocIterator it(*function, mask); !it.done(); it.next()) {
for (RelocIterator it(*code, mask); !it.done(); it.next()) {
it.rinfo()->update_wasm_memory_reference(old_start, start, prev_size,
new_size);
}
}
}
void RelocateGlobals(Handle<JSObject> instance, Address old_start,
void RelocateGlobals(Handle<FixedArray> code_table, Address old_start,
Address globals_start) {
Handle<FixedArray> functions = Handle<FixedArray>(
FixedArray::cast(instance->GetInternalField(kWasmModuleCodeTable)));
uint32_t function_count = static_cast<uint32_t>(functions->length());
for (uint32_t i = 0; i < function_count; ++i) {
Handle<Code> function = Handle<Code>(Code::cast(functions->get(i)));
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_GLOBAL_REFERENCE;
for (RelocIterator it(*function, mask); !it.done(); it.next()) {
for (RelocIterator it(*code, mask); !it.done(); it.next()) {
it.rinfo()->update_wasm_global_reference(old_start, globals_start);
}
}
......@@ -265,9 +188,9 @@ bool LinkFunction(Handle<Code> unlinked,
return modified;
}
void FlushICache(Isolate* isolate, Handle<FixedArray> functions) {
for (int i = 0; i < functions->length(); ++i) {
Handle<Code> code = functions->GetValueChecked<Code>(isolate, i);
void FlushICache(Isolate* isolate, Handle<FixedArray> code_table) {
for (int i = 0; i < code_table->length(); ++i) {
Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
Assembler::FlushICache(isolate, code->instruction_start(),
code->instruction_size());
}
......@@ -356,69 +279,6 @@ Address GetGlobalStartAddressFromCodeTemplate(Object* undefined,
return old_address;
}
Handle<FixedArray> EncodeImports(Factory* factory, const WasmModule* module) {
// TODO(wasm): Encode this in one big FixedArray.
Handle<FixedArray> ret = factory->NewFixedArray(
static_cast<int>(module->import_table.size()), TENURED);
for (size_t i = 0; i < module->import_table.size(); ++i) {
const WasmImport& import = module->import_table[i];
Handle<FixedArray> encoded_import =
factory->NewFixedArray(kWasmImportDataSize, TENURED);
encoded_import->set(kImportKind, Smi::FromInt(import.kind));
encoded_import->set(kImportIndex, Smi::FromInt(import.index));
// Add the module and function name.
WasmName module_name = module->GetNameOrNull(import.module_name_offset,
import.module_name_length);
WasmName function_name = module->GetNameOrNull(import.field_name_offset,
import.field_name_length);
Handle<String> module_name_string =
factory->InternalizeUtf8String(module_name);
encoded_import->set(kModuleName, *module_name_string);
if (!function_name.is_empty()) {
Handle<String> function_name_string =
factory->InternalizeUtf8String(function_name);
encoded_import->set(kFunctionName, *function_name_string);
}
switch (import.kind) {
case kExternalFunction: {
// Encode the signature into the import.
FunctionSig* fsig = module->functions[import.index].sig;
Handle<ByteArray> sig = factory->NewByteArray(
static_cast<int>(fsig->parameter_count() + fsig->return_count()),
TENURED);
sig->copy_in(0, reinterpret_cast<const byte*>(fsig->raw_data()),
sig->length());
encoded_import->set(
kOutputCount, Smi::FromInt(static_cast<int>(fsig->return_count())));
encoded_import->set(kSignature, *sig);
break;
}
case kExternalTable:
// Nothing extra required for imported tables.
break;
case kExternalMemory:
// Nothing extra required for imported memories.
break;
case kExternalGlobal: {
// Encode the offset and the global type into the import.
const WasmGlobal& global = module->globals[import.index];
TRACE("import[%zu].type = %s\n", i, WasmOpcodes::TypeName(global.type));
encoded_import->set(
kImportGlobalType,
Smi::FromInt(WasmOpcodes::LocalTypeCodeFor(global.type)));
encoded_import->set(kImportIndex, Smi::FromInt(global.offset));
break;
}
}
ret->set(static_cast<int>(i), *encoded_import);
}
return ret;
}
void InitializeParallelCompilation(
Isolate* isolate, const std::vector<WasmFunction>& functions,
std::vector<compiler::WasmCompilationUnit*>& compilation_units,
......@@ -610,15 +470,22 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
WasmCompiledModule* compiled_module) {
TRACE("Resetting %d\n", compiled_module->instance_id());
Object* undefined = *isolate->factory()->undefined_value();
uint32_t old_mem_size = compiled_module->has_heap()
? compiled_module->mem_size()
: compiled_module->default_mem_size();
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_heap();
Object* mem_start = compiled_module->ptr_to_memory();
Address old_mem_address = nullptr;
Address globals_start =
GetGlobalStartAddressFromCodeTemplate(undefined, owner);
// Reset function tables.
FixedArray* function_tables = nullptr;
FixedArray* empty_function_tables = nullptr;
if (compiled_module->has_function_tables()) {
function_tables = compiled_module->ptr_to_function_tables();
empty_function_tables = compiled_module->ptr_to_empty_function_tables();
compiled_module->set_ptr_to_function_tables(empty_function_tables);
}
if (old_mem_size > 0) {
CHECK_NE(mem_start, undefined);
old_mem_address =
......@@ -626,8 +493,11 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
}
int mode_mask = RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_SIZE_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::WASM_GLOBAL_REFERENCE);
RelocInfo::ModeMask(RelocInfo::WASM_GLOBAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
// Patch code to update memory references, global references, and function
// table references.
Object* fct_obj = compiled_module->ptr_to_code_table();
if (fct_obj != nullptr && fct_obj != undefined &&
(old_mem_size > 0 || globals_start != nullptr)) {
......@@ -642,10 +512,17 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
it.rinfo()->update_wasm_memory_reference(
old_mem_address, nullptr, old_mem_size, default_mem_size);
changed = true;
} else {
CHECK(RelocInfo::IsWasmGlobalReference(mode));
} else if (RelocInfo::IsWasmGlobalReference(mode)) {
it.rinfo()->update_wasm_global_reference(globals_start, nullptr);
changed = true;
} else if (RelocInfo::IsEmbeddedObject(mode) && function_tables) {
Object* old = it.rinfo()->target_object();
for (int i = 0; i < function_tables->length(); ++i) {
if (function_tables->get(i) == old) {
it.rinfo()->set_target_object(empty_function_tables->get(i));
changed = true;
}
}
}
}
if (changed) {
......@@ -654,7 +531,7 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
}
}
}
compiled_module->reset_heap();
compiled_module->reset_memory();
}
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
......@@ -663,16 +540,16 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
WasmCompiledModule* compiled_module = GetCompiledModule(owner);
TRACE("Finalizing %d {\n", compiled_module->instance_id());
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
DCHECK(compiled_module->has_weak_module_object());
WeakCell* weak_module_obj = compiled_module->ptr_to_weak_module_object();
DCHECK(compiled_module->has_weak_wasm_module());
WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
// weak_module_obj may have been cleared, meaning the module object
// weak_wasm_module may have been cleared, meaning the module object
// was GC-ed. In that case, there won't be any new instances created,
// and we don't need to maintain the links between instances.
if (!weak_module_obj->cleared()) {
JSObject* module_obj = JSObject::cast(weak_module_obj->value());
if (!weak_wasm_module->cleared()) {
JSObject* wasm_module = JSObject::cast(weak_wasm_module->value());
WasmCompiledModule* current_template =
WasmCompiledModule::cast(module_obj->GetInternalField(0));
WasmCompiledModule::cast(wasm_module->GetInternalField(0));
TRACE("chain before {\n");
TRACE_CHAIN(current_template);
......@@ -687,7 +564,7 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
ResetCompiledModule(isolate, owner, compiled_module);
} else {
DCHECK(next->value()->IsFixedArray());
module_obj->SetInternalField(0, next->value());
wasm_module->SetInternalField(0, next->value());
DCHECK_NULL(prev);
WasmCompiledModule::cast(next->value())->reset_weak_prev_instance();
}
......@@ -716,7 +593,7 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
}
}
TRACE("chain after {\n");
TRACE_CHAIN(WasmCompiledModule::cast(module_obj->GetInternalField(0)));
TRACE_CHAIN(WasmCompiledModule::cast(wasm_module->GetInternalField(0)));
TRACE("}\n");
}
compiled_module->reset_weak_owning_instance();
......@@ -724,36 +601,6 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
TRACE("}\n");
}
Handle<FixedArray> SetupIndirectFunctionTable(
Isolate* isolate, Handle<FixedArray> wasm_functions,
Handle<FixedArray> indirect_table_template,
Handle<FixedArray> tables_to_replace) {
Factory* factory = isolate->factory();
Handle<FixedArray> cloned_indirect_tables =
factory->CopyFixedArray(indirect_table_template);
for (int i = 0; i < cloned_indirect_tables->length(); ++i) {
Handle<FixedArray> orig_metadata =
cloned_indirect_tables->GetValueChecked<FixedArray>(isolate, i);
Handle<FixedArray> cloned_metadata = factory->CopyFixedArray(orig_metadata);
cloned_indirect_tables->set(i, *cloned_metadata);
Handle<FixedArray> orig_table =
cloned_metadata->GetValueChecked<FixedArray>(isolate, kTable);
Handle<FixedArray> cloned_table = factory->CopyFixedArray(orig_table);
cloned_metadata->set(kTable, *cloned_table);
// Patch the cloned code to refer to the cloned kTable.
Handle<FixedArray> table_to_replace =
tables_to_replace->GetValueChecked<FixedArray>(isolate, i)
->GetValueChecked<FixedArray>(isolate, kTable);
for (int fct_index = 0; fct_index < wasm_functions->length(); ++fct_index) {
Handle<Code> wasm_function =
wasm_functions->GetValueChecked<Code>(isolate, fct_index);
PatchFunctionTable(wasm_function, table_to_replace, cloned_table);
}
}
return cloned_indirect_tables;
}
} // namespace
const char* wasm::SectionName(WasmSectionCode code) {
......@@ -824,10 +671,11 @@ std::ostream& wasm::operator<<(std::ostream& os, const WasmFunctionName& pair) {
}
Handle<JSFunction> wasm::WrapExportCodeAsJSFunction(
Isolate* isolate, Handle<Code> export_code, Handle<String> name, int arity,
MaybeHandle<ByteArray> maybe_signature, Handle<JSObject> instance) {
Isolate* isolate, Handle<Code> export_code, Handle<String> name,
FunctionSig* sig, int func_index, Handle<JSObject> instance) {
Handle<SharedFunctionInfo> shared =
isolate->factory()->NewSharedFunctionInfo(name, export_code, false);
int arity = static_cast<int>(sig->parameter_count());
shared->set_length(arity);
shared->set_internal_formal_parameter_count(arity);
Handle<JSFunction> function = isolate->factory()->NewFunction(
......@@ -835,13 +683,7 @@ Handle<JSFunction> wasm::WrapExportCodeAsJSFunction(
function->set_shared(*shared);
function->SetInternalField(kInternalModuleInstance, *instance);
// add another Internal Field as the function arity
function->SetInternalField(kInternalArity, Smi::FromInt(arity));
// add another Internal Field as the signature of the foreign function
Handle<ByteArray> signature;
if (maybe_signature.ToHandle(&signature)) {
function->SetInternalField(kInternalSignature, *signature);
}
function->SetInternalField(kInternalFunctionIndex, Smi::FromInt(func_index));
return function;
}
......@@ -857,78 +699,25 @@ Object* wasm::GetOwningWasmInstance(Code* code) {
return cell->value();
}
int wasm::GetNumImportedFunctions(Handle<JSObject> instance) {
// TODO(wasm): Cache this number if it ever becomes a performance problem.
WasmModule* GetCppModule(Handle<JSObject> instance) {
DCHECK(IsWasmInstance(*instance));
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
Handle<FixedArray> imports =
WasmCompiledModule::cast(compiled_module)->imports();
int num_imports = imports->length();
int num_imported_functions = 0;
for (int i = 0; i < num_imports; ++i) {
FixedArray* encoded_import = FixedArray::cast(imports->get(i));
int kind = Smi::cast(encoded_import->get(kImportKind))->value();
if (kind == kExternalFunction) ++num_imported_functions;
}
return num_imported_functions;
return reinterpret_cast<WasmModuleWrapper*>(
*GetCompiledModule(*instance)->module_wrapper())
->get();
}
WasmModule::WasmModule(byte* module_start)
: module_start(module_start),
module_end(nullptr),
min_mem_pages(0),
max_mem_pages(0),
mem_export(false),
start_function_index(-1),
origin(kWasmOrigin),
globals_size(0),
num_imported_functions(0),
num_declared_functions(0),
num_exported_functions(0),
pending_tasks(new base::Semaphore(0)) {}
namespace {
void EncodeInit(const WasmModule* module, Factory* factory,
Handle<FixedArray> entry, int kind_index, int value_index,
const WasmInitExpr& expr) {
entry->set(kind_index, Smi::kZero);
Handle<Object> value;
switch (expr.kind) {
case WasmInitExpr::kGlobalIndex: {
TRACE(" kind = 1, global index %u\n", expr.val.global_index);
entry->set(kind_index, Smi::FromInt(1));
uint32_t offset = module->globals[expr.val.global_index].offset;
entry->set(value_index, Smi::FromInt(offset));
return;
}
case WasmInitExpr::kI32Const:
TRACE(" kind = 0, i32 = %d\n", expr.val.i32_const);
value = factory->NewNumber(expr.val.i32_const);
break;
case WasmInitExpr::kI64Const:
// TODO(titzer): implement initializers for i64 globals.
UNREACHABLE();
break;
case WasmInitExpr::kF32Const:
TRACE(" kind = 0, f32 = %f\n", expr.val.f32_const);
value = factory->NewNumber(expr.val.f32_const);
break;
case WasmInitExpr::kF64Const:
TRACE(" kind = 0, f64 = %lf\n", expr.val.f64_const);
value = factory->NewNumber(expr.val.f64_const);
break;
default:
UNREACHABLE();
}
entry->set(value_index, *value);
int wasm::GetNumImportedFunctions(Handle<JSObject> instance) {
return static_cast<int>(GetCppModule(instance)->num_imported_functions);
}
} // namespace
WasmModule::WasmModule(Zone* owned, const byte* module_start)
: owned_zone(owned),
module_start(module_start),
pending_tasks(new base::Semaphore(0)) {}
MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
Isolate* isolate, ErrorThrower* thrower) const {
Isolate* isolate, Handle<WasmModuleWrapper> module_wrapper,
ErrorThrower* thrower) const {
Factory* factory = isolate->factory();
MaybeHandle<WasmCompiledModule> nothing;
......@@ -939,20 +728,13 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
temp_instance.mem_start = nullptr;
temp_instance.globals_start = nullptr;
MaybeHandle<FixedArray> indirect_table =
function_tables.size()
? factory->NewFixedArray(static_cast<int>(function_tables.size()),
TENURED)
: MaybeHandle<FixedArray>();
for (uint32_t i = 0; i < function_tables.size(); ++i) {
Handle<FixedArray> values = wasm::BuildFunctionTable(isolate, i, this);
temp_instance.function_tables[i] = values;
Handle<FixedArray> metadata = isolate->factory()->NewFixedArray(
kWasmIndirectFunctionTableDataSize, TENURED);
metadata->set(kSize, Smi::FromInt(function_tables[i].size));
metadata->set(kTable, *values);
indirect_table.ToHandleChecked()->set(i, *metadata);
// Initialize the indirect tables with placeholders.
int function_table_count = static_cast<int>(this->function_tables.size());
Handle<FixedArray> function_tables =
factory->NewFixedArray(function_table_count);
for (int i = 0; i < function_table_count; ++i) {
temp_instance.function_tables[i] = factory->NewFixedArray(0);
function_tables->set(i, *temp_instance.function_tables[i]);
}
HistogramTimerScope wasm_compile_module_time_scope(
......@@ -1023,122 +805,30 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
// serializable. Instantiation may occur off a deserialized version of this
// object.
Handle<WasmCompiledModule> ret =
WasmCompiledModule::New(isolate, min_mem_pages, globals_size, origin);
WasmCompiledModule::New(isolate, module_wrapper);
ret->set_code_table(code_table);
if (!indirect_table.is_null()) {
ret->set_indirect_function_tables(indirect_table.ToHandleChecked());
ret->set_min_mem_pages(min_mem_pages);
if (function_table_count > 0) {
ret->set_function_tables(function_tables);
ret->set_empty_function_tables(function_tables);
}
// Create and set import data.
Handle<FixedArray> imports = EncodeImports(factory, this);
ret->set_imports(imports);
// Create and set export data.
int export_size = static_cast<int>(export_table.size());
if (export_size > 0) {
Handle<FixedArray> exports = factory->NewFixedArray(export_size, TENURED);
int index = 0;
int func_index = 0;
for (const WasmExport& exp : export_table) {
if (thrower->error()) return nothing;
Handle<FixedArray> encoded_export =
factory->NewFixedArray(kWasmExportDataSize, TENURED);
WasmName str = GetName(exp.name_offset, exp.name_length);
Handle<String> name = factory->InternalizeUtf8String(str);
encoded_export->set(kExportKind, Smi::FromInt(exp.kind));
encoded_export->set(kExportName, *name);
encoded_export->set(kExportIndex,
Smi::FromInt(static_cast<int>(exp.index)));
exports->set(index, *encoded_export);
switch (exp.kind) {
case kExternalFunction: {
// Copy the signature and arity.
FunctionSig* funcSig = functions[exp.index].sig;
Handle<ByteArray> exportedSig = factory->NewByteArray(
static_cast<int>(funcSig->parameter_count() +
funcSig->return_count()),
TENURED);
exportedSig->copy_in(
0, reinterpret_cast<const byte*>(funcSig->raw_data()),
exportedSig->length());
encoded_export->set(kExportedSignature, *exportedSig);
encoded_export->set(
kExportArity,
Smi::FromInt(static_cast<int>(funcSig->parameter_count())));
// Compile a wrapper for an exported function.
Handle<Code> code =
code_table->GetValueChecked<Code>(isolate, exp.index);
Handle<Code> export_code = compiler::CompileJSToWasmWrapper(
isolate, &module_env, code, exp.index);
int code_table_index =
static_cast<int>(functions.size() + func_index);
code_table->set(code_table_index, *export_code);
encoded_export->set(kExportIndex, Smi::FromInt(code_table_index));
++func_index;
}
case kExternalTable:
// Nothing special about exported tables.
break;
case kExternalMemory:
// Nothing special about exported tables.
break;
case kExternalGlobal: {
// Encode the global type and the global offset.
const WasmGlobal& global = globals[exp.index];
encoded_export->set(
kExportGlobalType,
Smi::FromInt(WasmOpcodes::LocalTypeCodeFor(global.type)));
encoded_export->set(kExportIndex, Smi::FromInt(global.offset));
break;
}
}
++index;
}
ret->set_exports(exports);
}
// Create and set init data.
int init_size = static_cast<int>(globals.size());
if (init_size > 0) {
Handle<FixedArray> inits = factory->NewFixedArray(init_size, TENURED);
int index = 0;
for (const WasmGlobal& global : globals) {
// Skip globals that have no initializer (e.g. imported ones).
if (global.init.kind == WasmInitExpr::kNone) continue;
Handle<FixedArray> encoded_init =
factory->NewFixedArray(kWasmGlobalInitDataSize, TENURED);
inits->set(index, *encoded_init);
TRACE("init[%d].type = %s\n", index, WasmOpcodes::TypeName(global.type));
encoded_init->set(
kGlobalInitType,
Smi::FromInt(WasmOpcodes::LocalTypeCodeFor(global.type)));
encoded_init->set(kGlobalInitIndex, Smi::FromInt(global.offset));
EncodeInit(this, factory, encoded_init, kGlobalInitKind, kGlobalInitValue,
global.init);
++index;
}
inits->Shrink(index);
ret->set_inits(inits);
// 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);
int export_index = static_cast<int>(functions.size() + func_index);
code_table->set(export_index, *wrapper_code);
func_index++;
}
// Record data for startup function.
if (start_function_index >= 0) {
HandleScope scope(isolate);
Handle<FixedArray> startup_data =
factory->NewFixedArray(kWasmExportDataSize, TENURED);
startup_data->set(kExportArity, Smi::kZero);
startup_data->set(kExportIndex, Smi::FromInt(start_function_index));
ret->set_startup_function(startup_data);
}
// TODO(wasm): saving the module bytes for debugging is wasteful. We should
// consider downloading this on-demand.
{
// 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,
......@@ -1150,11 +840,6 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
ret->set_module_bytes(Handle<SeqOneByteString>::cast(module_bytes_string));
}
Handle<ByteArray> function_name_table =
BuildFunctionNamesTable(isolate, module_env.module);
ret->set_function_names(function_name_table);
if (data_segments.size() > 0) SaveDataSegmentInfo(factory, this, ret);
DCHECK_EQ(ret->default_mem_size(), temp_instance.mem_size);
return ret;
}
......@@ -1218,6 +903,8 @@ class WasmInstanceBuilder {
DCHECK(!owner.is_null());
TRACE("Cloning from %d\n", original->instance_id());
compiled_module_ = WasmCompiledModule::Clone(isolate_, original);
// Avoid creating too many handles in the outer scope.
HandleScope scope(isolate_);
// Clone the code for WASM functions and exports.
for (int i = 0; i < code_table->length(); ++i) {
......@@ -1246,15 +933,17 @@ class WasmInstanceBuilder {
}
compiled_module_->set_code_table(code_table);
}
module_ = reinterpret_cast<WasmModuleWrapper*>(
*compiled_module_->module_wrapper())
->get();
//--------------------------------------------------------------------------
// Allocate the instance object.
//--------------------------------------------------------------------------
Handle<Map> map = factory->NewMap(
JS_OBJECT_TYPE,
JSObject::kHeaderSize + kWasmModuleInternalFieldCount * kPointerSize);
JSObject::kHeaderSize + kWasmInstanceInternalFieldCount * kPointerSize);
Handle<JSObject> instance = factory->NewJSObjectFromMap(map, TENURED);
instance->SetInternalField(kWasmModuleCodeTable, *code_table);
instance->SetInternalField(kWasmMemObject, *factory->undefined_value());
//--------------------------------------------------------------------------
......@@ -1262,7 +951,7 @@ class WasmInstanceBuilder {
//--------------------------------------------------------------------------
MaybeHandle<JSArrayBuffer> old_globals;
MaybeHandle<JSArrayBuffer> globals;
uint32_t globals_size = compiled_module_->globals_size();
uint32_t globals_size = module_->globals_size;
if (globals_size > 0) {
Handle<JSArrayBuffer> global_buffer =
NewArrayBuffer(isolate_, globals_size);
......@@ -1276,7 +965,7 @@ class WasmInstanceBuilder {
: GetGlobalStartAddressFromCodeTemplate(
*factory->undefined_value(),
JSObject::cast(*owner.ToHandleChecked()));
RelocateGlobals(instance, old_address,
RelocateGlobals(code_table, old_address,
static_cast<Address>(global_buffer->backing_store()));
instance->SetInternalField(kWasmGlobalsArrayBuffer, *global_buffer);
}
......@@ -1290,15 +979,14 @@ class WasmInstanceBuilder {
//--------------------------------------------------------------------------
// Process the initialization for the module's globals.
//--------------------------------------------------------------------------
ProcessInits(globals);
InitGlobals(globals);
//--------------------------------------------------------------------------
// Set up the memory for the new instance.
//--------------------------------------------------------------------------
MaybeHandle<JSArrayBuffer> old_memory;
// TODO(titzer): handle imported memory properly.
uint32_t min_mem_pages = compiled_module_->min_memory_pages();
uint32_t min_mem_pages = module_->min_mem_pages;
isolate_->counters()->wasm_min_mem_pages_count()->AddSample(min_mem_pages);
// TODO(wasm): re-enable counter for max_mem_pages when we use that field.
......@@ -1314,16 +1002,15 @@ class WasmInstanceBuilder {
static_cast<uint32_t>(memory_->byte_length()->Number());
LoadDataSegments(globals, mem_start, mem_size);
uint32_t old_mem_size = compiled_module_->has_heap()
? compiled_module_->mem_size()
: compiled_module_->default_mem_size();
uint32_t old_mem_size = compiled_module_->mem_size();
Address old_mem_start =
compiled_module_->has_heap()
? static_cast<Address>(compiled_module_->heap()->backing_store())
compiled_module_->has_memory()
? static_cast<Address>(
compiled_module_->memory()->backing_store())
: nullptr;
RelocateInstanceCode(instance, old_mem_start, mem_start, old_mem_size,
mem_size);
compiled_module_->set_heap(memory_);
RelocateMemoryReferencesInCode(code_table, old_mem_start, mem_start,
old_mem_size, mem_size);
compiled_module_->set_memory(memory_);
}
//--------------------------------------------------------------------------
......@@ -1346,33 +1033,52 @@ class WasmInstanceBuilder {
//--------------------------------------------------------------------------
// Set up the indirect function tables for the new instance.
//--------------------------------------------------------------------------
{
std::vector<Handle<Code>> functions(
static_cast<size_t>(code_table->length()));
for (int i = 0; i < code_table->length(); ++i) {
functions[i] = code_table->GetValueChecked<Code>(isolate_, i);
int function_table_count =
static_cast<int>(module_->function_tables.size());
if (function_table_count > 0) {
Handle<FixedArray> old_function_tables =
compiled_module_->function_tables();
Handle<FixedArray> new_function_tables =
factory->NewFixedArray(function_table_count);
for (int index = 0; index < function_table_count; ++index) {
WasmIndirectFunctionTable& table = module_->function_tables[index];
uint32_t size = table.max_size;
Handle<FixedArray> new_table = factory->NewFixedArray(size * 2);
for (int i = 0; i < new_table->length(); ++i) {
static const int kInvalidSigIndex = -1;
// Fill the table with invalid signature indexes so that uninitialized
// entries will always fail the signature check.
new_table->set(i, Smi::FromInt(kInvalidSigIndex));
}
for (auto table_init : module_->table_inits) {
uint32_t base = EvalUint32InitExpr(globals, table_init.offset);
uint32_t table_size = static_cast<uint32_t>(new_table->length());
if (base > table_size ||
(base + table_init.entries.size() > table_size)) {
thrower_->CompileError("table initializer is out of bounds");
continue;
}
for (size_t i = 0; i < table_init.entries.size(); ++i) {
FunctionSig* sig = module_->functions[table_init.entries[i]].sig;
int32_t sig_index = table.map.Find(sig);
new_table->set(static_cast<int>(i + base), Smi::FromInt(sig_index));
new_table->set(static_cast<int>(i + base + size),
code_table->get(table_init.entries[i]));
}
}
new_function_tables->set(static_cast<int>(index), *new_table);
}
if (compiled_module_->has_indirect_function_tables()) {
Handle<FixedArray> indirect_tables_template =
compiled_module_->indirect_function_tables();
Handle<FixedArray> to_replace =
owner.is_null() ? indirect_tables_template
: handle(FixedArray::cast(
owner.ToHandleChecked()->GetInternalField(
kWasmModuleFunctionTable)));
Handle<FixedArray> indirect_tables = SetupIndirectFunctionTable(
isolate_, code_table, indirect_tables_template, to_replace);
for (int i = 0; i < indirect_tables->length(); ++i) {
Handle<FixedArray> metadata =
indirect_tables->GetValueChecked<FixedArray>(isolate_, i);
uint32_t size = Smi::cast(metadata->get(kSize))->value();
Handle<FixedArray> table =
metadata->GetValueChecked<FixedArray>(isolate_, kTable);
PopulateFunctionTable(table, size, &functions);
// Patch all code that has references to the old indirect table.
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_));
}
instance->SetInternalField(kWasmModuleFunctionTable, *indirect_tables);
}
compiled_module_->set_function_tables(new_function_tables);
}
//--------------------------------------------------------------------------
......@@ -1413,8 +1119,8 @@ class WasmInstanceBuilder {
compiled_module_->set_weak_next_instance(
link_to_original.ToHandleChecked());
original.ToHandleChecked()->set_weak_prev_instance(link_to_clone);
compiled_module_->set_weak_module_object(
original.ToHandleChecked()->weak_module_object());
compiled_module_->set_weak_wasm_module(
original.ToHandleChecked()->weak_wasm_module());
}
module_object_->SetInternalField(0, *compiled_module_);
instance->SetInternalField(kWasmCompiledModule, *compiled_module_);
......@@ -1433,19 +1139,15 @@ class WasmInstanceBuilder {
//--------------------------------------------------------------------------
// Run the start function if one was specified.
//--------------------------------------------------------------------------
if (compiled_module_->has_startup_function()) {
Handle<FixedArray> startup_data = compiled_module_->startup_function();
if (module_->start_function_index >= 0) {
HandleScope scope(isolate_);
int32_t start_index =
startup_data->GetValueChecked<Smi>(isolate_, kExportIndex)->value();
int start_index = module_->start_function_index;
Handle<Code> startup_code =
code_table->GetValueChecked<Code>(isolate_, start_index);
int arity = Smi::cast(startup_data->get(kExportArity))->value();
MaybeHandle<ByteArray> startup_signature =
startup_data->GetValue<ByteArray>(isolate_, kExportedSignature);
FunctionSig* sig = module_->functions[start_index].sig;
Handle<JSFunction> startup_fct = WrapExportCodeAsJSFunction(
isolate_, startup_code, factory->InternalizeUtf8String("start"),
arity, startup_signature, instance);
isolate_, startup_code, factory->InternalizeUtf8String("start"), sig,
start_index, instance);
RecordStats(isolate_, *startup_code);
// Call the JS function.
Handle<Object> undefined = factory->undefined_value();
......@@ -1471,6 +1173,7 @@ class WasmInstanceBuilder {
private:
Isolate* isolate_;
WasmModule* module_;
ErrorThrower* thrower_;
Handle<JSObject> module_object_;
Handle<JSReceiver> ffi_;
......@@ -1534,79 +1237,65 @@ class WasmInstanceBuilder {
return result;
}
uint32_t EvalUint32InitExpr(MaybeHandle<JSArrayBuffer> globals,
WasmInitExpr& expr) {
switch (expr.kind) {
case WasmInitExpr::kI32Const:
return expr.val.i32_const;
case WasmInitExpr::kGlobalIndex: {
uint32_t offset = module_->globals[expr.val.global_index].offset;
return *reinterpret_cast<uint32_t*>(raw_buffer_ptr(globals, offset));
}
default:
UNREACHABLE();
return 0;
}
}
// Load data segments into the memory.
void LoadDataSegments(MaybeHandle<JSArrayBuffer> globals, Address mem_addr,
size_t mem_size) {
CHECK(compiled_module_->has_data_segments() ==
compiled_module_->has_data_segments_info());
// If we have neither, we're done.
if (!compiled_module_->has_data_segments()) return;
Handle<ByteArray> data = compiled_module_->data_segments();
Handle<FixedArray> segments = compiled_module_->data_segments_info();
uint32_t last_extraction_pos = 0;
for (int i = 0; i < segments->length(); ++i) {
Handle<ByteArray> segment =
Handle<ByteArray>(ByteArray::cast(segments->get(i)));
uint32_t dest_addr =
static_cast<uint32_t>(segment->get_int(kDestAddrValue));
if (segment->get_int(kDestAddrKind) == 1) {
// The destination address is the value of a global variable.
dest_addr =
*reinterpret_cast<uint32_t*>(raw_buffer_ptr(globals, dest_addr));
Handle<SeqOneByteString> module_bytes = compiled_module_->module_bytes();
for (auto segment : module_->data_segments) {
uint32_t dest_offset = EvalUint32InitExpr(globals, segment.dest_addr);
uint32_t source_size = segment.source_size;
if (dest_offset >= mem_size || source_size >= mem_size ||
dest_offset >= (mem_size - source_size)) {
thrower_->RangeError("data segment does not fit into memory");
}
uint32_t source_size =
static_cast<uint32_t>(segment->get_int(kSourceSize));
// TODO(titzer): These should be runtime errors and not CHECKs if
// dest_addr is global (and therefore initialized at linktime to an
// possibly-invalid value).
CHECK_LT(dest_addr, mem_size);
CHECK_LE(source_size, mem_size);
CHECK_LE(dest_addr, mem_size - source_size);
byte* addr = mem_addr + dest_addr;
data->copy_out(last_extraction_pos, addr, source_size);
last_extraction_pos += source_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);
}
}
Handle<Code> CompileImportWrapper(int index, Handle<FixedArray> data,
Handle<Code> CompileImportWrapper(int index, const WasmImport& import,
Handle<JSReceiver> target,
Handle<String> module_name,
MaybeHandle<String> import_name) {
// TODO(mtrofin): this is an uint32_t, actually. We should rationalize
// it when we rationalize signed/unsigned stuff.
int ret_count = Smi::cast(data->get(kOutputCount))->value();
CHECK_GE(ret_count, 0);
Handle<ByteArray> sig_data =
data->GetValueChecked<ByteArray>(isolate_, kSignature);
int sig_data_size = sig_data->length();
int param_count = sig_data_size - ret_count;
CHECK(param_count >= 0);
FunctionSig* sig = module_->functions[import.index].sig;
Handle<Code> code;
bool isMatch = false;
bool is_match = false;
Handle<Code> export_wrapper_code;
if (target->IsJSFunction()) {
Handle<JSFunction> func = Handle<JSFunction>::cast(target);
export_wrapper_code = handle(func->code());
if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) {
int exported_param_count =
Smi::cast(func->GetInternalField(kInternalArity))->value();
Handle<ByteArray> exportedSig = Handle<ByteArray>(
ByteArray::cast(func->GetInternalField(kInternalSignature)));
if (exported_param_count == param_count &&
exportedSig->length() == sig_data->length() &&
memcmp(exportedSig->GetDataStartAddress(),
sig_data->GetDataStartAddress(),
exportedSig->length()) == 0) {
isMatch = true;
}
// Compare signature of other exported wasm function.
Handle<JSObject> other_instance(
JSObject::cast(func->GetInternalField(kInternalModuleInstance)),
isolate_);
int func_index =
Smi::cast(func->GetInternalField(kInternalFunctionIndex))->value();
FunctionSig* other_sig =
GetCppModule(other_instance)->functions[func_index].sig;
is_match = sig->Equals(other_sig);
}
}
if (isMatch) {
if (is_match) {
// Signature matched. Unwrap the JS->WASM wrapper and return the naked
// WASM function code.
int wasm_count = 0;
int const mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
for (RelocIterator it(*export_wrapper_code, mask); !it.done();
......@@ -1622,22 +1311,14 @@ class WasmInstanceBuilder {
DCHECK(wasm_count == 1);
return code;
} else {
// Copy the signature to avoid a raw pointer into a heap object when
// GC can happen.
Zone zone(isolate_->allocator(), ZONE_NAME);
MachineRepresentation* reps =
zone.NewArray<MachineRepresentation>(sig_data_size);
memcpy(reps, sig_data->GetDataStartAddress(),
sizeof(MachineRepresentation) * sig_data_size);
FunctionSig sig(ret_count, param_count, reps);
return compiler::CompileWasmToJSWrapper(isolate_, target, &sig, index,
// Signature mismatch. Compile a new wrapper for the new signature.
return compiler::CompileWasmToJSWrapper(isolate_, target, sig, index,
module_name, import_name);
}
}
void WriteGlobalValue(MaybeHandle<JSArrayBuffer> globals, uint32_t offset,
Handle<Object> value, int type) {
void WriteGlobalValue(WasmGlobal& global, MaybeHandle<JSArrayBuffer> globals,
Handle<Object> value) {
double num = 0;
if (value->IsSmi()) {
num = Smi::cast(*value)->value();
......@@ -1646,21 +1327,21 @@ class WasmInstanceBuilder {
} else {
UNREACHABLE();
}
TRACE("init [globals+%u] = %lf, type = %d\n", offset, num, type);
byte* ptr = raw_buffer_ptr(globals, offset);
switch (type) {
case kLocalI32:
*reinterpret_cast<int32_t*>(ptr) = static_cast<int32_t>(num);
TRACE("init [globals+%u] = %lf, type = %s\n", global.offset, num,
WasmOpcodes::TypeName(global.type));
switch (global.type) {
case kAstI32:
*GetRawGlobalPtr<int32_t>(global, globals) = static_cast<int32_t>(num);
break;
case kLocalI64:
case kAstI64:
// TODO(titzer): initialization of imported i64 globals.
UNREACHABLE();
break;
case kLocalF32:
*reinterpret_cast<float*>(ptr) = static_cast<float>(num);
case kAstF32:
*GetRawGlobalPtr<float>(global, globals) = static_cast<float>(num);
break;
case kLocalF64:
*reinterpret_cast<double*>(ptr) = num;
case kAstF64:
*GetRawGlobalPtr<double>(global, globals) = static_cast<double>(num);
break;
default:
UNREACHABLE();
......@@ -1673,25 +1354,27 @@ class WasmInstanceBuilder {
int ProcessImports(MaybeHandle<JSArrayBuffer> globals,
Handle<FixedArray> code_table, Handle<JSObject> instance) {
int num_imported_functions = 0;
if (!compiled_module_->has_imports()) return num_imported_functions;
Handle<FixedArray> imports = compiled_module_->imports();
for (int index = 0; index < imports->length(); ++index) {
Handle<FixedArray> data =
imports->GetValueChecked<FixedArray>(isolate_, index);
for (int index = 0; index < static_cast<int>(module_->import_table.size());
++index) {
WasmImport& import = module_->import_table[index];
Handle<String> module_name =
data->GetValueChecked<String>(isolate_, kModuleName);
MaybeHandle<String> function_name =
data->GetValue<String>(isolate_, kFunctionName);
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();
}
MaybeHandle<Object> result =
LookupImport(index, module_name, function_name);
if (thrower_->error()) return -1;
WasmExternalKind kind = static_cast<WasmExternalKind>(
Smi::cast(data->get(kImportKind))->value());
switch (kind) {
switch (import.kind) {
case kExternalFunction: {
// Function imports must be callable.
Handle<Object> function = result.ToHandleChecked();
......@@ -1702,10 +1385,9 @@ class WasmInstanceBuilder {
}
Handle<Code> import_wrapper = CompileImportWrapper(
index, data, Handle<JSReceiver>::cast(function), module_name,
index, import, Handle<JSReceiver>::cast(function), module_name,
function_name);
int func_index = Smi::cast(data->get(kImportIndex))->value();
code_table->set(func_index, *import_wrapper);
code_table->set(num_imported_functions, *import_wrapper);
RecordStats(isolate_, *import_wrapper);
num_imported_functions++;
break;
......@@ -1735,9 +1417,7 @@ class WasmInstanceBuilder {
return -1;
}
Handle<Object> val = number.ToHandleChecked();
int offset = Smi::cast(data->get(kImportIndex))->value();
int type = Smi::cast(data->get(kImportGlobalType))->value();
WriteGlobalValue(globals, offset, val, type);
WriteGlobalValue(module_->globals[import.index], globals, val);
break;
}
default:
......@@ -1748,29 +1428,49 @@ class WasmInstanceBuilder {
return num_imported_functions;
}
template <typename T>
T* GetRawGlobalPtr(WasmGlobal& global, MaybeHandle<JSArrayBuffer> globals) {
return reinterpret_cast<T*>(raw_buffer_ptr(globals, global.offset));
}
// Process initialization of globals.
void ProcessInits(MaybeHandle<JSArrayBuffer> globals) {
if (!compiled_module_->has_inits()) return;
Handle<FixedArray> inits = compiled_module_->inits();
for (int index = 0; index < inits->length(); ++index) {
Handle<FixedArray> data =
inits->GetValueChecked<FixedArray>(isolate_, index);
int offset = Smi::cast(data->get(kGlobalInitIndex))->value();
Handle<Object> val(data->get(kGlobalInitValue), isolate_);
int type = Smi::cast(data->get(kGlobalInitType))->value();
if (Smi::cast(data->get(kGlobalInitKind))->value() == 0) {
// Initialize with a constant.
WriteGlobalValue(globals, offset, val, type);
} else {
// Initialize with another global.
int old_offset = Smi::cast(*val)->value();
TRACE("init [globals+%u] = [globals+%d]\n", offset, old_offset);
int size = sizeof(int32_t);
if (type == kLocalI64 || type == kLocalF64) size = sizeof(double);
memcpy(raw_buffer_ptr(globals, offset),
raw_buffer_ptr(globals, old_offset), size);
void InitGlobals(MaybeHandle<JSArrayBuffer> globals) {
for (auto global : module_->globals) {
switch (global.init.kind) {
case WasmInitExpr::kI32Const:
*GetRawGlobalPtr<int32_t>(global, globals) =
global.init.val.i32_const;
break;
case WasmInitExpr::kI64Const:
*GetRawGlobalPtr<int64_t>(global, globals) =
global.init.val.i64_const;
break;
case WasmInitExpr::kF32Const:
*GetRawGlobalPtr<float>(global, globals) = global.init.val.f32_const;
break;
case WasmInitExpr::kF64Const:
*GetRawGlobalPtr<double>(global, globals) = global.init.val.f64_const;
break;
case WasmInitExpr::kGlobalIndex: {
// Initialize with another global.
uint32_t new_offset = global.offset;
uint32_t old_offset =
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 = sizeof(double)
: sizeof(int32_t);
memcpy(raw_buffer_ptr(globals, new_offset),
raw_buffer_ptr(globals, old_offset), size);
break;
}
case WasmInitExpr::kNone:
// Happens with imported globals.
break;
default:
UNREACHABLE();
break;
}
}
}
......@@ -1795,10 +1495,10 @@ class WasmInstanceBuilder {
void ProcessExports(MaybeHandle<JSArrayBuffer> globals,
Handle<FixedArray> code_table,
Handle<JSObject> instance) {
if (!compiled_module_->has_exports()) return;
if (module_->export_table.size() == 0) return;
Handle<JSObject> exports_object = instance;
if (compiled_module_->origin() == kWasmOrigin) {
if (module_->origin == kWasmOrigin) {
// Create the "exports" object.
Handle<JSFunction> object_function = Handle<JSFunction>(
isolate_->native_context()->object_function(), isolate_);
......@@ -1812,27 +1512,23 @@ class WasmInstanceBuilder {
PropertyDescriptor desc;
desc.set_writable(false);
Handle<FixedArray> exports = compiled_module_->exports();
for (int i = 0; i < exports->length(); ++i) {
Handle<FixedArray> export_data =
exports->GetValueChecked<FixedArray>(isolate_, i);
int func_index = 0;
for (auto exp : module_->export_table) {
Handle<String> name =
export_data->GetValueChecked<String>(isolate_, kExportName);
WasmExternalKind kind = static_cast<WasmExternalKind>(
Smi::cast(export_data->get(kExportKind))->value());
switch (kind) {
ExtractStringFromModuleBytes(isolate_, compiled_module_,
exp.name_offset, exp.name_length)
.ToHandleChecked();
switch (exp.kind) {
case kExternalFunction: {
// Wrap and export the code as a JSFunction.
int code_table_index =
Smi::cast(export_data->get(kExportIndex))->value();
WasmFunction& function = module_->functions[exp.index];
int export_index =
static_cast<int>(module_->functions.size() + func_index);
Handle<Code> export_code =
code_table->GetValueChecked<Code>(isolate_, code_table_index);
int arity = Smi::cast(export_data->get(kExportArity))->value();
MaybeHandle<ByteArray> signature =
export_data->GetValue<ByteArray>(isolate_, kExportedSignature);
code_table->GetValueChecked<Code>(isolate_, export_index);
desc.set_value(WrapExportCodeAsJSFunction(
isolate_, export_code, name, arity, signature, instance));
isolate_, export_code, name, function.sig, func_index, instance));
func_index++;
break;
}
case kExternalTable:
......@@ -1859,18 +1555,17 @@ class WasmInstanceBuilder {
}
case kExternalGlobal: {
// Export the value of the global variable as a number.
int offset = Smi::cast(export_data->get(kExportIndex))->value();
byte* ptr = raw_buffer_ptr(globals, offset);
WasmGlobal& global = module_->globals[exp.index];
double num = 0;
switch (Smi::cast(export_data->get(kExportGlobalType))->value()) {
case kLocalI32:
num = *reinterpret_cast<int32_t*>(ptr);
switch (global.type) {
case kAstI32:
num = *GetRawGlobalPtr<int32_t>(global, globals);
break;
case kLocalF32:
num = *reinterpret_cast<float*>(ptr);
case kAstF32:
num = *GetRawGlobalPtr<float>(global, globals);
break;
case kLocalF64:
num = *reinterpret_cast<double*>(ptr);
case kAstF64:
num = *GetRawGlobalPtr<double>(global, globals);
break;
default:
UNREACHABLE();
......@@ -1898,37 +1593,26 @@ class WasmInstanceBuilder {
// WebAssembly.Module.
MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
ErrorThrower* thrower,
Handle<JSObject> module_object,
Handle<JSObject> wasm_module,
Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory) {
WasmInstanceBuilder builder(isolate, thrower, module_object, ffi, memory);
WasmInstanceBuilder builder(isolate, thrower, wasm_module, ffi, memory);
return builder.Build();
}
Handle<WasmCompiledModule> WasmCompiledModule::New(Isolate* isolate,
uint32_t min_memory_pages,
uint32_t globals_size,
ModuleOrigin origin) {
Handle<WasmCompiledModule> WasmCompiledModule::New(
Isolate* isolate, Handle<WasmModuleWrapper> module_wrapper) {
Handle<FixedArray> ret =
isolate->factory()->NewFixedArray(PropertyIndices::Count, TENURED);
// Globals size is expected to fit into an int without overflow. This is not
// supported by the spec at the moment, however, we don't support array
// buffer sizes over 1g, so, for now, we avoid alocating a HeapNumber for
// the globals size. The CHECK guards this assumption.
CHECK_GE(static_cast<int>(globals_size), 0);
ret->set(kID_min_memory_pages,
Smi::FromInt(static_cast<int>(min_memory_pages)));
ret->set(kID_globals_size, Smi::FromInt(static_cast<int>(globals_size)));
ret->set(kID_origin, Smi::FromInt(static_cast<int>(origin)));
// WasmCompiledModule::cast would fail since module bytes are not set yet.
Handle<WasmCompiledModule> module(reinterpret_cast<WasmCompiledModule*>(*ret),
isolate);
module->Init();
return module;
Handle<WasmCompiledModule> compiled_module(
reinterpret_cast<WasmCompiledModule*>(*ret), isolate);
compiled_module->InitId();
compiled_module->set_module_wrapper(module_wrapper);
return compiled_module;
}
void WasmCompiledModule::Init() {
void WasmCompiledModule::InitId() {
#if DEBUG
static uint32_t instance_id_counter = 0;
set(kID_instance_id, Smi::FromInt(instance_id_counter++));
......@@ -1972,27 +1656,26 @@ void WasmCompiledModule::PrintInstancesChain() {
}
Handle<Object> wasm::GetWasmFunctionNameOrNull(Isolate* isolate,
Handle<Object> wasm,
Handle<Object> instance,
uint32_t func_index) {
if (!wasm->IsUndefined(isolate)) {
DCHECK(IsWasmInstance(*wasm));
WasmCompiledModule* compiled_module =
GetCompiledModule(JSObject::cast(*wasm));
Handle<ByteArray> func_names = compiled_module->function_names();
// TODO(clemens): Extract this from the module bytes; skip whole function
// name table.
Handle<Object> name;
if (GetWasmFunctionNameFromTable(func_names, func_index).ToHandle(&name)) {
return name;
}
if (!instance->IsUndefined(isolate)) {
DCHECK(IsWasmInstance(*instance));
WasmModule* module = GetCppModule(Handle<JSObject>::cast(instance));
WasmFunction& function = module->functions[func_index];
Handle<WasmCompiledModule> compiled_module(GetCompiledModule(*instance),
isolate);
MaybeHandle<String> string = ExtractStringFromModuleBytes(
isolate, compiled_module, function.name_offset, function.name_length);
if (!string.is_null()) return string.ToHandleChecked();
}
return isolate->factory()->null_value();
}
Handle<String> wasm::GetWasmFunctionName(Isolate* isolate, Handle<Object> wasm,
Handle<String> wasm::GetWasmFunctionName(Isolate* isolate,
Handle<Object> instance,
uint32_t func_index) {
Handle<Object> name_or_null =
GetWasmFunctionNameOrNull(isolate, wasm, func_index);
GetWasmFunctionNameOrNull(isolate, instance, func_index);
if (!name_or_null->IsNull(isolate)) {
return Handle<String>::cast(name_or_null);
}
......@@ -2004,13 +1687,12 @@ bool wasm::IsWasmInstance(Object* object) {
JSObject* obj = JSObject::cast(object);
Isolate* isolate = obj->GetIsolate();
if (obj->GetInternalFieldCount() != kWasmModuleInternalFieldCount) {
if (obj->GetInternalFieldCount() != kWasmInstanceInternalFieldCount) {
return false;
}
Object* mem = obj->GetInternalField(kWasmMemArrayBuffer);
if (!obj->GetInternalField(kWasmModuleCodeTable)->IsFixedArray() ||
!(mem->IsUndefined(isolate) || mem->IsJSArrayBuffer()) ||
if (!(mem->IsUndefined(isolate) || mem->IsJSArrayBuffer()) ||
!WasmCompiledModule::IsWasmCompiledModule(
obj->GetInternalField(kWasmCompiledModule))) {
return false;
......@@ -2020,27 +1702,27 @@ bool wasm::IsWasmInstance(Object* object) {
return true;
}
WasmCompiledModule* wasm::GetCompiledModule(JSObject* wasm) {
return WasmCompiledModule::cast(wasm->GetInternalField(kWasmCompiledModule));
WasmCompiledModule* wasm::GetCompiledModule(Object* instance) {
DCHECK(IsWasmInstance(instance));
return WasmCompiledModule::cast(
JSObject::cast(instance)->GetInternalField(kWasmCompiledModule));
}
bool wasm::WasmIsAsmJs(Object* wasm, Isolate* isolate) {
if (wasm->IsUndefined(isolate)) return false;
DCHECK(IsWasmInstance(wasm));
WasmCompiledModule* compiled_module = GetCompiledModule(JSObject::cast(wasm));
return compiled_module->has_asm_js_script();
bool wasm::WasmIsAsmJs(Object* instance, Isolate* isolate) {
return IsWasmInstance(instance) &&
GetCompiledModule(JSObject::cast(instance))->has_asm_js_script();
}
Handle<Script> wasm::GetAsmWasmScript(Handle<JSObject> wasm) {
DCHECK(IsWasmInstance(*wasm));
WasmCompiledModule* compiled_module = GetCompiledModule(*wasm);
Handle<Script> wasm::GetAsmWasmScript(Handle<JSObject> instance) {
DCHECK(IsWasmInstance(*instance));
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
return compiled_module->asm_js_script();
}
int wasm::GetAsmWasmSourcePosition(Handle<JSObject> wasm, int func_index,
int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
int byte_offset) {
return WasmDebugInfo::GetAsmJsSourcePosition(GetDebugInfo(wasm), func_index,
byte_offset);
return WasmDebugInfo::GetAsmJsSourcePosition(GetDebugInfo(instance),
func_index, byte_offset);
}
Handle<SeqOneByteString> wasm::GetWasmBytes(Handle<JSObject> instance) {
......@@ -2059,72 +1741,33 @@ Handle<WasmDebugInfo> wasm::GetDebugInfo(Handle<JSObject> instance) {
return new_info;
}
Handle<FixedArray> wasm::BuildFunctionTable(Isolate* isolate, uint32_t index,
const WasmModule* module) {
const WasmIndirectFunctionTable* table = &module->function_tables[index];
DCHECK_EQ(table->size, table->values.size());
DCHECK_GE(table->max_size, table->size);
Handle<FixedArray> values =
isolate->factory()->NewFixedArray(2 * table->max_size, TENURED);
for (uint32_t i = 0; i < table->size; ++i) {
const WasmFunction* function = &module->functions[table->values[i]];
int32_t index = table->map.Find(function->sig);
DCHECK_GE(index, 0);
values->set(i, Smi::FromInt(index));
values->set(i + table->max_size, Smi::FromInt(table->values[i]));
}
// Set the remaining elements to -1 (instead of "undefined"). These
// elements are accessed directly as SMIs (without a check). On 64-bit
// platforms, it is possible to have the top bits of "undefined" take
// small integer values (or zero), which are more likely to be equal to
// the signature index we check against.
for (uint32_t i = table->size; i < table->max_size; ++i) {
values->set(i, Smi::FromInt(-1));
}
return values;
}
void wasm::PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size,
const std::vector<Handle<Code>>* code_table) {
uint32_t max_size = table->length() / 2;
for (uint32_t i = max_size; i < max_size + table_size; ++i) {
int index = Smi::cast(table->get(static_cast<int>(i)))->value();
DCHECK_GE(index, 0);
DCHECK_LT(static_cast<size_t>(index), code_table->size());
table->set(static_cast<int>(i), *(*code_table)[index]);
}
}
int wasm::GetNumberOfFunctions(Handle<JSObject> instance) {
DCHECK(IsWasmInstance(*instance));
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
ByteArray* func_names_arr = compiled_module->ptr_to_function_names();
// TODO(clemensh): this looks inside an array constructed elsewhere. Refactor.
return func_names_arr->get_int(0);
return static_cast<int>(GetCppModule(instance)->functions.size());
}
Handle<JSObject> wasm::CreateCompiledModuleObject(
Handle<JSObject> wasm::CreateWasmModuleObject(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
ModuleOrigin origin) {
Handle<JSObject> module_obj;
Handle<JSObject> wasm_module;
if (origin == ModuleOrigin::kWasmOrigin) {
Handle<JSFunction> module_cons(
isolate->native_context()->wasm_module_constructor());
module_obj = isolate->factory()->NewJSObject(module_cons);
wasm_module = isolate->factory()->NewJSObject(module_cons);
} else {
DCHECK(origin == ModuleOrigin::kAsmJsOrigin);
Handle<Map> map = isolate->factory()->NewMap(
JS_OBJECT_TYPE, JSObject::kHeaderSize + kPointerSize);
module_obj = isolate->factory()->NewJSObjectFromMap(map, TENURED);
wasm_module = isolate->factory()->NewJSObjectFromMap(map, TENURED);
}
module_obj->SetInternalField(0, *compiled_module);
wasm_module->SetInternalField(0, *compiled_module);
if (origin == ModuleOrigin::kWasmOrigin) {
Handle<Symbol> module_sym(isolate->native_context()->wasm_module_sym());
Object::SetProperty(module_obj, module_sym, module_obj, STRICT).Check();
Object::SetProperty(wasm_module, module_sym, wasm_module, STRICT).Check();
}
Handle<WeakCell> link_to_module = isolate->factory()->NewWeakCell(module_obj);
compiled_module->set_weak_module_object(link_to_module);
return module_obj;
Handle<WeakCell> link_to_module =
isolate->factory()->NewWeakCell(wasm_module);
compiled_module->set_weak_wasm_module(link_to_module);
return wasm_module;
}
// TODO(clemensh): origin can be inferred from asm_js_script; remove it.
......@@ -2134,17 +1777,23 @@ MaybeHandle<JSObject> wasm::CreateModuleObjectFromBytes(
const byte* asm_js_offset_tables_start,
const byte* asm_js_offset_tables_end) {
MaybeHandle<JSObject> nothing;
Zone zone(isolate->allocator(), ZONE_NAME);
ModuleResult result =
DecodeWasmModule(isolate, &zone, start, end, false, origin);
std::unique_ptr<const WasmModule> decoded_module(result.val);
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 =
WasmModuleWrapper::New(isolate, const_cast<WasmModule*>(result.val));
// Compile the functions of the module, producing a compiled module.
MaybeHandle<WasmCompiledModule> maybe_compiled_module =
decoded_module->CompileFunctions(isolate, thrower);
result.val->CompileFunctions(isolate, module_wrapper, thrower);
if (maybe_compiled_module.is_null()) return nothing;
Handle<WasmCompiledModule> compiled_module =
maybe_compiled_module.ToHandleChecked();
......@@ -2163,15 +1812,13 @@ MaybeHandle<JSObject> wasm::CreateModuleObjectFromBytes(
compiled_module->set_asm_js_offset_tables(offset_tables);
}
return CreateCompiledModuleObject(isolate, compiled_module, origin);
return CreateWasmModuleObject(isolate, compiled_module, origin);
}
bool wasm::ValidateModuleBytes(Isolate* isolate, const byte* start,
const byte* end, ErrorThrower* thrower,
ModuleOrigin origin) {
Zone zone(isolate->allocator(), ZONE_NAME);
ModuleResult result =
DecodeWasmModule(isolate, &zone, start, end, false, origin);
ModuleResult result = DecodeWasmModule(isolate, start, end, false, origin);
if (result.ok()) {
DCHECK_NOT_NULL(result.val);
delete result.val;
......@@ -2193,7 +1840,7 @@ void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer) {
DCHECK(IsWasmInstance(*instance));
instance->SetInternalField(kWasmMemArrayBuffer, buffer);
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
compiled_module->set_ptr_to_heap(buffer);
compiled_module->set_ptr_to_memory(buffer);
}
int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
......@@ -2256,30 +1903,30 @@ int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
memcpy(new_mem_start, old_mem_start, old_size);
}
SetInstanceMemory(instance, *buffer);
RelocateInstanceCode(instance, old_mem_start, new_mem_start, old_size,
new_size);
Handle<FixedArray> code_table = GetCompiledModule(*instance)->code_table();
RelocateMemoryReferencesInCode(code_table, old_mem_start, new_mem_start,
old_size, new_size);
DCHECK(old_size % WasmModule::kPageSize == 0);
return (old_size / WasmModule::kPageSize);
}
void testing::ValidateInstancesChain(Isolate* isolate,
Handle<JSObject> module_obj,
Handle<JSObject> wasm_module,
int instance_count) {
CHECK_GE(instance_count, 0);
DisallowHeapAllocation no_gc;
WasmCompiledModule* compiled_module =
WasmCompiledModule::cast(module_obj->GetInternalField(0));
CHECK_EQ(
JSObject::cast(compiled_module->ptr_to_weak_module_object()->value()),
*module_obj);
WasmCompiledModule::cast(wasm_module->GetInternalField(0));
CHECK_EQ(JSObject::cast(compiled_module->ptr_to_weak_wasm_module()->value()),
*wasm_module);
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_module_object()->value(),
*module_obj);
CHECK_EQ(current_instance->ptr_to_weak_wasm_module()->value(),
*wasm_module);
CHECK(IsWasmInstance(
current_instance->ptr_to_weak_owning_instance()->value()));
prev = current_instance;
......@@ -2292,22 +1939,52 @@ void testing::ValidateInstancesChain(Isolate* isolate,
}
void testing::ValidateModuleState(Isolate* isolate,
Handle<JSObject> module_obj) {
Handle<JSObject> wasm_module) {
DisallowHeapAllocation no_gc;
WasmCompiledModule* compiled_module =
WasmCompiledModule::cast(module_obj->GetInternalField(0));
CHECK(compiled_module->has_weak_module_object());
CHECK_EQ(compiled_module->ptr_to_weak_module_object()->value(), *module_obj);
WasmCompiledModule::cast(wasm_module->GetInternalField(0));
CHECK(compiled_module->has_weak_wasm_module());
CHECK_EQ(compiled_module->ptr_to_weak_wasm_module()->value(), *wasm_module);
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> instance) {
Handle<JSObject> wasm_module) {
DisallowHeapAllocation no_gc;
CHECK(IsWasmInstance(*instance));
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
CHECK(compiled_module->has_weak_module_object());
CHECK(compiled_module->ptr_to_weak_module_object()->cleared());
CHECK(IsWasmInstance(*wasm_module));
WasmCompiledModule* compiled_module = GetCompiledModule(*wasm_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);
WasmModule* module = nullptr;
{
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;
const byte* start =
reinterpret_cast<const byte*>(module_bytes->GetCharsAddress());
const byte* end = start + module_bytes->length();
// TODO(titzer): remember the module origin in the compiled_module
// For now, we assume serialized modules did not originate from asm.js.
ModuleResult result =
DecodeWasmModule(isolate, start, end, false, kWasmOrigin);
CHECK(result.ok());
CHECK_NOT_NULL(result.val);
module = const_cast<WasmModule*>(result.val);
}
Handle<WasmModuleWrapper> module_wrapper =
WasmModuleWrapper::New(isolate, module);
compiled_module->set_module_wrapper(module_wrapper);
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
}
......@@ -12,6 +12,7 @@
#include "src/handles.h"
#include "src/parsing/preparse-data.h"
#include "src/wasm/managed.h"
#include "src/wasm/signature-map.h"
#include "src/wasm/wasm-opcodes.h"
......@@ -176,22 +177,23 @@ struct V8_EXPORT_PRIVATE WasmModule {
static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb
const byte* module_start; // starting address for the module bytes.
const byte* module_end; // end address for the module bytes.
uint32_t min_mem_pages; // minimum size of the memory in 64k pages.
uint32_t max_mem_pages; // maximum size of the memory in 64k pages.
bool mem_export; // true if the memory is exported.
Zone* owned_zone;
const byte* module_start = nullptr; // starting address for the module bytes
const byte* module_end = nullptr; // end address for the module bytes
uint32_t min_mem_pages = 0; // minimum size of the memory in 64k pages
uint32_t max_mem_pages = 0; // maximum size of the memory in 64k pages
bool mem_export = false; // true if the memory is exported
// TODO(wasm): reconcile start function index being an int with
// the fact that we index on uint32_t, so we may technically not be
// able to represent some start_function_index -es.
int start_function_index; // start function, if any.
ModuleOrigin origin; // origin of the module
int start_function_index = -1; // start function, if any
ModuleOrigin origin = kWasmOrigin; // origin of the module
std::vector<WasmGlobal> globals; // globals in this module.
uint32_t globals_size; // size of globals table.
uint32_t num_imported_functions; // number of imported functions.
uint32_t num_declared_functions; // number of declared functions.
uint32_t num_exported_functions; // number of exported functions.
uint32_t globals_size = 0; // size of globals table.
uint32_t num_imported_functions = 0; // number of imported functions.
uint32_t num_declared_functions = 0; // number of declared functions.
uint32_t num_exported_functions = 0; // number of exported functions.
std::vector<FunctionSig*> signatures; // signatures in this module.
std::vector<WasmFunction> functions; // functions in this module.
std::vector<WasmDataSegment> data_segments; // data segments in this module.
......@@ -208,8 +210,11 @@ struct V8_EXPORT_PRIVATE WasmModule {
// switch to libc-2.21 or higher.
std::unique_ptr<base::Semaphore> pending_tasks;
WasmModule() : WasmModule(nullptr) {}
explicit WasmModule(byte* module_start);
WasmModule() : WasmModule(nullptr, nullptr) {}
WasmModule(Zone* owned_zone, const byte* module_start);
~WasmModule() {
if (owned_zone) delete owned_zone;
}
// Get a string stored in the module bytes representing a name.
WasmName GetName(uint32_t offset, uint32_t length) const {
......@@ -248,17 +253,17 @@ struct V8_EXPORT_PRIVATE WasmModule {
// Creates a new instantiation of the module in the given isolate.
static MaybeHandle<JSObject> Instantiate(Isolate* isolate,
ErrorThrower* thrower,
Handle<JSObject> module_object,
Handle<JSObject> wasm_module,
Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory);
MaybeHandle<WasmCompiledModule> CompileFunctions(Isolate* isolate,
ErrorThrower* thrower) const;
private:
DISALLOW_COPY_AND_ASSIGN(WasmModule);
MaybeHandle<WasmCompiledModule> CompileFunctions(
Isolate* isolate, Handle<Managed<WasmModule>> module_wrapper,
ErrorThrower* thrower) const;
};
typedef Managed<WasmModule> WasmModuleWrapper;
// An instantiated WASM module, including memory, function table, etc.
struct WasmInstance {
const WasmModule* module; // static representation of the module.
......@@ -270,18 +275,15 @@ struct WasmInstance {
std::vector<Handle<FixedArray>> function_tables; // indirect function tables.
std::vector<Handle<Code>> function_code; // code objects for each function.
// -- raw memory ------------------------------------------------------------
byte* mem_start; // start of linear memory.
uint32_t mem_size; // size of the linear memory.
byte* mem_start = nullptr; // start of linear memory.
uint32_t mem_size = 0; // size of the linear memory.
// -- raw globals -----------------------------------------------------------
byte* globals_start; // start of the globals area.
byte* globals_start = nullptr; // start of the globals area.
explicit WasmInstance(const WasmModule* m)
: module(m),
function_tables(m->function_tables.size()),
function_code(m->functions.size()),
mem_start(nullptr),
mem_size(0),
globals_start(nullptr) {}
function_code(m->functions.size()) {}
};
// Interface provided to the decoder/graph builder which contains only
......@@ -379,7 +381,8 @@ class WasmCompiledModule : public FixedArray {
#define WCM_SMALL_NUMBER(TYPE, NAME) \
TYPE NAME() const { \
return static_cast<TYPE>(Smi::cast(get(kID_##NAME))->value()); \
}
} \
void set_##NAME(TYPE value) { set(kID_##NAME, Smi::FromInt(value)); }
#define WCM_WEAK_LINK(TYPE, NAME) \
WCM_OBJECT_OR_WEAK(WeakCell, weak_##NAME, kID_##NAME); \
......@@ -390,25 +393,18 @@ class WasmCompiledModule : public FixedArray {
#define CORE_WCM_PROPERTY_TABLE(MACRO) \
MACRO(OBJECT, FixedArray, code_table) \
MACRO(OBJECT, FixedArray, imports) \
MACRO(OBJECT, FixedArray, exports) \
MACRO(OBJECT, FixedArray, inits) \
MACRO(OBJECT, FixedArray, startup_function) \
MACRO(OBJECT, FixedArray, indirect_function_tables) \
MACRO(OBJECT, Foreign, module_wrapper) \
MACRO(OBJECT, SeqOneByteString, module_bytes) \
MACRO(OBJECT, ByteArray, function_names) \
MACRO(OBJECT, Script, asm_js_script) \
MACRO(OBJECT, FixedArray, function_tables) \
MACRO(OBJECT, FixedArray, empty_function_tables) \
MACRO(OBJECT, ByteArray, asm_js_offset_tables) \
MACRO(SMALL_NUMBER, uint32_t, min_memory_pages) \
MACRO(OBJECT, FixedArray, data_segments_info) \
MACRO(OBJECT, ByteArray, data_segments) \
MACRO(SMALL_NUMBER, uint32_t, globals_size) \
MACRO(OBJECT, JSArrayBuffer, heap) \
MACRO(SMALL_NUMBER, ModuleOrigin, origin) \
MACRO(OBJECT, JSArrayBuffer, memory) \
MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \
MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
MACRO(WEAK_LINK, JSObject, owning_instance) \
MACRO(WEAK_LINK, JSObject, module_object)
MACRO(WEAK_LINK, JSObject, wasm_module)
#if DEBUG
#define DEBUG_ONLY_TABLE(MACRO) MACRO(SMALL_NUMBER, uint32_t, instance_id)
......@@ -429,16 +425,14 @@ class WasmCompiledModule : public FixedArray {
};
public:
static Handle<WasmCompiledModule> New(Isolate* isolate,
uint32_t min_memory_pages,
uint32_t globals_size,
ModuleOrigin origin);
static Handle<WasmCompiledModule> New(
Isolate* isolate, Handle<Managed<WasmModule>> module_wrapper);
static Handle<WasmCompiledModule> Clone(Isolate* isolate,
Handle<WasmCompiledModule> module) {
Handle<WasmCompiledModule> ret = Handle<WasmCompiledModule>::cast(
isolate->factory()->CopyFixedArray(module));
ret->Init();
ret->InitId();
ret->reset_weak_owning_instance();
ret->reset_weak_next_instance();
ret->reset_weak_prev_instance();
......@@ -446,12 +440,12 @@ class WasmCompiledModule : public FixedArray {
}
uint32_t mem_size() const {
DCHECK(has_heap());
return heap()->byte_length()->Number();
return has_memory() ? memory()->byte_length()->Number()
: default_mem_size();
}
uint32_t default_mem_size() const {
return min_memory_pages() * WasmModule::kPageSize;
return min_mem_pages() * WasmModule::kPageSize;
}
#define DECLARATION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME)
......@@ -462,8 +456,11 @@ class WasmCompiledModule : public FixedArray {
void PrintInstancesChain();
static void RecreateModuleWrapper(Isolate* isolate,
Handle<FixedArray> compiled_module);
private:
void Init();
void InitId();
DISALLOW_IMPLICIT_CONSTRUCTORS(WasmCompiledModule);
};
......@@ -493,19 +490,19 @@ int GetNumberOfFunctions(Handle<JSObject> wasm);
// Create and export JSFunction
Handle<JSFunction> WrapExportCodeAsJSFunction(Isolate* isolate,
Handle<Code> export_code,
Handle<String> name, int arity,
MaybeHandle<ByteArray> signature,
Handle<String> name,
FunctionSig* sig, int func_index,
Handle<JSObject> instance);
// Check whether the given object is a wasm object.
// Check whether the given object represents a WebAssembly.Instance instance.
// This checks the number and type of internal fields, so it's not 100 percent
// secure. If it turns out that we need more complete checks, we could add a
// special marker as internal field, which will definitely never occur anywhere
// else.
bool IsWasmInstance(Object* object);
bool IsWasmInstance(Object* instance);
// Return the compiled module object for this wasm object.
WasmCompiledModule* GetCompiledModule(JSObject* instance);
// Return the compiled module object for this WASM instance.
WasmCompiledModule* GetCompiledModule(Object* wasm_instance);
// Check whether the wasm module was generated from asm.js code.
bool WasmIsAsmJs(Object* instance, Isolate* isolate);
......@@ -528,7 +525,7 @@ Handle<FixedArray> BuildFunctionTable(Isolate* isolate, uint32_t index,
void PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size,
const std::vector<Handle<Code>>* code_table);
Handle<JSObject> CreateCompiledModuleObject(
Handle<JSObject> CreateWasmModuleObject(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
ModuleOrigin origin);
......@@ -560,9 +557,9 @@ int32_t GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
namespace testing {
void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> module_obj,
void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> wasm_module,
int instance_count);
void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj);
void ValidateModuleState(Isolate* isolate, Handle<JSObject> wasm_module);
void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance);
} // namespace testing
......
......@@ -277,15 +277,18 @@ class WasmSerializationTest {
testing::SetupIsolateForWasmModule(serialization_isolate);
ModuleResult decoding_result =
DecodeWasmModule(serialization_isolate, zone(), buffer.begin(),
buffer.end(), false, kWasmOrigin);
std::unique_ptr<const WasmModule> module(decoding_result.val);
DecodeWasmModule(serialization_isolate, buffer.begin(), buffer.end(),
false, kWasmOrigin);
CHECK(!decoding_result.failed());
Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New(
serialization_isolate, const_cast<WasmModule*>(decoding_result.val));
MaybeHandle<WasmCompiledModule> compiled_module =
module->CompileFunctions(serialization_isolate, &thrower);
decoding_result.val->CompileFunctions(serialization_isolate,
module_wrapper, &thrower);
CHECK(!compiled_module.is_null());
Handle<JSObject> module_obj = CreateCompiledModuleObject(
Handle<JSObject> module_obj = CreateWasmModuleObject(
serialization_isolate, compiled_module.ToHandleChecked(),
ModuleOrigin::kWasmOrigin);
v8::Local<v8::Object> v8_module_obj = v8::Utils::ToLocal(module_obj);
......@@ -470,7 +473,7 @@ TEST(TestInterruptLoop) {
ErrorThrower thrower(isolate, "Test");
const Handle<JSObject> instance =
testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(),
isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null());
......@@ -544,7 +547,7 @@ TEST(Run_WasmModule_GrowMemOobFixedIndex) {
ErrorThrower thrower(isolate, "Test");
Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(),
isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null());
......@@ -589,7 +592,7 @@ TEST(Run_WasmModule_GrowMemOobVariableIndex) {
ErrorThrower thrower(isolate, "Test");
Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(),
isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null());
......
......@@ -209,16 +209,9 @@ class TestingModule : public ModuleEnv {
WasmJs::InstallWasmMapsIfNeeded(isolate_, isolate_->native_context());
Handle<Code> ret_code =
compiler::CompileJSToWasmWrapper(isolate_, this, code, index);
FunctionSig* funcSig = this->module->functions[index].sig;
Handle<ByteArray> exportedSig = isolate_->factory()->NewByteArray(
static_cast<int>(funcSig->parameter_count() + funcSig->return_count()),
TENURED);
exportedSig->copy_in(0, reinterpret_cast<const byte*>(funcSig->raw_data()),
exportedSig->length());
Handle<JSFunction> ret = WrapExportCodeAsJSFunction(
isolate_, ret_code, name,
static_cast<int>(this->module->functions[index].sig->parameter_count()),
exportedSig, module_object);
isolate_, ret_code, name, this->module->functions[index].sig,
static_cast<int>(index), module_object);
return ret;
}
......@@ -226,27 +219,33 @@ class TestingModule : public ModuleEnv {
instance->function_code[index] = code;
}
void AddIndirectFunctionTable(uint16_t* functions, uint32_t table_size) {
void AddIndirectFunctionTable(uint16_t* function_indexes,
uint32_t table_size) {
module_.function_tables.push_back({table_size, table_size,
std::vector<int32_t>(), false, false,
SignatureMap()});
WasmIndirectFunctionTable& table = module_.function_tables.back();
for (uint32_t i = 0; i < table_size; ++i) {
table.values.push_back(functions[i]);
table.map.FindOrInsert(module_.functions[functions[i]].sig);
table.values.push_back(function_indexes[i]);
table.map.FindOrInsert(module_.functions[function_indexes[i]].sig);
}
Handle<FixedArray> values = BuildFunctionTable(
isolate_, static_cast<int>(module_.function_tables.size() - 1),
&module_);
instance->function_tables.push_back(values);
instance->function_tables.push_back(
isolate_->factory()->NewFixedArray(table_size * 2));
}
void PopulateIndirectFunctionTable() {
// Initialize the fixed arrays in instance->function_tables.
for (uint32_t i = 0; i < instance->function_tables.size(); i++) {
PopulateFunctionTable(instance->function_tables[i],
module_.function_tables[i].size,
&instance->function_code);
WasmIndirectFunctionTable& table = module_.function_tables[i];
Handle<FixedArray> array = instance->function_tables[i];
int table_size = static_cast<int>(table.values.size());
for (int j = 0; j < table_size; j++) {
WasmFunction& function = module_.functions[table.values[j]];
array->set(j, Smi::FromInt(table.map.Find(function.sig)));
array->set(j + table_size,
*instance->function_code[function.func_index]);
}
}
}
......
......@@ -23,7 +23,7 @@ uint32_t GetMinModuleMemSize(const WasmModule* module) {
return WasmModule::kPageSize * module->min_mem_pages;
}
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate,
ErrorThrower* thrower,
const byte* module_start,
const byte* module_end,
......@@ -31,18 +31,19 @@ const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
// Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway.
ModuleResult decoding_result =
DecodeWasmModule(isolate, zone, module_start, module_end, false, origin);
DecodeWasmModule(isolate, module_start, module_end, false, origin);
std::unique_ptr<const WasmModule> module(decoding_result.val);
if (decoding_result.failed()) {
// Module verification failed. throw.
thrower->CompileError("WASM.compileRun() failed: %s",
decoding_result.error_msg.get());
return nullptr;
}
if (thrower->error()) return nullptr;
return module.release();
if (thrower->error()) {
if (decoding_result.val) delete decoding_result.val;
return nullptr;
}
return decoding_result.val;
}
const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
......@@ -78,16 +79,16 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
}
const Handle<JSObject> CompileInstantiateWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, Zone* zone,
const byte* module_start, const byte* module_end, ModuleOrigin origin) {
std::unique_ptr<const WasmModule> module(DecodeWasmModuleForTesting(
isolate, zone, thrower, module_start, module_end, origin));
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_end, ModuleOrigin origin) {
const WasmModule* module = DecodeWasmModuleForTesting(
isolate, thrower, module_start, module_end, origin);
if (module == nullptr) {
thrower->CompileError("Wasm module decoding failed");
return Handle<JSObject>::null();
}
return InstantiateModuleForTesting(isolate, thrower, module.get());
return InstantiateModuleForTesting(isolate, thrower, module);
}
int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
......@@ -102,10 +103,9 @@ int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end, ModuleOrigin origin) {
HandleScope scope(isolate);
Zone zone(isolate->allocator(), ZONE_NAME);
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
Handle<JSObject> instance = CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, module_start, module_end, origin);
isolate, &thrower, module_start, module_end, origin);
if (instance.is_null()) {
return -1;
}
......
......@@ -19,7 +19,7 @@ namespace wasm {
namespace testing {
// Decodes the given encoded module.
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate,
ErrorThrower* thrower,
const byte* module_start,
const byte* module_end,
......@@ -49,8 +49,8 @@ int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower* thrower,
// Compiles WasmModule bytes and return an instance of the compiled module.
const Handle<JSObject> CompileInstantiateWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, Zone* zone,
const byte* module_start, const byte* module_end, ModuleOrigin origin);
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_end, ModuleOrigin origin);
// Runs the module instance with arguments.
int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
......
......@@ -56,7 +56,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
i_isolate, &zone, &interpreter_thrower, buffer.begin(), buffer.end(),
i_isolate, &interpreter_thrower, buffer.begin(), buffer.end(),
v8::internal::wasm::ModuleOrigin::kWasmOrigin));
if (module == nullptr) {
......
......@@ -57,7 +57,7 @@ int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data,
ErrorThrower thrower(i_isolate, "decoder");
std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
i_isolate, &zone, &thrower, buffer.begin(), buffer.end(), kWasmOrigin));
i_isolate, &thrower, buffer.begin(), buffer.end(), kWasmOrigin));
return 0;
}
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --random-seed=891196975 --expose-gc --allow-natives-syntax
// Flags: --gc-interval=207 --stress-compaction --validate-asm
//
// /v8/test/mjsunit/wasm/grow-memory.js
// /v8/test/mjsunit/regress/regress-540.js
// /v8/test/mjsunit/regress/wasm/regression-02862.js
// /v8/test/mjsunit/regress/regress-2813.js
// /v8/test/mjsunit/regress/regress-323845.js
// Begin stripped down and modified version of mjsunit.js for easy minimization in CF.
function MjsUnitAssertionError(message) {}
MjsUnitAssertionError.prototype.toString = function() {
return this.message;
};
var assertSame;
var assertEquals;
var assertEqualsDelta;
var assertArrayEquals;
var assertPropertiesEqual;
var assertToStringEquals;
var assertTrue;
var assertFalse;
var triggerAssertFalse;
var assertNull;
var assertNotNull;
var assertThrows;
var assertDoesNotThrow;
var assertInstanceof;
var assertUnreachable;
var assertOptimized;
var assertUnoptimized;
function classOf(object) {
var string = Object.prototype.toString.call(object);
return string.substring(8, string.length - 1);
}
function PrettyPrint(value) {
return "";
}
function PrettyPrintArrayElement(value, index, array) {
return "";
}
function fail(expectedText, found, name_opt) {}
function deepObjectEquals(a, b) {
var aProps = Object.keys(a);
aProps.sort();
var bProps = Object.keys(b);
bProps.sort();
if (!deepEquals(aProps, bProps)) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
if (!deepEquals(a[aProps[i]], b[aProps[i]])) {
return false;
}
}
return true;
}
function deepEquals(a, b) {
if (a === b) {
if (a === 0) return (1 / a) === (1 / b);
return true;
}
if (typeof a != typeof b) return false;
if (typeof a == "number") return isNaN(a) && isNaN(b);
if (typeof a !== "object" && typeof a !== "function") return false;
var objectClass = classOf(a);
if (objectClass !== classOf(b)) return false;
if (objectClass === "RegExp") {
return (a.toString() === b.toString());
}
if (objectClass === "Function") return false;
if (objectClass === "Array") {
var elementCount = 0;
if (a.length != b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!deepEquals(a[i], b[i])) return false;
}
return true;
}
if (objectClass == "String" || objectClass == "Number" || objectClass == "Boolean" || objectClass == "Date") {
if (a.valueOf() !== b.valueOf()) return false;
}
return deepObjectEquals(a, b);
}
assertSame = function assertSame(expected, found, name_opt) {
if (found === expected) {
if (expected !== 0 || (1 / expected) == (1 / found)) return;
} else if ((expected !== expected) && (found !== found)) {
return;
}
fail(PrettyPrint(expected), found, name_opt);
};
assertEquals = function assertEquals(expected, found, name_opt) {
if (!deepEquals(found, expected)) {
fail(PrettyPrint(expected), found, name_opt);
}
};
assertEqualsDelta = function assertEqualsDelta(expected, found, delta, name_opt) {
assertTrue(Math.abs(expected - found) <= delta, name_opt);
};
assertArrayEquals = function assertArrayEquals(expected, found, name_opt) {
var start = "";
if (name_opt) {
start = name_opt + " - ";
}
assertEquals(expected.length, found.length, start + "array length");
if (expected.length == found.length) {
for (var i = 0; i < expected.length; ++i) {
assertEquals(expected[i], found[i], start + "array element at index " + i);
}
}
};
assertPropertiesEqual = function assertPropertiesEqual(expected, found, name_opt) {
if (!deepObjectEquals(expected, found)) {
fail(expected, found, name_opt);
}
};
assertToStringEquals = function assertToStringEquals(expected, found, name_opt) {
if (expected != String(found)) {
fail(expected, found, name_opt);
}
};
assertTrue = function assertTrue(value, name_opt) {
assertEquals(true, value, name_opt);
};
assertFalse = function assertFalse(value, name_opt) {
assertEquals(false, value, name_opt);
};
assertNull = function assertNull(value, name_opt) {
if (value !== null) {
fail("null", value, name_opt);
}
};
assertNotNull = function assertNotNull(value, name_opt) {
if (value === null) {
fail("not null", value, name_opt);
}
};
assertThrows = function assertThrows(code, type_opt, cause_opt) {
var threwException = true;
try {
if (typeof code == 'function') {
code();
} else {
eval(code);
}
threwException = false;
} catch (e) {
if (typeof type_opt == 'function') {
assertInstanceof(e, type_opt);
}
if (arguments.length >= 3) {
assertEquals(e.type, cause_opt);
}
return;
}
};
assertInstanceof = function assertInstanceof(obj, type) {
if (!(obj instanceof type)) {
var actualTypeName = null;
var actualConstructor = Object.getPrototypeOf(obj).constructor;
if (typeof actualConstructor == "function") {
actualTypeName = actualConstructor.name || String(actualConstructor);
}
fail("Object <" + PrettyPrint(obj) + "> is not an instance of <" + (type.name || type) + ">" + (actualTypeName ? " but of < " + actualTypeName + ">" : ""));
}
};
assertDoesNotThrow = function assertDoesNotThrow(code, name_opt) {
try {
if (typeof code == 'function') {
code();
} else {
eval(code);
}
} catch (e) {
fail("threw an exception: ", e.message || e, name_opt);
}
};
assertUnreachable = function assertUnreachable(name_opt) {
var message = "Fail" + "ure: unreachable";
if (name_opt) {
message += " - " + name_opt;
}
};
var OptimizationStatus = function() {}
assertUnoptimized = function assertUnoptimized(fun, sync_opt, name_opt) {
if (sync_opt === undefined) sync_opt = "";
assertTrue(OptimizationStatus(fun, sync_opt) != 1, name_opt);
}
assertOptimized = function assertOptimized(fun, sync_opt, name_opt) {
if (sync_opt === undefined) sync_opt = "";
assertTrue(OptimizationStatus(fun, sync_opt) != 2, name_opt);
}
triggerAssertFalse = function() {}
try {
console.log;
print = console.log;
alert = console.log;
} catch (e) {}
function runNearStackLimit(f) {
function t() {
try {
t();
} catch (e) {
f();
}
};
try {
t();
} catch (e) {}
}
function quit() {}
function nop() {}
try {
gc;
} catch (e) {
gc = nop;
}
function getRandomProperty(v, rand) {
var properties = Object.getOwnPropertyNames(v);
var proto = Object.getPrototypeOf(v);
if (proto) {
properties = properties.concat(Object.getOwnPropertyNames(proto));
}
if (properties.includes("constructor") && v.constructor.hasOwnProperty("__proto__")) {
properties = properties.concat(Object.getOwnPropertyNames(v.constructor.__proto__));
}
if (properties.length == 0) {
return "0";
}
return properties[rand % properties.length];
}
// End stripped down and modified version of mjsunit.js.
var __v_0 = {};
var __v_1 = {};
var __v_2 = {};
var __v_3 = {};
var __v_4 = -1073741824;
var __v_5 = {};
var __v_6 = 1;
var __v_7 = 1073741823;
var __v_8 = {};
var __v_9 = {};
var __v_10 = 4294967295;
var __v_11 = this;
var __v_12 = {};
var __v_13 = {};
function __f_18(__f_17, y) {
eval(__f_17);
return y();
}
try {
var __v_17 = __f_18("function y() { return 1; }", function() {
return 0;
})
assertEquals(1, __v_17);
gc();
__v_17 =
(function(__f_17) {
function __f_17() {
return 3;
}
return __f_17();
})(function() {
return 2;
});
assertEquals(3, __v_17);
__v_17 =
(function(__f_17) {
function __f_17() {
return 5;
}
return arguments[0]();
})(function() {
return -1073741825;
});
assertEquals(5, __v_17);
} catch (e) {
print("Caught: " + e);
}
function __f_27() {}
try {
var __v_24 = {};
var __v_21 = {};
var __v_22 = {};
var __v_20 = {};
__v_58 = {
instantiateModuleFromAsm: function(text, ffi, heap) {
var __v_21 = eval('(' + text + ')');
if (__f_27()) {
throw "validate failure";
}
var __v_20 = __v_21();
if (__f_27()) {
throw "bad module args";
}
}
};
__f_21 = function __f_21() {
if (found === expected) {
if (1 / expected) return;
} else if ((expected !== expected) && (found !== found)) {
return;
};
};
__f_28 = function __f_28() {
if (!__f_23()) {
__f_125(__f_69(), found, name_opt);
}
};
__f_24 = function __f_24(code, type_opt, cause_opt) {
var __v_24 = true;
try {
if (typeof code == 'function') {
code();
} else {
eval();
}
__v_24 = false;
} catch (e) {
if (typeof type_opt == 'function') {
__f_22();
}
if (arguments.length >= 3) {
__f_28();
}
return;
}
};
__f_22 = function __f_22() {
if (obj instanceof type) {
obj.constructor;
if (typeof __v_57 == "function") {;
};
}
};
try {
__f_28();
__v_82.__p_750895751 = __v_82[getRandomProperty()];
} catch (e) {
"Caught: " + e;
}
__f_19();
gc();
__f_19(19, __f_24);
__f_19();
__f_19();
__f_24(function() {
__v_58.instantiateModuleFromAsm(__f_28.toString()).__f_20();
});
} catch (e) {
print("Caught: " + e);
}
function __f_19() {
"use asm";
function __f_20() {}
return {
__f_20: __f_20
};
}
try {
__f_19();
__f_19();
__f_19();
} catch (e) {
print("Caught: " + e);
}
function __f_29() {}
try {
__f_19();
try {
__f_19();
gc();
__f_25();
} catch (e) {
"Caught: " + e;
}
__f_19();
__f_19();
__f_19();
} catch (e) {
print("Caught: " + e);
}
function __f_23() {
"use asm";
function __f_20() {}
return {
__f_20: __f_20
};
}
try {
__f_19();
__f_19();
__f_19();
__f_19();
gc();
__f_19();
__f_19();
__f_19();
} catch (e) {
print("Caught: " + e);
}
function __f_26(stdlib) {
"use asm";
var __v_2 = new stdlib.Int32Array();
__v_22[4294967295] | 14 + 1 | 14;
return {
__f_20: __f_20
};
}
function __f_25() {
var __v_19 = new ArrayBuffer();
var __v_23 = new Int32Array(__v_19);
var module = __v_58.instantiateModuleFromAsm(__f_26.toString());
__f_28();
gc();
}
try {
(function() {})();
(function() {})();
try {
(function() {
__v_23.__defineGetter__(getRandomProperty(__v_23, 580179357), function() {
gc();
return __f_25(__v_23);
});
var __v_23 = 0x87654321;
__v_19.__f_89();
})();
} catch (e) {;
}
} catch (e) {
print("Caught: " + e);
}
function __f_30(x) {
var __v_30 = x + 1;
var __v_31 = x + 2;
if (x != 0) {
if (x > 0 & x < 100) {
return __v_30;
}
}
return 0;
}
try {
assertEquals(0, __f_30(0));
assertEquals(0, __f_30(0));
%OptimizeFunctionOnNextCall(__f_30);
assertEquals(3, __f_30(2));
} catch (e) {
print("Caught: " + e);
}
function __f_31() {
__f_32.arguments;
}
function __f_32(x) {
__f_31();
}
function __f_33() {
__f_32({});
}
try {
__f_33();
__f_33();
__f_33();
%OptimizeFunctionOnNextCall(__f_33);
__f_33();
gc();
} catch (e) {
print("Caught: " + e);
}
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --stress-gc --expose-gc
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function run(f) {
// wrap the creation in a closure so that the only thing returned is
// the module (i.e. the underlying array buffer of WASM wire bytes dies).
var module = (() => {
var builder = new WasmModuleBuilder();
builder.addImport("the_name_of_my_import", kSig_i_i);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprCallFunction, 0])
.exportAs("main");
print("module");
return new WebAssembly.Module(builder.toBuffer());
})();
gc();
for (var i = 0; i < 10; i++) {
print(" instance " + i);
var instance = new WebAssembly.Instance(module, {the_name_of_my_import: f});
var g = instance.exports.main;
assertEquals("function", typeof g);
for (var j = 0; j < 10; j++) {
assertEquals(f(j), g(j));
}
}
}
(function test() {
for (var i = 0; i < 3; i++) {
run(x => (x + 19));
run(x => (x - 18));
}
})();
......@@ -127,3 +127,111 @@ assertEquals(35, module.exports.main(2, 1));
assertEquals(32, module.exports.main(1, 2));
assertEquals(31, module.exports.main(2, 2));
assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
(function ConstBaseTest() {
print("ConstBaseTest...");
function instanceWithTable(base, length) {
var builder = new WasmModuleBuilder();
var mul = builder.addFunction("mul", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Mul // --
]);
var add = builder.addFunction("add", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
var sub = builder.addFunction("sub", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub // --
]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0]) // --
.exportAs("main");
builder.setFunctionTableLength(length);
builder.addFunctionTableInit(base, false, [add.index, sub.index, mul.index]);
return builder.instantiate();
}
for (var i = 0; i < 5; i++) {
print(" base = " + i);
var module = instanceWithTable(i, 10);
main = module.exports.main;
for (var j = 0; j < i; j++) {
assertTraps(kTrapFuncSigMismatch, "main(12, " + j + ")");
}
assertEquals(34, main(1, i + 0));
assertEquals(35, main(2, i + 0));
assertEquals(32, main(1, i + 1));
assertEquals(31, main(2, i + 1));
assertEquals(33, main(1, i + 2));
assertEquals(66, main(2, i + 2));
assertTraps(kTrapFuncInvalid, "main(12, 10)");
}
})();
(function GlobalBaseTest() {
print("GlobalBaseTest...");
var builder = new WasmModuleBuilder();
var mul = builder.addFunction("mul", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Mul // --
]);
var add = builder.addFunction("add", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
var sub = builder.addFunction("sub", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub // --
]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0]) // --
.exportAs("main");
builder.setFunctionTableLength(10);
var g = builder.addImportedGlobal("base", undefined, kAstI32);
builder.addFunctionTableInit(g, true, [mul.index, add.index, sub.index]);
var module = new WebAssembly.Module(builder.toBuffer());
for (var i = 0; i < 5; i++) {
print(" base = " + i);
var instance = new WebAssembly.Instance(module, {base: i});
main = instance.exports.main;
for (var j = 0; j < i; j++) {
assertTraps(kTrapFuncSigMismatch, "main(12, " + j + ")");
}
assertEquals(33, main(1, i + 0));
assertEquals(66, main(2, i + 0));
assertEquals(34, main(1, i + 1));
assertEquals(35, main(2, i + 1));
assertEquals(32, main(1, i + 2));
assertEquals(31, main(2, i + 2));
assertTraps(kTrapFuncInvalid, "main(12, 10)");
}
})();
......@@ -132,10 +132,11 @@ class WasmModuleBuilder {
this.exports = [];
this.globals = [];
this.functions = [];
this.table = [];
this.function_table = [];
this.function_table_length = 0;
this.function_table_inits = [];
this.segments = [];
this.explicit = [];
this.pad = null;
this.num_imported_funcs = 0;
this.num_imported_globals = 0;
return this;
......@@ -151,11 +152,6 @@ class WasmModuleBuilder {
return this;
}
addPadFunctionTable(size) {
this.pad = size;
return this;
}
addExplicitSection(bytes) {
this.explicit.push(bytes);
return this;
......@@ -220,8 +216,21 @@ class WasmModuleBuilder {
this.exports.push({name: name, kind: kExternalMemory, index: 0});
}
addFunctionTableInit(base, is_global, array) {
this.function_table_inits.push({base: base, is_global: is_global, array: array});
if (!is_global) {
var length = base + array.length;
if (length > this.function_table_length) this.function_table_length = length;
}
return this;
}
appendToTable(array) {
this.table.push(...array);
return this.addFunctionTableInit(this.function_table.length, false, array);
}
setFunctionTableLength(length) {
this.function_table_length = length;
return this;
}
......@@ -292,15 +301,15 @@ class WasmModuleBuilder {
});
}
// Add table.
if (wasm.table.length > 0) {
// Add function_table.
if (wasm.function_table_length > 0) {
if (debug) print("emitting table @ " + binary.length);
binary.emit_section(kTableSectionCode, section => {
section.emit_u8(1); // one table entry
section.emit_u8(kWasmAnyFunctionTypeForm);
section.emit_u8(1);
section.emit_u32v(wasm.table.length);
section.emit_u32v(wasm.table.length);
section.emit_u32v(wasm.function_table_length);
section.emit_u32v(wasm.function_table_length);
});
}
......@@ -394,17 +403,25 @@ class WasmModuleBuilder {
}
// Add table elements.
if (wasm.table.length > 0) {
if (wasm.function_table_inits.length > 0) {
if (debug) print("emitting table @ " + binary.length);
binary.emit_section(kElementSectionCode, section => {
section.emit_u8(1);
var inits = wasm.function_table_inits;
section.emit_u32v(inits.length);
section.emit_u8(0); // table index
section.emit_u8(kExprI32Const);
section.emit_u8(0);
section.emit_u8(kExprEnd);
section.emit_u32v(wasm.table.length);
for (let index of wasm.table) {
section.emit_u32v(index);
for (let init of inits) {
if (init.is_global) {
section.emit_u8(kExprGetGlobal);
} else {
section.emit_u8(kExprI32Const);
}
section.emit_u32v(init.base);
section.emit_u8(kExprEnd);
section.emit_u32v(init.array.length);
for (let index of init.array) {
section.emit_u32v(index);
}
}
});
}
......
......@@ -143,14 +143,14 @@ class WasmModuleVerifyTest : public TestWithIsolateAndZone {
auto temp = new byte[total];
memcpy(temp, header, sizeof(header));
memcpy(temp + sizeof(header), module_start, size);
ModuleResult result = DecodeWasmModule(isolate(), zone(), temp,
temp + total, false, kWasmOrigin);
ModuleResult result =
DecodeWasmModule(isolate(), temp, temp + total, false, kWasmOrigin);
delete[] temp;
return result;
}
ModuleResult DecodeModuleNoHeader(const byte* module_start,
const byte* module_end) {
return DecodeWasmModule(isolate(), zone(), module_start, module_end, false,
return DecodeWasmModule(isolate(), module_start, module_end, false,
kWasmOrigin);
}
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment