summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcheck.py3
-rw-r--r--src/wasm-binary.h399
-rw-r--r--src/wasm/wasm-binary.cpp45
-rw-r--r--src/wasm/wasm-s-parser.cpp5
4 files changed, 242 insertions, 210 deletions
diff --git a/check.py b/check.py
index d6b1dc601..df9633de8 100755
--- a/check.py
+++ b/check.py
@@ -445,7 +445,8 @@ for t in tests:
print '\n[ checking wasm-shell spec testcases... ]\n'
if len(requested) == 0:
- BLACKLIST = ['memory.wast'] # FIXME we support old and new memory formats, for now, until 0xc, and so can't pass this old-style test
+ BLACKLIST = ['memory.wast', 'binary.wast'] # FIXME we support old and new memory formats, for now, until 0xc, and so can't pass this old-style test.
+ # FIXME to update the spec to 0xd, we need to implement (register "name") for import.wast
spec_tests = [os.path.join('spec', t) for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'spec'))) if t not in BLACKLIST]
else:
spec_tests = requested[:]
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;