Bug 1313024: Allow table/memory references in wast and simplify AstRef; r=luke
authorBenjamin Bouvier <benj@benj.me>
Tue, 25 Oct 2016 17:02:52 +0200
changeset 346644 ef0ab8f78fe9911e3b0dfcf30fbc315f95b4d665
parent 346643 4963d6702c2c29aac5b1597cc1db7ca707141433
child 346645 58c6affba442284f0ca3b02f14b1f10ea1907109
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1313024
milestone52.0a1
Bug 1313024: Allow table/memory references in wast and simplify AstRef; r=luke MozReview-Commit-ID: H9fWARnb0je
js/src/asmjs/WasmAST.h
js/src/asmjs/WasmBinaryToAST.cpp
js/src/asmjs/WasmTextToBinary.cpp
js/src/jit-test/tests/wasm/spec/exports.wast.js
js/src/jit-test/tests/wasm/text.js
--- a/js/src/asmjs/WasmAST.h
+++ b/js/src/asmjs/WasmAST.h
@@ -72,25 +72,23 @@ class AstName
 };
 
 class AstRef
 {
     AstName name_;
     uint32_t index_;
 
   public:
-    AstRef()
-      : index_(AstNoIndex)
-    {
+    AstRef() : index_(AstNoIndex) {
         MOZ_ASSERT(isInvalid());
     }
-    AstRef(AstName name, uint32_t index)
-      : name_(name), index_(index)
-    {
-        MOZ_ASSERT(name.empty() ^ (index == AstNoIndex));
+    explicit AstRef(AstName name) : name_(name), index_(AstNoIndex) {
+        MOZ_ASSERT(!isInvalid());
+    }
+    explicit AstRef(uint32_t index) : index_(index) {
         MOZ_ASSERT(!isInvalid());
     }
     bool isInvalid() const {
         return name_.empty() && index_ == AstNoIndex;
     }
     AstName name() const {
         return name_;
     }
@@ -744,21 +742,23 @@ class AstStartFunc : public AstNode
 
     AstRef& func() {
         return func_;
     }
 };
 
 struct AstResizable
 {
+    AstName name;
     Limits limits;
     bool imported;
 
-    AstResizable(Limits limits, bool imported)
-      : limits(limits),
+    AstResizable(Limits limits, bool imported, AstName name = AstName())
+      : name(name),
+        limits(limits),
         imported(imported)
     {}
 };
 
 class AstModule : public AstNode
 {
   public:
     typedef AstVector<AstFunc*> FuncVector;
@@ -798,27 +798,27 @@ class AstModule : public AstNode
         funcs_(lifo),
         dataSegments_(lifo),
         elemSegments_(lifo),
         globals_(lifo)
     {}
     bool init() {
         return sigMap_.init();
     }
-    bool addMemory(Limits memory) {
-        return memories_.append(AstResizable(memory, false));
+    bool addMemory(AstName name, Limits memory) {
+        return memories_.append(AstResizable(memory, false, name));
     }
     bool hasMemory() const {
         return !!memories_.length();
     }
     const AstResizableVector& memories() const {
         return memories_;
     }
-    bool addTable(Limits table) {
-        return tables_.append(AstResizable(table, false));
+    bool addTable(AstName name, Limits table) {
+        return tables_.append(AstResizable(table, false, name));
     }
     bool hasTable() const {
         return !!tables_.length();
     }
     const AstResizableVector& tables() const {
         return tables_;
     }
     bool append(AstDataSegment* seg) {
--- a/js/src/asmjs/WasmBinaryToAST.cpp
+++ b/js/src/asmjs/WasmBinaryToAST.cpp
@@ -246,26 +246,26 @@ AstDecodeGenerateName(AstDecodeContext& 
 }
 
 static bool
 AstDecodeGenerateRef(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstRef* ref)
 {
     MOZ_ASSERT(index != AstNoIndex);
 
     if (!c.generateNames) {
-        *ref = AstRef(AstName(), index);
+        *ref = AstRef(index);
         return true;
     }
 
     AstName name;
     if (!AstDecodeGenerateName(c, prefix, index, &name))
         return false;
     MOZ_ASSERT(!name.empty());
 
-    *ref = AstRef(name, AstNoIndex);
+    *ref = AstRef(name);
     ref->setIndex(index);
     return true;
 }
 
 static bool
 AstDecodeCallArgs(AstDecodeContext& c, const AstSig& sig, AstExprVector* funcArgs)
 {
     MOZ_ASSERT(c.iter().inReachableCode());
@@ -329,17 +329,17 @@ AstDecodeCall(AstDecodeContext& c)
     if (!c.iter().inReachableCode())
         return true;
 
     uint32_t sigIndex;
     AstRef funcRef;
     if (calleeIndex < c.module().funcImportNames().length()) {
         AstImport* import = c.module().imports()[calleeIndex];
         sigIndex = import->funcSig().index();
-        funcRef = AstRef(import->name(), AstNoIndex);
+        funcRef = AstRef(import->name());
     } else {
         uint32_t funcDefIndex = calleeIndex - c.module().funcImportNames().length();
         if (funcDefIndex >= c.funcSigs().length())
             return c.iter().fail("callee index out of range");
 
         sigIndex = c.funcSigs()[funcDefIndex];
 
         if (!AstDecodeGenerateRef(c, AstName(u"func"), calleeIndex, &funcRef))
@@ -411,26 +411,26 @@ AstDecodeCallIndirect(AstDecodeContext& 
     return true;
 }
 
 static bool
 AstDecodeGetBlockRef(AstDecodeContext& c, uint32_t depth, AstRef* ref)
 {
     if (!c.generateNames || depth >= c.blockLabels().length()) {
         // Also ignoring if it's a function body label.
-        *ref = AstRef(AstName(), depth);
+        *ref = AstRef(depth);
         return true;
     }
 
     uint32_t index = c.blockLabels().length() - depth - 1;
     if (c.blockLabels()[index].empty()) {
         if (!AstDecodeGenerateName(c, AstName(u"label"), c.nextLabelIndex(), &c.blockLabels()[index]))
             return false;
     }
-    *ref = AstRef(c.blockLabels()[index], AstNoIndex);
+    *ref = AstRef(c.blockLabels()[index]);
     ref->setIndex(depth);
     return true;
 }
 
 static bool
 AstDecodeBrTable(AstDecodeContext& c)
 {
     uint32_t tableLength;
@@ -1571,17 +1571,21 @@ AstDecodeTableSection(AstDecodeContext& 
         return false;
 
     if (table.initial > MaxTableElems)
         return c.d.fail("too many table elements");
 
     if (c.module().hasTable())
         return c.d.fail("already have a table");
 
-    if (!c.module().addTable(table))
+    AstName name;
+    if (!AstDecodeGenerateName(c, AstName(u"table"), c.module().tables().length(), &name))
+        return false;
+
+    if (!c.module().addTable(name, table))
         return false;
 
     if (!c.d.finishSection(sectionStart, sectionSize, "table"))
         return false;
 
     return true;
 }
 
@@ -1745,17 +1749,20 @@ static bool
 AstDecodeMemorySection(AstDecodeContext& c)
 {
     bool present;
     Limits memory;
     if (!DecodeMemorySection(c.d, c.module().hasMemory(), &memory, &present))
         return false;
 
     if (present) {
-        if (!c.module().addMemory(memory))
+        AstName name;
+        if (!AstDecodeGenerateName(c, AstName(u"memory"), c.module().memories().length(), &name))
+            return false;
+        if (!c.module().addMemory(name, memory))
             return false;
     }
 
     return true;
 }
 
 static AstExpr*
 ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr)
@@ -1847,18 +1854,17 @@ AstDecodeExport(AstDecodeContext& c, Ast
     uint32_t kindValue;
     if (!c.d.readVarU32(&kindValue))
         return c.d.fail("expected export kind");
 
     uint32_t index;
     if (!c.d.readVarU32(&index))
         return c.d.fail("expected export internal index");
 
-    *export_ = new(c.lifo) AstExport(fieldName, DefinitionKind(kindValue),
-                                     AstRef(AstName(), index));
+    *export_ = new(c.lifo) AstExport(fieldName, DefinitionKind(kindValue), AstRef(index));
     if (!*export_)
         return false;
 
     return true;
 }
 
 static bool
 AstDecodeExportSection(AstDecodeContext& c)
@@ -2085,17 +2091,17 @@ AstDecodeElemSection(AstDecodeContext &c
         if (!elems.resize(count))
             return false;
 
         for (uint32_t i = 0; i < count; i++) {
             uint32_t index;
             if (!c.d.readVarU32(&index))
                 return c.d.fail("expected element index");
 
-            elems[i] = AstRef(AstName(), index);
+            elems[i] = AstRef(index);
         }
 
         AstElemSegment* segment = new(c.lifo) AstElemSegment(offset, Move(elems));
         if (!segment || !c.module().append(segment))
             return false;
     }
 
     if (!c.d.finishSection(sectionStart, sectionSize, "elem"))
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -634,20 +634,20 @@ class WasmTokenStream
     bool match(WasmToken::Kind expect, UniqueChars* error) {
         WasmToken token;
         return match(expect, &token, error);
     }
     bool matchRef(AstRef* ref, UniqueChars* error) {
         WasmToken token = get();
         switch (token.kind()) {
           case WasmToken::Name:
-            *ref = AstRef(token.name(), AstNoIndex);
+            *ref = AstRef(token.name());
             break;
           case WasmToken::Index:
-            *ref = AstRef(AstName(), token.index());
+            *ref = AstRef(token.index());
             break;
           default:
             generateError(token, error);
             return false;
         }
         return true;
     }
 };
@@ -2503,29 +2503,23 @@ ParseLocalOrParam(WasmParseContext& c, A
 static bool
 ParseInlineImport(WasmParseContext& c, InlineImport* import)
 {
     return c.ts.match(WasmToken::Text, &import->module, c.error) &&
            c.ts.match(WasmToken::Text, &import->field, c.error);
 }
 
 static bool
-ParseInlineExport(WasmParseContext& c, DefinitionKind kind, AstModule* module,
-                  AstRef ref = AstRef())
+ParseInlineExport(WasmParseContext& c, DefinitionKind kind, AstModule* module, AstRef ref)
 {
     WasmToken name;
     if (!c.ts.match(WasmToken::Text, &name, c.error))
         return false;
 
-    AstExport* exp = nullptr;
-    if (!ref.isInvalid())
-        exp = new(c.lifo) AstExport(name.text(), kind, ref);
-    else
-        exp = new(c.lifo) AstExport(name.text(), kind);
-
+    AstExport* exp = new(c.lifo) AstExport(name.text(), kind, ref);
     return exp && module->append(exp);
 }
 
 static bool
 MaybeParseTypeUse(WasmParseContext& c, AstRef* sig)
 {
     WasmToken openParen;
     if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
@@ -2618,18 +2612,18 @@ ParseFunc(WasmParseContext& c, AstModule
                 return false;
 
             auto* imp = new(c.lifo) AstImport(funcName, names.module.text(), names.field.text(), sig);
             return imp && module->append(imp);
         }
 
         if (c.ts.getIf(WasmToken::Export)) {
             AstRef ref = funcName.empty()
-                         ? AstRef(AstName(), module->funcImportNames().length() + module->funcs().length())
-                         : AstRef(funcName, AstNoIndex);
+                         ? AstRef(module->funcImportNames().length() + module->funcs().length())
+                         : AstRef(funcName);
             if (!ParseInlineExport(c, DefinitionKind::Function, module, ref))
                 return false;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return false;
         } else {
             c.ts.unget(openParen);
         }
     }
@@ -2787,17 +2781,18 @@ ParseMemory(WasmParseContext& c, WasmTok
                 return false;
 
             auto* imp = new(c.lifo) AstImport(name, names.module.text(), names.field.text(),
                                               DefinitionKind::Memory, memory);
             return imp && module->append(imp);
         }
 
         if (c.ts.getIf(WasmToken::Export)) {
-            if (!ParseInlineExport(c, DefinitionKind::Memory, module))
+            AstRef ref = name.empty() ? AstRef(module->memories().length()) : AstRef(name);
+            if (!ParseInlineExport(c, DefinitionKind::Memory, module, ref))
                 return false;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return false;
         } else {
             c.ts.unget(openParen);
         }
     }
 
@@ -2817,30 +2812,30 @@ ParseMemory(WasmParseContext& c, WasmTok
                 return false;
 
             pages = AlignBytes<size_t>(segment->text().length(), PageSize) / PageSize;
             if (pages != uint32_t(pages))
                 return false;
         }
 
         Limits memory = { uint32_t(pages), Some(uint32_t(pages)) };
-        if (!module->addMemory(memory))
+        if (!module->addMemory(name, memory))
             return false;
 
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
 
         return true;
     }
 
     Limits memory;
     if (!ParseLimits(c, &memory))
         return false;
 
-    return module->addMemory(memory);
+    return module->addMemory(name, memory);
 }
 
 static bool
 ParseStartFunc(WasmParseContext& c, WasmToken token, AstModule* module)
 {
     AstRef func;
     if (!c.ts.matchRef(&func, c.error))
         return false;
@@ -2982,76 +2977,66 @@ ParseExport(WasmParseContext& c)
 {
     WasmToken name;
     if (!c.ts.match(WasmToken::Text, &name, c.error))
         return nullptr;
 
     WasmToken exportee = c.ts.get();
     switch (exportee.kind()) {
       case WasmToken::Index:
-        return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
-                                     AstRef(AstName(), exportee.index()));
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Function, AstRef(exportee.index()));
       case WasmToken::Name:
-        return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
-                                     AstRef(exportee.name(), AstNoIndex));
-      case WasmToken::Table:
-        return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
-      case WasmToken::Memory:
-        return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Function, AstRef(exportee.name()));
+      case WasmToken::Table: {
+        AstRef ref;
+        if (!c.ts.getIfRef(&ref))
+            ref = AstRef(0);
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Table, ref);
+      }
+      case WasmToken::Memory: {
+        AstRef ref;
+        if (!c.ts.getIfRef(&ref))
+            ref = AstRef(0);
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory, ref);
+      }
       case WasmToken::Global: {
         AstRef ref;
         if (!c.ts.matchRef(&ref, c.error))
             return nullptr;
         return new(c.lifo) AstExport(name.text(), DefinitionKind::Global, ref);
       }
       case WasmToken::OpenParen: {
         exportee = c.ts.get();
+
+        DefinitionKind kind;
         switch (exportee.kind()) {
-          case WasmToken::Func: {
-            exportee = c.ts.get();
-            switch (exportee.kind()) {
-              case WasmToken::Index:
-                if (!c.ts.match(WasmToken::CloseParen, c.error))
-                    return nullptr;
-                return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
-                                             AstRef(AstName(), exportee.index()));
-              case WasmToken::Name:
-                if (!c.ts.match(WasmToken::CloseParen, c.error))
-                    return nullptr;
-                return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
-                                             AstRef(exportee.name(), AstNoIndex));
-              default:
-                break;
-            }
+          case WasmToken::Func:
+            kind = DefinitionKind::Function;
             break;
-          }
           case WasmToken::Table:
-            if (!MaybeParseOwnerIndex(c))
-                return nullptr;
-            if (!c.ts.match(WasmToken::CloseParen, c.error))
-                return nullptr;
-            return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
+            kind = DefinitionKind::Table;
+            break;
           case WasmToken::Memory:
-            if (!MaybeParseOwnerIndex(c))
-                return nullptr;
-            if (!c.ts.match(WasmToken::CloseParen, c.error))
-                return nullptr;
-            return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
-          case WasmToken::Global: {
-            AstRef ref;
-            if (!c.ts.matchRef(&ref, c.error))
-                return nullptr;
-            if (!c.ts.match(WasmToken::CloseParen, c.error))
-                return nullptr;
-            return new(c.lifo) AstExport(name.text(), DefinitionKind::Global, ref);
-          }
+            kind = DefinitionKind::Memory;
+            break;
+          case WasmToken::Global:
+            kind = DefinitionKind::Global;
+            break;
           default:
-            break;
+            c.ts.generateError(exportee, c.error);
+            return nullptr;
         }
-        break;
+
+        AstRef ref;
+        if (!c.ts.matchRef(&ref, c.error))
+            return nullptr;
+        if (!c.ts.match(WasmToken::CloseParen, c.error))
+            return nullptr;
+
+        return new(c.lifo) AstExport(name.text(), kind, ref);
       }
       default:
         break;
     }
 
     c.ts.generateError(exportee, c.error);
     return nullptr;
 }
@@ -3080,28 +3065,29 @@ ParseTable(WasmParseContext& c, WasmToke
             return import && module->append(import);
         }
 
         if (!c.ts.match(WasmToken::Export, c.error)) {
             c.ts.generateError(token, c.error);
             return false;
         }
 
-        if (!ParseInlineExport(c, DefinitionKind::Table, module))
+        AstRef ref = name.empty() ? AstRef(module->tables().length()) : AstRef(name);
+        if (!ParseInlineExport(c, DefinitionKind::Table, module, ref))
             return false;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
     }
 
     // Either: min max? anyfunc
     if (c.ts.peek().kind() == WasmToken::Index) {
         Limits table;
         if (!ParseTableSig(c, &table))
             return false;
-        return module->addTable(table);
+        return module->addTable(name, table);
     }
 
     // Or: anyfunc (elem 1 2 ...)
     if (!ParseElemType(c))
         return false;
 
     if (!c.ts.match(WasmToken::OpenParen, c.error))
         return false;
@@ -3119,17 +3105,17 @@ ParseTable(WasmParseContext& c, WasmToke
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return false;
 
     uint32_t numElements = uint32_t(elems.length());
     if (numElements != elems.length())
         return false;
 
     Limits r = { numElements, Some(numElements) };
-    if (!module->addTable(r))
+    if (!module->addTable(name, r))
         return false;
 
     auto* zero = new(c.lifo) AstConst(Val(uint32_t(0)));
     if (!zero)
         return false;
 
     AstElemSegment* segment = new(c.lifo) AstElemSegment(zero, Move(elems));
     return segment && module->append(segment);
@@ -3183,19 +3169,17 @@ ParseGlobal(WasmParseContext& c, AstModu
 
             auto* imp = new(c.lifo) AstImport(name, names.module.text(), names.field.text(),
                                               AstGlobal(AstName(), typeToken.valueType(),
                                                         isMutable));
             return imp && module->append(imp);
         }
 
         if (c.ts.getIf(WasmToken::Export)) {
-            AstRef ref = name.empty()
-                         ? AstRef(AstName(), module->globals().length())
-                         : AstRef(name, AstNoIndex);
+            AstRef ref = name.empty() ? AstRef(module->globals().length()) : AstRef(name);
             if (!ParseInlineExport(c, DefinitionKind::Global, module, ref))
                 return false;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return false;
         } else {
             c.ts.unget(openParen);
         }
     }
@@ -3309,16 +3293,18 @@ namespace {
 class Resolver
 {
     UniqueChars* error_;
     AstNameMap varMap_;
     AstNameMap globalMap_;
     AstNameMap sigMap_;
     AstNameMap funcMap_;
     AstNameMap importMap_;
+    AstNameMap tableMap_;
+    AstNameMap memoryMap_;
     AstNameVector targetStack_;
 
     bool registerName(AstNameMap& map, AstName name, size_t index) {
         AstNameMap::AddPtr p = map.lookupForAdd(name);
         if (!p) {
             if (!map.add(p, name, index))
                 return false;
         } else {
@@ -3352,77 +3338,75 @@ class Resolver
   public:
     explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
       : error_(error),
         varMap_(lifo),
         globalMap_(lifo),
         sigMap_(lifo),
         funcMap_(lifo),
         importMap_(lifo),
+        tableMap_(lifo),
+        memoryMap_(lifo),
         targetStack_(lifo)
     {}
     bool init() {
         return sigMap_.init() &&
                funcMap_.init() &&
                importMap_.init() &&
+               tableMap_.init() &&
+               memoryMap_.init() &&
                varMap_.init() &&
                globalMap_.init();
     }
     void beginFunc() {
         varMap_.clear();
         MOZ_ASSERT(targetStack_.empty());
     }
-    bool registerSigName(AstName name, size_t index) {
-        return name.empty() || registerName(sigMap_, name, index);
-    }
-    bool registerFuncName(AstName name, size_t index) {
-        return name.empty() || registerName(funcMap_, name, index);
+
+#define REGISTER(what, map)                                    \
+    bool register##what##Name(AstName name, size_t index) {    \
+        return name.empty() || registerName(map, name, index); \
     }
-    bool registerImportName(AstName name, size_t index) {
-        return name.empty() || registerName(importMap_, name, index);
-    }
-    bool registerVarName(AstName name, size_t index) {
-        return name.empty() || registerName(varMap_, name, index);
-    }
-    bool registerGlobalName(AstName name, size_t index) {
-        return name.empty() || registerName(globalMap_, name, index);
-    }
+
+    REGISTER(Sig, sigMap_)
+    REGISTER(Func, funcMap_)
+    REGISTER(Import, importMap_)
+    REGISTER(Var, varMap_)
+    REGISTER(Global, globalMap_)
+    REGISTER(Table, tableMap_)
+    REGISTER(Memory, memoryMap_)
+
+#undef REGISTER
+
     bool pushTarget(AstName name) {
         return targetStack_.append(name);
     }
     void popTarget(AstName name) {
         MOZ_ASSERT(targetStack_.back() == name);
         targetStack_.popBack();
     }
 
-    bool resolveSignature(AstRef& ref) {
-        if (!ref.name().empty() && !resolveRef(sigMap_, ref))
-            return failResolveLabel("signature", ref.name());
-        return true;
-    }
-    bool resolveFunction(AstRef& ref) {
-        if (!ref.name().empty() && !resolveRef(funcMap_, ref))
-            return failResolveLabel("function", ref.name());
-        return true;
+#define RESOLVE(map, label)                               \
+    bool resolve##label(AstRef& ref) {                    \
+        MOZ_ASSERT(!ref.isInvalid());                     \
+        if (!ref.name().empty() && !resolveRef(map, ref)) \
+            return failResolveLabel(#label, ref.name());  \
+        return true;                                      \
     }
-    bool resolveImport(AstRef& ref) {
-        if (!ref.name().empty() && !resolveRef(importMap_, ref))
-            return failResolveLabel("import", ref.name());
-        return true;
-    }
-    bool resolveLocal(AstRef& ref) {
-        if (!ref.name().empty() && !resolveRef(varMap_, ref))
-            return failResolveLabel("local", ref.name());
-        return true;
-    }
-    bool resolveGlobal(AstRef& ref) {
-        if (!ref.name().empty() && !resolveRef(globalMap_, ref))
-            return failResolveLabel("global", ref.name());
-        return true;
-    }
+
+    RESOLVE(sigMap_, Signature)
+    RESOLVE(funcMap_, Function)
+    RESOLVE(importMap_, Import)
+    RESOLVE(varMap_, Local)
+    RESOLVE(globalMap_, Global)
+    RESOLVE(tableMap_, Table)
+    RESOLVE(memoryMap_, Memory)
+
+#undef RESOLVE
+
     bool resolveBranchTarget(AstRef& ref) {
         if (ref.name().empty())
             return true;
         for (size_t i = 0, e = targetStack_.length(); i < e; i++) {
             if (targetStack_[e - i - 1] == ref.name()) {
                 ref.setIndex(i);
                 return true;
             }
@@ -3768,61 +3752,86 @@ ResolveModule(LifoAlloc& lifo, AstModule
     for (size_t i = 0; i < numSigs; i++) {
         AstSig* sig = module->sigs()[i];
         if (!r.registerSigName(sig->name(), i))
             return r.fail("duplicate signature");
     }
 
     size_t lastFuncIndex = 0;
     size_t lastGlobalIndex = 0;
+    size_t lastMemoryIndex = 0;
+    size_t lastTableIndex = 0;
     for (AstImport* imp : module->imports()) {
         switch (imp->kind()) {
           case DefinitionKind::Function:
             if (!r.registerFuncName(imp->name(), lastFuncIndex++))
                 return r.fail("duplicate import");
             if (!r.resolveSignature(imp->funcSig()))
                 return false;
             break;
           case DefinitionKind::Global:
             if (!r.registerGlobalName(imp->name(), lastGlobalIndex++))
                 return r.fail("duplicate import");
             break;
           case DefinitionKind::Memory:
+            if (!r.registerMemoryName(imp->name(), lastMemoryIndex++))
+                return r.fail("duplicate import");
+            break;
           case DefinitionKind::Table:
+            if (!r.registerTableName(imp->name(), lastTableIndex++))
+                return r.fail("duplicate import");
             break;
         }
     }
 
     for (AstFunc* func : module->funcs()) {
         if (!r.resolveSignature(func->sig()))
             return false;
         if (!r.registerFuncName(func->name(), lastFuncIndex++))
             return r.fail("duplicate function");
     }
 
-    const AstGlobalVector& globals = module->globals();
-    for (const AstGlobal* global : globals) {
+    for (const AstGlobal* global : module->globals()) {
         if (!r.registerGlobalName(global->name(), lastGlobalIndex++))
             return r.fail("duplicate import");
         if (global->hasInit() && !ResolveExpr(r, global->init()))
             return false;
     }
 
+    for (const AstResizable& table : module->tables()) {
+        if (table.imported)
+            continue;
+        if (!r.registerTableName(table.name, lastTableIndex++))
+            return r.fail("duplicate import");
+    }
+
+    for (const AstResizable& memory : module->memories()) {
+        if (memory.imported)
+            continue;
+        if (!r.registerMemoryName(memory.name, lastMemoryIndex++))
+            return r.fail("duplicate import");
+    }
+
     for (AstExport* export_ : module->exports()) {
         switch (export_->kind()) {
           case DefinitionKind::Function:
             if (!r.resolveFunction(export_->ref()))
                 return false;
             break;
           case DefinitionKind::Global:
             if (!r.resolveGlobal(export_->ref()))
                 return false;
             break;
           case DefinitionKind::Table:
+            if (!r.resolveTable(export_->ref()))
+                return false;
+            break;
           case DefinitionKind::Memory:
+            if (!r.resolveMemory(export_->ref()))
+                return false;
             break;
         }
     }
 
     for (AstFunc* func : module->funcs()) {
         if (!ResolveFunc(r, *func))
             return false;
     }
@@ -4471,28 +4480,18 @@ static bool
 EncodeExport(Encoder& e, AstExport& exp)
 {
     if (!EncodeBytes(e, exp.name()))
         return false;
 
     if (!e.writeVarU32(uint32_t(exp.kind())))
         return false;
 
-    switch (exp.kind()) {
-      case DefinitionKind::Function:
-      case DefinitionKind::Global:
-        if (!e.writeVarU32(exp.ref().index()))
-            return false;
-        break;
-      case DefinitionKind::Table:
-      case DefinitionKind::Memory:
-        if (!e.writeVarU32(0))
-            return false;
-        break;
-    }
+    if (!e.writeVarU32(exp.ref().index()))
+        return false;
 
     return true;
 }
 
 static bool
 EncodeExportSection(Encoder& e, AstModule& module)
 {
     uint32_t numExports = module.exports().length();
--- a/js/src/jit-test/tests/wasm/spec/exports.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/exports.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO syntax error (allow to reference table/memories)
-quit();
 var importedArgs = ['exports.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/text.js
+++ b/js/src/jit-test/tests/wasm/text.js
@@ -6,25 +6,25 @@ var parsingError = /parsing wasm text at
 assertErrorMessage(() => wasmEvalText(''), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('('), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(m'), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(module'), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(moduler'), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(module (func) (export "a'), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(module (func (local $a i32) (param $b f32)))'), SyntaxError, parsingError);
 
-assertErrorMessage(() => wasmEvalText('(module (func $a) (func) (export "a" $a) (export "b" $b))'), SyntaxError, /function label '\$b' not found/);
+assertErrorMessage(() => wasmEvalText('(module (func $a) (func) (export "a" $a) (export "b" $b))'), SyntaxError, /Function label '\$b' not found/);
 assertErrorMessage(() => wasmEvalText('(module (import $foo "a" "b") (import $foo "a" "b"))'), SyntaxError, /duplicate import/);
 assertErrorMessage(() => wasmEvalText('(module (func $foo) (func $foo))'), SyntaxError, /duplicate function/);
 assertErrorMessage(() => wasmEvalText('(module (func (param $a i32) (local $a i32)))'), SyntaxError, /duplicate var/);
-assertErrorMessage(() => wasmEvalText('(module (func (get_local $a)))'), SyntaxError, /local label '\$a' not found/);
+assertErrorMessage(() => wasmEvalText('(module (func (get_local $a)))'), SyntaxError, /Local label '\$a' not found/);
 assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (type $a (func (param i32))))'), SyntaxError, /duplicate signature/);
-assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call $abc)))'), SyntaxError, /function label '\$abc' not found/);
-assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (type $b) (i32.const 13)))'), SyntaxError, /signature label '\$b' not found/);
-assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (call_indirect $c (i32.const 0) (get_local 0))))'), SyntaxError, /signature label '\$c' not found/);
+assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call $abc)))'), SyntaxError, /Function label '\$abc' not found/);
+assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (type $b) (i32.const 13)))'), SyntaxError, /Signature label '\$b' not found/);
+assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (call_indirect $c (i32.const 0) (get_local 0))))'), SyntaxError, /Signature label '\$c' not found/);
 assertErrorMessage(() => wasmEvalText('(module (func (br $a)))'), SyntaxError, /branch target label '\$a' not found/);
 assertErrorMessage(() => wasmEvalText('(module (func (block $a ) (br $a)))'), SyntaxError, /branch target label '\$a' not found/);
 
 wasmEvalText('(module (func (param $a i32)))');
 wasmEvalText('(module (func (param i32)))');
 wasmEvalText('(module (func (param i32 i32 f32 f64 i32)))');
 assertErrorMessage(() => wasmEvalText('(module (func (param $a)))'), SyntaxError, parsingError);
 assertErrorMessage(() => wasmEvalText('(module (func (param $a i32 i32)))'), SyntaxError, parsingError);