Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/helper-module-context/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export class ModuleContext {

defineGlobal(global /*: Global*/) {
const type = global.globalType.valtype;
const mutability = global.mutability;
const mutability = global.globalType.mutability;

this.globals.push({ type, mutability });

Expand Down
20 changes: 15 additions & 5 deletions packages/validation/src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// @flow

import importOrderValidate from "./import-order";
import isConst from "./is-const";
import typeChecker from "./type-checker";
import { moduleContextFromModuleAST } from "@webassemblyjs/helper-module-context";

export default function validateAST(ast: Program) {
const errors = [];

errors.push(...importOrderValidate(ast));
errors.push(...typeChecker(ast));
const errors = getValidationErrors(ast);

if (errors.length !== 0) {
const errorMessage = "Validation errors:\n" + errors.join("\n");
Expand All @@ -16,7 +15,18 @@ export default function validateAST(ast: Program) {
}
}

export { isConst } from "./is-const";
export function getValidationErrors(ast: Program): Array<string> {
const errors = [];
const moduleContext = moduleContextFromModuleAST(ast.body[0]);

errors.push(...isConst(ast, moduleContext));
errors.push(...importOrderValidate(ast));
errors.push(...typeChecker(ast, moduleContext));

return errors;
}

export { getType, typeEq } from "./type-inference";
export { isConst };

export const stack = typeChecker;
39 changes: 26 additions & 13 deletions packages/validation/src/is-const.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// @flow

import { traverse } from "@webassemblyjs/ast";

/**
* Determine if a sequence of instructions form a constant expression
*
Expand All @@ -8,23 +10,18 @@
* TODO(sven): get_global x should check the mutability of x, but we don't have
* access to the program at this point.
*/
export function isConst(instrs: Array<Instruction>): boolean {
if (instrs.length === 0) {
return false;
}

return instrs.reduce((acc, instr) => {
// Bailout
if (acc === false) {
return acc;
}

export default function isConst(
ast: Program,
moduleContext: Object
): Array<string> {
function isConstInstruction(instr): boolean {
if (instr.id === "const") {
return true;
}

if (instr.id === "get_global") {
return true;
const index = instr.args[0].value;
return !moduleContext.isMutableGlobal(index);
}

// FIXME(sven): this shoudln't be needed, we need to inject our end
Expand All @@ -34,5 +31,21 @@ export function isConst(instrs: Array<Instruction>): boolean {
}

return false;
}, true);
}

const errors = [];

traverse(ast, {
Global(path) {
const isValid = path.node.init.reduce(
(acc, instr) => acc && isConstInstruction(instr),
true
);
if (!isValid) {
errors.push("initializer expression cannot reference mutable global");
}
}
});

return errors;
}
6 changes: 1 addition & 5 deletions packages/validation/src/type-checker.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { traverse, isInstruction } from "@webassemblyjs/ast";

import { moduleContextFromModuleAST } from "@webassemblyjs/helper-module-context";
import getType from "./type-checker/get-type.js";
import { ANY, POLYMORPHIC } from "./type-checker/types.js";

Expand Down Expand Up @@ -30,14 +29,11 @@ function checkTypes(a, b) {
}
}

export default function validate(ast) {
export default function validate(ast, moduleContext) {
if (!ast.body || !ast.body[0] || !ast.body[0].fields) {
return [];
}

// Module context
const moduleContext = moduleContextFromModuleAST(ast.body[0]);

errors = [];

// Simulate stack types throughout all function bodies
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(module
(global (mut i32) (i32.const 1))
(global i32 (get_global 0))

(global i32 (i32.const 0))
(global i32 (get_global 1))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
initializer expression cannot reference mutable global
4 changes: 2 additions & 2 deletions packages/validation/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe("validation", () => {

describe("wast", () => {
const pre = f => {
const errors = validations.stack(parse(f));
const errors = validations.getValidationErrors(parse(f));

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

const errors = validations.stack(decode(buffer));
const errors = validations.getValidationErrors(decode(buffer));

return errorsToString(errors);
};
Expand Down
42 changes: 0 additions & 42 deletions packages/validation/test/is-const.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import { isConst, getType, typeEq } from "@webassemblyjs/validation";
import { getType, typeEq } from "@webassemblyjs/validation";

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

if (node.init.length > 0 && isConst(node.init) === false) {
throw new CompileError("constant expression required");
}

// None or multiple constant expressions in the initializer seems not possible
// TODO(sven): find a specification reference for that
// FIXME(sven): +1 because of the implicit end, change the order of validations
Expand Down