v8/test/mjsunit/wasm/table-fill.js
Andreas Haas 9155ea6196 [wasm] Allow table.fill of size 0 at position table.length
This CL fixes an issue where V8 does not satisfy the WebAssembly spec of
the anyref proposal.

The table.fill instruction has 3 parameters, {start_index}, {length},
and {value}. V8 trapped with table-out-of-bounds when
{start_index >= table_size}. However, the spec requires that
{start_index == table_size} is valid when {length == 0}.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: I5f83a03fb8e349b48c887535f6f065492feb9ac2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1609537
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61689}
2019-05-21 12:46:08 +00:00

201 lines
6.6 KiB
JavaScript

// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --experimental-wasm-anyref
load('test/mjsunit/wasm/wasm-module-builder.js');
function dummy_func(val) {
let builder = new WasmModuleBuilder();
builder.addFunction('dummy', kSig_i_v)
.addBody([kExprI32Const, val])
.exportAs('dummy');
return builder.instantiate().exports.dummy;
}
let kSig_v_iri = makeSig([kWasmI32, kWasmAnyRef, kWasmI32], []);
let kSig_v_iai = makeSig([kWasmI32, kWasmAnyFunc, kWasmI32], []);
let kSig_r_i = makeSig([kWasmI32], [kWasmAnyRef]);
const builder = new WasmModuleBuilder();
const size = 10;
const maximum = size;
const import_ref =
builder.addImportedTable('imp', 'table_ref', size, maximum, kWasmAnyRef);
const import_func =
builder.addImportedTable('imp', 'table_func', size, maximum, kWasmAnyFunc);
const internal_ref = builder.addTable(kWasmAnyRef, size, maximum).index;
const internal_func = builder.addTable(kWasmAnyFunc, size, maximum).index;
// Add fill and get functions for the anyref tables.
for (index of [import_ref, internal_ref]) {
builder.addFunction(`fill${index}`, kSig_v_iri)
.addBody([
kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix,
kExprTableFill, index
])
.exportFunc();
builder.addFunction(`get${index}`, kSig_r_i)
.addBody([kExprGetLocal, 0, kExprGetTable, index])
.exportFunc();
}
// Add fill and call functions for the anyfunc tables.
const sig_index = builder.addType(kSig_i_v);
for (index of [import_func, internal_func]) {
builder.addFunction(`fill${index}`, kSig_v_iai)
.addBody([
kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kNumericPrefix,
kExprTableFill, index
])
.exportFunc();
builder.addFunction(`call${index}`, kSig_i_i)
.addBody([kExprGetLocal, 0, kExprCallIndirect, sig_index, index])
.exportFunc();
}
const table_ref =
new WebAssembly.Table({element: 'anyref', initial: size, maximum: maximum});
const table_func = new WebAssembly.Table(
{element: 'anyfunc', initial: size, maximum: maximum});
const instance =
builder.instantiate({imp: {table_ref: table_ref, table_func: table_func}});
function checkAnyRefTable(getter, start, count, value) {
for (i = 0; i < count; ++i) {
assertEquals(value, getter(start + i));
}
}
(function testAnyRefTableIsUninitialized() {
print(arguments.callee.name);
checkAnyRefTable(instance.exports[`get${import_ref}`], 0, size, null);
checkAnyRefTable(instance.exports[`get${internal_ref}`], 0, size, null);
})();
(function testAnyRefTableFill() {
print(arguments.callee.name);
// Fill table and check the content.
let start = 1;
let value = {foo: 23};
let count = 3;
instance.exports[`fill${import_ref}`](start, value, count);
checkAnyRefTable(instance.exports[`get${import_ref}`], start, count, value);
value = 'foo';
instance.exports[`fill${internal_ref}`](start, value, count);
checkAnyRefTable(instance.exports[`get${internal_ref}`], start, count, value);
})();
(function testAnyRefTableFillOOB() {
print(arguments.callee.name);
// Fill table out-of-bounds, check if the table got filled as much as
// possible.
let start = 7;
let value = {foo: 27};
// {maximum + 4} elements definitely don't fit into the table.
let count = maximum + 4;
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${import_ref}`](start, value, count));
checkAnyRefTable(
instance.exports[`get${import_ref}`], start, size - start, value);
value = 45;
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${internal_ref}`](start, value, count));
checkAnyRefTable(
instance.exports[`get${internal_ref}`], start, size - start, value);
})();
(function testAnyRefTableFillOOBCountZero() {
print(arguments.callee.name);
// Fill 0 elements at an oob position. This should trap.
let start = size + 32;
let value = 'bar';
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${import_ref}`](start, value, 0));
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${internal_ref}`](start, value, 0));
})();
function checkAnyFuncTable(call, start, count, value) {
for (i = 0; i < count; ++i) {
if (value) {
assertEquals(value, call(start + i));
} else {
assertTraps(kTrapFuncSigMismatch, () => call(start + i));
}
}
}
(function testAnyRefTableIsUninitialized() {
print(arguments.callee.name);
// Check that the table is uninitialized.
checkAnyFuncTable(instance.exports[`call${import_func}`], 0, size);
checkAnyFuncTable(instance.exports[`call${internal_func}`], 0, size);
})();
(function testAnyFuncTableFill() {
print(arguments.callee.name);
// Fill and check the result.
let start = 1;
let value = 44;
let count = 3;
instance.exports[`fill${import_func}`](start, dummy_func(value), count);
checkAnyFuncTable(
instance.exports[`call${import_func}`], start, count, value);
value = 21;
instance.exports[`fill${internal_func}`](start, dummy_func(value), count);
checkAnyFuncTable(
instance.exports[`call${internal_func}`], start, count, value);
})();
(function testAnyFuncTableFillOOB() {
print(arguments.callee.name);
// Fill table out-of-bounds, check if the table got filled as much as
// possible.
let start = 7;
let value = 38;
// {maximum + 4} elements definitely don't fit into the table.
let count = maximum + 4;
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${import_func}`](
start, dummy_func(value), count));
checkAnyFuncTable(
instance.exports[`call${import_func}`], start, size - start, value);
value = 46;
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${internal_func}`](
start, dummy_func(value), count));
checkAnyFuncTable(
instance.exports[`call${internal_func}`], start, size - start, value);
})();
(function testAnyFuncTableFillOOBCountZero() {
print(arguments.callee.name);
// Fill 0 elements at an oob position. This should trap.
let start = size + 32;
let value = dummy_func(33);
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${import_func}`](start, null, 0));
assertTraps(
kTrapTableOutOfBounds,
() => instance.exports[`fill${internal_func}`](start, null, 0));
// Check that table.fill at position `size` is still valid.
instance.exports[`fill${import_func}`](size, null, 0);
instance.exports[`fill${internal_func}`](size, null, 0);
})();