[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}
This commit is contained in:
parent
f7f591ab6b
commit
b7aff1ff64
@ -298,6 +298,7 @@ WasmGraphBuilder::WasmGraphBuilder(
|
||||
mem_buffer_(nullptr),
|
||||
mem_size_(nullptr),
|
||||
function_tables_(zone),
|
||||
function_table_sizes_(zone),
|
||||
control_(nullptr),
|
||||
effect_(nullptr),
|
||||
cur_buffer_(def_buffer_),
|
||||
@ -1712,7 +1713,7 @@ Node* WasmGraphBuilder::GrowMemory(Node* input) {
|
||||
graph(), jsgraph()->common(),
|
||||
graph()->NewNode(
|
||||
jsgraph()->machine()->Uint32LessThanOrEqual(), input,
|
||||
jsgraph()->Uint32Constant(wasm::WasmModule::kMaxMemPages)),
|
||||
jsgraph()->Uint32Constant(wasm::WasmModule::kV8MaxPages)),
|
||||
BranchHint::kTrue);
|
||||
|
||||
check_input_range.Chain(*control_);
|
||||
@ -2154,28 +2155,17 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
|
||||
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) {
|
||||
DCHECK_NOT_NULL(args[0]);
|
||||
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.
|
||||
DCHECK_LE(module_->instance->function_tables.size(), 1u);
|
||||
// Bounds check the index.
|
||||
uint32_t table_size =
|
||||
module_->IsValidTable(0) ? module_->GetTable(0)->max_size : 0;
|
||||
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 {
|
||||
uint32_t table_index = 0;
|
||||
wasm::FunctionSig* sig = module_->GetSignature(sig_index);
|
||||
|
||||
if (!module_->IsValidTable(table_index)) {
|
||||
// No function table. Generate a trap and return a constant.
|
||||
trap_->AddTrapIfFalse(wasm::kTrapFuncInvalid, Int32Constant(0), position);
|
||||
(*rets) = Buffer(sig->return_count());
|
||||
@ -2184,7 +2174,16 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets,
|
||||
}
|
||||
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.
|
||||
// The table is a FixedArray; signatures are encoded as SMIs.
|
||||
@ -2208,6 +2207,7 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args, Node*** rets,
|
||||
}
|
||||
|
||||
// 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;
|
||||
Node* load_code = graph()->NewNode(
|
||||
machine->Load(MachineType::AnyTagged()), table,
|
||||
@ -2854,17 +2854,15 @@ Node* WasmGraphBuilder::MemSize(uint32_t offset) {
|
||||
}
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::FunctionTable(uint32_t index) {
|
||||
DCHECK(module_ && module_->instance &&
|
||||
index < module_->instance->function_tables.size());
|
||||
if (!function_tables_.size()) {
|
||||
void WasmGraphBuilder::EnsureFunctionTableNodes() {
|
||||
if (function_tables_.size() > 0) return;
|
||||
for (size_t i = 0; i < module_->instance->function_tables.size(); ++i) {
|
||||
DCHECK(!module_->instance->function_tables[i].is_null());
|
||||
function_tables_.push_back(
|
||||
HeapConstant(module_->instance->function_tables[i]));
|
||||
auto handle = module_->instance->function_tables[i];
|
||||
DCHECK(!handle.is_null());
|
||||
function_tables_.push_back(HeapConstant(handle));
|
||||
uint32_t table_size = module_->module->function_tables[i].min_size;
|
||||
function_table_sizes_.push_back(Uint32Constant(table_size));
|
||||
}
|
||||
}
|
||||
return function_tables_[index];
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::GetGlobal(uint32_t index) {
|
||||
|
@ -170,7 +170,7 @@ class WasmGraphBuilder {
|
||||
Node* ToJS(Node* node, wasm::LocalType type);
|
||||
Node* FromJS(Node* node, Node* context, wasm::LocalType type);
|
||||
Node* Invert(Node* node);
|
||||
Node* FunctionTable(uint32_t index);
|
||||
void EnsureFunctionTableNodes();
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Operations that concern the linear memory.
|
||||
@ -219,6 +219,7 @@ class WasmGraphBuilder {
|
||||
Node* mem_buffer_;
|
||||
Node* mem_size_;
|
||||
NodeVector function_tables_;
|
||||
NodeVector function_table_sizes_;
|
||||
Node** control_;
|
||||
Node** effect_;
|
||||
Node** cur_buffer_;
|
||||
|
@ -31,8 +31,6 @@ namespace {
|
||||
const char* kNameString = "name";
|
||||
const size_t kNameStringLength = 4;
|
||||
|
||||
static const uint32_t kMaxTableSize = 1 << 28;
|
||||
|
||||
LocalType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
|
||||
switch (expr.kind) {
|
||||
case WasmInitExpr::kNone:
|
||||
@ -311,19 +309,22 @@ class ModuleDecoder : public Decoder {
|
||||
// ===== Imported table ==========================================
|
||||
import->index =
|
||||
static_cast<uint32_t>(module->function_tables.size());
|
||||
module->function_tables.push_back(
|
||||
{0, 0, std::vector<int32_t>(), true, false, SignatureMap()});
|
||||
module->function_tables.push_back({0, 0, false,
|
||||
std::vector<int32_t>(), true,
|
||||
false, SignatureMap()});
|
||||
expect_u8("element type", kWasmAnyFunctionTypeForm);
|
||||
WasmIndirectFunctionTable* table = &module->function_tables.back();
|
||||
consume_resizable_limits("element count", "elements", kMaxTableSize,
|
||||
&table->size, &table->max_size);
|
||||
consume_resizable_limits(
|
||||
"element count", "elements", WasmModule::kV8MaxTableSize,
|
||||
&table->min_size, &table->has_max, &table->max_size);
|
||||
break;
|
||||
}
|
||||
case kExternalMemory: {
|
||||
// ===== Imported memory =========================================
|
||||
consume_resizable_limits(
|
||||
"memory", "pages", WasmModule::kMaxLegalPages,
|
||||
&module->min_mem_pages, &module->max_mem_pages);
|
||||
bool has_max = false;
|
||||
consume_resizable_limits("memory", "pages", WasmModule::kV8MaxPages,
|
||||
&module->min_mem_pages, &has_max,
|
||||
&module->max_mem_pages);
|
||||
break;
|
||||
}
|
||||
case kExternalGlobal: {
|
||||
@ -375,15 +376,16 @@ class ModuleDecoder : public Decoder {
|
||||
error(pos, pos, "invalid table count %d, maximum 1", table_count);
|
||||
}
|
||||
if (module->function_tables.size() < 1) {
|
||||
module->function_tables.push_back(
|
||||
{0, 0, std::vector<int32_t>(), false, false, SignatureMap()});
|
||||
module->function_tables.push_back({0, 0, false, std::vector<int32_t>(),
|
||||
false, false, SignatureMap()});
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; ok() && i < table_count; i++) {
|
||||
WasmIndirectFunctionTable* table = &module->function_tables.back();
|
||||
expect_u8("table type", kWasmAnyFunctionTypeForm);
|
||||
consume_resizable_limits("table elements", "elements", kMaxUInt32,
|
||||
&table->size, &table->max_size);
|
||||
consume_resizable_limits("table elements", "elements",
|
||||
WasmModule::kV8MaxTableSize, &table->min_size,
|
||||
&table->has_max, &table->max_size);
|
||||
}
|
||||
section_iter.advance();
|
||||
}
|
||||
@ -398,8 +400,9 @@ class ModuleDecoder : public Decoder {
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; ok() && i < memory_count; i++) {
|
||||
consume_resizable_limits("memory", "pages", WasmModule::kMaxLegalPages,
|
||||
&module->min_mem_pages,
|
||||
bool has_max = false;
|
||||
consume_resizable_limits("memory", "pages", WasmModule::kV8MaxPages,
|
||||
&module->min_mem_pages, &has_max,
|
||||
&module->max_mem_pages);
|
||||
}
|
||||
section_iter.advance();
|
||||
@ -623,7 +626,6 @@ class ModuleDecoder : public Decoder {
|
||||
|
||||
if (ok()) {
|
||||
CalculateGlobalOffsets(module);
|
||||
PreinitializeIndirectFunctionTables(module);
|
||||
}
|
||||
const WasmModule* finished_module = module;
|
||||
ModuleResult result = toResult(finished_module);
|
||||
@ -749,30 +751,6 @@ class ModuleDecoder : public Decoder {
|
||||
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.
|
||||
void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv,
|
||||
WasmFunction* function) {
|
||||
@ -866,21 +844,24 @@ class ModuleDecoder : public Decoder {
|
||||
|
||||
void consume_resizable_limits(const char* name, const char* units,
|
||||
uint32_t max_value, uint32_t* initial,
|
||||
uint32_t* maximum) {
|
||||
bool* has_max, uint32_t* maximum) {
|
||||
uint32_t flags = consume_u32v("resizable limits flags");
|
||||
const byte* pos = pc();
|
||||
*initial = consume_u32v("initial size");
|
||||
*has_max = false;
|
||||
if (*initial > max_value) {
|
||||
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);
|
||||
}
|
||||
if (flags & 1) {
|
||||
*has_max = true;
|
||||
pos = pc();
|
||||
*maximum = consume_u32v("maximum size");
|
||||
if (*maximum > max_value) {
|
||||
error(pos, pos,
|
||||
"maximum %s size (%u %s) is larger than maximum allowable (%u)",
|
||||
error(
|
||||
pos, pos,
|
||||
"maximum %s size (%u %s) is larger than implementation limit (%u)",
|
||||
name, *maximum, units, max_value);
|
||||
}
|
||||
if (*maximum < *initial) {
|
||||
@ -888,7 +869,8 @@ class ModuleDecoder : public Decoder {
|
||||
name, *maximum, units, *initial, units);
|
||||
}
|
||||
} else {
|
||||
*maximum = 0;
|
||||
*has_max = false;
|
||||
*maximum = max_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -663,16 +663,13 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
|
||||
WasmInstance* instance) {
|
||||
// TODO(ahaas): Move memory allocation to wasm-module.cc for better
|
||||
// encapsulation.
|
||||
if (delta_pages > wasm::WasmModule::kMaxMemPages) {
|
||||
if (delta_pages > wasm::WasmModule::kV8MaxPages) {
|
||||
return -1;
|
||||
}
|
||||
uint32_t old_size = instance->mem_size;
|
||||
uint32_t new_size;
|
||||
byte* new_mem_start;
|
||||
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.
|
||||
new_size = delta_pages * wasm::WasmModule::kPageSize;
|
||||
new_mem_start = static_cast<byte*>(calloc(new_size, sizeof(byte)));
|
||||
@ -683,7 +680,7 @@ static inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
|
||||
DCHECK_NOT_NULL(instance->mem_start);
|
||||
new_size = old_size + delta_pages * wasm::WasmModule::kPageSize;
|
||||
if (new_size >
|
||||
wasm::WasmModule::kMaxMemPages * wasm::WasmModule::kPageSize) {
|
||||
wasm::WasmModule::kV8MaxPages * wasm::WasmModule::kPageSize) {
|
||||
return -1;
|
||||
}
|
||||
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,
|
||||
}
|
||||
instance->mem_start = new_mem_start;
|
||||
instance->mem_size = new_size;
|
||||
// realloc
|
||||
// update mem_start
|
||||
// update mem_size
|
||||
return static_cast<int32_t>(old_size / WasmModule::kPageSize);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ namespace v8 {
|
||||
|
||||
static const int kWasmTableArrayFieldIndex = 0;
|
||||
static const int kWasmTableMaximumFieldIndex = 1;
|
||||
static const int kWasmTableDispatchTablesFieldIndex = 2;
|
||||
|
||||
enum WasmMemoryObjectData {
|
||||
kWasmMemoryBuffer,
|
||||
@ -37,8 +38,8 @@ enum WasmMemoryObjectData {
|
||||
};
|
||||
|
||||
enum WasmInternalFieldCountData {
|
||||
kWasmTableInternalFieldCount = 2,
|
||||
kWasmMemoryInternalFieldCount
|
||||
kWasmTableInternalFieldCount = 3,
|
||||
kWasmMemoryInternalFieldCount = 3
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -518,9 +519,19 @@ void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
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) {
|
||||
@ -612,26 +623,60 @@ i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject(
|
||||
|
||||
i::Handle<i::JSObject> i::WasmJs::CreateWasmTableObject(
|
||||
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_isolate->native_context()->wasm_table_constructor());
|
||||
i::Handle<i::JSObject> table_obj =
|
||||
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();
|
||||
// TODO(titzer): consider moving FixedArray to size_t.
|
||||
for (int i = 0; i < static_cast<int>(initial); ++i) (*array)->set(i, null);
|
||||
table_obj->SetInternalField(kWasmTableArrayFieldIndex, *(*array));
|
||||
for (int i = 0; i < static_cast<int>(initial); ++i) {
|
||||
(*js_functions)->set(i, null);
|
||||
}
|
||||
table_obj->SetInternalField(kWasmTableArrayFieldIndex, *(*js_functions));
|
||||
table_obj->SetInternalField(
|
||||
kWasmTableMaximumFieldIndex,
|
||||
has_maximum
|
||||
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
|
||||
: 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::Object::SetProperty(table_obj, table_sym, table_obj, i::STRICT).Check();
|
||||
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
|
||||
// internal guts are too ugly to replicate here.
|
||||
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_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()) {
|
||||
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, sym);
|
||||
Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, symbol);
|
||||
if (has_brand.IsNothing()) return false;
|
||||
if (has_brand.ToChecked()) return true;
|
||||
}
|
||||
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<Object> value) {
|
||||
DCHECK(IsWasmMemoryObject(isolate, value));
|
||||
@ -847,7 +910,7 @@ uint32_t WasmJs::GetWasmMemoryMaximumSize(Isolate* isolate,
|
||||
DCHECK(IsWasmMemoryObject(isolate, value));
|
||||
Object* max_mem =
|
||||
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();
|
||||
return max_pages;
|
||||
}
|
||||
|
@ -24,16 +24,25 @@ class WasmJs {
|
||||
Handle<JSGlobalObject> global,
|
||||
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,
|
||||
Handle<JSArrayBuffer> buffer,
|
||||
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 Handle<JSArrayBuffer> GetWasmMemoryArrayBuffer(Isolate* isolate,
|
||||
|
@ -37,6 +37,34 @@ namespace base = v8::base;
|
||||
instance->PrintInstancesChain(); \
|
||||
} while (false)
|
||||
|
||||
static const int kInvalidSigIndex = -1;
|
||||
|
||||
// Collects all the data values to which a given WASM code object may be
|
||||
// specialized.
|
||||
struct Specialization {
|
||||
// The native context, which is used in JS->WASM and WASM->JS wrappers
|
||||
// and calls to the runtime.
|
||||
Handle<Context> context;
|
||||
|
||||
// Specialization to the memory.
|
||||
byte* memory_base;
|
||||
uint32_t memory_size;
|
||||
|
||||
// Specialization to the globals.
|
||||
byte* globals_base;
|
||||
|
||||
// Specialization to the function table.
|
||||
uint32_t function_table_size;
|
||||
Handle<FixedArray> function_table_sigs;
|
||||
Handle<FixedArray> function_table_code;
|
||||
|
||||
Specialization()
|
||||
: memory_base(nullptr),
|
||||
memory_size(0),
|
||||
globals_base(nullptr),
|
||||
function_table_size(0) {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
static const int kPlaceholderMarker = 1000000000;
|
||||
@ -87,7 +115,7 @@ void ReplaceReferenceInCode(Handle<Code> code, Handle<Object> old_ref,
|
||||
}
|
||||
|
||||
Handle<JSArrayBuffer> NewArrayBuffer(Isolate* isolate, size_t size) {
|
||||
if (size > (WasmModule::kMaxMemPages * WasmModule::kPageSize)) {
|
||||
if (size > (WasmModule::kV8MaxPages * WasmModule::kPageSize)) {
|
||||
// TODO(titzer): lift restriction on maximum memory allocated here.
|
||||
return Handle<JSArrayBuffer>::null();
|
||||
}
|
||||
@ -500,7 +528,7 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
|
||||
// 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)) {
|
||||
(old_mem_size > 0 || globals_start != nullptr || function_tables)) {
|
||||
FixedArray* functions = FixedArray::cast(fct_obj);
|
||||
for (int i = 0; i < functions->length(); ++i) {
|
||||
Code* code = Code::cast(functions->get(i));
|
||||
@ -517,9 +545,9 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
|
||||
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));
|
||||
for (int j = 0; j < function_tables->length(); ++j) {
|
||||
if (function_tables->get(j) == old) {
|
||||
it.rinfo()->set_target_object(empty_function_tables->get(j));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
@ -843,6 +871,101 @@ MaybeHandle<WasmCompiledModule> WasmModule::CompileFunctions(
|
||||
return ret;
|
||||
}
|
||||
|
||||
static WasmFunction* GetWasmFunctionForImportWrapper(Isolate* isolate,
|
||||
Handle<Object> target) {
|
||||
if (target->IsJSFunction()) {
|
||||
Handle<JSFunction> func = Handle<JSFunction>::cast(target);
|
||||
Handle<Code> export_wrapper_code = handle(func->code());
|
||||
if (export_wrapper_code->kind() == Code::JS_TO_WASM_FUNCTION) {
|
||||
Handle<JSObject> other_instance(
|
||||
JSObject::cast(func->GetInternalField(kInternalModuleInstance)),
|
||||
isolate);
|
||||
int func_index =
|
||||
Smi::cast(func->GetInternalField(kInternalFunctionIndex))->value();
|
||||
return &GetCppModule(other_instance)->functions[func_index];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Handle<Code> UnwrapImportWrapper(Handle<Object> target) {
|
||||
Handle<JSFunction> func = Handle<JSFunction>::cast(target);
|
||||
Handle<Code> export_wrapper_code = handle(func->code());
|
||||
int found = 0;
|
||||
int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
|
||||
Handle<Code> code;
|
||||
for (RelocIterator it(*export_wrapper_code, mask); !it.done(); it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == Code::WASM_FUNCTION ||
|
||||
target->kind() == Code::WASM_TO_JS_FUNCTION) {
|
||||
++found;
|
||||
code = handle(target);
|
||||
}
|
||||
}
|
||||
DCHECK(found == 1);
|
||||
return code;
|
||||
}
|
||||
|
||||
static Handle<Code> CompileImportWrapper(Isolate* isolate, int index,
|
||||
FunctionSig* sig,
|
||||
Handle<JSReceiver> target,
|
||||
Handle<String> module_name,
|
||||
MaybeHandle<String> import_name) {
|
||||
Handle<Code> code;
|
||||
WasmFunction* other_func = GetWasmFunctionForImportWrapper(isolate, target);
|
||||
if (other_func && sig->Equals(other_func->sig)) {
|
||||
// Signature matched. Unwrap the JS->WASM wrapper and return the raw
|
||||
// WASM function code.
|
||||
return UnwrapImportWrapper(target);
|
||||
} else {
|
||||
// Signature mismatch. Compile a new wrapper for the new signature.
|
||||
return compiler::CompileWasmToJSWrapper(isolate, target, sig, index,
|
||||
module_name, import_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateDispatchTablesInternal(Isolate* isolate,
|
||||
Handle<FixedArray> dispatch_tables,
|
||||
int index, WasmFunction* function,
|
||||
Handle<Code> code) {
|
||||
DCHECK_EQ(0, dispatch_tables->length() % 3);
|
||||
for (int i = 0; i < dispatch_tables->length(); i += 3) {
|
||||
Handle<Object> instance(dispatch_tables->get(i), isolate);
|
||||
WasmModule* module = GetCppModule(Handle<JSObject>::cast(instance));
|
||||
int table_index = Smi::cast(dispatch_tables->get(i + 1))->value();
|
||||
Handle<FixedArray> dispatch_table(
|
||||
FixedArray::cast(dispatch_tables->get(i + 2)), isolate);
|
||||
if (function) {
|
||||
// TODO(titzer): the signature might need to be copied to avoid
|
||||
// a dangling pointer in the signature map.
|
||||
int sig_index = static_cast<int>(
|
||||
module->function_tables[table_index].map.FindOrInsert(function->sig));
|
||||
dispatch_table->set(index, Smi::FromInt(sig_index));
|
||||
dispatch_table->set(index + (dispatch_table->length() / 2), *code);
|
||||
} else {
|
||||
Code* code = nullptr;
|
||||
dispatch_table->set(index, Smi::FromInt(-1));
|
||||
dispatch_table->set(index + (dispatch_table->length() / 2), code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wasm::UpdateDispatchTables(Isolate* isolate,
|
||||
Handle<FixedArray> dispatch_tables, int index,
|
||||
Handle<JSFunction> function) {
|
||||
if (function.is_null()) {
|
||||
UpdateDispatchTablesInternal(isolate, dispatch_tables, index, nullptr,
|
||||
Handle<Code>::null());
|
||||
} else {
|
||||
UpdateDispatchTablesInternal(
|
||||
isolate, dispatch_tables, index,
|
||||
GetWasmFunctionForImportWrapper(isolate, function),
|
||||
UnwrapImportWrapper(function));
|
||||
}
|
||||
}
|
||||
|
||||
// A helper class to simplify instantiating a module from a compiled module.
|
||||
// It closes over the {Isolate}, the {ErrorThrower}, the {WasmCompiledModule},
|
||||
// etc.
|
||||
@ -951,13 +1074,12 @@ class WasmInstanceBuilder {
|
||||
// Set up the globals for the new instance.
|
||||
//--------------------------------------------------------------------------
|
||||
MaybeHandle<JSArrayBuffer> old_globals;
|
||||
MaybeHandle<JSArrayBuffer> globals;
|
||||
uint32_t globals_size = module_->globals_size;
|
||||
if (globals_size > 0) {
|
||||
Handle<JSArrayBuffer> global_buffer =
|
||||
NewArrayBuffer(isolate_, globals_size);
|
||||
globals = global_buffer;
|
||||
if (globals.is_null()) {
|
||||
globals_ = global_buffer;
|
||||
if (globals_.is_null()) {
|
||||
thrower_->RangeError("Out of memory: wasm globals");
|
||||
return nothing;
|
||||
}
|
||||
@ -971,16 +1093,28 @@ class WasmInstanceBuilder {
|
||||
instance->SetInternalField(kWasmGlobalsArrayBuffer, *global_buffer);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Prepare for initialization of function tables.
|
||||
//--------------------------------------------------------------------------
|
||||
int function_table_count =
|
||||
static_cast<int>(module_->function_tables.size());
|
||||
table_instances_.reserve(module_->function_tables.size());
|
||||
for (int index = 0; index < function_table_count; ++index) {
|
||||
table_instances_.push_back({Handle<JSObject>::null(),
|
||||
Handle<FixedArray>::null(),
|
||||
Handle<FixedArray>::null()});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Process the imports for the module.
|
||||
//--------------------------------------------------------------------------
|
||||
int num_imported_functions = ProcessImports(globals, code_table, instance);
|
||||
int num_imported_functions = ProcessImports(code_table, instance);
|
||||
if (num_imported_functions < 0) return nothing;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Process the initialization for the module's globals.
|
||||
//--------------------------------------------------------------------------
|
||||
InitGlobals(globals);
|
||||
InitGlobals();
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up the memory for the new instance.
|
||||
@ -1004,7 +1138,7 @@ class WasmInstanceBuilder {
|
||||
Address mem_start = static_cast<Address>(memory_->backing_store());
|
||||
uint32_t mem_size =
|
||||
static_cast<uint32_t>(memory_->byte_length()->Number());
|
||||
LoadDataSegments(globals, mem_start, mem_size);
|
||||
LoadDataSegments(mem_start, mem_size);
|
||||
|
||||
uint32_t old_mem_size = compiled_module_->mem_size();
|
||||
Address old_mem_start =
|
||||
@ -1034,76 +1168,15 @@ class WasmInstanceBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up the indirect function tables for the new instance.
|
||||
//--------------------------------------------------------------------------
|
||||
int function_table_count =
|
||||
static_cast<int>(module_->function_tables.size());
|
||||
std::vector<InitializedTable> inited_tables;
|
||||
inited_tables.reserve(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 table_size = table.size;
|
||||
Handle<FixedArray> new_table = factory->NewFixedArray(table_size * 2);
|
||||
|
||||
inited_tables.push_back({Handle<JSObject>::null(),
|
||||
Handle<FixedArray>::null(), new_table,
|
||||
std::vector<WasmFunction*>()});
|
||||
InitializedTable& init_table = inited_tables.back();
|
||||
if (table.exported) {
|
||||
init_table.new_entries.insert(init_table.new_entries.begin(),
|
||||
table_size, nullptr);
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
WasmFunction* func = &module_->functions[table_init.entries[i]];
|
||||
if (table.exported) {
|
||||
init_table.new_entries[i + base] = func;
|
||||
}
|
||||
FunctionSig* sig = func->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 + table_size),
|
||||
code_table->get(table_init.entries[i]));
|
||||
}
|
||||
}
|
||||
new_function_tables->set(static_cast<int>(index), *new_table);
|
||||
}
|
||||
// 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_));
|
||||
}
|
||||
}
|
||||
compiled_module_->set_function_tables(new_function_tables);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up the exports object for the new instance.
|
||||
//--------------------------------------------------------------------------
|
||||
ProcessExports(globals, inited_tables, code_table, instance);
|
||||
ProcessExports(code_table, instance);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up the indirect function tables for the new instance.
|
||||
//--------------------------------------------------------------------------
|
||||
if (function_table_count > 0) InitializeTables(code_table, instance);
|
||||
|
||||
if (num_imported_functions > 0 || !owner.is_null()) {
|
||||
// If the code was cloned, or new imports were compiled, patch.
|
||||
@ -1192,11 +1265,10 @@ class WasmInstanceBuilder {
|
||||
|
||||
private:
|
||||
// Represents the initialized state of a table.
|
||||
struct InitializedTable {
|
||||
struct TableInstance {
|
||||
Handle<JSObject> table_object; // WebAssembly.Table instance
|
||||
Handle<FixedArray> js_functions; // JSFunctions exported
|
||||
Handle<FixedArray> js_wrappers; // JSFunctions exported
|
||||
Handle<FixedArray> dispatch_table; // internal (code, sig) pairs
|
||||
std::vector<WasmFunction*> new_entries; // overwriting entries
|
||||
};
|
||||
|
||||
Isolate* isolate_;
|
||||
@ -1205,7 +1277,10 @@ class WasmInstanceBuilder {
|
||||
Handle<JSObject> module_object_;
|
||||
Handle<JSReceiver> ffi_;
|
||||
Handle<JSArrayBuffer> memory_;
|
||||
Handle<JSArrayBuffer> globals_;
|
||||
Handle<WasmCompiledModule> compiled_module_;
|
||||
std::vector<TableInstance> table_instances_;
|
||||
std::vector<Handle<JSFunction>> js_wrappers_;
|
||||
|
||||
// Helper routine to print out errors with imports (FFI).
|
||||
MaybeHandle<JSFunction> ReportFFIError(const char* error, uint32_t index,
|
||||
@ -1264,14 +1339,13 @@ class WasmInstanceBuilder {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t EvalUint32InitExpr(MaybeHandle<JSArrayBuffer> globals,
|
||||
WasmInitExpr& expr) {
|
||||
uint32_t EvalUint32InitExpr(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));
|
||||
return *reinterpret_cast<uint32_t*>(raw_buffer_ptr(globals_, offset));
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@ -1280,11 +1354,10 @@ class WasmInstanceBuilder {
|
||||
}
|
||||
|
||||
// Load data segments into the memory.
|
||||
void LoadDataSegments(MaybeHandle<JSArrayBuffer> globals, Address mem_addr,
|
||||
size_t mem_size) {
|
||||
void LoadDataSegments(Address mem_addr, size_t mem_size) {
|
||||
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 dest_offset = EvalUint32InitExpr(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)) {
|
||||
@ -1297,55 +1370,7 @@ class WasmInstanceBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
Handle<Code> CompileImportWrapper(int index, const WasmImport& import,
|
||||
Handle<JSReceiver> target,
|
||||
Handle<String> module_name,
|
||||
MaybeHandle<String> import_name) {
|
||||
FunctionSig* sig = module_->functions[import.index].sig;
|
||||
Handle<Code> code;
|
||||
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) {
|
||||
// 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 (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();
|
||||
it.next()) {
|
||||
RelocInfo* rinfo = it.rinfo();
|
||||
Address target_address = rinfo->target_address();
|
||||
Code* target = Code::GetCodeFromTargetAddress(target_address);
|
||||
if (target->kind() == Code::WASM_FUNCTION) {
|
||||
++wasm_count;
|
||||
code = handle(target);
|
||||
}
|
||||
}
|
||||
DCHECK(wasm_count == 1);
|
||||
return code;
|
||||
} else {
|
||||
// Signature mismatch. Compile a new wrapper for the new signature.
|
||||
return compiler::CompileWasmToJSWrapper(isolate_, target, sig, index,
|
||||
module_name, import_name);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteGlobalValue(WasmGlobal& global, MaybeHandle<JSArrayBuffer> globals,
|
||||
Handle<Object> value) {
|
||||
void WriteGlobalValue(WasmGlobal& global, Handle<Object> value) {
|
||||
double num = 0;
|
||||
if (value->IsSmi()) {
|
||||
num = Smi::cast(*value)->value();
|
||||
@ -1358,17 +1383,17 @@ class WasmInstanceBuilder {
|
||||
WasmOpcodes::TypeName(global.type));
|
||||
switch (global.type) {
|
||||
case kAstI32:
|
||||
*GetRawGlobalPtr<int32_t>(global, globals) = static_cast<int32_t>(num);
|
||||
*GetRawGlobalPtr<int32_t>(global) = static_cast<int32_t>(num);
|
||||
break;
|
||||
case kAstI64:
|
||||
// TODO(titzer): initialization of imported i64 globals.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
case kAstF32:
|
||||
*GetRawGlobalPtr<float>(global, globals) = static_cast<float>(num);
|
||||
*GetRawGlobalPtr<float>(global) = static_cast<float>(num);
|
||||
break;
|
||||
case kAstF64:
|
||||
*GetRawGlobalPtr<double>(global, globals) = static_cast<double>(num);
|
||||
*GetRawGlobalPtr<double>(global) = static_cast<double>(num);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@ -1378,9 +1403,9 @@ class WasmInstanceBuilder {
|
||||
// Process the imports, including functions, tables, globals, and memory, in
|
||||
// order, loading them from the {ffi_} object. Returns the number of imported
|
||||
// functions.
|
||||
int ProcessImports(MaybeHandle<JSArrayBuffer> globals,
|
||||
Handle<FixedArray> code_table, Handle<JSObject> instance) {
|
||||
int ProcessImports(Handle<FixedArray> code_table, Handle<JSObject> instance) {
|
||||
int num_imported_functions = 0;
|
||||
int num_imported_tables = 0;
|
||||
for (int index = 0; index < static_cast<int>(module_->import_table.size());
|
||||
++index) {
|
||||
WasmImport& import = module_->import_table[index];
|
||||
@ -1412,16 +1437,64 @@ class WasmInstanceBuilder {
|
||||
}
|
||||
|
||||
Handle<Code> import_wrapper = CompileImportWrapper(
|
||||
index, import, Handle<JSReceiver>::cast(function), module_name,
|
||||
function_name);
|
||||
isolate_, index, module_->functions[import.index].sig,
|
||||
Handle<JSReceiver>::cast(function), module_name, function_name);
|
||||
code_table->set(num_imported_functions, *import_wrapper);
|
||||
RecordStats(isolate_, *import_wrapper);
|
||||
num_imported_functions++;
|
||||
break;
|
||||
}
|
||||
case kExternalTable:
|
||||
// TODO(titzer): Table imports must be a WebAssembly.Table.
|
||||
case kExternalTable: {
|
||||
Handle<Object> value = result.ToHandleChecked();
|
||||
if (!WasmJs::IsWasmTableObject(isolate_, value)) {
|
||||
ReportFFIError("table import requires a WebAssembly.Table", index,
|
||||
module_name, function_name);
|
||||
return -1;
|
||||
}
|
||||
WasmIndirectFunctionTable& table =
|
||||
module_->function_tables[num_imported_tables];
|
||||
TableInstance& table_instance = table_instances_[num_imported_tables];
|
||||
table_instance.table_object = Handle<JSObject>::cast(value);
|
||||
table_instance.js_wrappers = WasmJs::GetWasmTableFunctions(
|
||||
isolate_, table_instance.table_object);
|
||||
|
||||
// TODO(titzer): import table size must match exactly for now.
|
||||
int table_size = table_instance.js_wrappers->length();
|
||||
if (table_size != table.min_size) {
|
||||
thrower_->TypeError(
|
||||
"table import %d is wrong size (%d), expected %u", index,
|
||||
table_size, table.min_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate a new dispatch table.
|
||||
table_instance.dispatch_table =
|
||||
isolate_->factory()->NewFixedArray(table_size * 2);
|
||||
for (int i = 0; i < table_size * 2; ++i) {
|
||||
table_instance.dispatch_table->set(i,
|
||||
Smi::FromInt(kInvalidSigIndex));
|
||||
}
|
||||
// Initialize the dispatch table with the (foreign) JS functions
|
||||
// that are already in the table.
|
||||
for (int i = 0; i < table_size; ++i) {
|
||||
Handle<Object> val(table_instance.js_wrappers->get(i), isolate_);
|
||||
if (!val->IsJSFunction()) continue;
|
||||
WasmFunction* function =
|
||||
GetWasmFunctionForImportWrapper(isolate_, val);
|
||||
if (function == nullptr) {
|
||||
thrower_->TypeError("table import %d[%d] is not a WASM function",
|
||||
index, i);
|
||||
return -1;
|
||||
}
|
||||
int sig_index = table.map.FindOrInsert(function->sig);
|
||||
table_instance.dispatch_table->set(i, Smi::FromInt(sig_index));
|
||||
table_instance.dispatch_table->set(i + table_size,
|
||||
*UnwrapImportWrapper(val));
|
||||
}
|
||||
|
||||
num_imported_tables++;
|
||||
break;
|
||||
}
|
||||
case kExternalMemory: {
|
||||
Handle<Object> object = result.ToHandleChecked();
|
||||
if (!WasmJs::IsWasmMemoryObject(isolate_, object)) {
|
||||
@ -1435,7 +1508,7 @@ class WasmInstanceBuilder {
|
||||
}
|
||||
case kExternalGlobal: {
|
||||
// Global imports are converted to numbers and written into the
|
||||
// {globals} array buffer.
|
||||
// {globals_} array buffer.
|
||||
Handle<Object> object = result.ToHandleChecked();
|
||||
MaybeHandle<Object> number = Object::ToNumber(object);
|
||||
if (number.is_null()) {
|
||||
@ -1444,7 +1517,7 @@ class WasmInstanceBuilder {
|
||||
return -1;
|
||||
}
|
||||
Handle<Object> val = number.ToHandleChecked();
|
||||
WriteGlobalValue(module_->globals[import.index], globals, val);
|
||||
WriteGlobalValue(module_->globals[import.index], val);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -1456,27 +1529,25 @@ class WasmInstanceBuilder {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* GetRawGlobalPtr(WasmGlobal& global, MaybeHandle<JSArrayBuffer> globals) {
|
||||
return reinterpret_cast<T*>(raw_buffer_ptr(globals, global.offset));
|
||||
T* GetRawGlobalPtr(WasmGlobal& global) {
|
||||
return reinterpret_cast<T*>(raw_buffer_ptr(globals_, global.offset));
|
||||
}
|
||||
|
||||
// Process initialization of globals.
|
||||
void InitGlobals(MaybeHandle<JSArrayBuffer> globals) {
|
||||
void InitGlobals() {
|
||||
for (auto global : module_->globals) {
|
||||
switch (global.init.kind) {
|
||||
case WasmInitExpr::kI32Const:
|
||||
*GetRawGlobalPtr<int32_t>(global, globals) =
|
||||
global.init.val.i32_const;
|
||||
*GetRawGlobalPtr<int32_t>(global) = global.init.val.i32_const;
|
||||
break;
|
||||
case WasmInitExpr::kI64Const:
|
||||
*GetRawGlobalPtr<int64_t>(global, globals) =
|
||||
global.init.val.i64_const;
|
||||
*GetRawGlobalPtr<int64_t>(global) = global.init.val.i64_const;
|
||||
break;
|
||||
case WasmInitExpr::kF32Const:
|
||||
*GetRawGlobalPtr<float>(global, globals) = global.init.val.f32_const;
|
||||
*GetRawGlobalPtr<float>(global) = global.init.val.f32_const;
|
||||
break;
|
||||
case WasmInitExpr::kF64Const:
|
||||
*GetRawGlobalPtr<double>(global, globals) = global.init.val.f64_const;
|
||||
*GetRawGlobalPtr<double>(global) = global.init.val.f64_const;
|
||||
break;
|
||||
case WasmInitExpr::kGlobalIndex: {
|
||||
// Initialize with another global.
|
||||
@ -1488,8 +1559,8 @@ class WasmInstanceBuilder {
|
||||
size_t size = (global.type == kAstI64 || global.type == kAstF64)
|
||||
? sizeof(double)
|
||||
: sizeof(int32_t);
|
||||
memcpy(raw_buffer_ptr(globals, new_offset),
|
||||
raw_buffer_ptr(globals, old_offset), size);
|
||||
memcpy(raw_buffer_ptr(globals_, new_offset),
|
||||
raw_buffer_ptr(globals_, old_offset), size);
|
||||
break;
|
||||
}
|
||||
case WasmInitExpr::kNone:
|
||||
@ -1504,7 +1575,7 @@ class WasmInstanceBuilder {
|
||||
|
||||
// Allocate memory for a module instance as a new JSArrayBuffer.
|
||||
Handle<JSArrayBuffer> AllocateMemory(uint32_t min_mem_pages) {
|
||||
if (min_mem_pages > WasmModule::kMaxMemPages) {
|
||||
if (min_mem_pages > WasmModule::kV8MaxPages) {
|
||||
thrower_->RangeError("Out of memory: wasm memory too large");
|
||||
return Handle<JSArrayBuffer>::null();
|
||||
}
|
||||
@ -1519,30 +1590,29 @@ class WasmInstanceBuilder {
|
||||
|
||||
// Process the exports, creating wrappers for functions, tables, memories,
|
||||
// and globals.
|
||||
void ProcessExports(MaybeHandle<JSArrayBuffer> globals,
|
||||
std::vector<InitializedTable>& inited_tables,
|
||||
Handle<FixedArray> code_table,
|
||||
void ProcessExports(Handle<FixedArray> code_table,
|
||||
Handle<JSObject> instance) {
|
||||
if (module_->export_table.size() == 0) return;
|
||||
|
||||
// Allocate a table to cache the exported JSFunctions if needed.
|
||||
bool has_exported_functions = module_->num_exported_functions > 0;
|
||||
if (!has_exported_functions) {
|
||||
for (auto table : module_->function_tables) {
|
||||
if (table.exported) {
|
||||
has_exported_functions = true;
|
||||
bool needs_wrappers = module_->num_exported_functions > 0;
|
||||
for (auto table_instance : table_instances_) {
|
||||
if (!table_instance.js_wrappers.is_null()) {
|
||||
needs_wrappers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto table : module_->function_tables) {
|
||||
if (table.exported) {
|
||||
needs_wrappers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needs_wrappers) {
|
||||
// Fill the table to cache the exported JSFunction wrappers.
|
||||
js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(),
|
||||
Handle<JSFunction>::null());
|
||||
}
|
||||
Handle<FixedArray> exported_functions =
|
||||
has_exported_functions
|
||||
? isolate_->factory()->NewFixedArray(
|
||||
static_cast<int>(module_->functions.size()))
|
||||
: Handle<FixedArray>::null();
|
||||
|
||||
Handle<JSObject> exports_object = instance;
|
||||
if (module_->origin == kWasmOrigin) {
|
||||
if (module_->export_table.size() > 0 && module_->origin == kWasmOrigin) {
|
||||
// Create the "exports" object.
|
||||
Handle<JSFunction> object_function = Handle<JSFunction>(
|
||||
isolate_->native_context()->object_function(), isolate_);
|
||||
@ -1556,6 +1626,7 @@ class WasmInstanceBuilder {
|
||||
PropertyDescriptor desc;
|
||||
desc.set_writable(false);
|
||||
|
||||
// Process each export in the export table.
|
||||
int func_index = 0;
|
||||
for (auto exp : module_->export_table) {
|
||||
Handle<String> name =
|
||||
@ -1568,34 +1639,31 @@ class WasmInstanceBuilder {
|
||||
WasmFunction& function = module_->functions[exp.index];
|
||||
int export_index =
|
||||
static_cast<int>(module_->functions.size() + func_index);
|
||||
Handle<Object> value(exported_functions->get(exp.index), isolate_);
|
||||
Handle<JSFunction> js_function;
|
||||
if (value->IsJSFunction()) {
|
||||
// There already is a JSFunction for this WASM function.
|
||||
js_function = Handle<JSFunction>::cast(value);
|
||||
} else {
|
||||
Handle<JSFunction> js_function = js_wrappers_[exp.index];
|
||||
if (js_function.is_null()) {
|
||||
// Wrap the exported code as a JSFunction.
|
||||
Handle<Code> export_code =
|
||||
code_table->GetValueChecked<Code>(isolate_, export_index);
|
||||
js_function =
|
||||
WrapExportCodeAsJSFunction(isolate_, export_code, name,
|
||||
function.sig, func_index, instance);
|
||||
exported_functions->set(exp.index, *js_function);
|
||||
js_wrappers_[exp.index] = js_function;
|
||||
}
|
||||
desc.set_value(js_function);
|
||||
func_index++;
|
||||
break;
|
||||
}
|
||||
case kExternalTable: {
|
||||
InitializedTable& init_table = inited_tables[exp.index];
|
||||
// Export a table as a WebAssembly.Table object.
|
||||
TableInstance& table_instance = table_instances_[exp.index];
|
||||
WasmIndirectFunctionTable& table =
|
||||
module_->function_tables[exp.index];
|
||||
if (init_table.table_object.is_null()) {
|
||||
init_table.table_object = WasmJs::CreateWasmTableObject(
|
||||
isolate_, table.size, table.max_size != 0, table.max_size,
|
||||
&init_table.js_functions);
|
||||
if (table_instance.table_object.is_null()) {
|
||||
table_instance.table_object = WasmJs::CreateWasmTableObject(
|
||||
isolate_, table.min_size, table.has_max, table.max_size,
|
||||
&table_instance.js_wrappers);
|
||||
}
|
||||
desc.set_value(init_table.table_object);
|
||||
desc.set_value(table_instance.table_object);
|
||||
break;
|
||||
}
|
||||
case kExternalMemory: {
|
||||
@ -1622,13 +1690,13 @@ class WasmInstanceBuilder {
|
||||
double num = 0;
|
||||
switch (global.type) {
|
||||
case kAstI32:
|
||||
num = *GetRawGlobalPtr<int32_t>(global, globals);
|
||||
num = *GetRawGlobalPtr<int32_t>(global);
|
||||
break;
|
||||
case kAstF32:
|
||||
num = *GetRawGlobalPtr<float>(global, globals);
|
||||
num = *GetRawGlobalPtr<float>(global);
|
||||
break;
|
||||
case kAstF64:
|
||||
num = *GetRawGlobalPtr<double>(global, globals);
|
||||
num = *GetRawGlobalPtr<double>(global);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@ -1649,12 +1717,72 @@ class WasmInstanceBuilder {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill out the contents of WebAssembly.Table instances.
|
||||
if (!exported_functions.is_null()) {
|
||||
// TODO(titzer): We compile JS->WASM wrappers for any function that is a
|
||||
// member of an exported indirect table that is not itself exported. This
|
||||
// should be done at compile time and cached instead.
|
||||
void InitializeTables(Handle<FixedArray> code_table,
|
||||
Handle<JSObject> instance) {
|
||||
Handle<FixedArray> old_function_tables =
|
||||
compiled_module_->function_tables();
|
||||
int function_table_count =
|
||||
static_cast<int>(module_->function_tables.size());
|
||||
Handle<FixedArray> new_function_tables =
|
||||
isolate_->factory()->NewFixedArray(function_table_count);
|
||||
for (int index = 0; index < function_table_count; ++index) {
|
||||
WasmIndirectFunctionTable& table = module_->function_tables[index];
|
||||
TableInstance& table_instance = table_instances_[index];
|
||||
int table_size = static_cast<int>(table.min_size);
|
||||
|
||||
if (table_instance.dispatch_table.is_null()) {
|
||||
// Create a new dispatch table if necessary.
|
||||
table_instance.dispatch_table =
|
||||
isolate_->factory()->NewFixedArray(table_size * 2);
|
||||
for (int i = 0; i < table_size; ++i) {
|
||||
// Fill the table with invalid signature indexes so that
|
||||
// uninitialized entries will always fail the signature check.
|
||||
table_instance.dispatch_table->set(i, Smi::FromInt(kInvalidSigIndex));
|
||||
}
|
||||
}
|
||||
|
||||
new_function_tables->set(static_cast<int>(index),
|
||||
*table_instance.dispatch_table);
|
||||
|
||||
Handle<FixedArray> all_dispatch_tables;
|
||||
if (!table_instance.table_object.is_null()) {
|
||||
// Get the existing dispatch table(s) with the WebAssembly.Table object.
|
||||
all_dispatch_tables = WasmJs::AddWasmTableDispatchTable(
|
||||
isolate_, table_instance.table_object, Handle<JSObject>::null(),
|
||||
index, Handle<FixedArray>::null());
|
||||
}
|
||||
|
||||
// TODO(titzer): this does redundant work if there are multiple tables,
|
||||
// since initializations are not sorted by table index.
|
||||
for (auto table_init : module_->table_inits) {
|
||||
uint32_t base = EvalUint32InitExpr(table_init.offset);
|
||||
if (base > static_cast<uint32_t>(table_size) ||
|
||||
(base + table_init.entries.size() >
|
||||
static_cast<uint32_t>(table_size))) {
|
||||
thrower_->CompileError("table initializer is out of bounds");
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < static_cast<int>(table_init.entries.size()); ++i) {
|
||||
uint32_t func_index = table_init.entries[i];
|
||||
WasmFunction* function = &module_->functions[func_index];
|
||||
int table_index = static_cast<int>(i + base);
|
||||
int32_t sig_index = table.map.Find(function->sig);
|
||||
DCHECK_GE(sig_index, 0);
|
||||
table_instance.dispatch_table->set(table_index,
|
||||
Smi::FromInt(sig_index));
|
||||
table_instance.dispatch_table->set(table_index + table_size,
|
||||
code_table->get(func_index));
|
||||
|
||||
if (!all_dispatch_tables.is_null()) {
|
||||
Handle<Code> wasm_code(Code::cast(code_table->get(func_index)),
|
||||
isolate_);
|
||||
if (js_wrappers_[func_index].is_null()) {
|
||||
// No JSFunction entry yet exists for this function. Create one.
|
||||
// TODO(titzer): We compile JS->WASM wrappers for functions are
|
||||
// not exported but are in an exported table. This should be done
|
||||
// at module compile time and cached instead.
|
||||
WasmInstance temp_instance(module_);
|
||||
temp_instance.context = isolate_->native_context();
|
||||
temp_instance.mem_size = 0;
|
||||
@ -1666,30 +1794,43 @@ class WasmInstanceBuilder {
|
||||
module_env.instance = &temp_instance;
|
||||
module_env.origin = module_->origin;
|
||||
|
||||
// Fill the exported tables with JSFunctions.
|
||||
for (auto inited_table : inited_tables) {
|
||||
if (inited_table.js_functions.is_null()) continue;
|
||||
for (int i = 0; i < static_cast<int>(inited_table.new_entries.size());
|
||||
i++) {
|
||||
if (inited_table.new_entries[i] == nullptr) continue;
|
||||
int func_index =
|
||||
static_cast<int>(inited_table.new_entries[i]->func_index);
|
||||
if (!exported_functions->get(func_index)->IsJSFunction()) {
|
||||
// No JSFunction entry yet exists for this function. Create one.
|
||||
Handle<Code> wasm_code(Code::cast(code_table->get(func_index)),
|
||||
isolate_);
|
||||
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
|
||||
isolate_, &module_env, wasm_code, func_index);
|
||||
Handle<JSFunction> js_function = WrapExportCodeAsJSFunction(
|
||||
isolate_, wrapper_code, isolate_->factory()->empty_string(),
|
||||
module_->functions[func_index].sig, func_index, instance);
|
||||
exported_functions->set(func_index, *js_function);
|
||||
function->sig, func_index, instance);
|
||||
js_wrappers_[func_index] = js_function;
|
||||
}
|
||||
inited_table.js_functions->set(i,
|
||||
exported_functions->get(func_index));
|
||||
table_instance.js_wrappers->set(table_index,
|
||||
*js_wrappers_[func_index]);
|
||||
|
||||
UpdateDispatchTablesInternal(isolate_, all_dispatch_tables,
|
||||
table_index, function, wasm_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(titzer): we add the new dispatch table at the end to avoid
|
||||
// redundant work and also because the new instance is not yet fully
|
||||
// initialized.
|
||||
if (!table_instance.table_object.is_null()) {
|
||||
// Add the new dispatch table to the WebAssembly.Table object.
|
||||
all_dispatch_tables = WasmJs::AddWasmTableDispatchTable(
|
||||
isolate_, table_instance.table_object, instance, index,
|
||||
table_instance.dispatch_table);
|
||||
}
|
||||
}
|
||||
// Patch all code that has references to the old indirect tables.
|
||||
for (int i = 0; i < code_table->length(); ++i) {
|
||||
if (!code_table->get(i)->IsCode()) continue;
|
||||
Handle<Code> code(Code::cast(code_table->get(i)), isolate_);
|
||||
for (int j = 0; j < function_table_count; ++j) {
|
||||
ReplaceReferenceInCode(
|
||||
code, Handle<Object>(old_function_tables->get(j), isolate_),
|
||||
Handle<Object>(new_function_tables->get(j), isolate_));
|
||||
}
|
||||
}
|
||||
compiled_module_->set_function_tables(new_function_tables);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1960,7 +2101,7 @@ int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
|
||||
}
|
||||
|
||||
uint32_t GetMaxInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) {
|
||||
uint32_t max_pages = WasmModule::kMaxMemPages;
|
||||
uint32_t max_pages = WasmModule::kV8MaxPages;
|
||||
Handle<Object> memory_object(instance->GetInternalField(kWasmMemObject),
|
||||
isolate);
|
||||
if (memory_object->IsUndefined(isolate)) return max_pages;
|
||||
@ -1972,7 +2113,7 @@ int32_t wasm::GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
|
||||
if (!IsWasmInstance(*instance)) return -1;
|
||||
if (pages == 0) return GetInstanceMemorySize(isolate, instance);
|
||||
uint32_t max_pages = GetMaxInstanceMemorySize(isolate, instance);
|
||||
if (WasmModule::kMaxMemPages < max_pages) return -1;
|
||||
if (WasmModule::kV8MaxPages < max_pages) return -1;
|
||||
|
||||
Address old_mem_start = nullptr;
|
||||
uint32_t old_size = 0, new_size = 0;
|
||||
|
@ -133,8 +133,10 @@ struct WasmDataSegment {
|
||||
|
||||
// Static representation of a wasm indirect call table.
|
||||
struct WasmIndirectFunctionTable {
|
||||
uint32_t size; // initial table size.
|
||||
uint32_t min_size; // minimum 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.
|
||||
bool imported; // true if imported.
|
||||
bool exported; // true if exported.
|
||||
@ -173,9 +175,9 @@ class WasmCompiledModule;
|
||||
// Static representation of a module.
|
||||
struct V8_EXPORT_PRIVATE WasmModule {
|
||||
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 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;
|
||||
const byte* module_start = nullptr; // starting address for the module bytes
|
||||
@ -557,6 +559,9 @@ int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance);
|
||||
int32_t GrowInstanceMemory(Isolate* isolate, Handle<JSObject> instance,
|
||||
uint32_t pages);
|
||||
|
||||
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
int index, Handle<JSFunction> js_function);
|
||||
|
||||
namespace testing {
|
||||
|
||||
void ValidateInstancesChain(Isolate* isolate, Handle<JSObject> wasm_module,
|
||||
|
@ -223,10 +223,12 @@ class TestingModule : public ModuleEnv {
|
||||
|
||||
void AddIndirectFunctionTable(uint16_t* function_indexes,
|
||||
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,
|
||||
SignatureMap()});
|
||||
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) {
|
||||
table.values.push_back(function_indexes[i]);
|
||||
table.map.FindOrInsert(module_.functions[function_indexes[i]].sig);
|
||||
|
@ -30,6 +30,8 @@ function AddFunctions(builder) {
|
||||
return {mul: mul, add: add, sub: sub};
|
||||
}
|
||||
|
||||
function js_div(a, b) { return (a / b) | 0; }
|
||||
|
||||
(function ExportedTableTest() {
|
||||
print("ExportedTableTest...");
|
||||
|
||||
@ -56,8 +58,6 @@ function AddFunctions(builder) {
|
||||
|
||||
let module = new WebAssembly.Module(builder.toBuffer());
|
||||
|
||||
function js_div(a, b) { return (a / b) | 0; }
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
print(" base = " + i);
|
||||
let instance = new WebAssembly.Instance(module, {base: i, js_div: js_div});
|
||||
@ -100,3 +100,278 @@ function AddFunctions(builder) {
|
||||
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 {
|
||||
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) {
|
||||
this.exports.push({name: name, kind: kExternalFunction, index: index});
|
||||
return this;
|
||||
@ -289,6 +295,12 @@ class WasmModuleBuilder {
|
||||
section.emit_u8(has_max ? 1 : 0); // flags
|
||||
section.emit_u32v(imp.initial); // initial
|
||||
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 {
|
||||
throw new Error("unknown/unsupported import kind " + imp.kind);
|
||||
}
|
||||
|
@ -510,8 +510,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
|
||||
EXPECT_EQ(1, result.val->signatures.size());
|
||||
EXPECT_EQ(1, result.val->functions.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].values[0]);
|
||||
EXPECT_EQ(1, result.val->function_tables[0].min_size);
|
||||
}
|
||||
if (result.val) delete result.val;
|
||||
}
|
||||
@ -537,8 +536,7 @@ TEST_F(WasmModuleVerifyTest, OneIndirectFunction_one_entry) {
|
||||
EXPECT_EQ(1, result.val->signatures.size());
|
||||
EXPECT_EQ(1, result.val->functions.size());
|
||||
EXPECT_EQ(1, result.val->function_tables.size());
|
||||
EXPECT_EQ(1, result.val->function_tables[0].values.size());
|
||||
EXPECT_EQ(0, result.val->function_tables[0].values[0]);
|
||||
EXPECT_EQ(1, result.val->function_tables[0].min_size);
|
||||
}
|
||||
if (result.val) delete result.val;
|
||||
}
|
||||
@ -575,10 +573,7 @@ TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
|
||||
EXPECT_EQ(2, result.val->signatures.size());
|
||||
EXPECT_EQ(4, result.val->functions.size());
|
||||
EXPECT_EQ(1, result.val->function_tables.size());
|
||||
EXPECT_EQ(8, result.val->function_tables[0].values.size());
|
||||
for (int i = 0; i < 8; i++) {
|
||||
EXPECT_EQ(i & 3, result.val->function_tables[0].values[i]);
|
||||
}
|
||||
EXPECT_EQ(8, result.val->function_tables[0].min_size);
|
||||
}
|
||||
if (result.val) delete result.val;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user