Commit 61ea7c48 authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[wasm] Implement table.init bytecode

The table.init bytecode copies a range of elements from an element
segment into a table, trapping if the segment is not passive, is
dropped, or would cause out-of-bounds accesses.

R=mstarzinger@chromium.org
CC=binji@chromium.org
BUG=v8:7747

Change-Id: Ib27af9cca45a464fd1f876ddd092e99941481896
Reviewed-on: https://chromium-review.googlesource.com/c/1430063
Commit-Queue: Ben Titzer <titzer@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59037}
parent aecb020e
......@@ -4390,6 +4390,7 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index,
uint32_t elem_segment_index, Node* dst,
Node* src, Node* size,
wasm::WasmCodePosition position) {
CheckElemSegmentIsPassiveAndNotDropped(elem_segment_index, position);
Node* args[] = {
graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)),
graph()->NewNode(mcgraph()->common()->NumberConstant(elem_segment_index)),
......
......@@ -303,6 +303,20 @@ RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) {
timeout_ms);
}
namespace {
Object ThrowTableOutOfBounds(Isolate* isolate,
Handle<WasmInstanceObject> instance) {
// Handle out-of-bounds access here in the runtime call, rather
// than having the lower-level layers deal with JS exceptions.
if (isolate->context().is_null()) {
isolate->set_context(instance->native_context());
}
Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
MessageTemplate::kWasmTrapTableOutOfBounds);
return isolate->Throw(*error_obj);
}
} // namespace
RUNTIME_FUNCTION(Runtime_WasmTableInit) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());
......@@ -312,21 +326,15 @@ RUNTIME_FUNCTION(Runtime_WasmTableInit) {
CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 1);
CONVERT_UINT32_ARG_CHECKED(dst, 2);
CONVERT_UINT32_ARG_CHECKED(src, 3);
CONVERT_UINT32_ARG_CHECKED(size, 4);
PrintF(
"TableInit(table_index=%u, elem_segment_index=%u, dst=%u, src=%u, "
"size=%u)\n",
table_index, elem_segment_index, dst, src, size);
CONVERT_UINT32_ARG_CHECKED(count, 4);
USE(instance);
USE(table_index);
USE(elem_segment_index);
USE(dst);
USE(src);
USE(size);
DCHECK(isolate->context().is_null());
isolate->set_context(instance->native_context());
UNREACHABLE();
bool oob = !WasmInstanceObject::InitTableEntries(
isolate, instance, table_index, elem_segment_index, dst, src, count);
if (oob) return ThrowTableOutOfBounds(isolate, instance);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
......@@ -341,15 +349,7 @@ RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
bool oob = !WasmInstanceObject::CopyTableEntries(
isolate, instance, table_index, dst, src, count);
if (oob) {
// Handle out-of-bounds access here in the runtime call, rather
// than having the lower-level layers deal with JS exceptions.
DCHECK(isolate->context().is_null());
isolate->set_context(instance->native_context());
Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
MessageTemplate::kWasmTrapTableOutOfBounds);
return isolate->Throw(*error_obj);
}
if (oob) return ThrowTableOutOfBounds(isolate, instance);
return ReadOnlyRoots(isolate).undefined_value();
}
} // namespace internal
......
......@@ -25,6 +25,32 @@ namespace {
byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
}
uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
const WasmInitExpr& expr) {
switch (expr.kind) {
case WasmInitExpr::kI32Const:
return expr.val.i32_const;
case WasmInitExpr::kGlobalIndex: {
uint32_t offset =
instance->module()->globals[expr.val.global_index].offset;
auto raw_addr =
reinterpret_cast<Address>(
instance->untagged_globals_buffer()->backing_store()) +
offset;
return ReadLittleEndianValue<uint32_t>(raw_addr);
}
default:
UNREACHABLE();
}
}
// Represents the initialized state of a table.
struct TableInstance {
Handle<WasmTableObject> table_object; // WebAssembly.Table instance
Handle<FixedArray> js_functions; // JSFunctions exported
size_t table_size;
};
} // namespace
// A helper class to simplify instantiating a module from a module object.
......@@ -42,13 +68,6 @@ class InstanceBuilder {
bool ExecuteStartFunction();
private:
// Represents the initialized state of a table.
struct TableInstance {
Handle<WasmTableObject> table_object; // WebAssembly.Table instance
Handle<FixedArray> js_wrappers; // JSFunctions exported
size_t table_size;
};
// A pre-evaluated value to use in import binding.
struct SanitizedImport {
Handle<String> module_name;
......@@ -66,7 +85,6 @@ class InstanceBuilder {
Handle<JSArrayBuffer> untagged_globals_;
Handle<FixedArray> tagged_globals_;
std::vector<TableInstance> table_instances_;
std::vector<Handle<JSFunction>> js_wrappers_;
std::vector<Handle<WasmExceptionObject>> exception_wrappers_;
Handle<WasmExportedFunction> start_function_;
JSToWasmWrapperCache js_to_wasm_cache_;
......@@ -109,8 +127,6 @@ class InstanceBuilder {
MaybeHandle<Object> LookupImportAsm(uint32_t index,
Handle<String> import_name);
uint32_t EvalUint32InitExpr(const WasmInitExpr& expr);
// Load data segments into the memory.
void LoadDataSegments(Handle<WasmInstanceObject> instance);
......@@ -427,7 +443,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
for (const WasmElemSegment& elem_segment : module_->elem_segments) {
if (!elem_segment.active) continue;
DCHECK(elem_segment.table_index < table_instances_.size());
uint32_t base = EvalUint32InitExpr(elem_segment.offset);
uint32_t base = EvalUint32InitExpr(instance, elem_segment.offset);
size_t table_size = table_instances_[elem_segment.table_index].table_size;
if (!IsInBounds(base, elem_segment.entries.size(), table_size)) {
thrower_->LinkError("table initializer is out of bounds");
......@@ -440,7 +456,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
//--------------------------------------------------------------------------
for (const WasmDataSegment& seg : module_->data_segments) {
if (!seg.active) continue;
uint32_t base = EvalUint32InitExpr(seg.dest_addr);
uint32_t base = EvalUint32InitExpr(instance, seg.dest_addr);
if (!IsInBounds(base, seg.source.length(), instance->memory_size())) {
thrower_->LinkError("data segment is out of bounds");
return {};
......@@ -595,20 +611,6 @@ MaybeHandle<Object> InstanceBuilder::LookupImportAsm(
return result;
}
uint32_t InstanceBuilder::EvalUint32InitExpr(const 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 ReadLittleEndianValue<uint32_t>(
reinterpret_cast<Address>(raw_buffer_ptr(untagged_globals_, offset)));
}
default:
UNREACHABLE();
}
}
// Load data segments into the memory.
void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
Vector<const uint8_t> wire_bytes =
......@@ -619,7 +621,7 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
if (source_size == 0) continue;
// Passive segments are not copied during instantiation.
if (!segment.active) continue;
uint32_t dest_offset = EvalUint32InitExpr(segment.dest_addr);
uint32_t dest_offset = EvalUint32InitExpr(instance, segment.dest_addr);
DCHECK(IsInBounds(dest_offset, source_size, instance->memory_size()));
byte* dest = instance->memory_start() + dest_offset;
const byte* src = wire_bytes.start() + segment.source.offset();
......@@ -818,10 +820,10 @@ bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
TableInstance& table_instance = table_instances_[table_index];
table_instance.table_object = Handle<WasmTableObject>::cast(value);
instance->set_table_object(*table_instance.table_object);
table_instance.js_wrappers =
table_instance.js_functions =
Handle<FixedArray>(table_instance.table_object->functions(), isolate_);
int imported_table_size = table_instance.js_wrappers->length();
int imported_table_size = table_instance.js_functions->length();
if (imported_table_size < static_cast<int>(table.initial_size)) {
thrower_->LinkError("table import %d is smaller than initial %d, got %u",
import_index, table.initial_size, imported_table_size);
......@@ -854,7 +856,7 @@ bool InstanceBuilder::ProcessImportedTable(Handle<WasmInstanceObject> instance,
// Initialize the dispatch table with the (foreign) JS functions
// that are already in the table.
for (int i = 0; i < imported_table_size; ++i) {
Handle<Object> val(table_instance.js_wrappers->get(i), isolate_);
Handle<Object> val(table_instance.js_functions->get(i), isolate_);
// TODO(mtrofin): this is the same logic as WasmTableObject::Set:
// insert in the local table a wrapper from the other module, and add
// a reference to the owning instance of the other module.
......@@ -1220,7 +1222,7 @@ Handle<JSArrayBuffer> InstanceBuilder::AllocateMemory(uint32_t num_pages) {
bool InstanceBuilder::NeedsWrappers() const {
if (module_->num_exported_functions > 0) return true;
for (auto& table_instance : table_instances_) {
if (!table_instance.js_wrappers.is_null()) return true;
if (!table_instance.js_functions.is_null()) return true;
}
for (auto& table : module_->tables) {
if (table.exported) return true;
......@@ -1234,20 +1236,18 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
Handle<FixedArray> export_wrappers(module_object_->export_wrappers(),
isolate_);
if (NeedsWrappers()) {
// Fill the table to cache the exported JSFunction wrappers.
js_wrappers_.insert(js_wrappers_.begin(), module_->functions.size(),
Handle<JSFunction>::null());
// If an imported WebAssembly function gets exported, the exported function
// has to be identical to to imported function. Therefore we put all
// imported WebAssembly functions into the js_wrappers_ list.
// has to be identical to to imported function. Therefore we cache all
// imported WebAssembly functions in the instance.
for (int index = 0, end = static_cast<int>(module_->import_table.size());
index < end; ++index) {
const WasmImport& import = module_->import_table[index];
if (import.kind == kExternalFunction) {
Handle<Object> value = sanitized_imports_[index].value;
if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
js_wrappers_[import.index] = Handle<JSFunction>::cast(value);
WasmInstanceObject::SetWasmExportedFunction(
isolate_, instance, import.index,
Handle<WasmExportedFunction>::cast(value));
}
}
}
......@@ -1298,9 +1298,12 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
switch (exp.kind) {
case kExternalFunction: {
// Wrap and export the code as a JSFunction.
// TODO(wasm): reduce duplication with LoadElemSegment() further below
const WasmFunction& function = module_->functions[exp.index];
Handle<JSFunction> js_function = js_wrappers_[exp.index];
if (js_function.is_null()) {
MaybeHandle<WasmExportedFunction> wasm_exported_function =
WasmInstanceObject::GetWasmExportedFunction(isolate_, instance,
exp.index);
if (wasm_exported_function.is_null()) {
// Wrap the exported code as a JSFunction.
Handle<Code> export_code =
export_wrappers->GetValueChecked<Code>(isolate_, export_index);
......@@ -1314,12 +1317,14 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
isolate_, module_object_, func_name_ref)
.ToHandleChecked();
}
js_function = WasmExportedFunction::New(
wasm_exported_function = WasmExportedFunction::New(
isolate_, instance, func_name, function.func_index,
static_cast<int>(function.sig->parameter_count()), export_code);
js_wrappers_[exp.index] = js_function;
WasmInstanceObject::SetWasmExportedFunction(
isolate_, instance, exp.index,
wasm_exported_function.ToHandleChecked());
}
desc.set_value(js_function);
desc.set_value(wasm_exported_function.ToHandleChecked());
export_index++;
break;
}
......@@ -1332,7 +1337,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
: FLAG_wasm_max_table_size;
table_instance.table_object =
WasmTableObject::New(isolate_, table.initial_size, maximum,
&table_instance.js_wrappers);
&table_instance.js_functions);
}
instance->set_table_object(*table_instance.table_object);
desc.set_value(table_instance.table_object);
......@@ -1451,60 +1456,84 @@ void InstanceBuilder::InitializeTables(Handle<WasmInstanceObject> instance) {
}
}
bool LoadElemSegmentImpl(Isolate* isolate, Handle<WasmInstanceObject> instance,
const TableInstance& table_instance,
JSToWasmWrapperCache* js_to_wasm_cache,
const WasmElemSegment& elem_segment, uint32_t dst,
uint32_t src, size_t count) {
if (count == 0) return true; // nothing to do.
if (!IsInBounds(dst, count, table_instance.table_size)) return false;
if (!IsInBounds(src, count, elem_segment.entries.size())) return false;
const WasmModule* module = instance->module();
for (uint32_t i = 0; i < count; ++i) {
uint32_t func_index = elem_segment.entries[src + i];
const WasmFunction* function = &module->functions[func_index];
int entry_index = static_cast<int>(dst + i);
// Update the local dispatch table first.
uint32_t sig_id = module->signature_ids[function->sig_index];
IndirectFunctionTableEntry(instance, entry_index)
.Set(sig_id, instance, func_index);
if (!table_instance.table_object.is_null()) {
// Update the table object's other dispatch tables.
MaybeHandle<WasmExportedFunction> wasm_exported_function =
WasmInstanceObject::GetWasmExportedFunction(isolate, instance,
func_index);
if (wasm_exported_function.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.
Handle<Code> wrapper_code =
js_to_wasm_cache->GetOrCompileJSToWasmWrapper(
isolate, function->sig, function->imported);
MaybeHandle<String> func_name;
if (module->origin == kAsmJsOrigin) {
// For modules arising from asm.js, honor the names section.
auto module_object =
Handle<WasmModuleObject>(instance->module_object(), isolate);
WireBytesRef func_name_ref = module->LookupFunctionName(
ModuleWireBytes(module_object->native_module()->wire_bytes()),
func_index);
func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
isolate, module_object, func_name_ref)
.ToHandleChecked();
}
wasm_exported_function = WasmExportedFunction::New(
isolate, instance, func_name, func_index,
static_cast<int>(function->sig->parameter_count()), wrapper_code);
WasmInstanceObject::SetWasmExportedFunction(
isolate, instance, func_index,
wasm_exported_function.ToHandleChecked());
}
table_instance.js_functions->set(
entry_index, *wasm_exported_function.ToHandleChecked());
// UpdateDispatchTables() updates all other dispatch tables, since
// we have not yet added the dispatch table we are currently building.
WasmTableObject::UpdateDispatchTables(
isolate, table_instance.table_object, entry_index, function->sig,
instance, func_index);
}
}
return true;
}
void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
NativeModule* native_module = module_object_->native_module();
for (auto& elem_segment : module_->elem_segments) {
// Passive segments are not copied during instantiation.
if (!elem_segment.active) continue;
uint32_t base = EvalUint32InitExpr(elem_segment.offset);
uint32_t num_entries = static_cast<uint32_t>(elem_segment.entries.size());
uint32_t index = elem_segment.table_index;
TableInstance& table_instance = table_instances_[index];
DCHECK(IsInBounds(base, num_entries, table_instance.table_size));
for (uint32_t i = 0; i < num_entries; ++i) {
uint32_t func_index = elem_segment.entries[i];
const WasmFunction* function = &module_->functions[func_index];
int table_index = static_cast<int>(i + base);
// Update the local dispatch table first.
uint32_t sig_id = module_->signature_ids[function->sig_index];
IndirectFunctionTableEntry(instance, table_index)
.Set(sig_id, instance, func_index);
if (!table_instance.table_object.is_null()) {
// Update the table object's other dispatch tables.
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.
Handle<Code> wrapper_code =
js_to_wasm_cache_.GetOrCompileJSToWasmWrapper(
isolate_, function->sig, function->imported);
MaybeHandle<String> func_name;
if (module_->origin == kAsmJsOrigin) {
// For modules arising from asm.js, honor the names section.
WireBytesRef func_name_ref = module_->LookupFunctionName(
ModuleWireBytes(native_module->wire_bytes()), func_index);
func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
isolate_, module_object_, func_name_ref)
.ToHandleChecked();
}
Handle<WasmExportedFunction> js_function = WasmExportedFunction::New(
isolate_, instance, func_name, func_index,
static_cast<int>(function->sig->parameter_count()), wrapper_code);
js_wrappers_[func_index] = js_function;
}
table_instance.js_wrappers->set(table_index, *js_wrappers_[func_index]);
// UpdateDispatchTables() updates all other dispatch tables, since
// we have not yet added the dispatch table we are currently building.
WasmTableObject::UpdateDispatchTables(
isolate_, table_instance.table_object, table_index, function->sig,
instance, func_index);
}
}
uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset);
uint32_t src = 0;
size_t count = elem_segment.entries.size();
bool success = LoadElemSegmentImpl(
isolate_, instance, table_instances_[elem_segment.table_index],
&js_to_wasm_cache_, elem_segment, dst, src, count);
CHECK(success);
}
int table_count = static_cast<int>(module_->tables.size());
......@@ -1530,6 +1559,26 @@ void InstanceBuilder::InitializeExceptions(
}
}
bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
uint32_t table_index, uint32_t segment_index, uint32_t dst,
uint32_t src, uint32_t count) {
JSToWasmWrapperCache js_to_wasm_cache;
Handle<WasmTableObject> table_object;
Handle<FixedArray> js_functions;
if (instance->has_table_object()) {
table_object = Handle<WasmTableObject>(instance->table_object(), isolate);
js_functions = Handle<FixedArray>(table_object->functions(), isolate);
}
TableInstance table_instance = {table_object, js_functions,
instance->indirect_function_table_size()};
auto& elem_segment = instance->module()->elem_segments[segment_index];
return LoadElemSegmentImpl(isolate, instance, table_instance,
&js_to_wasm_cache, elem_segment, dst, src, count);
}
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -5,6 +5,9 @@
#ifndef V8_WASM_MODULE_INSTANTIATE_H_
#define V8_WASM_MODULE_INSTANTIATE_H_
#include <stdint.h>
#include "include/v8config.h"
namespace v8 {
namespace internal {
......@@ -28,6 +31,10 @@ MaybeHandle<WasmInstanceObject> InstantiateToInstanceObject(
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
MaybeHandle<JSArrayBuffer> memory);
bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
uint32_t table_index, uint32_t segment_index, uint32_t dst,
uint32_t src, uint32_t count) V8_WARN_UNUSED_RESULT;
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -233,6 +233,8 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, exceptions_table, FixedArray,
ACCESSORS(WasmInstanceObject, undefined_value, Oddball, kUndefinedValueOffset)
ACCESSORS(WasmInstanceObject, null_value, Oddball, kNullValueOffset)
ACCESSORS(WasmInstanceObject, centry_stub, Code, kCEntryStubOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, wasm_exported_functions, FixedArray,
kWasmExportedFunctionsOffset)
inline bool WasmInstanceObject::has_indirect_function_table() {
return indirect_function_table_sig_ids() != nullptr;
......
......@@ -19,6 +19,7 @@
#include "src/wasm/jump-table-assembler.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/module-instantiate.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-limits.h"
......@@ -1477,6 +1478,48 @@ bool WasmInstanceObject::CopyTableEntries(Isolate* isolate,
return true;
}
// static
bool WasmInstanceObject::InitTableEntries(Isolate* isolate,
Handle<WasmInstanceObject> instance,
uint32_t table_index,
uint32_t segment_index, uint32_t dst,
uint32_t src, uint32_t count) {
// Note that this implementation just calls through to module instantiation.
// This is intentional, so that the runtime only depends on the object
// methods, and not the module instantiation logic.
return wasm::LoadElemSegment(isolate, instance, table_index, segment_index,
dst, src, count);
}
MaybeHandle<WasmExportedFunction> WasmInstanceObject::GetWasmExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index) {
MaybeHandle<WasmExportedFunction> result;
if (instance->has_wasm_exported_functions()) {
Object val = instance->wasm_exported_functions()->get(index);
if (!val->IsUndefined(isolate)) {
result = Handle<WasmExportedFunction>(WasmExportedFunction::cast(val),
isolate);
}
}
return result;
}
void WasmInstanceObject::SetWasmExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index,
Handle<WasmExportedFunction> val) {
Handle<FixedArray> functions;
if (!instance->has_wasm_exported_functions()) {
// lazily-allocate the wasm exported functions.
functions = isolate->factory()->NewFixedArray(
static_cast<int>(instance->module()->functions.size()));
instance->set_wasm_exported_functions(*functions);
} else {
functions =
Handle<FixedArray>(instance->wasm_exported_functions(), isolate);
}
functions->set(index, *val);
}
// static
Handle<WasmExceptionObject> WasmExceptionObject::New(
Isolate* isolate, const wasm::FunctionSig* sig,
......
......@@ -40,6 +40,7 @@ class WasmDebugInfo;
class WasmExceptionTag;
class WasmInstanceObject;
class WasmModuleObject;
class WasmExportedFunction;
template <class CppType>
class Managed;
......@@ -411,6 +412,7 @@ class WasmInstanceObject : public JSObject {
DECL_ACCESSORS(undefined_value, Oddball)
DECL_ACCESSORS(null_value, Oddball)
DECL_ACCESSORS(centry_stub, Code)
DECL_OPTIONAL_ACCESSORS(wasm_exported_functions, FixedArray)
DECL_PRIMITIVE_ACCESSORS(memory_start, byte*)
DECL_PRIMITIVE_ACCESSORS(memory_size, size_t)
DECL_PRIMITIVE_ACCESSORS(memory_mask, size_t)
......@@ -454,6 +456,7 @@ class WasmInstanceObject : public JSObject {
V(kUndefinedValueOffset, kTaggedSize) \
V(kNullValueOffset, kTaggedSize) \
V(kCEntryStubOffset, kTaggedSize) \
V(kWasmExportedFunctionsOffset, kTaggedSize) \
V(kEndOfTaggedFieldsOffset, 0) \
/* Raw data. */ \
V(kIndirectFunctionTableSizeOffset, kUInt32Size) \
......@@ -509,9 +512,24 @@ class WasmInstanceObject : public JSObject {
uint32_t table_index, uint32_t dst, uint32_t src,
uint32_t count) V8_WARN_UNUSED_RESULT;
// Copy table entries from an element segment. Returns {false} if the ranges
// are out-of-bounds.
static bool InitTableEntries(Isolate* isolate,
Handle<WasmInstanceObject> instance,
uint32_t table_index, uint32_t segment_index,
uint32_t dst, uint32_t src,
uint32_t count) V8_WARN_UNUSED_RESULT;
// Iterates all fields in the object except the untagged fields.
class BodyDescriptor;
static MaybeHandle<WasmExportedFunction> GetWasmExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index);
static void SetWasmExportedFunction(Isolate* isolate,
Handle<WasmInstanceObject> instance,
int index,
Handle<WasmExportedFunction> val);
OBJECT_CONSTRUCTORS(WasmInstanceObject, JSObject)
private:
......
......@@ -321,28 +321,6 @@ function getMemoryFill(mem) {
kTrapMemOutOfBounds, () => memoryFill(kPageSize + 1, v, kPageSize));
})();
(function TestTableInit0() {
let builder = new WasmModuleBuilder();
let sig_v_iii = builder.addType(kSig_v_iii);
builder.setTableBounds(5, 5);
builder.addElementSegment(0, false, []);
builder.addElementSegment(0, false, []);
builder.addFunction("init0", sig_v_iii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 2,
kNumericPrefix, kExprTableInit, kTableZero, kSegmentZero])
.exportAs("init0");
let instance = builder.instantiate();
let init = instance.exports.init0;
// TODO(titzer): we only check that a function containing TableInit can be compiled.
// init(1, 2, 3);
})();
(function TestTableDropActive() {
const builder = new WasmModuleBuilder();
builder.setTableBounds(5, 5);
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-bulk-memory
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function addFunction(builder, k) {
let m = builder.addFunction("", kSig_i_v)
.addBody([...wasmI32Const(k)]);
return m;
}
function addFunctions(builder, count, exportf = false) {
let o = {};
for (var i = 0; i < count; i++) {
let name = `f${i}`;
o[name] = addFunction(builder, i);
if (exportf) o[name].exportAs(name);
}
return o;
}
function assertTable(obj, ...elems) {
for (var i = 0; i < elems.length; i++) {
assertEquals(elems[i], obj.get(i));
}
}
(function TestTableInitInBounds() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_v_iii = builder.addType(kSig_v_iii);
let kTableSize = 5;
builder.setTableBounds(kTableSize, kTableSize);
{
let o = addFunctions(builder, kTableSize, true);
builder.addPassiveElementSegment(
[o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index]);
}
builder.addFunction("init0", sig_v_iii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 2,
kNumericPrefix, kExprTableInit, kTableZero, kSegmentZero])
.exportAs("init0");
builder.addExportOfKind("table", kExternalTable, 0);
let instance = builder.instantiate();
let x = instance.exports;
assertTable(x.table, null, null, null, null, null);
// 0-count is not oob.
x.init0(0, 0, 0);
assertTable(x.table, null, null, null, null, null);
x.init0(kTableSize+1, 0, 0);
assertTable(x.table, null, null, null, null, null);
x.init0(0, kTableSize+1, 0);
assertTable(x.table, null, null, null, null, null);
// test actual writes.
x.init0(0, 0, 1);
assertTable(x.table, x.f0, null, null, null, null);
x.init0(0, 0, 2);
assertTable(x.table, x.f0, x.f1, null, null, null);
x.init0(0, 0, 3);
assertTable(x.table, x.f0, x.f1, x.f2, null, null);
x.init0(3, 0, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f0, x.f1);
x.init0(3, 1, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f1, x.f2);
x.init0(3, 2, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f2, x.f3);
x.init0(3, 3, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f3, x.f4);
})();
(function TestTableInitOob() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_v_iii = builder.addType(kSig_v_iii);
let kTableSize = 5;
builder.setTableBounds(kTableSize, kTableSize);
{
let o = addFunctions(builder, kTableSize);
builder.addPassiveElementSegment(
[o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index]);
}
builder.addFunction("init0", sig_v_iii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 2,
kNumericPrefix, kExprTableInit, kTableZero, kSegmentZero])
.exportAs("init0");
builder.addExportOfKind("table", kExternalTable, 0);
let instance = builder.instantiate();
let x = instance.exports;
assertTable(x.table, null, null, null, null, null);
assertThrows(() => x.init0(0, 0, 6));
assertThrows(() => x.init0(0, 1, 5));
assertThrows(() => x.init0(0, 2, 4));
assertThrows(() => x.init0(0, 3, 3));
assertThrows(() => x.init0(0, 4, 2));
assertThrows(() => x.init0(0, 5, 1));
assertThrows(() => x.init0(0, 0, 6));
assertThrows(() => x.init0(1, 0, 5));
assertThrows(() => x.init0(2, 0, 4));
assertThrows(() => x.init0(3, 0, 3));
assertThrows(() => x.init0(4, 0, 2));
assertThrows(() => x.init0(5, 0, 1));
assertThrows(() => x.init0(10, 0, 1));
assertThrows(() => x.init0(0, 10, 1));
})();
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