Bug 1313024: Enable a few more spec tests; r=luke
authorBenjamin Bouvier <benj@benj.me>
Tue, 25 Oct 2016 19:29:40 +0200
changeset 320005 c9ecc2d1411406a81d4204f6fe16831245410002
parent 320004 ddb7b94d3a6ae8a8c415e61f4bdf34794f09ba77
child 320006 0418a48ed6f28cd74878531acddaf6478ec766b1
push id20749
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:21:21 +0000
treeherderfx-team@1b170b39ed6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1313024
milestone52.0a1
Bug 1313024: Enable a few more spec tests; r=luke MozReview-Commit-ID: 37OzreumhIw
js/src/asmjs/WasmModule.cpp
js/src/jit-test/tests/wasm/spec.js
js/src/jit-test/tests/wasm/spec/comments.wast.js
js/src/jit-test/tests/wasm/spec/linking.wast
js/src/jit-test/tests/wasm/spec/linking.wast.js
js/src/jit-test/tests/wasm/spec/loop.wast
js/src/jit-test/tests/wasm/spec/loop.wast.js
js/src/jit-test/tests/wasm/spec/memory.wast
js/src/jit-test/tests/wasm/spec/memory.wast.js
js/src/jit-test/tests/wasm/spec/names.wast.js
js/src/jit-test/tests/wasm/spec/resizing.wast.js
js/src/jit-test/tests/wasm/spec/start.wast.js
js/src/shell/js.cpp
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -710,17 +710,17 @@ CheckLimits(JSContext* cx, uint32_t decl
         return true;
     }
 
     if (actualLength < declaredMin || actualLength > declaredMax.valueOr(UINT32_MAX)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, kind);
         return false;
     }
 
-    if ((actualMax && (!declaredMax || *actualMax > *declaredMax)) || (!actualMax && declaredMax)) {
+    if ((actualMax && declaredMax && *actualMax > *declaredMax) || (!actualMax && declaredMax)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_MAX, kind);
         return false;
     }
 
     return true;
 }
 
 // asm.js module instantiation supplies its own buffer, but for wasm, create and
--- a/js/src/jit-test/tests/wasm/spec.js
+++ b/js/src/jit-test/tests/wasm/spec.js
@@ -44,153 +44,150 @@ Element.prototype.toString = function() 
     }
     return `(${this.list.map(x => x.toString()).join(" ")})`;
 };
 
 setJitCompilerOption('wasm.test-mode', 1);
 
 // Creates a tree of s-expressions. Ported from Binaryen's SExpressionParser.
 function parseSExpression(text) {
-    var input = 0;
-
-    var commentDepth = 0;
-    function skipBlockComment() {
-        while (true) {
-            if (text[input] === '(' && text[input + 1] === ';') {
-                input += 2;
-                commentDepth++;
-            } else if (text[input] === ';' && text[input + 1] === ')') {
-                input += 2;
-                commentDepth--;
-                if (!commentDepth) {
-                    return;
-                }
-            } else {
-                input++;
-            }
-        }
-    }
-
-    function parseInnerList() {
-        if (text[input] === ';') {
-            // Parse comment.
-            input++;
-            if (text[input] === ';') {
-                while (text[input] != '\n') input++;
-                return null;
-            }
-            assert(false, 'malformed comment');
-        }
-
-        if (text[input] === '(' && text[input + 1] === ';') {
-            skipBlockComment();
-            return null;
-        }
-
-        var start = input;
-        var ret = new Element();
-        while (true) {
-            var curr = parse();
-            if (!curr) {
-                ret.lineno = countLines(text, input);
-                return ret;
-            }
-            ret.list.push(curr);
-        }
-    }
+    var pos = 0;
 
     function isSpace(c) {
         switch (c) {
             case '\n':
             case ' ':
             case '\r':
             case '\t':
             case '\v':
             case '\f':
                 return true;
             default:
                 return false;
         }
     }
 
-    function skipWhitespace() {
+    function skip() {
         while (true) {
-            while (isSpace(text[input]))
-                input++;
+            let prevPos = pos;
+
+            if (pos + 2 < text.length) {
+
+                // Block comments.
+                if (text[pos] === '(' && text[pos + 1] === ';')
+                {
+                    pos += 2;
+                    let blockDepth = 1;
+                    while (pos + 2 < text.length) {
+                        if (text[pos] === '(' && text[pos + 1] === ';') {
+                            pos += 2;
+                            blockDepth++;
+                        } else if (text[pos] === ';' && text[pos + 1] === ')') {
+                            pos += 2;
+                            if (!--blockDepth)
+                                break;
+                        } else {
+                            pos++;
+                        }
+                    }
+                }
+
+                // Inline comments.
+                if (text[pos] === ';' && text[pos + 1] === ';') {
+                    pos += 2;
+                    while (text[pos] !== '\n')
+                        pos++;
+                }
+            }
 
-            if (text[input] === ';' && text[input + 1] === ';') {
-                while (text.length > input && text[input] != '\n') input++;
-            } else if (text[input] === '(' && text[input + 1] === ';') {
-                skipBlockComment();
-            } else {
-                return;
+            // Whitespaces.
+            while (isSpace(text[pos])) {
+                pos++;
             }
+
+            if (pos === prevPos)
+                break;
+        }
+    }
+
+    function parse() {
+        skip();
+
+        if (text.length === pos || text[pos] === ')')
+            return null;
+
+        if (text[pos] === '(') {
+            pos++;
+            var ret = parseInParens();
+            skip();
+            assert(text[pos] === ')', 'inner list ends with a )');
+            pos++;
+            return ret;
+        }
+
+        return parseString();
+    }
+
+    function parseInParens() {
+        skip();
+        var start = pos;
+        var ret = new Element();
+        while (true) {
+            var curr = parse();
+            if (!curr) {
+                ret.lineno = countLines(text, pos);
+                return ret;
+            }
+            ret.list.push(curr);
         }
     }
 
     function parseString() {
         var dollared = false;
         var quoted = false;
-        if (text[input] === '$') {
-            input++;
+        if (text[pos] === '$') {
+            pos++;
             dollared = true;
         }
 
-        var start = input;
-        if (text[input] === '"') {
+        var start = pos;
+        if (text[pos] === '"') {
             quoted = true;
             // Parse escaping \", but leave code escaped - we'll handle escaping in memory segments specifically.
-            input++;
+            pos++;
             var str = "";
             while (true) {
-                if (text[input] === '"') break;
-                if (text[input] === '\\') {
-                    str += text[input];
-                    str += text[input + 1];
-                    input += 2;
+                if (text[pos] === '"') break;
+                if (text[pos] === '\\') {
+                    str += text[pos];
+                    str += text[pos + 1];
+                    pos += 2;
                     continue;
                 }
-                str += text[input];
-                input++;
+                str += text[pos];
+                pos++;
             }
-            input++;
+            pos++;
             return new Element(str, dollared, quoted);
         }
 
-        while (text.length > input &&
-               !isSpace(text[input]) &&
-               text[input] != ')' &&
-               text[input] != '(') {
-            input++;
+        while (pos < text.length &&
+               !isSpace(text[pos]) &&
+               text[pos] != ';' &&
+               text[pos] != ')' &&
+               text[pos] != '(') {
+            pos++;
         }
 
-        return new Element(text.substring(start, input), dollared);
-    }
-
-    function parse() {
-        skipWhitespace();
-
-        if (text.length === input || text[input] === ')')
-            return null;
-
-        if (text[input] === '(') {
-            input++;
-            var ret = parseInnerList();
-            skipWhitespace();
-            assert(text[input] === ')', 'inner list ends with a )');
-            input++;
-            return ret;
-        }
-
-        return parseString();
+        return new Element(text.substring(start, pos), dollared);
     }
 
     var root = null;
     while (!root) { // Keep parsing until we pass an initial comment.
-        root = parseInnerList();
+        root = parseInParens();
     }
     return root;
 }
 
 var imports = {
     spectest: {
         print,
         global: 666,
@@ -299,16 +296,41 @@ function exec(e) {
         moduleCache.set('__last_module__', module);
         if (moduleName) {
             moduleCache.set(moduleName, module);
         }
 
         return;
     }
 
+    if (exprName === "register") {
+        // (register IMPORT_NAME MODULE_NAME?)
+        assert(e.list[1].quoted, "first arg to register is quoted");
+        let importName = e.list[1].str;
+
+        let moduleName = '__last_module__';
+        if (e.list.length > 2) {
+            moduleName = e.list[2].str;
+        }
+
+        if (!moduleCache.has(moduleName)) {
+            throw new Error("can't register an unknown module for imports");
+        }
+
+        let module = moduleCache.get(moduleName);
+
+        imports[importName] = {};
+
+        for (let [k, v] of Object.entries(module)) {
+            imports[importName][k] = v;
+        }
+
+        return;
+    }
+
     if (exprName === "invoke") {
         let [module, field, args] = getModuleAndField(e);
 
         let fn = null;
         if (typeof module[field] === "function") {
             fn = module[field];
         } else {
             throw new Error("Exported function not found: " + e);
@@ -380,17 +402,17 @@ function exec(e) {
         let moduleText = e.list[1].toString();
         let errMsg = e.list[2];
         if (errMsg) {
             assert(errMsg.quoted, "assert_invalid second argument must be a string");
             errMsg.quoted = false;
         }
         // assert_invalid tests both the decoder *and* the parser itself.
         try {
-            assertEq(WebAssembly.validate(wasmTextToBinary(moduleText)), false);
+            assertEq(WebAssembly.validate(wasmTextToBinary(moduleText)), false, "assert_invalid failed");
         } catch(e) {
             if (/wasm text error/.test(e.toString()))
                 return;
             throw e;
         }
         return;
     }
 
--- a/js/src/jit-test/tests/wasm/spec/comments.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/comments.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO: Error: Invalid UTF-8 in file
-quit();
 var importedArgs = ['comments.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/spec/linking.wast
+++ b/js/src/jit-test/tests/wasm/spec/linking.wast
@@ -138,17 +138,17 @@
 (assert_trap (invoke $N "M.call" (i32.const 0)) "uninitialized")
 (assert_return (invoke $N "call" (i32.const 0)) (i32.const 5))
 (assert_trap (invoke $N "call M.call" (i32.const 0)) "uninitialized")
 (assert_trap (invoke $O "call" (i32.const 0)) "uninitialized")
 
 (assert_trap (invoke $O "call" (i32.const 20)) "undefined")
 
 (assert_unlinkable
-  (module $Q
+  (module
     (func $host (import "spectest" "print"))
     (table (import "M" "tab") 10 anyfunc)
     (elem (i32.const 7) $own)
     (elem (i32.const 9) $host)
     (func $own (result i32) (i32.const 666))
   )
   "invalid use of host function"
 )
@@ -210,17 +210,17 @@
 (assert_return (invoke $P "grow" (i32.const 0)) (i32.const 3))
 (assert_return (invoke $P "grow" (i32.const 1)) (i32.const 3))
 (assert_return (invoke $P "grow" (i32.const 1)) (i32.const 4))
 (assert_return (invoke $P "grow" (i32.const 0)) (i32.const 5))
 (assert_return (invoke $P "grow" (i32.const 1)) (i32.const -1))
 (assert_return (invoke $P "grow" (i32.const 0)) (i32.const 5))
 
 (assert_unlinkable
-  (module $Q
+  (module
     (func $host (import "spectest" "print"))
     (memory (import "M" "mem") 1)
     (table 10 anyfunc)
     (data (i32.const 0) "abc")
     (elem (i32.const 9) $host)
     (func $own (result i32) (i32.const 666))
   )
   "invalid use of host function"
--- a/js/src/jit-test/tests/wasm/spec/linking.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/linking.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO: module linking tests
-quit();
 var importedArgs = ['linking.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/spec/loop.wast
+++ b/js/src/jit-test/tests/wasm/spec/loop.wast
@@ -275,28 +275,29 @@
 (assert_invalid
   (module (func $type-value-num-vs-num-after-break (result i32)
     (loop (br 1 (i32.const 1)) (f32.const 0))
   ))
   "type mismatch"
 )
 ;)
 
-(assert_invalid
-  (module (func $type-cont-last-void-vs-empty (result i32)
-    (loop (br 0 (nop)))
-  ))
-  "type mismatch"
-)
-(assert_invalid
-  (module (func $type-cont-last-num-vs-empty (result i32)
-    (loop (br 0 (i32.const 0)))
-  ))
-  "type mismatch"
-)
+;; TODO: bug in the test: https://github.com/WebAssembly/spec/issues/338
+;;(assert_invalid
+;;  (module (func $type-cont-last-void-vs-empty (result i32)
+;;    (loop (br 0 (nop)))
+;;  ))
+;;  "type mismatch"
+;;)
+;;(assert_invalid
+;;  (module (func $type-cont-last-num-vs-empty (result i32)
+;;    (loop (br 0 (i32.const 0)))
+;;  ))
+;;  "type mismatch"
+;;)
 
 ;; TODO(stack): move these elsewhere
 (module (func $type-cont-num-vs-void
   (loop (i32.const 0) (br 0))
 ))
 (module (func $type-cont-nested-num-vs-void
   (block (loop (i32.const 1) (loop (i32.const 1) (br 2)) (br 1)))
 ))
--- a/js/src/jit-test/tests/wasm/spec/loop.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/loop.wast.js
@@ -1,5 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO: bug in the test: https://github.com/WebAssembly/spec/issues/338
-// TODO: ion compile bug with loop return values
-quit();
 var importedArgs = ['loop.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/spec/memory.wast
+++ b/js/src/jit-test/tests/wasm/spec/memory.wast
@@ -1,13 +1,14 @@
 ;; Test memory section structure
 (module (memory 0 0))
 (module (memory 0 1))
 (module (memory 1 256))
-(module (memory 0 65536))
+;; Memory too big for testing under SM.
+;;(module (memory 0 65536))
 (module (memory 0 0) (data (i32.const 0)))
 (module (memory 0 0) (data (i32.const 0) ""))
 (module (memory 1 1) (data (i32.const 0) "a"))
 (module (memory 1 2) (data (i32.const 0) "a") (data (i32.const 65535) "b"))
 (module (memory 1 2)
   (data (i32.const 0) "a") (data (i32.const 1) "b") (data (i32.const 2) "c")
 )
 
@@ -42,31 +43,32 @@
 (assert_invalid
   (module (memory 0 0) (data (i32.const 0) "a"))
   "data segment does not fit"
 )
 (assert_invalid
   (module (memory 1 2) (data (i32.const 0) "a") (data (i32.const 98304) "b"))
   "data segment does not fit"
 )
-(assert_invalid
-  (module (memory 1 2) (data (i32.const 0) "abc") (data (i32.const 0) "def"))
-  "data segment not disjoint and ordered"
-)
-(assert_invalid
-  (module (memory 1 2) (data (i32.const 3) "ab") (data (i32.const 0) "de"))
-  "data segment not disjoint and ordered"
-)
-(assert_invalid
-  (module
-    (memory 1 2)
-    (data (i32.const 0) "a") (data (i32.const 2) "b") (data (i32.const 1) "c")
-  )
-  "data segment not disjoint and ordered"
-)
+;; TODO the spec interpreter still wants initializers to be disjoint and ordered.
+;;(assert_invalid
+;;  (module (memory 1 2) (data (i32.const 0) "abc") (data (i32.const 0) "def"))
+;;  "data segment not disjoint and ordered"
+;;)
+;;(assert_invalid
+;;  (module (memory 1 2) (data (i32.const 3) "ab") (data (i32.const 0) "de"))
+;;  "data segment not disjoint and ordered"
+;;)
+;;(assert_invalid
+;;  (module
+;;    (memory 1 2)
+;;    (data (i32.const 0) "a") (data (i32.const 2) "b") (data (i32.const 1) "c")
+;;  )
+;;  "data segment not disjoint and ordered"
+;;)
 (assert_invalid
   (module (memory 65537))
   "memory size must be at most 65536 pages (4GiB)"
 )
 (assert_invalid
   (module (memory 2147483648))
   "memory size must be at most 65536 pages (4GiB)"
 )
--- a/js/src/jit-test/tests/wasm/spec/memory.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/memory.wast.js
@@ -1,5 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO: maximum memory size too big
-// TODO: spec still requires initializers be disjoint and ordered
-quit();
 var importedArgs = ['memory.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/spec/names.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/names.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO: new import syntax
-quit();
 var importedArgs = ['names.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/spec/resizing.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/resizing.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO: OOMs some some test machines
-quit();
 var importedArgs = ['resizing.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/spec/start.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/start.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO: new import syntax
-quit();
 var importedArgs = ['start.wast']; load(scriptdir + '../spec.js');
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1842,17 +1842,17 @@ js::shell::FileAsString(JSContext* cx, J
             if (!pathname.encodeUtf8(cx, pathnameStr))
                 return nullptr;
             JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.ptr());
         }
         return nullptr;
     }
 
     UniqueTwoByteChars ucbuf(
-        JS::UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len), &len).get()
+        JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len), &len).get()
     );
     if (!ucbuf) {
         pathname.clear();
         if (!pathname.encodeUtf8(cx, pathnameStr))
             return nullptr;
         JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.ptr());
         return nullptr;
     }