Skip to content

Commit cffdbe4

Browse files
authored
Merge pull request #392 from xtuc/refactor-validation
Refactor validation
2 parents ee05bce + cca0451 commit cffdbe4

File tree

11 files changed

+101
-78
lines changed

11 files changed

+101
-78
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
NODE_OPTS =
22

3-
TEST_TIMEOUT = 6000
3+
TEST_TIMEOUT = 10000
44

55
LERNA = ./node_modules/.bin/lerna
66
FLOWTYPED = ./node_modules/.bin/flow-typed

packages/repl/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const {
1212
} = require("webassemblyjs/lib/interpreter/kernel/memory");
1313
const { decode } = require("@webassemblyjs/wasm-parser");
1414
const t = require("@webassemblyjs/ast");
15-
const typeCheck = require("@webassemblyjs/validation").stack;
15+
const { getValidationErrors } = require("@webassemblyjs/validation");
1616
const denormalizeTypeReferences = require("@webassemblyjs/ast/lib/transform/denormalize-type-references")
1717
.transform;
1818

@@ -308,7 +308,7 @@ export function createRepl({ isVerbose, onAssert, onLog, onOk }) {
308308
if (enableTypeChecking === true) {
309309
denormalizeTypeReferences(moduleNode);
310310

311-
const typeErrors = typeCheck(t.program([moduleNode]));
311+
const typeErrors = getValidationErrors(t.program([moduleNode]));
312312

313313
if (typeErrors.length > 0) {
314314
const containsImmutableGlobalViolation = typeErrors.some(x =>

packages/validation/src/imports.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @flow
2+
3+
import { traverse } from "@webassemblyjs/ast";
4+
5+
/**
6+
* Determine if a sequence of instructions form a constant expression
7+
*
8+
* See https://webassembly.github.io/spec/core/multipage/valid/instructions.html#valid-constant
9+
*/
10+
export default function(
11+
ast: Program /*, moduleContext: Object */
12+
): Array<string> {
13+
const errors = [];
14+
15+
traverse(ast, {
16+
ModuleImport({ node }) {
17+
const { mutability } = node.descr;
18+
19+
if (mutability === "var") {
20+
errors.push("mutable globals cannot be imported");
21+
}
22+
}
23+
});
24+
25+
return errors;
26+
}

packages/validation/src/index.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// @flow
22

33
import importOrderValidate from "./import-order";
4+
import isConst from "./is-const";
45
import typeChecker from "./type-checker";
6+
import imports from "./imports";
7+
import { moduleContextFromModuleAST } from "@webassemblyjs/helper-module-context";
58

69
export default function validateAST(ast: Program) {
7-
const errors = [];
8-
9-
errors.push(...importOrderValidate(ast));
10-
errors.push(...typeChecker(ast));
10+
const errors = getValidationErrors(ast);
1111

1212
if (errors.length !== 0) {
1313
const errorMessage = "Validation errors:\n" + errors.join("\n");
@@ -16,7 +16,35 @@ export default function validateAST(ast: Program) {
1616
}
1717
}
1818

19-
export { isConst } from "./is-const";
19+
export function getValidationErrors(ast: Program): Array<string> {
20+
const errors = [];
21+
22+
let modules = [];
23+
24+
// $FlowIgnore
25+
if (ast.type === "Module") {
26+
modules = [ast];
27+
}
28+
29+
// $FlowIgnore
30+
if (ast.type === "Program") {
31+
modules = ast.body.filter(({ type }) => type === "Module");
32+
}
33+
34+
modules.forEach(m => {
35+
const moduleContext = moduleContextFromModuleAST(m);
36+
37+
// $FlowIgnore
38+
errors.push(...imports(ast, moduleContext));
39+
errors.push(...isConst(ast, moduleContext));
40+
errors.push(...importOrderValidate(ast));
41+
errors.push(...typeChecker(ast, moduleContext));
42+
});
43+
44+
return errors;
45+
}
46+
2047
export { getType, typeEq } from "./type-inference";
48+
export { isConst };
2149

2250
export const stack = typeChecker;
Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
11
// @flow
22

3+
import { traverse } from "@webassemblyjs/ast";
4+
35
/**
46
* Determine if a sequence of instructions form a constant expression
57
*
68
* See https://webassembly.github.io/spec/core/multipage/valid/instructions.html#valid-constant
7-
*
8-
* TODO(sven): get_global x should check the mutability of x, but we don't have
9-
* access to the program at this point.
109
*/
11-
export function isConst(instrs: Array<Instruction>): boolean {
12-
if (instrs.length === 0) {
13-
return false;
14-
}
15-
16-
return instrs.reduce((acc, instr) => {
17-
// Bailout
18-
if (acc === false) {
19-
return acc;
20-
}
21-
10+
export default function isConst(
11+
ast: Program,
12+
moduleContext: Object
13+
): Array<string> {
14+
function isConstInstruction(instr): boolean {
2215
if (instr.id === "const") {
2316
return true;
2417
}
2518

2619
if (instr.id === "get_global") {
27-
return true;
20+
const index = instr.args[0].value;
21+
return !moduleContext.isMutableGlobal(index);
2822
}
2923

3024
// FIXME(sven): this shoudln't be needed, we need to inject our end
@@ -34,5 +28,23 @@ export function isConst(instrs: Array<Instruction>): boolean {
3428
}
3529

3630
return false;
37-
}, true);
31+
}
32+
33+
const errors = [];
34+
35+
traverse(ast, {
36+
Global(path) {
37+
const isValid = path.node.init.reduce(
38+
(acc, instr) => acc && isConstInstruction(instr),
39+
true
40+
);
41+
if (!isValid) {
42+
errors.push(
43+
"constant expression required: initializer expression cannot reference mutable global"
44+
);
45+
}
46+
}
47+
});
48+
49+
return errors;
3850
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(module
2+
(global (mut i32) (i32.const 1))
3+
(global i32 (get_global 0))
4+
5+
(global i32 (i32.const 0))
6+
(global i32 (get_global 1))
7+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
constant expression required: initializer expression cannot reference mutable global

packages/validation/test/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe("validation", () => {
2222

2323
describe("wast", () => {
2424
const pre = f => {
25-
const errors = validations.stack(parse(f));
25+
const errors = validations.getValidationErrors(parse(f));
2626

2727
return errorsToString(errors);
2828
};
@@ -35,7 +35,7 @@ describe("validation", () => {
3535
const module = wabt.parseWat(suite, f);
3636
const { buffer } = module.toBinary({ write_debug_names: false });
3737

38-
const errors = validations.stack(decode(buffer));
38+
const errors = validations.getValidationErrors(decode(buffer));
3939

4040
return errorsToString(errors);
4141
};

packages/validation/test/is-const.js

Lines changed: 0 additions & 42 deletions
This file was deleted.

packages/webassemblyjs/src/interpreter/runtime/values/global.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow
22

3-
import { isConst, getType, typeEq } from "@webassemblyjs/validation";
3+
import { getType, typeEq } from "@webassemblyjs/validation";
44

55
const { evaluate } = require("../../partial-evaluation");
66
const { CompileError } = require("../../../errors");
@@ -12,10 +12,6 @@ export function createInstance(
1212
let value;
1313
const { valtype, mutability } = node.globalType;
1414

15-
if (node.init.length > 0 && isConst(node.init) === false) {
16-
throw new CompileError("constant expression required");
17-
}
18-
1915
// None or multiple constant expressions in the initializer seems not possible
2016
// TODO(sven): find a specification reference for that
2117
// FIXME(sven): +1 because of the implicit end, change the order of validations

0 commit comments

Comments
 (0)