Commit b7aff1ff authored by titzer's avatar titzer Committed by Commit bot

[wasm] Support for restricted table imports.

This CL implements basic table import functionality.

Missing: growing of tables (WebAssembly.Grow) doesn't change dispatch tables
Missing: allowing larger table imports than minimum size

R=rossberg@chromium.org,bradnelson@chromium.org
BUG=v8:5507

Review-Url: https://codereview.chromium.org/2454503005
Cr-Commit-Position: refs/heads/master@{#40661}
parent f7f591ab
...@@ -298,6 +298,7 @@ WasmGraphBuilder::WasmGraphBuilder( ...@@ -298,6 +298,7 @@ WasmGraphBuilder::WasmGraphBuilder(
mem_buffer_(nullptr), mem_buffer_(nullptr),
mem_size_(nullptr), mem_size_(nullptr),
function_tables_(zone), function_tables_(zone),
function_table_sizes_(zone),
control_(nullptr), control_(nullptr),
effect_(nullptr), effect_(nullptr),
cur_buffer_(def_buffer_), cur_buffer_(def_buffer_),
...@@ -1712,7 +1713,7 @@ Node* WasmGraphBuilder::GrowMemory(Node* input) { ...@@ -1712,7 +1713,7 @@ Node* WasmGraphBuilder::GrowMemory(Node* input) {
graph(), jsgraph()->common(), graph(), jsgraph()->common(),
graph()->NewNode( graph()->NewNode(
jsgraph()->machine()->Uint32LessThanOrEqual(), input, jsgraph()->machine()->Uint32LessThanOrEqual(), input,
jsgraph()->Uint32Constant(wasm::WasmModule::kMaxMemPages)), jsgraph()->Uint32Constant(wasm::WasmModule::kV8MaxPages)),
BranchHint::kTrue); BranchHint::kTrue);
check_input_range.Chain(*control_); check_input_range.Chain(*control_);
...@@ -2154,28 +2155,17 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets, ...@@ -2154,28 +2155,17 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
return BuildWasmCall(sig, args, rets, position); return BuildWasmCall(sig, args, rets, position);
} }
Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets, Node* WasmGraphBuilder::CallIndirect(uint32_t sig_index, Node** args,
Node*** rets,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
DCHECK_NOT_NULL(args[0]); DCHECK_NOT_NULL(args[0]);
DCHECK(module_ && module_->instance); DCHECK(module_ && module_->instance);
MachineOperatorBuilder* machine = jsgraph()->machine();
// Compute the code object by loading it from the function table.
Node* key = args[0];
// Assume only one table for now. // Assume only one table for now.
DCHECK_LE(module_->instance->function_tables.size(), 1u); uint32_t table_index = 0;
// Bounds check the index. wasm::FunctionSig* sig = module_->GetSignature(sig_index);
uint32_t table_size =
module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0; if (!module_->IsValidTable(table_index)) {
wasm::FunctionSig* sig = module_->GetSignature(index);
if (table_size > 0) {
// Bounds check against the table size.
Node* size = Uint32Constant(table_size);
Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size);
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, in_bounds, position);
} else {
// No function table. Generate a trap and return a constant. // No function table. Generate a trap and return a constant.
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position); trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position);
(*rets) = Buffer(sig->return_count()); (*rets) = Buffer(sig->return_count());
...@@ -2184,7 +2174,16 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets, ...@@ -2184,7 +2174,16 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets,
} }
return trap_->GetTrapValue(sig); return trap_->GetTrapValue(sig);
} }
Node* table = FunctionTable(0);
EnsureFunctionTableNodes();
MachineOperatorBuilder* machine = jsgraph()->machine();
Node* key = args[0];
// Bounds check against the table size.
Node* size = function_table_sizes_[table_index];
Node* in_bounds = graph()->NewNode(machine->Uint32LessThan(), key, size);
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, in_bounds, position);
Node* table = function_tables_[table_index];
// Load signature from the table and check. // Load signature from the table and check.
// The table is a FixedArray; signatures are encoded as SMIs. // The table is a FixedArray; signatures are encoded as SMIs.
...@@ -2208,6 +2207,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets, ...@@ -2208,6 +2207,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets,
} }
// Load code object from the table. // Load code object from the table.
uint32_t table_size = module_->module->function_tables[table_index].min_size;
uint32_t offset = fixed_offset + kPointerSize * table_size; uint32_t offset = fixed_offset + kPointerSize * table_size;
Node* load_code = graph()->NewNode( Node* load_code = graph()->NewNode(
machine->Load(MachineType::AnyTagged()), table, machine->Load(MachineType::AnyTagged()), table,
...@@ -2854,17 +2854,15 @@ Node* WasmGraphBuilder::MemSize(uint32_t offset) { ...@@ -2854,17 +2854,15 @@ Node* WasmGraphBuilder::MemSize(uint32_t offset) {
} }
} }
Node* WasmGraphBuilder::FunctionTable(uint32_t index) { void WasmGraphBuilder::EnsureFunctionTableNodes() {
DCHECK(module_ && module_->instance && if (function_tables_.size() > 0) return;
index < module_->instance->function_tables.size()); for (size_t i = 0; i < module_->instance->function_tables.size(); ++i) {
if (!function_tables_.size()) { auto handle = module_->instance->function_tables[i];
for (size_t i = 0; i < module_->instance->function_tables.size(); ++i) { DCHECK(!handle.is_null());
DCHECK(!module_->instance->function_tables[i].is_null()); function_tables_.push_back(HeapConstant(handle));
function_tables_.push_back( uint32_t table_size = module_->module->function_tables[i].min_size;
HeapConstant(module_->instance->function_tables[i])); function_table_sizes_.push_back(Uint32Constant(table_size));
}
} }
return function_tables_[index];
} }
Node* WasmGraphBuilder::GetGlobal(uint32_t index) { Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
......
...@@ -170,7 +170,7 @@ class WasmGraphBuilder { ...@@ -170,7 +170,7 @@ class WasmGraphBuilder {
Node* ToJS(Node* node, wasm::LocalType type); Node* ToJS(Node* node, wasm::LocalType type);
Node* FromJS(Node* node, Node* context, wasm::LocalType type); Node* FromJS(Node* node, Node* context, wasm::LocalType type);
Node* Invert(Node* node); Node* Invert(Node* node);
Node* FunctionTable(uint32_t index); void EnsureFunctionTableNodes();
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Operations that concern the linear memory. // Operations that concern the linear memory.
...@@ -219,6 +219,7 @@ class WasmGraphBuilder { ...@@ -219,6 +219,7 @@ class WasmGraphBuilder {
Node* mem_buffer_; Node* mem_buffer_;
Node* mem_size_; Node* mem_size_;
NodeVector function_tables_; NodeVector function_tables_;
NodeVector function_table_sizes_;
Node** control_; Node** control_;
Node** effect_; Node** effect_;
Node** cur_buffer_; Node** cur_buffer_;
......
...@@ -31,8 +31,6 @@ namespace { ...@@ -31,8 +31,6 @@ namespace {
const char* kNameString = "name"; const char* kNameString = "name";
const size_t kNameStringLength = 4; const size_t kNameStringLength = 4;
static const uint32_t kMaxTableSize = 1 << 28;
LocalType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { LocalType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
switch (expr.kind) { switch (expr.kind) {
case WasmInitExpr::kNone: case WasmInitExpr::kNone:
...@@ -311,19 +309,22 @@ class ModuleDecoder : public Decoder { ...@@ -311,19 +309,22 @@ class ModuleDecoder : public Decoder {
// ===== Imported table ========================================== // ===== Imported table ==========================================
import->index = import->index =
static_cast<uint32_t>(module->function_tables.size()); static_cast<uint32_t>(module->function_tables.size());
module->function_tables.push_back( module->function_tables.push_back({0, 0, false,
{0, 0, std::vector<int32_t>(), true, false, SignatureMap()}); std::vector<int32_t>(), true,
false, SignatureMap()});
expect_u8("element type", kWasmAnyFunctionTypeForm); expect_u8("element type", kWasmAnyFunctionTypeForm);
WasmIndirectFunctionTable* table = &module->function_tables.back(); WasmIndirectFunctionTable* table = &module->function_tables.back();
consume_resizable_limits("element count", "elements", kMaxTableSize, consume_resizable_limits(
&table->size, &table->max_size); "element count", "elements", WasmModule::kV8MaxTableSize,
&table->min_size, &table->has_max, &table->max_size);
break; break;
} }
case kExternalMemory: { case kExternalMemory: {
// ===== Imported memory ========================================= // ===== Imported memory =========================================
consume_resizable_limits( bool has_max = false;
"memory", "pages", WasmModule::kMaxLegalPages, consume_resizable_limits("memory", "pages", WasmModule::kV8MaxPages,
&module->min_mem_pages, &module->max_mem_pages); &module->min_mem_pages, &has_max,
&module->max_mem_pages);
break; break;
} }
case kExternalGlobal: { case kExternalGlobal: {
...@@ -375,15 +376,16 @@ class ModuleDecoder : public Decoder { ...@@ -375,15 +376,16 @@ class ModuleDecoder : public Decoder {
error(pos, pos, "invalid table count %d, maximum 1", table_count); error(pos, pos, "invalid table count %d, maximum 1", table_count);
} }
if (module->function_tables.size() < 1) { if (module->function_tables.size() < 1) {
module->function_tables.push_back( module->function_tables.push_back({0, 0, false, std::vector<int32_t>(),
{0, 0, std::vector<int32_t>(), false, false, SignatureMap()}); false, false, SignatureMap()});
} }
for (uint32_t i = 0; ok() && i < table_count; i++) { for (uint32_t i = 0; ok() && i < table_count; i++) {
WasmIndirectFunctionTable* table = &module->function_tables.back(); WasmIndirectFunctionTable* table = &module->function_tables.back();
expect_u8("table type", kWasmAnyFunctionTypeForm); expect_u8("table type", kWasmAnyFunctionTypeForm);
consume_resizable_limits("table elements", "elements", kMaxUInt32, consume_resizable_limits("table elements", "elements",
&table->size, &table->max_size); WasmModule::kV8MaxTableSize, &table->min_size,
&table->has_max, &table->max_size);
} }
section_iter.advance(); section_iter.advance();
} }
...@@ -398,8 +400,9 @@ class ModuleDecoder : public Decoder { ...@@ -398,8 +400,9 @@ class ModuleDecoder : public Decoder {
} }
for (uint32_t i = 0; ok() && i < memory_count; i++) { for (uint32_t i = 0; ok() && i < memory_count; i++) {
consume_resizable_limits("memory", "pages", WasmModule::kMaxLegalPages, bool has_max = false;
&module->min_mem_pages, consume_resizable_limits("memory", "pages", WasmModule::kV8MaxPages,
&module->min_mem_pages, &has_max,
&module->max_mem_pages); &module->max_mem_pages);
} }
section_iter.advance(); section_iter.advance();
...@@ -623,7 +626,6 @@ class ModuleDecoder : public Decoder { ...@@ -623,7 +626,6 @@ class ModuleDecoder : public Decoder {
if (ok()) { if (ok()) {
CalculateGlobalOffsets(module); CalculateGlobalOffsets(module);
PreinitializeIndirectFunctionTables(module);
} }
const WasmModule* finished_module = module; const WasmModule* finished_module = module;
ModuleResult result = toResult(finished_module); ModuleResult result = toResult(finished_module);
...@@ -749,30 +751,6 @@ class ModuleDecoder : public Decoder { ...@@ -749,30 +751,6 @@ class ModuleDecoder : public Decoder {
module->globals_size = offset; module->globals_size = offset;
} }
// TODO(titzer): this only works without overlapping initializations from
// global bases for entries
void PreinitializeIndirectFunctionTables(WasmModule* module) {
// Fill all tables with invalid entries first.
for (WasmIndirectFunctionTable& table : module->function_tables) {
table.values.resize(table.size);
for (size_t i = 0; i < table.size; i++) {
table.values[i] = kInvalidFunctionIndex;
}
}
for (WasmTableInit& init : module->table_inits) {
if (init.offset.kind != WasmInitExpr::kI32Const) continue;
if (init.table_index >= module->function_tables.size()) continue;
WasmIndirectFunctionTable& table =
module->function_tables[init.table_index];
for (size_t i = 0; i < init.entries.size(); i++) {
size_t index = i + init.offset.val.i32_const;
if (index < table.values.size()) {
table.values[index] = init.entries[i];
}
}
}
}
// Verifies the body (code) of a given function. // Verifies the body (code) of a given function.
void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv, void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv,
WasmFunction* function) { WasmFunction* function) {
...@@ -866,29 +844,33 @@ class ModuleDecoder : public Decoder { ...@@ -866,29 +844,33 @@ class ModuleDecoder : public Decoder {
void consume_resizable_limits(const char* name, const char* units, void consume_resizable_limits(const char* name, const char* units,
uint32_t max_value, uint32_t* initial, uint32_t max_value, uint32_t* initial,
uint32_t* maximum) { bool* has_max, uint32_t* maximum) {
uint32_t flags = consume_u32v("resizable limits flags"); uint32_t flags = consume_u32v("resizable limits flags");
const byte* pos = pc(); const byte* pos = pc();
*initial = consume_u32v("initial size"); *initial = consume_u32v("initial size");
*has_max = false;
if (*initial > max_value) { if (*initial > max_value) {
error(pos, pos, error(pos, pos,
"initial %s size (%u %s) is larger than maximum allowable (%u)", "initial %s size (%u %s) is larger than implementation limit (%u)",
name, *initial, units, max_value); name, *initial, units, max_value);
} }
if (flags & 1) { if (flags & 1) {
*has_max = true;
pos = pc(); pos = pc();
*maximum = consume_u32v("maximum size"); *maximum = consume_u32v("maximum size");
if (*maximum > max_value) { if (*maximum > max_value) {
error(pos, pos, error(
"maximum %s size (%u %s) is larger than maximum allowable (%u)", pos, pos,
name, *maximum, units, max_value); "maximum %s size (%u %s) is larger than implementation limit (%u)",
name, *maximum, units, max_value);
} }
if (*maximum < *initial) { if (*maximum < *initial) {
error(pos, pos, "maximum %s size (%u %s) is less than initial (%u %s)", error(pos, pos, "maximum %s size (%u %s) is less than initial (%u %s)",
name, *maximum, units, *initial, units); name, *maximum, units, *initial, units);
} }
} else { } else {
*maximum = 0; *has_max = false;
*maximum = max_value;
} }
} }
......
...@@ -663,16 +663,13 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages, ...@@ -663,16 +663,13 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
WasmInstance* instance) { WasmInstance* instance) {
// TODO(ahaas): Move memory allocation to wasm-module.cc for better // TODO(ahaas): Move memory allocation to wasm-module.cc for better
// encapsulation. // encapsulation.
if (delta_pages > wasm::WasmModule::kMaxMemPages) { if (delta_pages > wasm::WasmModule::kV8MaxPages) {
return -1; return -1;
} }
uint32_t old_size = instance->mem_size; uint32_t old_size = instance->mem_size;
uint32_t new_size; uint32_t new_size;
byte* new_mem_start; byte* new_mem_start;
if (instance->mem_size == 0) { if (instance->mem_size == 0) {
if (delta_pages > wasm::WasmModule::kMaxMemPages) {
return -1;
}
// TODO(gdeepti): Fix bounds check to take into account size of memtype. // TODO(gdeepti): Fix bounds check to take into account size of memtype.
new_size = delta_pages * wasm::WasmModule::kPageSize; new_size = delta_pages * wasm::WasmModule::kPageSize;
new_mem_start = static_cast<byte*>(calloc(new_size, sizeof(byte))); new_mem_start = static_cast<byte*>(calloc(new_size, sizeof(byte)));
...@@ -683,7 +680,7 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages, ...@@ -683,7 +680,7 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
DCHECK_NOT_NULL(instance->mem_start); DCHECK_NOT_NULL(instance->mem_start);
new_size = old_size + delta_pages * wasm::WasmModule::kPageSize; new_size = old_size + delta_pages * wasm::WasmModule::kPageSize;
if (new_size > if (new_size >
wasm::WasmModule::kMaxMemPages * wasm::WasmModule::kPageSize) { wasm::WasmModule::kV8MaxPages * wasm::WasmModule::kPageSize) {
return -1; return -1;
} }
new_mem_start = static_cast<byte*>(realloc(instance->mem_start, new_size)); new_mem_start = static_cast<byte*>(realloc(instance->mem_start, new_size));
...@@ -695,9 +692,6 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages, ...@@ -695,9 +692,6 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
} }
instance->mem_start = new_mem_start; instance->mem_start = new_mem_start;
instance->mem_size = new_size; instance->mem_size = new_size;
// realloc
// update mem_start
// update mem_size
return static_cast<int32_t>(old_size / WasmModule::kPageSize); return static_cast<int32_t>(old_size / WasmModule::kPageSize);
} }
......
...@@ -29,6 +29,7 @@ namespace v8 { ...@@ -29,6 +29,7 @@ namespace v8 {
static const int kWasmTableArrayFieldIndex = 0; static const int kWasmTableArrayFieldIndex = 0;
static const int kWasmTableMaximumFieldIndex = 1; static const int kWasmTableMaximumFieldIndex = 1;
static const int kWasmTableDispatchTablesFieldIndex = 2;
enum WasmMemoryObjectData { enum WasmMemoryObjectData {
kWasmMemoryBuffer, kWasmMemoryBuffer,
...@@ -37,8 +38,8 @@ enum WasmMemoryObjectData { ...@@ -37,8 +38,8 @@ enum WasmMemoryObjectData {
}; };
enum WasmInternalFieldCountData { enum WasmInternalFieldCountData {
kWasmTableInternalFieldCount = 2, kWasmTableInternalFieldCount = 3,
kWasmMemoryInternalFieldCount kWasmMemoryInternalFieldCount = 3
}; };
namespace { namespace {
...@@ -518,9 +519,19 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -518,9 +519,19 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
return; return;
} }
i::Handle<i::FixedArray>::cast(array)->set(i, *value); i::Handle<i::FixedArray> dispatch_tables(
i::FixedArray::cast(
receiver->GetInternalField(kWasmTableDispatchTablesFieldIndex)),
i_isolate);
if (value->IsNull(i_isolate)) {
i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i,
i::Handle<i::JSFunction>::null());
} else {
i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i,
i::Handle<i::JSFunction>::cast(value));
}
// TODO(titzer): update relevant instances. i::Handle<i::FixedArray>::cast(array)->set(i, *value);
} }
void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
...@@ -612,26 +623,60 @@ i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject( ...@@ -612,26 +623,60 @@ i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject(
i::Handle<i::JSObject> i::WasmJs::CreateWasmTableObject( i::Handle<i::JSObject> i::WasmJs::CreateWasmTableObject(
i::Isolate* i_isolate, uint32_t initial, bool has_maximum, uint32_t maximum, i::Isolate* i_isolate, uint32_t initial, bool has_maximum, uint32_t maximum,
i::Handle<i::FixedArray>* array) { i::Handle<i::FixedArray>* js_functions) {
i::Handle<i::JSFunction> table_ctor( i::Handle<i::JSFunction> table_ctor(
i_isolate->native_context()->wasm_table_constructor()); i_isolate->native_context()->wasm_table_constructor());
i::Handle<i::JSObject> table_obj = i::Handle<i::JSObject> table_obj =
i_isolate->factory()->NewJSObject(table_ctor); i_isolate->factory()->NewJSObject(table_ctor);
*array = i_isolate->factory()->NewFixedArray(initial); *js_functions = i_isolate->factory()->NewFixedArray(initial);
i::Object* null = i_isolate->heap()->null_value(); i::Object* null = i_isolate->heap()->null_value();
// TODO(titzer): consider moving FixedArray to size_t. // TODO(titzer): consider moving FixedArray to size_t.
for (int i = 0; i < static_cast<int>(initial); ++i) (*array)->set(i, null); for (int i = 0; i < static_cast<int>(initial); ++i) {
table_obj->SetInternalField(kWasmTableArrayFieldIndex, *(*array)); (*js_functions)->set(i, null);
}
table_obj->SetInternalField(kWasmTableArrayFieldIndex, *(*js_functions));
table_obj->SetInternalField( table_obj->SetInternalField(
kWasmTableMaximumFieldIndex, kWasmTableMaximumFieldIndex,
has_maximum has_maximum
? static_cast<i::Object*>(i::Smi::FromInt(maximum)) ? static_cast<i::Object*>(i::Smi::FromInt(maximum))
: static_cast<i::Object*>(i_isolate->heap()->undefined_value())); : static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
Handle<FixedArray> dispatch_tables = i_isolate->factory()->NewFixedArray(0);
table_obj->SetInternalField(kWasmTableDispatchTablesFieldIndex,
*dispatch_tables);
i::Handle<i::Symbol> table_sym(i_isolate->native_context()->wasm_table_sym()); i::Handle<i::Symbol> table_sym(i_isolate->native_context()->wasm_table_sym());
i::Object::SetProperty(table_obj, table_sym, table_obj, i::STRICT).Check(); i::Object::SetProperty(table_obj, table_sym, table_obj, i::STRICT).Check();
return table_obj; return table_obj;
} }
i::Handle<i::FixedArray> i::WasmJs::AddWasmTableDispatchTable(
i::Isolate* i_isolate, i::Handle<i::JSObject> table_obj,
i::Handle<i::JSObject> instance, int table_index,
i::Handle<i::FixedArray> dispatch_table) {
DCHECK(IsWasmTableObject(i_isolate, table_obj));
i::Handle<i::FixedArray> dispatch_tables(
i::FixedArray::cast(
table_obj->GetInternalField(kWasmTableDispatchTablesFieldIndex)),
i_isolate);
DCHECK_EQ(0, dispatch_tables->length() % 3);
if (instance.is_null()) return dispatch_tables;
// TODO(titzer): use weak cells here to avoid leaking instances.
// Grow the dispatch table and add a new pair at the end.
i::Handle<i::FixedArray> new_dispatch_tables =
i_isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 3);
new_dispatch_tables->set(dispatch_tables->length() + 0, *instance);
new_dispatch_tables->set(dispatch_tables->length() + 1,
Smi::FromInt(table_index));
new_dispatch_tables->set(dispatch_tables->length() + 2, *dispatch_table);
table_obj->SetInternalField(kWasmTableDispatchTablesFieldIndex,
*new_dispatch_tables);
return new_dispatch_tables;
}
// TODO(titzer): we use the API to create the function template because the // TODO(titzer): we use the API to create the function template because the
// internal guts are too ugly to replicate here. // internal guts are too ugly to replicate here.
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
...@@ -817,17 +862,35 @@ void WasmJs::InstallWasmMapsIfNeeded(Isolate* isolate, ...@@ -817,17 +862,35 @@ void WasmJs::InstallWasmMapsIfNeeded(Isolate* isolate,
} }
} }
bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) { static bool HasBrand(i::Handle<i::Object> value, i::Handle<i::Symbol> symbol) {
if (value->IsJSObject()) { if (value->IsJSObject()) {
i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value); i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
i::Handle<i::Symbol> sym(isolate->context()->wasm_memory_sym(), isolate); Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, symbol);
Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym);
if (has_brand.IsNothing()) return false; if (has_brand.IsNothing()) return false;
if (has_brand.ToChecked()) return true; if (has_brand.ToChecked()) return true;
} }
return false; return false;
} }
bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) {
i::Handle<i::Symbol> symbol(isolate->context()->wasm_memory_sym(), isolate);
return HasBrand(value, symbol);
}
bool WasmJs::IsWasmTableObject(Isolate* isolate, Handle<Object> value) {
i::Handle<i::Symbol> symbol(isolate->context()->wasm_table_sym(), isolate);
return HasBrand(value, symbol);
}
Handle<FixedArray> WasmJs::GetWasmTableFunctions(Isolate* isolate,
Handle<JSObject> value) {
DCHECK(IsWasmTableObject(isolate, value));
Handle<Object> arr(
JSObject::cast(*value)->GetInternalField(kWasmTableArrayFieldIndex),
isolate);
return Handle<FixedArray>::cast(arr);
}
Handle<JSArrayBuffer> WasmJs::GetWasmMemoryArrayBuffer(Isolate* isolate, Handle<JSArrayBuffer> WasmJs::GetWasmMemoryArrayBuffer(Isolate* isolate,
Handle<Object> value) { Handle<Object> value) {
DCHECK(IsWasmMemoryObject(isolate, value)); DCHECK(IsWasmMemoryObject(isolate, value));
...@@ -847,7 +910,7 @@ uint32_t WasmJs::GetWasmMemoryMaximumSize(Isolate* isolate, ...@@ -847,7 +910,7 @@ uint32_t WasmJs::GetWasmMemoryMaximumSize(Isolate* isolate,
DCHECK(IsWasmMemoryObject(isolate, value)); DCHECK(IsWasmMemoryObject(isolate, value));
Object* max_mem = Object* max_mem =
JSObject::cast(*value)->GetInternalField(kWasmMemoryMaximum); JSObject::cast(*value)->GetInternalField(kWasmMemoryMaximum);
if (max_mem->IsUndefined(isolate)) return wasm::WasmModule::kMaxMemPages; if (max_mem->IsUndefined(isolate)) return wasm::WasmModule::kV8MaxPages;
uint32_t max_pages = Smi::cast(max_mem)->value(); uint32_t max_pages = Smi::cast(max_mem)->value();
return max_pages; return max_pages;
} }
......
...@@ -24,16 +24,25 @@ class WasmJs { ...@@ -24,16 +24,25 @@ class WasmJs {
Handle<JSGlobalObject> global, Handle<JSGlobalObject> global,
Handle<Context> context); Handle<Context> context);
// WebAssembly.Table.
static Handle<JSObject> CreateWasmTableObject(
Isolate* isolate, uint32_t initial, bool has_maximum, uint32_t maximum,
Handle<FixedArray>* js_functions);
static bool IsWasmTableObject(Isolate* isolate, Handle<Object> value);
static Handle<FixedArray> GetWasmTableFunctions(Isolate* isolate,
Handle<JSObject> object);
static Handle<FixedArray> AddWasmTableDispatchTable(
Isolate* isolate, Handle<JSObject> table_obj, Handle<JSObject> instance,
int table_index, Handle<FixedArray> dispatch_table);
// WebAssembly.Memory
static Handle<JSObject> CreateWasmMemoryObject(Isolate* isolate, static Handle<JSObject> CreateWasmMemoryObject(Isolate* isolate,
Handle<JSArrayBuffer> buffer, Handle<JSArrayBuffer> buffer,
bool has_maximum, int maximum); bool has_maximum, int maximum);
static Handle<JSObject> CreateWasmTableObject(Isolate* isolate,
uint32_t initial,
bool has_maximum,
uint32_t maximum,
Handle<FixedArray>* array);
static bool IsWasmMemoryObject(Isolate* isolate, Handle<Object> value); static bool IsWasmMemoryObject(Isolate* isolate, Handle<Object> value);
static Handle<JSArrayBuffer> GetWasmMemoryArrayBuffer(Isolate* isolate, static Handle<JSArrayBuffer> GetWasmMemoryArrayBuffer(Isolate* isolate,
......
This diff is collapsed.
...@@ -133,8 +133,10 @@ struct WasmDataSegment { ...@@ -133,8 +133,10 @@ struct WasmDataSegment {
// Static representation of a wasm indirect call table. // Static representation of a wasm indirect call table.
struct WasmIndirectFunctionTable { struct WasmIndirectFunctionTable {
uint32_t size; // initial table size. uint32_t min_size; // minimum table size.
uint32_t max_size; // maximum table size. uint32_t max_size; // maximum table size.
bool has_max; // true if there is a maximum size.
// TODO(titzer): Move this to WasmInstance. Needed by interpreter only.
std::vector<int32_t> values; // function table, -1 indicating invalid. std::vector<int32_t> values; // function table, -1 indicating invalid.
bool imported; // true if imported. bool imported; // true if imported.
bool exported; // true if exported. bool exported; // true if exported.
...@@ -173,9 +175,9 @@ class WasmCompiledModule; ...@@ -173,9 +175,9 @@ class WasmCompiledModule;
// Static representation of a module. // Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule { struct V8_EXPORT_PRIVATE WasmModule {
static const uint32_t kPageSize = 0x10000; // Page size, 64kb. static const uint32_t kPageSize = 0x10000; // Page size, 64kb.
static const uint32_t kMaxLegalPages = 65536; // Maximum legal pages
static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
static const uint32_t kMaxMemPages = 16384; // Maximum memory size = 1gb static const size_t kV8MaxPages = 16384; // Maximum memory size = 1gb
static const size_t kV8MaxTableSize = 16 * 1024 * 1024;
Zone* owned_zone; Zone* owned_zone;
const byte* module_start = nullptr; // starting address for the module bytes const byte* module_start = nullptr; // starting address for the module bytes
...@@ -557,6 +559,9 @@ int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance); ...@@ -557,6 +559,9 @@ int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance);
int32_t GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance, int32_t GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
uint32_t pages); uint32_t pages);
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
int index, Handle<JSFunction> js_function);
namespace testing { namespace testing {
void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> wasm_module, void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> wasm_module,
......
...@@ -223,10 +223,12 @@ class TestingModule : public ModuleEnv { ...@@ -223,10 +223,12 @@ class TestingModule : public ModuleEnv {
void AddIndirectFunctionTable(uint16_t* function_indexes, void AddIndirectFunctionTable(uint16_t* function_indexes,
uint32_t table_size) { uint32_t table_size) {
module_.function_tables.push_back({table_size, table_size, module_.function_tables.push_back({table_size, table_size, true,
std::vector<int32_t>(), false, false, std::vector<int32_t>(), false, false,
SignatureMap()}); SignatureMap()});
WasmIndirectFunctionTable& table = module_.function_tables.back(); WasmIndirectFunctionTable& table = module_.function_tables.back();
table.min_size = table_size;
table.max_size = table_size;
for (uint32_t i = 0; i < table_size; ++i) { for (uint32_t i = 0; i < table_size; ++i) {
table.values.push_back(function_indexes[i]); table.values.push_back(function_indexes[i]);
table.map.FindOrInsert(module_.functions[function_indexes[i]].sig); table.map.FindOrInsert(module_.functions[function_indexes[i]].sig);
......
...@@ -30,6 +30,8 @@ function AddFunctions(builder) { ...@@ -30,6 +30,8 @@ function AddFunctions(builder) {
return {mul: mul, add: add, sub: sub}; return {mul: mul, add: add, sub: sub};
} }
function js_div(a, b) { return (a / b) | 0; }
(function ExportedTableTest() { (function ExportedTableTest() {
print("ExportedTableTest..."); print("ExportedTableTest...");
...@@ -56,8 +58,6 @@ function AddFunctions(builder) { ...@@ -56,8 +58,6 @@ function AddFunctions(builder) {
let module = new WebAssembly.Module(builder.toBuffer()); let module = new WebAssembly.Module(builder.toBuffer());
function js_div(a, b) { return (a / b) | 0; }
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
print(" base = " + i); print(" base = " + i);
let instance = new WebAssembly.Instance(module, {base: i, js_div: js_div}); let instance = new WebAssembly.Instance(module, {base: i, js_div: js_div});
...@@ -100,3 +100,278 @@ function AddFunctions(builder) { ...@@ -100,3 +100,278 @@ function AddFunctions(builder) {
assertEquals(-44, exp_div(-88.1, 2)); assertEquals(-44, exp_div(-88.1, 2));
} }
})(); })();
(function ImportedTableTest() {
let kTableSize = 10;
print("ImportedTableTest...");
var builder = new WasmModuleBuilder();
let d = builder.addImport("js_div", kSig_i_ii);
let f = AddFunctions(builder);
builder.setFunctionTableLength(kTableSize);
let g = builder.addImportedGlobal("base", undefined, kAstI32);
builder.addFunctionTableInit(g, true, [f.mul.index, f.add.index,
f.sub.index,
d]);
builder.addExportOfKind("table", kExternalTable, 0);
let m1 = new WebAssembly.Module(builder.toBuffer());
var builder = new WasmModuleBuilder();
builder.addImportedTable("table", undefined, kTableSize, kTableSize);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, kTableZero]) // --
.exportAs("main");
let m2 = new WebAssembly.Module(builder.toBuffer());
// Run 5 trials at different table bases.
for (let i = 0; i < 5; i++) {
print(" base = " + i);
let i1 = new WebAssembly.Instance(m1, {base: i, js_div: js_div});
let table = i1.exports.table;
assertEquals(10, table.length);
let i2 = new WebAssembly.Instance(m2, {table: table});
let main = i2.exports.main;
for (var j = 0; j < i; j++) {
assertThrows(() => main(0, j));
assertSame(null, table.get(j));
}
// mul
assertEquals("function", typeof table.get(i+0));
assertEquals(0, main(0, i+0));
assertEquals(66, main(2, i+0));
// add
assertEquals("function", typeof table.get(i+1));
assertEquals(33, main(0, i+1));
assertEquals(38, main(5, i+1));
// sub
assertEquals("function", typeof table.get(i+2));
assertEquals(32, main(1, i+2));
assertEquals(28, main(5, i+2));
// div
assertEquals("function", typeof table.get(i+3));
assertEquals(8, main(4, i+3));
assertEquals(3, main(11, i+3));
for (var j = i + 4; j < (kTableSize + 5); j++) {
assertThrows(x => main(0, j));
if (j < kTableSize) assertSame(null, table.get(j));
}
}
})();
(function ImportedTableTest() {
let kTableSize = 10;
print("ManualTableTest...");
var builder = new WasmModuleBuilder();
let d = builder.addImport("js_div", kSig_i_ii);
builder.addImportedTable("table", undefined, kTableSize, kTableSize);
let g = builder.addImportedGlobal("base", undefined, kAstI32);
let f = AddFunctions(builder);
builder.addFunctionTableInit(g, true, [f.mul.index, f.add.index,
f.sub.index,
d]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 55, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, kTableZero]) // --
.exportAs("main");
let m2 = new WebAssembly.Module(builder.toBuffer());
// Run 5 trials at different table bases.
for (let i = 0; i < 5; i++) {
print(" base = " + i);
let table = new WebAssembly.Table({element: "anyfunc",
initial: kTableSize});
assertEquals(10, table.length);
let i2 = new WebAssembly.Instance(m2, {base: i, table: table,
js_div: js_div});
let main = i2.exports.main;
for (var j = 0; j < i; j++) {
assertThrows(() => main(0, j));
assertSame(null, table.get(j));
}
// mul
assertEquals("function", typeof table.get(i+0));
assertEquals(0, main(0, i+0));
assertEquals(110, main(2, i+0));
// add
assertEquals("function", typeof table.get(i+1));
assertEquals(55, main(0, i+1));
assertEquals(60, main(5, i+1));
// sub
assertEquals("function", typeof table.get(i+2));
assertEquals(54, main(1, i+2));
assertEquals(50, main(5, i+2));
// div
assertEquals("function", typeof table.get(i+3));
assertEquals(13, main(4, i+3));
assertEquals(5, main(11, i+3));
for (var j = i + 4; j < (kTableSize + 5); j++) {
assertThrows(x => main(0, j));
if (j < kTableSize) assertSame(null, table.get(j));
}
}
})();
(function CumulativeTest() {
print("CumulativeTest...");
let kTableSize = 10;
let table = new WebAssembly.Table({element: "anyfunc", initial: 10});
var builder = new WasmModuleBuilder();
builder.addImportedTable("table", undefined, kTableSize, kTableSize);
let g = builder.addImportedGlobal("base", undefined, kAstI32);
let sig_index = builder.addType(kSig_i_v);
builder.addFunction("g", sig_index)
.addBody([
kExprGetGlobal, g
]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprCallIndirect, sig_index, kTableZero]) // --
.exportAs("main");
builder.addFunctionTableInit(g, true, [g]);
let module = new WebAssembly.Module(builder.toBuffer());
for (var i = 0; i < kTableSize; i++) {
print(" base = " + i);
let instance = new WebAssembly.Instance(module, {base: i, table: table});
for (var j = 0; j < kTableSize; j++) {
let func = table.get(j);
if (j > i) {
assertSame(null, func);
assertTraps(kTrapFuncSigMismatch, () => instance.exports.main(j));
} else {
assertEquals("function", typeof func);
assertEquals(j, func());
assertEquals(j, instance.exports.main(j));
}
}
}
})();
(function TwoWayTest() {
print("TwoWayTest...");
let kTableSize = 3;
// Module {m1} defines the table and exports it.
var builder = new WasmModuleBuilder();
builder.addType(kSig_i_i);
builder.addType(kSig_i_ii);
var sig_index1 = builder.addType(kSig_i_v);
var f1 = builder.addFunction("f1", sig_index1)
.addBody([kExprI32Const, 11]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprCallIndirect, sig_index1, kTableZero]) // --
.exportAs("main");
builder.setFunctionTableLength(kTableSize);
builder.addFunctionTableInit(0, false, [f1.index]);
builder.addExportOfKind("table", kExternalTable, 0);
var m1 = new WebAssembly.Module(builder.toBuffer());
// Module {m2} imports the table and adds {f2}.
var builder = new WasmModuleBuilder();
builder.addType(kSig_i_ii);
var sig_index2 = builder.addType(kSig_i_v);
var f2 = builder.addFunction("f2", sig_index2)
.addBody([kExprI32Const, 22]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprCallIndirect, sig_index2, kTableZero]) // --
.exportAs("main");
builder.setFunctionTableLength(kTableSize);
builder.addFunctionTableInit(1, false, [f2.index]);
builder.addImportedTable("table", undefined, kTableSize, kTableSize);
var m2 = new WebAssembly.Module(builder.toBuffer());
assertFalse(sig_index1 == sig_index2);
var i1 = new WebAssembly.Instance(m1);
var i2 = new WebAssembly.Instance(m2, {table: i1.exports.table});
assertEquals(11, i1.exports.main(0));
assertEquals(11, i2.exports.main(0));
assertEquals(22, i1.exports.main(1));
assertEquals(22, i2.exports.main(1));
assertThrows(() => i1.exports.main(2));
assertThrows(() => i2.exports.main(2));
assertThrows(() => i1.exports.main(3));
assertThrows(() => i2.exports.main(3));
})();
(function MismatchedTableSize() {
print("MismatchedTableSize...");
let kTableSize = 5;
for (var expsize = 1; expsize < 4; expsize++) {
for (var impsize = 1; impsize < 4; impsize++) {
print(" expsize = " + expsize + ", impsize = " + impsize);
var builder = new WasmModuleBuilder();
builder.setFunctionTableLength(expsize);
builder.addExportOfKind("expfoo", kExternalTable, 0);
let m1 = new WebAssembly.Module(builder.toBuffer());
var builder = new WasmModuleBuilder();
builder.addImportedTable("impfoo", undefined, impsize, impsize);
let m2 = new WebAssembly.Module(builder.toBuffer());
var i1 = new WebAssembly.Instance(m1);
// TODO(titzer): v8 currently requires import table size to match
// export table size.
var ffi = {impfoo: i1.exports.expfoo};
if (expsize == impsize) {
var i2 = new WebAssembly.Instance(m2, ffi);
} else {
assertThrows(() => new WebAssembly.Instance(m2, ffi));
}
}
}
})();
...@@ -204,6 +204,12 @@ class WasmModuleBuilder { ...@@ -204,6 +204,12 @@ class WasmModuleBuilder {
return this; return this;
} }
addImportedTable(module, name, initial, maximum) {
let o = {module: module, name: name, kind: kExternalTable, initial: initial,
maximum: maximum};
this.imports.push(o);
}
addExport(name, index) { addExport(name, index) {
this.exports.push({name: name, kind: kExternalFunction, index: index}); this.exports.push({name: name, kind: kExternalFunction, index: index});
return this; return this;
...@@ -289,6 +295,12 @@ class WasmModuleBuilder { ...@@ -289,6 +295,12 @@ class WasmModuleBuilder {
section.emit_u8(has_max ? 1 : 0); // flags section.emit_u8(has_max ? 1 : 0); // flags
section.emit_u32v(imp.initial); // initial section.emit_u32v(imp.initial); // initial
if (has_max) section.emit_u32v(imp.maximum); // maximum if (has_max) section.emit_u32v(imp.maximum); // maximum
} else if (imp.kind == kExternalTable) {
section.emit_u8(kWasmAnyFunctionTypeForm);
var has_max = (typeof imp.maximum) != "undefined";
section.emit_u8(has_max ? 1 : 0); // flags
section.emit_u32v(imp.initial); // initial
if (has_max) section.emit_u32v(imp.maximum); // maximum
} else { } else {
throw new Error("unknown/unsupported import kind " + imp.kind); throw new Error("unknown/unsupported import kind " + imp.kind);
} }
......
...@@ -510,8 +510,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) { ...@@ -510,8 +510,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
EXPECT_EQ(1, result.val->signatures.size()); EXPECT_EQ(1, result.val->signatures.size());
EXPECT_EQ(1, result.val->functions.size()); EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(1, result.val->function_tables.size()); EXPECT_EQ(1, result.val->function_tables.size());
EXPECT_EQ(1, result.val->function_tables[0].values.size()); EXPECT_EQ(1, result.val->function_tables[0].min_size);
EXPECT_EQ(-1, result.val->function_tables[0].values[0]);
} }
if (result.val) delete result.val; if (result.val) delete result.val;
} }
...@@ -537,8 +536,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction_one_entry) { ...@@ -537,8 +536,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction_one_entry) {
EXPECT_EQ(1, result.val->signatures.size()); EXPECT_EQ(1, result.val->signatures.size());
EXPECT_EQ(1, result.val->functions.size()); EXPECT_EQ(1, result.val->functions.size());
EXPECT_EQ(1, result.val->function_tables.size()); EXPECT_EQ(1, result.val->function_tables.size());
EXPECT_EQ(1, result.val->function_tables[0].values.size()); EXPECT_EQ(1, result.val->function_tables[0].min_size);
EXPECT_EQ(0, result.val->function_tables[0].values[0]);
} }
if (result.val) delete result.val; if (result.val) delete result.val;
} }
...@@ -575,10 +573,7 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) { ...@@ -575,10 +573,7 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
EXPECT_EQ(2, result.val->signatures.size()); EXPECT_EQ(2, result.val->signatures.size());
EXPECT_EQ(4, result.val->functions.size()); EXPECT_EQ(4, result.val->functions.size());
EXPECT_EQ(1, result.val->function_tables.size()); EXPECT_EQ(1, result.val->function_tables.size());
EXPECT_EQ(8, result.val->function_tables[0].values.size()); EXPECT_EQ(8, result.val->function_tables[0].min_size);
for (int i = 0; i < 8; i++) {
EXPECT_EQ(i & 3, result.val->function_tables[0].values[i]);
}
} }
if (result.val) delete result.val; if (result.val) delete result.val;
} }
......
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