summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDerek Schuff <dschuff@chromium.org>2016-10-26 10:11:27 -0700
committerGitHub <noreply@github.com>2016-10-26 10:11:27 -0700
commitc5ab566cc3343d3b9e07eab4855b0dbfb2c81afe (patch)
tree60a911ebac590d63473b27b44169bcfdc1cfc48b /src
parentded69c16a2b3f27dd9b12b184d7045596d2a21d0 (diff)
downloadbinaryen-c5ab566cc3343d3b9e07eab4855b0dbfb2c81afe.tar.gz
binaryen-c5ab566cc3343d3b9e07eab4855b0dbfb2c81afe.tar.bz2
binaryen-c5ab566cc3343d3b9e07eab4855b0dbfb2c81afe.zip
Binary 0xd changes (#803)
* Renumber opcodes for 0xd * Unified type encoding * Add reserved flags fields to host instructions and call_indirect * Rename flags->reserved * Fix line numbers in wast parser Also don't throw if the memory is defined in the same Element as the export of memory (the validity is checked later anyway). * Skip spec binary.wast The spec testsuite is still on 0xc, so 0xd doesn't match. In order to update to 0xd we need to implement some additional functionality for the import test, namely (register)
Diffstat (limited to 'src')
-rw-r--r--src/wasm-binary.h399
-rw-r--r--src/wasm/wasm-binary.cpp45
-rw-r--r--src/wasm/wasm-s-parser.cpp5
3 files changed, 240 insertions, 209 deletions
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 4110c9cd6..efff5a77b 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -270,7 +270,7 @@ namespace BinaryConsts {
enum Meta {
Magic = 0x6d736100,
- Version = 0x0c
+ Version = 0x0d
};
enum Section {
@@ -288,8 +288,18 @@ enum Section {
Data = 11
};
-enum ElementType {
- AnyFunc = 0x20
+enum EncodedType {
+ // value_type
+ i32 = -0x1, // 0x7f
+ i64 = -0x2, // 0x7e
+ f32 = -0x3, // 0x7d
+ f64 = -0x4, // 0x7c
+ // elem_type
+ AnyFunc = -0x10, // 0x70
+ // func_type form
+ Func = -0x20, // 0x60
+ // block_type
+ Empty = -0x40 // 0x40
};
namespace UserSections {
@@ -297,182 +307,196 @@ extern const char* Name;
}
enum ASTNodes {
- CurrentMemory = 0x3b,
- GrowMemory = 0x39,
- I32Add = 0x40,
- I32Sub = 0x41,
- I32Mul = 0x42,
- I32DivS = 0x43,
- I32DivU = 0x44,
- I32RemS = 0x45,
- I32RemU = 0x46,
- I32And = 0x47,
- I32Or = 0x48,
- I32Xor = 0x49,
- I32Shl = 0x4a,
- I32ShrU = 0x4b,
- I32ShrS = 0x4c,
- I32Eq = 0x4d,
- I32Ne = 0x4e,
- I32LtS = 0x4f,
- I32LeS = 0x50,
- I32LtU = 0x51,
- I32LeU = 0x52,
- I32GtS = 0x53,
- I32GeS = 0x54,
- I32GtU = 0x55,
- I32GeU = 0x56,
- I32Clz = 0x57,
- I32Ctz = 0x58,
- I32Popcnt = 0x59,
- I32EqZ = 0x5a,
- I64Add = 0x5b,
- I64Sub = 0x5c,
- I64Mul = 0x5d,
- I64DivS = 0x5e,
- I64DivU = 0x5f,
- I64RemS = 0x60,
- I64RemU = 0x61,
- I64And = 0x62,
- I64Or = 0x63,
- I64Xor = 0x64,
- I64Shl = 0x65,
- I64ShrU = 0x66,
- I64ShrS = 0x67,
- I64Eq = 0x68,
- I64Ne = 0x69,
- I64LtS = 0x6a,
- I64LeS = 0x6b,
- I64LtU = 0x6c,
- I64LeU = 0x6d,
- I64GtS = 0x6e,
- I64GeS = 0x6f,
- I64GtU = 0x70,
- I64GeU = 0x71,
- I64Clz = 0x72,
- I64Ctz = 0x73,
- I64Popcnt = 0x74,
- I64EqZ = 0xba,
- F32Add = 0x75,
- F32Sub = 0x76,
- F32Mul = 0x77,
- F32Div = 0x78,
- F32Min = 0x79,
- F32Max = 0x7a,
- F32Abs = 0x7b,
- F32Neg = 0x7c,
- F32CopySign = 0x7d,
- F32Ceil = 0x7e,
- F32Floor = 0x7f,
- F32Trunc = 0x80,
- F32NearestInt = 0x81,
- F32Sqrt = 0x82,
- F32Eq = 0x83,
- F32Ne = 0x84,
- F32Lt = 0x85,
- F32Le = 0x86,
- F32Gt = 0x87,
- F32Ge = 0x88,
- F64Add = 0x89,
- F64Sub = 0x8a,
- F64Mul = 0x8b,
- F64Div = 0x8c,
- F64Min = 0x8d,
- F64Max = 0x8e,
- F64Abs = 0x8f,
- F64Neg = 0x90,
- F64CopySign = 0x91,
- F64Ceil = 0x92,
- F64Floor = 0x93,
- F64Trunc = 0x94,
- F64NearestInt = 0x95,
- F64Sqrt = 0x96,
- F64Eq = 0x97,
- F64Ne = 0x98,
- F64Lt = 0x99,
- F64Le = 0x9a,
- F64Gt = 0x9b,
- F64Ge = 0x9c,
-
- I32STruncF32 = 0x9d,
- I32STruncF64 = 0x9e,
- I32UTruncF32 = 0x9f,
- I32UTruncF64 = 0xa0,
- I32ConvertI64 = 0xa1,
- I64STruncF32 = 0xa2,
- I64STruncF64 = 0xa3,
- I64UTruncF32 = 0xa4,
- I64UTruncF64 = 0xa5,
- I64STruncI32 = 0xa6,
- I64UTruncI32 = 0xa7,
- F32SConvertI32 = 0xa8,
- F32UConvertI32 = 0xa9,
- F32SConvertI64 = 0xaa,
- F32UConvertI64 = 0xab,
- F32ConvertF64 = 0xac,
- F32ReinterpretI32 = 0xad,
- F64SConvertI32 = 0xae,
- F64UConvertI32 = 0xaf,
- F64SConvertI64 = 0xb0,
- F64UConvertI64 = 0xb1,
- F64ConvertF32 = 0xb2,
- F64ReinterpretI64 = 0xb3,
- I32ReinterpretF32 = 0xb4,
- I64ReinterpretF64 = 0xb5,
- I32RotR = 0xb6,
- I32RotL = 0xb7,
- I64RotR = 0xb8,
- I64RotL = 0xb9,
-
- I32LoadMem8S = 0x20,
- I32LoadMem8U = 0x21,
- I32LoadMem16S = 0x22,
- I32LoadMem16U = 0x23,
- I64LoadMem8S = 0x24,
- I64LoadMem8U = 0x25,
- I64LoadMem16S = 0x26,
- I64LoadMem16U = 0x27,
- I64LoadMem32S = 0x28,
- I64LoadMem32U = 0x29,
- I32LoadMem = 0x2a,
- I64LoadMem = 0x2b,
- F32LoadMem = 0x2c,
- F64LoadMem = 0x2d,
- I32StoreMem8 = 0x2e,
- I32StoreMem16 = 0x2f,
- I64StoreMem8 = 0x30,
- I64StoreMem16 = 0x31,
- I64StoreMem32 = 0x32,
- I32StoreMem = 0x33,
- I64StoreMem = 0x34,
- F32StoreMem = 0x35,
- F64StoreMem = 0x36,
-
- I32Const = 0x10,
- I64Const = 0x11,
- F64Const = 0x12,
- F32Const = 0x13,
- GetLocal = 0x14,
- SetLocal = 0x15,
- CallFunction = 0x16,
- CallIndirect = 0x17,
- TeeLocal = 0x19,
- GetGlobal = 0xbb,
- SetGlobal = 0xbc,
-
Unreachable = 0x00,
- Block = 0x01,
- Loop = 0x02,
- If = 0x03,
- Else = 0x04,
- Select = 0x05,
- Br = 0x06,
- BrIf = 0x07,
- TableSwitch = 0x08,
- Return = 0x09,
- Nop = 0x0a,
- Drop = 0x0b,
- End = 0x0f
+ Nop = 0x01,
+ Block = 0x02,
+ Loop = 0x03,
+ If = 0x04,
+ Else = 0x05,
+
+ End = 0x0b,
+ Br = 0x0c,
+ BrIf = 0x0d,
+ TableSwitch = 0x0e, // TODO: Rename to BrTable
+ Return = 0x0f,
+
+ CallFunction = 0x10,
+ CallIndirect = 0x11,
+
+ Drop = 0x1a,
+ Select = 0x1b,
+
+ GetLocal = 0x20,
+ SetLocal = 0x21,
+ TeeLocal = 0x22,
+ GetGlobal = 0x23,
+ SetGlobal = 0x24,
+
+
+ I32LoadMem = 0x28,
+ I64LoadMem = 0x29,
+ F32LoadMem = 0x2a,
+ F64LoadMem = 0x2b,
+
+ I32LoadMem8S = 0x2c,
+ I32LoadMem8U = 0x2d,
+ I32LoadMem16S = 0x2e,
+ I32LoadMem16U = 0x2f,
+ I64LoadMem8S = 0x30,
+ I64LoadMem8U = 0x31,
+ I64LoadMem16S = 0x32,
+ I64LoadMem16U = 0x33,
+ I64LoadMem32S = 0x34,
+ I64LoadMem32U = 0x35,
+
+ I32StoreMem = 0x36,
+ I64StoreMem = 0x37,
+ F32StoreMem = 0x38,
+ F64StoreMem = 0x39,
+
+ I32StoreMem8 = 0x3a,
+ I32StoreMem16 = 0x3b,
+ I64StoreMem8 = 0x3c,
+ I64StoreMem16 = 0x3d,
+ I64StoreMem32 = 0x3e,
+
+ CurrentMemory = 0x3f,
+ GrowMemory = 0x40,
+
+ I32Const = 0x41,
+ I64Const = 0x42,
+ F32Const = 0x43,
+ F64Const = 0x44,
+
+ I32EqZ = 0x45,
+ I32Eq = 0x46,
+ I32Ne = 0x47,
+ I32LtS = 0x48,
+ I32LtU = 0x49,
+ I32GtS = 0x4a,
+ I32GtU = 0x4b,
+ I32LeS = 0x4c,
+ I32LeU = 0x4d,
+ I32GeS = 0x4e,
+ I32GeU = 0x4f,
+ I64EqZ = 0x50,
+ I64Eq = 0x51,
+ I64Ne = 0x52,
+ I64LtS = 0x53,
+ I64LtU = 0x54,
+ I64GtS = 0x55,
+ I64GtU = 0x56,
+ I64LeS = 0x57,
+ I64LeU = 0x58,
+ I64GeS = 0x59,
+ I64GeU = 0x5a,
+ F32Eq = 0x5b,
+ F32Ne = 0x5c,
+ F32Lt = 0x5d,
+ F32Gt = 0x5e,
+ F32Le = 0x5f,
+ F32Ge = 0x60,
+ F64Eq = 0x61,
+ F64Ne = 0x62,
+ F64Lt = 0x63,
+ F64Gt = 0x64,
+ F64Le = 0x65,
+ F64Ge = 0x66,
+
+ I32Clz = 0x67,
+ I32Ctz = 0x68,
+ I32Popcnt = 0x69,
+ I32Add = 0x6a,
+ I32Sub = 0x6b,
+ I32Mul = 0x6c,
+ I32DivS = 0x6d,
+ I32DivU = 0x6e,
+ I32RemS = 0x6f,
+ I32RemU = 0x70,
+ I32And = 0x71,
+ I32Or = 0x72,
+ I32Xor = 0x73,
+ I32Shl = 0x74,
+ I32ShrS = 0x75,
+ I32ShrU = 0x76,
+ I32RotL = 0x77,
+ I32RotR = 0x78,
+
+ I64Clz = 0x79,
+ I64Ctz = 0x7a,
+ I64Popcnt = 0x7b,
+ I64Add = 0x7c,
+ I64Sub = 0x7d,
+ I64Mul = 0x7e,
+ I64DivS = 0x7f,
+ I64DivU = 0x80,
+ I64RemS = 0x81,
+ I64RemU = 0x82,
+ I64And = 0x83,
+ I64Or = 0x84,
+ I64Xor = 0x85,
+ I64Shl = 0x86,
+ I64ShrS = 0x87,
+ I64ShrU = 0x88,
+ I64RotL = 0x89,
+ I64RotR = 0x8a,
+
+ F32Abs = 0x8b,
+ F32Neg = 0x8c,
+ F32Ceil = 0x8d,
+ F32Floor = 0x8e,
+ F32Trunc = 0x8f,
+ F32NearestInt = 0x90,
+ F32Sqrt = 0x91,
+ F32Add = 0x92,
+ F32Sub = 0x93,
+ F32Mul = 0x94,
+ F32Div = 0x95,
+ F32Min = 0x96,
+ F32Max = 0x97,
+ F32CopySign = 0x98,
+
+ F64Abs = 0x99,
+ F64Neg = 0x9a,
+ F64Ceil = 0x9b,
+ F64Floor = 0x9c,
+ F64Trunc = 0x9d,
+ F64NearestInt = 0x9e,
+ F64Sqrt = 0x9f,
+ F64Add = 0xa0,
+ F64Sub = 0xa1,
+ F64Mul = 0xa2,
+ F64Div = 0xa3,
+ F64Min = 0xa4,
+ F64Max = 0xa5,
+ F64CopySign = 0xa6,
+
+ I32ConvertI64 = 0xa7, // TODO: rename to I32WrapI64
+ I32STruncF32 = 0xa8,
+ I32UTruncF32 = 0xa9,
+ I32STruncF64 = 0xaa,
+ I32UTruncF64 = 0xab,
+ I64STruncI32 = 0xac, // TODO: rename to I64SExtendI32
+ I64UTruncI32 = 0xad, // TODO: likewise
+ I64STruncF32 = 0xae,
+ I64UTruncF32 = 0xaf,
+ I64STruncF64 = 0xb0,
+ I64UTruncF64 = 0xb1,
+ F32SConvertI32 = 0xb2,
+ F32UConvertI32 = 0xb3,
+ F32SConvertI64 = 0xb4,
+ F32UConvertI64 = 0xb5,
+ F32ConvertF64 = 0xb6, // TODO: rename to F32DemoteI64
+ F64SConvertI32 = 0xb7,
+ F64UConvertI32 = 0xb8,
+ F64SConvertI64 = 0xb9,
+ F64UConvertI64 = 0xba,
+ F64ConvertF32 = 0xbb, // TODO: rename to F64PromoteF32
+
+ I32ReinterpretF32 = 0xbc,
+ I64ReinterpretF64 = 0xbd,
+ F32ReinterpretI32 = 0xbe,
+ F64ReinterpretI64 = 0xbf
};
enum MemoryAccess {
@@ -481,22 +505,21 @@ enum MemoryAccess {
NaturalAlignment = 0
};
-enum TypeForms {
- Basic = 0x40
-};
-
} // namespace BinaryConsts
-inline int8_t binaryWasmType(WasmType type) {
+inline S32LEB binaryWasmType(WasmType type) {
+ int ret;
switch (type) {
- case none: return 0;
- case i32: return 1;
- case i64: return 2;
- case f32: return 3;
- case f64: return 4;
+ // None only used for block signatures. TODO: Separate out?
+ case none: ret = BinaryConsts::EncodedType::Empty; break;
+ case i32: ret = BinaryConsts::EncodedType::i32; break;
+ case i64: ret = BinaryConsts::EncodedType::i64; break;
+ case f32: ret = BinaryConsts::EncodedType::f32; break;
+ case f64: ret = BinaryConsts::EncodedType::f64; break;
default: abort();
}
+ return S32LEB(ret);
}
class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 74169fce2..d2a648294 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -103,7 +103,7 @@ void WasmBinaryWriter::writeTypes() {
o << U32LEB(wasm->functionTypes.size());
for (auto& type : wasm->functionTypes) {
if (debug) std::cerr << "write one" << std::endl;
- o << int8_t(BinaryConsts::TypeForms::Basic);
+ o << S32LEB(BinaryConsts::EncodedType::Func);
o << U32LEB(type->params.size());
for (auto param : type->params) {
o << binaryWasmType(param);
@@ -139,7 +139,7 @@ void WasmBinaryWriter::writeImports() {
switch (import->kind) {
case ExternalKind::Function: o << U32LEB(getFunctionTypeIndex(import->functionType->name)); break;
case ExternalKind::Table: {
- o << U32LEB(BinaryConsts::ElementType::AnyFunc);
+ o << S32LEB(BinaryConsts::EncodedType::AnyFunc);
writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.max != Table::kMaxSize);
break;
}
@@ -344,7 +344,7 @@ void WasmBinaryWriter::writeFunctionTableDeclaration() {
if (debug) std::cerr << "== writeFunctionTableDeclaration" << std::endl;
auto start = startSection(BinaryConsts::Section::Table);
o << U32LEB(1); // Declare 1 table.
- o << U32LEB(BinaryConsts::ElementType::AnyFunc);
+ o << S32LEB(BinaryConsts::EncodedType::AnyFunc);
writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.max != Table::kMaxSize);
finishSection(start);
}
@@ -533,7 +533,9 @@ void WasmBinaryWriter::visitCallIndirect(CallIndirect *curr) {
recurse(operand);
}
recurse(curr->target);
- o << int8_t(BinaryConsts::CallIndirect) << U32LEB(getFunctionTypeIndex(curr->fullType));
+ o << int8_t(BinaryConsts::CallIndirect)
+ << U32LEB(getFunctionTypeIndex(curr->fullType))
+ << U32LEB(0); // Reserved flags field
}
void WasmBinaryWriter::visitGetLocal(GetLocal *curr) {
@@ -822,6 +824,7 @@ void WasmBinaryWriter::visitHost(Host *curr) {
}
default: abort();
}
+ o << U32LEB(0); // Reserved flags field
}
void WasmBinaryWriter::visitNop(Nop *curr) {
@@ -976,13 +979,14 @@ int64_t WasmBinaryBuilder::getS64LEB() {
}
WasmType WasmBinaryBuilder::getWasmType() {
- int8_t type = getInt8();
+ int type = getS32LEB();
switch (type) {
- case 0: return none;
- case 1: return i32;
- case 2: return i64;
- case 3: return f32;
- case 4: return f64;
+ // None only used for block signatures. TODO: Separate out?
+ case BinaryConsts::EncodedType::Empty: return none;
+ case BinaryConsts::EncodedType::i32: return i32;
+ case BinaryConsts::EncodedType::i64: return i64;
+ case BinaryConsts::EncodedType::f32: return f32;
+ case BinaryConsts::EncodedType::f64: return f64;
default: abort();
}
}
@@ -1060,9 +1064,9 @@ void WasmBinaryBuilder::readSignatures() {
for (size_t i = 0; i < numTypes; i++) {
if (debug) std::cerr << "read one" << std::endl;
auto curr = new FunctionType;
- auto form = getU32LEB();
+ auto form = getS32LEB();
WASM_UNUSED(form);
- assert(form == BinaryConsts::TypeForms::Basic);
+ assert(form == BinaryConsts::EncodedType::Func);
size_t numParams = getU32LEB();
if (debug) std::cerr << "num params: " << numParams << std::endl;
for (size_t j = 0; j < numParams; j++) {
@@ -1120,9 +1124,9 @@ void WasmBinaryBuilder::readImports() {
break;
}
case ExternalKind::Table: {
- auto elementType = getU32LEB();
+ auto elementType = getS32LEB();
WASM_UNUSED(elementType);
- if (elementType != BinaryConsts::ElementType::AnyFunc) throw ParseException("Imported table type is not AnyFunc");
+ if (elementType != BinaryConsts::EncodedType::AnyFunc) throw ParseException("Imported table type is not AnyFunc");
wasm.table.exists = true;
wasm.table.imported = true;
getResizableLimits(wasm.table.initial, wasm.table.max, Table::kMaxSize);
@@ -1358,8 +1362,8 @@ void WasmBinaryBuilder::readFunctionTableDeclaration() {
if (numTables != 1) throw ParseException("Only 1 table definition allowed in MVP");
if (wasm.table.exists) throw ParseException("Table cannot be both imported and defined");
wasm.table.exists = true;
- auto elemType = getU32LEB();
- if (elemType != BinaryConsts::ElementType::AnyFunc) throw ParseException("ElementType must be AnyFunc in MVP");
+ auto elemType = getS32LEB();
+ if (elemType != BinaryConsts::EncodedType::AnyFunc) throw ParseException("ElementType must be AnyFunc in MVP");
getResizableLimits(wasm.table.initial, wasm.table.max, Table::kMaxSize);
}
@@ -1535,7 +1539,7 @@ WasmBinaryBuilder::BreakTarget WasmBinaryBuilder::getBreakTarget(int32_t offset)
}
void WasmBinaryBuilder::visitBreak(Break *curr, uint8_t code) {
- if (debug) std::cerr << "zz node: Break" << std::endl;
+ if (debug) std::cerr << "zz node: Break, code "<< int32_t(code) << std::endl;
BreakTarget target = getBreakTarget(getU32LEB());
curr->name = target.name;
if (code == BinaryConsts::BrIf) curr->condition = popExpression();
@@ -1587,6 +1591,8 @@ Expression* WasmBinaryBuilder::visitCall() {
void WasmBinaryBuilder::visitCallIndirect(CallIndirect *curr) {
if (debug) std::cerr << "zz node: CallIndirect" << std::endl;
auto* fullType = wasm.functionTypes.at(getU32LEB()).get();
+ auto reserved = getU32LEB();
+ if (reserved != 0) throw ParseException("Invalid flags field in call_indirect");
curr->fullType = fullType->name;
auto num = fullType->params.size();
curr->operands.resize(num);
@@ -1693,6 +1699,7 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code) {
bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) {
Const* curr;
+ if (debug) std::cerr << "zz node: Const, code " << code << std::endl;
switch (code) {
case BinaryConsts::I32Const: curr = allocator.alloc<Const>(); curr->value = Literal(getS32LEB()); break;
case BinaryConsts::I64Const: curr = allocator.alloc<Const>(); curr->value = Literal(getS64LEB()); break;
@@ -1702,7 +1709,7 @@ bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) {
}
curr->type = curr->value.type;
out = curr;
- if (debug) std::cerr << "zz node: Const" << std::endl;
+
return true;
}
@@ -1865,6 +1872,8 @@ bool WasmBinaryBuilder::maybeVisitHost(Expression*& out, uint8_t code) {
default: return false;
}
if (debug) std::cerr << "zz node: Host" << std::endl;
+ auto reserved = getU32LEB();
+ if (reserved != 0) throw ParseException("Invalid reserved field on grow_memory/current_memory");
curr->finalize();
out = curr;
return true;
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index cbcdd5d3a..5898cc252 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -94,7 +94,7 @@ void Element::dump() {
SExpressionParser::SExpressionParser(char* input) : input(input) {
root = nullptr;
- line = 0;
+ line = 1;
lineStart = input;
while (!root) { // keep parsing until we pass an initial comment
root = parse();
@@ -138,7 +138,7 @@ void SExpressionParser::skipWhitespace() {
if (input[0] == ';' && input[1] == ';') {
while (input[0] && input[0] != '\n') input++;
line++;
- lineStart = input;
+ lineStart = ++input;
} else if (input[0] == '(' && input[1] == ';') {
// Skip nested block comments.
input += 2;
@@ -1438,7 +1438,6 @@ void SExpressionWasmBuilder::parseExport(Element& s) {
if (inner[0]->str() == FUNC) {
ex->kind = ExternalKind::Function;
} else if (inner[0]->str() == MEMORY) {
- if (!wasm.memory.exists) throw ParseException("memory exported but no memory");
ex->kind = ExternalKind::Memory;
} else if (inner[0]->str() == TABLE) {
ex->kind = ExternalKind::Table;