From d321c0aa26e90dfeffa9be1a2b7805ec991a5703 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 24 Aug 2015 20:58:27 -0500 Subject: [PATCH 1/4] Add command to test invalid modules --- ml-proto/runtests.py | 2 +- ml-proto/src/Makefile | 2 +- ml-proto/src/lexer.mll | 1 + ml-proto/src/parser.mly | 3 ++- ml-proto/src/script.ml | 11 +++++++++++ ml-proto/src/script.mli | 1 + ml-proto/test/memory.wasm | 26 ++++++++++++++++++++++++++ ml-proto/travis/build-test.sh | 2 +- 8 files changed, 44 insertions(+), 4 deletions(-) diff --git a/ml-proto/runtests.py b/ml-proto/runtests.py index ba04b40114..e8e2198d74 100755 --- a/ml-proto/runtests.py +++ b/ml-proto/runtests.py @@ -51,7 +51,7 @@ def find_interpreter(path): def rebuild_interpreter(path): print("// building %s" % path) sys.stdout.flush() - exitCode = subprocess.call(["ocamlbuild", "-libs", "bigarray", "main.native"], cwd=os.path.abspath("src")) + exitCode = subprocess.call(["ocamlbuild", "-libs", "bigarray, str", "main.native"], cwd=os.path.abspath("src")) if (exitCode != 0): raise Exception("ocamlbuild failed with exit code %i" % exitCode) if not os.path.exists(path): diff --git a/ml-proto/src/Makefile b/ml-proto/src/Makefile index 53dce08b21..c16f723efc 100644 --- a/ml-proto/src/Makefile +++ b/ml-proto/src/Makefile @@ -12,7 +12,7 @@ MODULES = \ NOMLI = flags types values ast sexpr main PARSERS = parser LEXERS = lexer -LIBRARIES = bigarray +LIBRARIES = bigarray str SAMPLES = TEXTS = diff --git a/ml-proto/src/lexer.mll b/ml-proto/src/lexer.mll index 914f78dffb..dfe7c318ef 100644 --- a/ml-proto/src/lexer.mll +++ b/ml-proto/src/lexer.mll @@ -251,6 +251,7 @@ rule token = parse | "export" { EXPORT } | "table" { TABLE } + | "invalid" { INVALID } | "invoke" { INVOKE } | "asserteq" { ASSERTEQ } diff --git a/ml-proto/src/parser.mly b/ml-proto/src/parser.mly index 823231f92c..038ed63ece 100644 --- a/ml-proto/src/parser.mly +++ b/ml-proto/src/parser.mly @@ -98,7 +98,7 @@ let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels} %token GETLOCAL SETLOCAL GETGLOBAL SETGLOBAL GETMEMORY SETMEMORY %token CONST UNARY BINARY COMPARE CONVERT %token FUNC PARAM RESULT LOCAL MODULE MEMORY SEGMENT GLOBAL IMPORT EXPORT TABLE -%token INVOKE ASSERTEQ +%token INVALID INVOKE ASSERTEQ %token EOF %token INT @@ -308,6 +308,7 @@ modul : cmd : | modul { Define $1 @@ at() } + | LPAR INVALID modul TEXT RPAR { Invalid ($3, $4) @@ at() } | LPAR INVOKE TEXT expr_list RPAR { Invoke ($3, $4 (c0 ())) @@ at() } | LPAR ASSERTEQ LPAR INVOKE TEXT expr_list RPAR expr_list RPAR diff --git a/ml-proto/src/script.ml b/ml-proto/src/script.ml index 65a02a3f38..5982a601df 100644 --- a/ml-proto/src/script.ml +++ b/ml-proto/src/script.ml @@ -9,6 +9,7 @@ open Source type command = command' phrase and command' = | Define of Ast.modul + | Invalid of Ast.modul * string | Invoke of string * Ast.expr list | AssertEqInvoke of string * Ast.expr list * Ast.expr list @@ -33,6 +34,15 @@ let run_command cmd = trace "Initializing..."; current_module := Some (Eval.init m) + | Invalid (m, re) -> + trace "Checking invalid..."; + (match try Check.check_module m; None with Error.Error (at, s) -> Some s with + | None -> + Error.error cmd.at "expected invalid module" + | Some s -> + if not (Str.string_match (Str.regexp re) s 0) then + Error.error cmd.at ("validation failure \"" ^ s ^ "\" does not match: " ^ re)) + | Invoke (name, es) -> trace "Invoking..."; let m = match !current_module with @@ -65,6 +75,7 @@ let dry_command cmd = | Define m -> Check.check_module m; if !Flags.print_sig then Print.print_module_sig m + | Invalid (m, re) -> () | Invoke _ -> () | AssertEqInvoke _ -> () diff --git a/ml-proto/src/script.mli b/ml-proto/src/script.mli index 339e07ac85..ad90a649f2 100644 --- a/ml-proto/src/script.mli +++ b/ml-proto/src/script.mli @@ -5,6 +5,7 @@ type command = command' Source.phrase and command' = | Define of Ast.modul + | Invalid of Ast.modul * string | Invoke of string * Ast.expr list | AssertEqInvoke of string * Ast.expr list * Ast.expr list diff --git a/ml-proto/test/memory.wasm b/ml-proto/test/memory.wasm index fb9a7f5262..73954c75f1 100644 --- a/ml-proto/test/memory.wasm +++ b/ml-proto/test/memory.wasm @@ -1,5 +1,31 @@ ;; (c) 2015 Andreas Rossberg +(module (memory 0 0)) +(module (memory 0 1)) +(module (memory 4096 16777216)) +(module (memory 0 0 (segment 0 ""))) +(module (memory 1 1 (segment 0 "a"))) +(module (memory 100 1000 (segment 0 "a") (segment 99 "b"))) +(module (memory 100 1000 (segment 0 "a") (segment 1 "b") (segment 2 "c"))) +(invalid + (module (memory 1 0)) + "initial memory size must be less than maximum") +(invalid + (module (memory 0 0 (segment 0 "a"))) + "data segment does not fit memory") +(invalid + (module (memory 100 1000 (segment 0 "a") (segment 500 "b"))) + "data segment does not fit memory") +(invalid + (module (memory 100 1000 (segment 0 "abc") (segment 0 "def"))) + "data segment not disjoint and ordered") +(invalid + (module (memory 100 1000 (segment 3 "ab") (segment 0 "de"))) + "data segment not disjoint and ordered") +(invalid + (module (memory 100 1000 (segment 0 "a") (segment 2 "b") (segment 1 "c"))) + "data segment not disjoint and ordered") + (module (memory 1024 (segment 0 "ABC\a7D") (segment 20 "WASM")) diff --git a/ml-proto/travis/build-test.sh b/ml-proto/travis/build-test.sh index 4156f1c83b..3afdc5cfaf 100755 --- a/ml-proto/travis/build-test.sh +++ b/ml-proto/travis/build-test.sh @@ -16,7 +16,7 @@ rm -f lexer.ml rm -f parser.ml rm -f parser.mli -ocamlbuild -libs bigarray main.native +ocamlbuild -libs bigarray str main.native make cd .. From 28b728355b2d45dc12323f99f8d21ed8305086ea Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 24 Aug 2015 21:13:13 -0500 Subject: [PATCH 2/4] Add export tests and catch duplicate exports in checker --- ml-proto/src/check.ml | 13 +++++++++---- ml-proto/test/basic.wasm | 9 --------- ml-proto/test/exports.wasm | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 13 deletions(-) delete mode 100644 ml-proto/test/basic.wasm create mode 100644 ml-proto/test/exports.wasm diff --git a/ml-proto/src/check.ml b/ml-proto/src/check.ml index 616ff4987d..666137532e 100644 --- a/ml-proto/src/check.ml +++ b/ml-proto/src/check.ml @@ -271,9 +271,14 @@ let check_table c tab = List.iter (fun xI -> check_func_type (func c xI) s xI.at) xs; {c with tables = c.tables @ [s]} -let check_export c ex = - let {name = _; func = x} = ex.it in - ignore (func c x) +module NameSet = Set.Make(String) + +let check_export c set ex = + let {name; func = x} = ex.it in + ignore (func c x); + require (not (NameSet.mem name set)) ex.at + "duplicate export name"; + NameSet.add name set let check_segment size prev_end seg = let seg_end = seg.it.Memory.addr + String.length seg.it.Memory.data in @@ -295,4 +300,4 @@ let check_module m = globals = List.map it globals} in let c' = List.fold_left check_table c tables in List.iter (check_func c') funcs; - List.iter (check_export c') exports + ignore (List.fold_left (check_export c') NameSet.empty exports) diff --git a/ml-proto/test/basic.wasm b/ml-proto/test/basic.wasm deleted file mode 100644 index 180b9ec22b..0000000000 --- a/ml-proto/test/basic.wasm +++ /dev/null @@ -1,9 +0,0 @@ -(module - (func $f (param $n i32) (result i32) - (return (add.i32 (getlocal $n) (const.i32 1))) - ) - - (export "e" $f) -) - -(asserteq (invoke "e" (const.i32 42)) (const.i32 43)) diff --git a/ml-proto/test/exports.wasm b/ml-proto/test/exports.wasm new file mode 100644 index 0000000000..620fb01008 --- /dev/null +++ b/ml-proto/test/exports.wasm @@ -0,0 +1,22 @@ +(module (func (const.i32 1)) (export "a" 0)) +(module (func (const.i32 1)) (export "a" 0) (export "b" 0)) +(module (func (const.i32 1)) (func (const.i32 2)) (export "a" 0) (export "b" 1)) +(invalid + (module (func (const.i32 1)) (export "a" 1)) + "unknown function 1") +(invalid + (module (func (const.i32 1)) (func (const.i32 2)) (export "a" 0) (export "a" 1)) + "duplicate export name") +(invalid + (module (func (const.i32 1)) (export "a" 0) (export "a" 0)) + "duplicate export name") + +(module + (func $f (param $n i32) (result i32) + (return (add.i32 (getlocal $n) (const.i32 1))) + ) + + (export "e" $f) +) + +(asserteq (invoke "e" (const.i32 42)) (const.i32 43)) From c8af2a5761ad99e2c289da993a83fa7a1b4ee031 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 25 Aug 2015 08:45:53 -0500 Subject: [PATCH 3/4] Address comments --- ml-proto/README.md | 1 + ml-proto/src/lexer.mll | 2 +- ml-proto/src/parser.mly | 6 +++--- ml-proto/src/script.ml | 17 +++++++++-------- ml-proto/src/script.mli | 4 ++-- ml-proto/test/exports.wasm | 6 +++--- ml-proto/test/memory.wasm | 12 ++++++------ 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/ml-proto/README.md b/ml-proto/README.md index 4aa5fd1514..53186d53ee 100644 --- a/ml-proto/README.md +++ b/ml-proto/README.md @@ -233,6 +233,7 @@ cmd: ;; define, validate, and initialize module ( invoke * ) ;; invoke export and print result ( asserteq (invoke * ) * ) ;; assert expected results of invocation + ( assertinvalid ) ;; assert invalid module with given failure string ``` Invocation is only possible after a module has been defined. diff --git a/ml-proto/src/lexer.mll b/ml-proto/src/lexer.mll index dfe7c318ef..accdf425be 100644 --- a/ml-proto/src/lexer.mll +++ b/ml-proto/src/lexer.mll @@ -251,7 +251,7 @@ rule token = parse | "export" { EXPORT } | "table" { TABLE } - | "invalid" { INVALID } + | "assertinvalid" { ASSERTINVALID } | "invoke" { INVOKE } | "asserteq" { ASSERTEQ } diff --git a/ml-proto/src/parser.mly b/ml-proto/src/parser.mly index 038ed63ece..2b6e5f5db4 100644 --- a/ml-proto/src/parser.mly +++ b/ml-proto/src/parser.mly @@ -98,7 +98,7 @@ let anon_label c = {c with labels = VarMap.map ((+) 1) c.labels} %token GETLOCAL SETLOCAL GETGLOBAL SETGLOBAL GETMEMORY SETMEMORY %token CONST UNARY BINARY COMPARE CONVERT %token FUNC PARAM RESULT LOCAL MODULE MEMORY SEGMENT GLOBAL IMPORT EXPORT TABLE -%token INVALID INVOKE ASSERTEQ +%token ASSERTINVALID INVOKE ASSERTEQ %token EOF %token INT @@ -308,11 +308,11 @@ modul : cmd : | modul { Define $1 @@ at() } - | LPAR INVALID modul TEXT RPAR { Invalid ($3, $4) @@ at() } + | LPAR ASSERTINVALID modul TEXT RPAR { AssertInvalid ($3, $4) @@ at() } | LPAR INVOKE TEXT expr_list RPAR { Invoke ($3, $4 (c0 ())) @@ at() } | LPAR ASSERTEQ LPAR INVOKE TEXT expr_list RPAR expr_list RPAR - { AssertEqInvoke ($5, $6 (c0 ()), $8 (c0 ())) @@ at() } + { AssertEq ($5, $6 (c0 ()), $8 (c0 ())) @@ at() } ; cmd_list : | /* empty */ { [] } diff --git a/ml-proto/src/script.ml b/ml-proto/src/script.ml index 5982a601df..92bcc602f7 100644 --- a/ml-proto/src/script.ml +++ b/ml-proto/src/script.ml @@ -9,9 +9,9 @@ open Source type command = command' phrase and command' = | Define of Ast.modul - | Invalid of Ast.modul * string + | AssertInvalid of Ast.modul * string | Invoke of string * Ast.expr list - | AssertEqInvoke of string * Ast.expr list * Ast.expr list + | AssertEq of string * Ast.expr list * Ast.expr list type script = command list @@ -34,14 +34,15 @@ let run_command cmd = trace "Initializing..."; current_module := Some (Eval.init m) - | Invalid (m, re) -> + | AssertInvalid (m, re) -> trace "Checking invalid..."; - (match try Check.check_module m; None with Error.Error (at, s) -> Some s with + (match try Check.check_module m; None with Error.Error (_, s) -> Some s with | None -> Error.error cmd.at "expected invalid module" | Some s -> if not (Str.string_match (Str.regexp re) s 0) then - Error.error cmd.at ("validation failure \"" ^ s ^ "\" does not match: " ^ re)) + Error.error cmd.at + ("validation failure \"" ^ s ^ "\" does not match: \"" ^ re ^ "\"")) | Invoke (name, es) -> trace "Invoking..."; @@ -53,7 +54,7 @@ let run_command cmd = let vs' = Eval.invoke m name vs in if vs' <> [] then Print.print_values vs' - | AssertEqInvoke (name, arg_es, expect_es) -> + | AssertEq (name, arg_es, expect_es) -> trace "Assert invoking..."; let m = match !current_module with | Some m -> m @@ -75,9 +76,9 @@ let dry_command cmd = | Define m -> Check.check_module m; if !Flags.print_sig then Print.print_module_sig m - | Invalid (m, re) -> () + | AssertInvalid _ -> () | Invoke _ -> () - | AssertEqInvoke _ -> () + | AssertEq _ -> () let run script = List.iter (if !Flags.dry then dry_command else run_command) script diff --git a/ml-proto/src/script.mli b/ml-proto/src/script.mli index ad90a649f2..e4ada117f7 100644 --- a/ml-proto/src/script.mli +++ b/ml-proto/src/script.mli @@ -5,9 +5,9 @@ type command = command' Source.phrase and command' = | Define of Ast.modul - | Invalid of Ast.modul * string + | AssertInvalid of Ast.modul * string | Invoke of string * Ast.expr list - | AssertEqInvoke of string * Ast.expr list * Ast.expr list + | AssertEq of string * Ast.expr list * Ast.expr list type script = command list diff --git a/ml-proto/test/exports.wasm b/ml-proto/test/exports.wasm index 620fb01008..c3eac40933 100644 --- a/ml-proto/test/exports.wasm +++ b/ml-proto/test/exports.wasm @@ -1,13 +1,13 @@ (module (func (const.i32 1)) (export "a" 0)) (module (func (const.i32 1)) (export "a" 0) (export "b" 0)) (module (func (const.i32 1)) (func (const.i32 2)) (export "a" 0) (export "b" 1)) -(invalid +(assertinvalid (module (func (const.i32 1)) (export "a" 1)) "unknown function 1") -(invalid +(assertinvalid (module (func (const.i32 1)) (func (const.i32 2)) (export "a" 0) (export "a" 1)) "duplicate export name") -(invalid +(assertinvalid (module (func (const.i32 1)) (export "a" 0) (export "a" 0)) "duplicate export name") diff --git a/ml-proto/test/memory.wasm b/ml-proto/test/memory.wasm index 73954c75f1..4c07f79b3b 100644 --- a/ml-proto/test/memory.wasm +++ b/ml-proto/test/memory.wasm @@ -7,22 +7,22 @@ (module (memory 1 1 (segment 0 "a"))) (module (memory 100 1000 (segment 0 "a") (segment 99 "b"))) (module (memory 100 1000 (segment 0 "a") (segment 1 "b") (segment 2 "c"))) -(invalid +(assertinvalid (module (memory 1 0)) "initial memory size must be less than maximum") -(invalid +(assertinvalid (module (memory 0 0 (segment 0 "a"))) "data segment does not fit memory") -(invalid +(assertinvalid (module (memory 100 1000 (segment 0 "a") (segment 500 "b"))) "data segment does not fit memory") -(invalid +(assertinvalid (module (memory 100 1000 (segment 0 "abc") (segment 0 "def"))) "data segment not disjoint and ordered") -(invalid +(assertinvalid (module (memory 100 1000 (segment 3 "ab") (segment 0 "de"))) "data segment not disjoint and ordered") -(invalid +(assertinvalid (module (memory 100 1000 (segment 0 "a") (segment 2 "b") (segment 1 "c"))) "data segment not disjoint and ordered") From 2039b2ef87f72275750c0d6689508b3408fea07a Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 25 Aug 2015 11:38:07 -0500 Subject: [PATCH 4/4] Fix travis/build-test.sh --- ml-proto/travis/build-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml-proto/travis/build-test.sh b/ml-proto/travis/build-test.sh index 3afdc5cfaf..fddb9d4077 100755 --- a/ml-proto/travis/build-test.sh +++ b/ml-proto/travis/build-test.sh @@ -16,7 +16,7 @@ rm -f lexer.ml rm -f parser.ml rm -f parser.mli -ocamlbuild -libs bigarray str main.native +ocamlbuild -libs "bigarray, str" main.native make cd ..