v8/test/mjsunit/wasm/indirect-call-non-zero-table.js
Manos Koukoutos cdb3da7f5f [wasm-gc][bug] call_indirect should check for null table entries
This was not happening when there was no need to typecheck the entry.

Additional changes:
- Add tests with null table entries for typed and untyped function
  tables.
- Allow AddIndirectFunctionTable in wasm-run-utils to specify table
  type.
- Add possibility to define tables in test-gc.cc.
- Merge trapTableOutOfBounds with trapInvalidFunc.
- Use trapTableOutOfBounds in call_indirect as appropriate.
- Fix emission of table types in wasm-module-builder.cc.

Bug: v8:9495
Change-Id: I4a857ff4378e5a87dc0646d94b4c75635a43c55b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2442622
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70311}
2020-10-05 13:08:20 +00:00

203 lines
7.0 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-reftypes --experimental-wasm-return-call
load("test/mjsunit/wasm/wasm-module-builder.js");
(function IndirectCallToNonZeroTable() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const placeholder = builder.addTable(kWasmAnyFunc, 3).index;
const table1 = builder.addTable(kWasmAnyFunc, 3).index;
const table2 = builder.addTable(kWasmAnyFunc, 5).index;
const sig_index = builder.addType(kSig_i_v);
const other_sig = builder.addType(kSig_i_i);
const v1 = 16;
const v2 = 26;
const v3 = 36;
const v4 = 46;
const v5 = 56;
const f_unreachable = builder.addFunction('unreachable', sig_index)
.addBody([kExprUnreachable]).index;
const f1 = builder.addFunction('f1', sig_index)
.addBody([kExprI32Const, v1])
.index;
const f2 = builder.addFunction('f2', sig_index)
.addBody([kExprI32Const, v2])
.index;
const f3 = builder.addFunction('f3', sig_index)
.addBody([kExprI32Const, v3])
.index;
const f4 = builder.addFunction('f4', sig_index)
.addBody([kExprI32Const, v4])
.index;
const f5 = builder.addFunction('f5', sig_index)
.addBody([kExprI32Const, v5])
.index;
builder.addFunction('call1', kSig_i_i)
.addBody([kExprLocalGet, 0, // function index
kExprCallIndirect, sig_index, table1])
.exportAs('call1');
builder.addFunction('return_call1', kSig_i_i)
.addBody([kExprLocalGet, 0, // function index
kExprReturnCallIndirect, sig_index, table1])
.exportAs('return_call1');
builder.addFunction('call2', kSig_i_i)
.addBody([kExprLocalGet, 0, // function index
kExprCallIndirect, sig_index, table2])
.exportAs('call2');
builder.addFunction('return_call2', kSig_i_i)
.addBody([kExprLocalGet, 0, // function index
kExprReturnCallIndirect, sig_index, table2])
.exportAs('return_call2');
builder.addFunction('call_invalid_sig', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprLocalGet, 0, // function index + param
kExprCallIndirect, other_sig, table2])
.exportAs('call_invalid_sig');
builder.addFunction('return_call_invalid_sig', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprLocalGet, 0, // function index + param
kExprReturnCallIndirect, other_sig, table2])
.exportAs('return_call_invalid_sig');
// We want to crash if we call through the table with index 0.
builder.addElementSegment(placeholder, 0, false,
[f_unreachable, f_unreachable, f_unreachable]);
builder.addElementSegment(table1, 0, false, [f1, f2, f3]);
// Keep one slot in table2 uninitialized. We should trap if we call it.
builder.addElementSegment(table2, 1, false,
[f_unreachable, f_unreachable, f4, f5]);
const instance = builder.instantiate();
assertEquals(v1, instance.exports.call1(0));
assertEquals(v2, instance.exports.call1(1));
assertEquals(v3, instance.exports.call1(2));
assertTraps(kTrapTableOutOfBounds, () => instance.exports.call1(3));
assertEquals(v1, instance.exports.return_call1(0));
assertEquals(v2, instance.exports.return_call1(1));
assertEquals(v3, instance.exports.return_call1(2));
assertTraps(kTrapTableOutOfBounds, () => instance.exports.return_call1(3));
// Try to call through the uninitialized table entry.
assertTraps(kTrapFuncSigMismatch, () => instance.exports.call2(0));
assertEquals(v4, instance.exports.call2(3));
assertEquals(v5, instance.exports.call2(4));
assertTraps(kTrapFuncSigMismatch,
() => instance.exports.call_invalid_sig(4));
assertTraps(kTrapFuncSigMismatch, () => instance.exports.return_call2(0));
assertEquals(v4, instance.exports.return_call2(3));
assertEquals(v5, instance.exports.return_call2(4));
assertTraps(kTrapFuncSigMismatch,
() => instance.exports.return_call_invalid_sig(4));
})();
(function IndirectCallToImportedNonZeroTable() {
print(arguments.callee.name);
const table_size = 10;
const placeholder = new WebAssembly.Table(
{ initial: table_size, maximum: table_size, element: "anyfunc" });
const table = new WebAssembly.Table(
{ initial: table_size, maximum: table_size, element: "anyfunc" });
const builder = new WasmModuleBuilder();
builder.addImportedTable("m", "placeholder", table_size, table_size);
const t1 = builder.addImportedTable("m", "table", table_size, table_size);
// We initialize the module twice and put the function f1 in the table at
// the index defined by {g}. Thereby we can initialize the table at different
// slots for different instances. The function f1 also returns {g} so that we
// can see that actually different functions get called.
const g = builder.addImportedGlobal("m", "base", kWasmI32);
const sig_index = builder.addType(kSig_i_v);
const f1 = builder.addFunction("foo", sig_index)
.addBody([kExprGlobalGet, g, kExprI32Const, 12, kExprI32Add]);
builder.addFunction('call', kSig_i_i)
.addBody([kExprLocalGet, 0, // function index
kExprCallIndirect, sig_index, t1])
.exportAs('call');
builder.addElementSegment(t1, g, true, [f1.index]);
const base1 = 3;
const base2 = 5;
const instance1 = builder.instantiate({
m: {
placeholder: placeholder,
table: table,
base: base1
}
});
const instance2 = builder.instantiate({
m: {
placeholder: placeholder,
table: table,
base: base2
}
});
assertEquals(base1 + 12, instance1.exports.call(base1));
assertEquals(base2 + 12, instance1.exports.call(base2));
assertEquals(base1 + 12, instance2.exports.call(base1));
assertEquals(base2 + 12, instance2.exports.call(base2));
})();
function js_div(a, b) { return (a / b) | 0; }
(function CallImportedFunction() {
let kTableSize = 10;
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let div = builder.addImport("q", "js_div", kSig_i_ii);
builder.addImportedTable("q", "placeholder", kTableSize, kTableSize);
let table_index = builder.addImportedTable("q", "table", kTableSize, kTableSize);
let g = builder.addImportedGlobal("q", "base", kWasmI32);
let sig_index = builder.addType(kSig_i_ii);
builder.addFunction("placeholder", sig_index)
.addBody([kExprLocalGet, 0]);
builder.addElementSegment(table_index, g, true, [div]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 55, // --
kExprLocalGet, 0, // --
kExprLocalGet, 1, // --
kExprCallIndirect, 0, table_index]) // --
.exportAs("main");
let m = new WebAssembly.Module(builder.toBuffer());
let table = new WebAssembly.Table({
element: "anyfunc",
initial: kTableSize,
maximum: kTableSize
});
let placeholder = new WebAssembly.Table({
element: "anyfunc",
initial: kTableSize,
maximum: kTableSize
});
let instance = new WebAssembly.Instance(m, {
q: {
base: 0, table: table, placeholder: placeholder,
js_div: js_div
}
});
assertEquals(13, instance.exports.main(4, 0));
})();