@@ -94,7 +94,7 @@ bool Opcode::IsEnabled(const Features& features) const {
case Opcode::Catch:
case Opcode::Throw:
case Opcode::Rethrow:
case Opcode::CatchAll:
case Opcode::IfExcept:
return features.exceptions_enabled();

case Opcode::I32TruncSSatF32:
@@ -112,6 +112,8 @@ bool Opcode::IsEnabled(const Features& features) const {
case Opcode::I64Extend8S:
case Opcode::I64Extend16S:
case Opcode::I64Extend32S:
return features.sign_extension_enabled();

case Opcode::AtomicWake:
case Opcode::I32AtomicWait:
case Opcode::I64AtomicWait:
@@ -181,7 +183,145 @@ bool Opcode::IsEnabled(const Features& features) const {
return features.threads_enabled();

case Opcode::V128Const:
case Opcode::V128Load:
case Opcode::V128Store:
case Opcode::I8X16Splat:
case Opcode::I16X8Splat:
case Opcode::I32X4Splat:
case Opcode::I64X2Splat:
case Opcode::F32X4Splat:
case Opcode::F64X2Splat:
case Opcode::I8X16ExtractLaneS:
case Opcode::I8X16ExtractLaneU:
case Opcode::I16X8ExtractLaneS:
case Opcode::I16X8ExtractLaneU:
case Opcode::I32X4ExtractLane:
case Opcode::I64X2ExtractLane:
case Opcode::F32X4ExtractLane:
case Opcode::F64X2ExtractLane:
case Opcode::I8X16ReplaceLane:
case Opcode::I16X8ReplaceLane:
case Opcode::I32X4ReplaceLane:
case Opcode::I64X2ReplaceLane:
case Opcode::F32X4ReplaceLane:
case Opcode::F64X2ReplaceLane:
case Opcode::V8X16Shuffle:
case Opcode::I8X16Add:
case Opcode::I16X8Add:
case Opcode::I32X4Add:
case Opcode::I64X2Add:
case Opcode::I8X16Sub:
case Opcode::I16X8Sub:
case Opcode::I32X4Sub:
case Opcode::I64X2Sub:
case Opcode::I8X16Mul:
case Opcode::I16X8Mul:
case Opcode::I32X4Mul:
case Opcode::I8X16Neg:
case Opcode::I16X8Neg:
case Opcode::I32X4Neg:
case Opcode::I64X2Neg:
case Opcode::I8X16AddSaturateS:
case Opcode::I8X16AddSaturateU:
case Opcode::I16X8AddSaturateS:
case Opcode::I16X8AddSaturateU:
case Opcode::I8X16SubSaturateS:
case Opcode::I8X16SubSaturateU:
case Opcode::I16X8SubSaturateS:
case Opcode::I16X8SubSaturateU:
case Opcode::I8X16Shl:
case Opcode::I16X8Shl:
case Opcode::I32X4Shl:
case Opcode::I64X2Shl:
case Opcode::I8X16ShrS:
case Opcode::I8X16ShrU:
case Opcode::I16X8ShrS:
case Opcode::I16X8ShrU:
case Opcode::I32X4ShrS:
case Opcode::I32X4ShrU:
case Opcode::I64X2ShrS:
case Opcode::I64X2ShrU:
case Opcode::V128And:
case Opcode::V128Or:
case Opcode::V128Xor:
case Opcode::V128Not:
case Opcode::V128BitSelect:
case Opcode::I8X16AnyTrue:
case Opcode::I16X8AnyTrue:
case Opcode::I32X4AnyTrue:
case Opcode::I64X2AnyTrue:
case Opcode::I8X16AllTrue:
case Opcode::I16X8AllTrue:
case Opcode::I32X4AllTrue:
case Opcode::I64X2AllTrue:
case Opcode::I8X16Eq:
case Opcode::I16X8Eq:
case Opcode::I32X4Eq:
case Opcode::F32X4Eq:
case Opcode::F64X2Eq:
case Opcode::I8X16Ne:
case Opcode::I16X8Ne:
case Opcode::I32X4Ne:
case Opcode::F32X4Ne:
case Opcode::F64X2Ne:
case Opcode::I8X16LtS:
case Opcode::I8X16LtU:
case Opcode::I16X8LtS:
case Opcode::I16X8LtU:
case Opcode::I32X4LtS:
case Opcode::I32X4LtU:
case Opcode::F32X4Lt:
case Opcode::F64X2Lt:
case Opcode::I8X16LeS:
case Opcode::I8X16LeU:
case Opcode::I16X8LeS:
case Opcode::I16X8LeU:
case Opcode::I32X4LeS:
case Opcode::I32X4LeU:
case Opcode::F32X4Le:
case Opcode::F64X2Le:
case Opcode::I8X16GtS:
case Opcode::I8X16GtU:
case Opcode::I16X8GtS:
case Opcode::I16X8GtU:
case Opcode::I32X4GtS:
case Opcode::I32X4GtU:
case Opcode::F32X4Gt:
case Opcode::F64X2Gt:
case Opcode::I8X16GeS:
case Opcode::I8X16GeU:
case Opcode::I16X8GeS:
case Opcode::I16X8GeU:
case Opcode::I32X4GeS:
case Opcode::I32X4GeU:
case Opcode::F32X4Ge:
case Opcode::F64X2Ge:
case Opcode::F32X4Neg:
case Opcode::F64X2Neg:
case Opcode::F32X4Abs:
case Opcode::F64X2Abs:
case Opcode::F32X4Min:
case Opcode::F64X2Min:
case Opcode::F32X4Max:
case Opcode::F64X2Max:
case Opcode::F32X4Add:
case Opcode::F64X2Add:
case Opcode::F32X4Sub:
case Opcode::F64X2Sub:
case Opcode::F32X4Div:
case Opcode::F64X2Div:
case Opcode::F32X4Mul:
case Opcode::F64X2Mul:
case Opcode::F32X4Sqrt:
case Opcode::F64X2Sqrt:
case Opcode::F32X4ConvertSI32X4:
case Opcode::F32X4ConvertUI32X4:
case Opcode::F64X2ConvertSI64X2:
case Opcode::F64X2ConvertUI64X2:
case Opcode::I32X4TruncSF32X4Sat:
case Opcode::I32X4TruncUF32X4Sat:
case Opcode::I64X2TruncSF64X2Sat:
case Opcode::I64X2TruncUF64X2Sat:
return features.simd_enabled();

// Interpreter opcodes are never "enabled".
@@ -197,4 +337,33 @@ bool Opcode::IsEnabled(const Features& features) const {
}
}

} // end anonymous namespace
uint32_t Opcode::GetSimdLaneCount() const {
switch (enum_) {
case Opcode::I8X16ExtractLaneS:
case Opcode::I8X16ExtractLaneU:
case Opcode::I8X16ReplaceLane:
return 16;
break;
case Opcode::I16X8ExtractLaneS:
case Opcode::I16X8ExtractLaneU:
case Opcode::I16X8ReplaceLane:
return 8;
break;
case Opcode::F32X4ExtractLane:
case Opcode::F32X4ReplaceLane:
case Opcode::I32X4ExtractLane:
case Opcode::I32X4ReplaceLane:
return 4;
break;
case Opcode::F64X2ExtractLane:
case Opcode::F64X2ReplaceLane:
case Opcode::I64X2ExtractLane:
case Opcode::I64X2ReplaceLane:
return 2;
break;
default:
WABT_UNREACHABLE;
}
}

} // namespace wabt

Large diffs are not rendered by default.

@@ -37,7 +37,7 @@ struct Opcode {
Invalid,
};

// Static opcode objects.
// Static opcode objects.
#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
text) \
static Opcode Name##_Opcode;
@@ -61,6 +61,9 @@ struct Opcode {
Type GetParamType3() const { return GetInfo().param3_type; }
Address GetMemorySize() const { return GetInfo().memory_size; }

// Get the lane count of an extract/replace simd op.
uint32_t GetSimdLaneCount() const;

// Return 1 if |alignment| matches the alignment of |opcode|, or if
// |alignment| is WABT_USE_NATURAL_ALIGNMENT.
bool IsNaturallyAligned(Address alignment) const;
@@ -131,6 +134,6 @@ struct Opcode {
Enum enum_;
};

} // end anonymous namespace
} // namespace wabt

#endif // WABT_OPCODE_H_
@@ -73,7 +73,7 @@ void OptionParser::AddOption(char short_name,

void OptionParser::AddOption(const char* long_name,
const char* help,
const NullCallback& callback){
const NullCallback& callback) {
Option option('\0', long_name, std::string(), HasArgument::No, help,
[callback](const char*) { callback(); });
AddOption(option);
@@ -210,7 +210,7 @@ void OptionParser::Parse(int argc, char* argv[]) {
// Allow short names to be combined, e.g. "-d -v" => "-dv".
for (int k = 1; arg[k]; ++k) {
bool matched = false;
for (const Option& option: options_) {
for (const Option& option : options_) {
if (option.short_name && arg[k] == option.short_name) {
const char* option_argument = nullptr;
if (option.has_argument) {
@@ -278,7 +278,7 @@ void OptionParser::PrintHelp() {

const size_t kExtraSpace = 8;
size_t longest_name_length = 0;
for (const Option& option: options_) {
for (const Option& option : options_) {
size_t length;
if (!option.long_name.empty()) {
length = option.long_name.size();
@@ -295,7 +295,7 @@ void OptionParser::PrintHelp() {
}
}

for (const Option& option: options_) {
for (const Option& option : options_) {
if (!option.short_name && option.long_name.empty()) {
continue;
}
@@ -0,0 +1,153 @@
/* Generated from 'wasm2c.c.tmpl' by wasm2c_tmpl.py, do not edit! */
const char SECTION_NAME(includes)[] =
"#include <assert.h>\n"
"#include <math.h>\n"
"#include <stdlib.h>\n"
"#include <string.h>\n"
;
const char SECTION_NAME(declarations)[] =
"#define UNLIKELY(x) __builtin_expect(!!(x), 0)\n"
"#define LIKELY(x) __builtin_expect(!!(x), 1)\n"
"\n"
"#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)\n"
"\n"
"#define FUNC_PROLOGUE \\\n"
" if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \\\n"
" TRAP(EXHAUSTION)\n"
"\n"
"#define FUNC_EPILOGUE --wasm_rt_call_stack_depth\n"
"\n"
"#define UNREACHABLE TRAP(UNREACHABLE)\n"
"\n"
"#define CALL_INDIRECT(table, t, ft, x, ...) \\\n"
" (LIKELY((x) < table.size && table.data[x].func && \\\n"
" table.data[x].func_type == func_types[ft]) \\\n"
" ? ((t)table.data[x].func)(__VA_ARGS__) \\\n"
" : TRAP(CALL_INDIRECT))\n"
"\n"
"#define MEMCHECK(mem, a, t) \\\n"
" if (UNLIKELY((a) + sizeof(t) > mem->size)) TRAP(OOB)\n"
"\n"
"#define DEFINE_LOAD(name, t1, t2, t3) \\\n"
" static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \\\n"
" MEMCHECK(mem, addr, t1); \\\n"
" t1 result; \\\n"
" memcpy(&result, &mem->data[addr], sizeof(t1)); \\\n"
" return (t3)(t2)result; \\\n"
" }\n"
"\n"
"#define DEFINE_STORE(name, t1, t2) \\\n"
" static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \\\n"
" MEMCHECK(mem, addr, t1); \\\n"
" t1 wrapped = (t1)value; \\\n"
" memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \\\n"
" }\n"
"\n"
"DEFINE_LOAD(i32_load, u32, u32, u32);\n"
"DEFINE_LOAD(i64_load, u64, u64, u64);\n"
"DEFINE_LOAD(f32_load, f32, f32, f32);\n"
"DEFINE_LOAD(f64_load, f64, f64, f64);\n"
"DEFINE_LOAD(i32_load8_s, s8, s32, u32);\n"
"DEFINE_LOAD(i64_load8_s, s8, s64, u64);\n"
"DEFINE_LOAD(i32_load8_u, u8, u32, u32);\n"
"DEFINE_LOAD(i64_load8_u, u8, u64, u64);\n"
"DEFINE_LOAD(i32_load16_s, s16, s32, u32);\n"
"DEFINE_LOAD(i64_load16_s, s16, s64, u64);\n"
"DEFINE_LOAD(i32_load16_u, u16, u32, u32);\n"
"DEFINE_LOAD(i64_load16_u, u16, u64, u64);\n"
"DEFINE_LOAD(i64_load32_s, s32, s64, u64);\n"
"DEFINE_LOAD(i64_load32_u, u32, u64, u64);\n"
"DEFINE_STORE(i32_store, u32, u32);\n"
"DEFINE_STORE(i64_store, u64, u64);\n"
"DEFINE_STORE(f32_store, f32, f32);\n"
"DEFINE_STORE(f64_store, f64, f64);\n"
"DEFINE_STORE(i32_store8, u8, u32);\n"
"DEFINE_STORE(i32_store16, u16, u32);\n"
"DEFINE_STORE(i64_store8, u8, u64);\n"
"DEFINE_STORE(i64_store16, u16, u64);\n"
"DEFINE_STORE(i64_store32, u32, u64);\n"
"\n"
"#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)\n"
"#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)\n"
"#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)\n"
"#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)\n"
"#define I32_POPCNT(x) (__builtin_popcount(x))\n"
"#define I64_POPCNT(x) (__builtin_popcountll(x))\n"
"\n"
"#define DIV_S(ut, min, x, y) \\\n"
" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \\\n"
" : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \\\n"
" : (ut)((x) / (y)))\n"
"\n"
"#define REM_S(ut, min, x, y) \\\n"
" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \\\n"
" : (UNLIKELY((x) == min && (y) == -1)) ? 0 \\\n"
" : (ut)((x) % (y)))\n"
"\n"
"#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y)\n"
"#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y)\n"
"#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y)\n"
"#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y)\n"
"\n"
"#define DIVREM_U(op, x, y) \\\n"
" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x) op (y)))\n"
"\n"
"#define DIV_U(x, y) DIVREM_U(/, x, y)\n"
"#define REM_U(x, y) DIVREM_U(%, x, y)\n"
"\n"
"#define ROTL(x, y, mask) \\\n"
" (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask))))\n"
"#define ROTR(x, y, mask) \\\n"
" (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask))))\n"
"\n"
"#define I32_ROTL(x, y) ROTL(x, y, 31)\n"
"#define I64_ROTL(x, y) ROTL(x, y, 63)\n"
"#define I32_ROTR(x, y) ROTR(x, y, 31)\n"
"#define I64_ROTR(x, y) ROTR(x, y, 63)\n"
"\n"
"#define FMIN(x, y) \\\n"
" ((UNLIKELY((x) != (x))) ? NAN \\\n"
" : (UNLIKELY((y) != (y))) ? NAN \\\n"
" : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \\\n"
" : (x < y) ? x : y)\n"
"\n"
"#define FMAX(x, y) \\\n"
" ((UNLIKELY((x) != (x))) ? NAN \\\n"
" : (UNLIKELY((y) != (y))) ? NAN \\\n"
" : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \\\n"
" : (x > y) ? x : y)\n"
"\n"
"#define TRUNC_S(ut, st, ft, min, max, maxop, x) \\\n"
" ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \\\n"
" : (UNLIKELY((x) < (ft)(min) || (x) maxop (ft)(max))) ? TRAP(INT_OVERFLOW) \\\n"
" : (ut)(st)(x))\n"
"\n"
"#define I32_TRUNC_S_F32(x) TRUNC_S(u32, s32, f32, INT32_MIN, INT32_MAX, >=, x)\n"
"#define I64_TRUNC_S_F32(x) TRUNC_S(u64, s64, f32, INT64_MIN, INT64_MAX, >=, x)\n"
"#define I32_TRUNC_S_F64(x) TRUNC_S(u32, s32, f64, INT32_MIN, INT32_MAX, >, x)\n"
"#define I64_TRUNC_S_F64(x) TRUNC_S(u64, s64, f64, INT64_MIN, INT64_MAX, >=, x)\n"
"\n"
"#define TRUNC_U(ut, ft, max, maxop, x) \\\n"
" ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \\\n"
" : (UNLIKELY((x) <= (ft)-1 || (x) maxop (ft)(max))) ? TRAP(INT_OVERFLOW) \\\n"
" : (ut)(x))\n"
"\n"
"#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, UINT32_MAX, >=, x)\n"
"#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, UINT64_MAX, >=, x)\n"
"#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, UINT32_MAX, >, x)\n"
"#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, UINT64_MAX, >=, x)\n"
"\n"
"#define DEFINE_REINTERPRET(name, t1, t2) \\\n"
" static inline t2 name(t1 x) { \\\n"
" t2 result; \\\n"
" memcpy(&result, &x, sizeof(result)); \\\n"
" return result; \\\n"
" }\n"
"\n"
"DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)\n"
"DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32)\n"
"DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)\n"
"DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)\n"
"\n"
;
@@ -0,0 +1,40 @@
/* Generated from 'wasm2c.h.tmpl' by wasm2c_tmpl.py, do not edit! */
const char SECTION_NAME(top)[] =
"#ifdef __cplusplus\n"
"extern \"C\" {\n"
"#endif\n"
"\n"
"#include <stdint.h>\n"
"\n"
"#include \"wasm-rt.h\"\n"
"\n"
"#ifndef WASM_RT_MODULE_PREFIX\n"
"#define WASM_RT_MODULE_PREFIX\n"
"#endif\n"
"\n"
"#define WASM_RT_PASTE_(x, y) x ## y\n"
"#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)\n"
"#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)\n"
"\n"
"#define WASM_RT_DEFINE_EXTERNAL(decl, target) decl = &target;\n"
"\n"
"/* TODO(binji): only use stdint.h types in header */\n"
"typedef uint8_t u8;\n"
"typedef int8_t s8;\n"
"typedef uint16_t u16;\n"
"typedef int16_t s16;\n"
"typedef uint32_t u32;\n"
"typedef int32_t s32;\n"
"typedef uint64_t u64;\n"
"typedef int64_t s64;\n"
"typedef float f32;\n"
"typedef double f64;\n"
"\n"
"extern void WASM_RT_ADD_PREFIX(init)(void);\n"
;

const char SECTION_NAME(bottom)[] =
"#ifdef __cplusplus\n"
"}\n"
"#endif\n"
;

Large diffs are not rendered by default.

@@ -32,6 +32,6 @@ struct Range {
typedef Range<Offset> OffsetRange;
typedef Range<int> ColumnRange;

} // namespace wabt
} // namespace wabt

#endif // WABT_RANGE_H_
@@ -45,11 +45,12 @@ class NameResolver : public ExprVisitor::DelegateNop {
Result OnBrTableExpr(BrTableExpr*) override;
Result OnCallExpr(CallExpr*) override;
Result OnCallIndirectExpr(CallIndirectExpr*) override;
Result OnCatchExpr(TryExpr*, Catch*) override;
Result OnGetGlobalExpr(GetGlobalExpr*) override;
Result OnGetLocalExpr(GetLocalExpr*) override;
Result BeginIfExpr(IfExpr*) override;
Result EndIfExpr(IfExpr*) override;
Result BeginIfExceptExpr(IfExceptExpr*) override;
Result EndIfExceptExpr(IfExceptExpr*) override;
Result BeginLoopExpr(LoopExpr*) override;
Result EndLoopExpr(LoopExpr*) override;
Result OnSetGlobalExpr(SetGlobalExpr*) override;
@@ -58,7 +59,6 @@ class NameResolver : public ExprVisitor::DelegateNop {
Result BeginTryExpr(TryExpr*) override;
Result EndTryExpr(TryExpr*) override;
Result OnThrowExpr(ThrowExpr*) override;
Result OnRethrowExpr(RethrowExpr*) override;

private:
void PrintError(const Location* loc, const char* fmt, ...);
@@ -272,6 +272,17 @@ Result NameResolver::EndIfExpr(IfExpr* expr) {
return Result::Ok;
}

Result NameResolver::BeginIfExceptExpr(IfExceptExpr* expr) {
PushLabel(expr->true_.label);
ResolveExceptionVar(&expr->except_var);
return Result::Ok;
}

Result NameResolver::EndIfExceptExpr(IfExceptExpr* expr) {
PopLabel();
return Result::Ok;
}

Result NameResolver::OnSetGlobalExpr(SetGlobalExpr* expr) {
ResolveGlobalVar(&expr->var);
return Result::Ok;
@@ -297,25 +308,11 @@ Result NameResolver::EndTryExpr(TryExpr*) {
return Result::Ok;
}

Result NameResolver::OnCatchExpr(TryExpr* expr, Catch* catch_) {
if (!catch_->IsCatchAll()) {
ResolveExceptionVar(&catch_->var);
}
return Result::Ok;
}

Result NameResolver::OnThrowExpr(ThrowExpr* expr) {
ResolveExceptionVar(&expr->var);
return Result::Ok;
}

Result NameResolver::OnRethrowExpr(RethrowExpr* expr) {
// Note: the variable refers to corresponding (enclosing) catch, using the try
// block label for context.
ResolveLabelVar(&expr->var);
return Result::Ok;
}

void NameResolver::VisitFunc(Func* func) {
current_func_ = func;
if (func->decl.has_func_type) {
@@ -44,8 +44,12 @@ inline Result& Result::operator|=(Result rhs) {
return *this;
}

inline bool Succeeded(Result result) { return result == Result::Ok; }
inline bool Failed(Result result) { return result == Result::Error; }
inline bool Succeeded(Result result) {
return result == Result::Ok;
}
inline bool Failed(Result result) {
return result == Result::Error;
}

#define CHECK_RESULT(expr) \
do { \
@@ -154,6 +154,13 @@ std::unique_ptr<OutputBuffer> MemoryStream::ReleaseOutputBuffer() {
return std::move(buf_);
}

void MemoryStream::Clear() {
if (buf_)
buf_->clear();
else
buf_.reset(new OutputBuffer());
}

Result MemoryStream::WriteDataImpl(size_t dst_offset,
const void* src,
size_t size) {
@@ -271,5 +278,4 @@ std::unique_ptr<FileStream> FileStream::CreateStderr() {
return std::unique_ptr<FileStream>(new FileStream(stderr));
}


} // namespace wabt
@@ -52,6 +52,7 @@ class Stream {

bool has_log_stream() const { return log_stream_ != nullptr; }

void ClearOffset() { offset_ = 0; }
void AddOffset(ssize_t delta);

void WriteData(const void* src,
@@ -97,8 +98,8 @@ class Stream {
Write(value, desc, print_chars);
}
void WriteU128(v128 value,
const char* desc = nullptr,
PrintChars print_chars = PrintChars::No) {
const char* desc = nullptr,
PrintChars print_chars = PrintChars::No) {
Write(value, desc, print_chars);
}

@@ -147,6 +148,7 @@ class Stream {
struct OutputBuffer {
Result WriteToFile(string_view filename) const;

void clear() { data.clear(); }
size_t size() const { return data.size(); }

std::vector<uint8_t> data;
@@ -162,6 +164,8 @@ class MemoryStream : public Stream {
OutputBuffer& output_buffer() { return *buf_; }
std::unique_ptr<OutputBuffer> ReleaseOutputBuffer();

void Clear();

Result WriteToFile(string_view filename) {
return buf_->WriteToFile(filename);
}
@@ -123,7 +123,7 @@ string_view::size_type string_view::find(const char* s, size_type pos) const {

string_view::size_type string_view::rfind(string_view s, size_type pos) const
noexcept {
pos = std::min(std::min(pos, size_ - s.size_) + s.size_, size_);
pos = std::min(std::min(pos, size_ - s.size_) + s.size_, size_);
reverse_iterator iter = std::search(reverse_iterator(begin() + pos), rend(),
s.rbegin(), s.rend());
return iter == rend() ? npos : (rend() - iter - s.size_);
@@ -173,7 +173,7 @@ string_view::size_type string_view::find_last_of(string_view s,
size_type pos) const noexcept {
pos = std::min(pos, size_ - 1);
reverse_iterator iter = std::find_first_of(
reverse_iterator(begin() + pos + 1), rend(), s.begin(), s.end());
reverse_iterator(begin() + (pos + 1)), rend(), s.begin(), s.end());
return iter == rend() ? npos : (rend() - iter - 1);
}

@@ -134,8 +134,8 @@ class string_view {
/*constexpr*/ size_type find_last_of(const char* s,
size_type pos = npos) const;

// TODO(binji): These are defined by C++17 basic_string_view but not
// implemented here.
// TODO(binji): These are defined by C++17 basic_string_view but not
// implemented here.
#if 0
constexpr size_type find_first_not_of(string_view s, size_type pos = 0) const
noexcept;
@@ -271,7 +271,7 @@ constexpr inline string_view::const_pointer string_view::data() const noexcept {
return data_;
}

} // namespace wabt
} // namespace wabt

namespace std {

@@ -283,6 +283,6 @@ struct hash<::wabt::string_view> {
}
};

}
} // namespace std

#endif // WABT_STRING_VIEW_H_
@@ -43,9 +43,7 @@ struct TestObject {
return *this;
}

TestObject(TestObject&& other) {
*this = std::move(other);
}
TestObject(TestObject&& other) { *this = std::move(other); }

TestObject& operator=(TestObject&& other) {
data = other.data;
@@ -108,7 +106,6 @@ class CircularArrayTest : public ::testing::Test {

} // end anonymous namespace


// Basic API tests

TEST_F(CircularArrayTest, default_constructor) {
@@ -73,9 +73,7 @@ class IntrusiveListTest : public ::testing::Test {
TestObject::creation_count = 0;
}

virtual void TearDown() {
ASSERT_EQ(0, TestObject::creation_count);
}
virtual void TearDown() { ASSERT_EQ(0, TestObject::creation_count); }

TestObjectList NewList(const std::vector<int>& data_values) {
TestObjectList result;
@@ -179,7 +177,6 @@ class IntrusiveListIteratorTest : public IntrusiveListTest {
ASSERT_EQ(count, expected.size());
}


TestObjectList list_;
const TestObjectList& clist_ = list_;
};
@@ -0,0 +1,111 @@
/*
* Copyright 2018 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <cstdio>
#include <thread>
#include <vector>

#include "gtest/gtest.h"

#include "src/literal.h"

using namespace wabt;

namespace {

void AssertHexFloatEquals(uint32_t expected_bits, const char* s) {
uint32_t actual_bits;
ASSERT_EQ(Result::Ok,
ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
ASSERT_EQ(expected_bits, actual_bits);
}

void AssertHexFloatFails(const char* s) {
uint32_t actual_bits;
ASSERT_EQ(Result::Error,
ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
}

void AssertHexDoubleEquals(uint64_t expected_bits, const char* s) {
uint64_t actual_bits;
ASSERT_EQ(Result::Ok,
ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
ASSERT_EQ(expected_bits, actual_bits);
}

void AssertHexDoubleFails(const char* s) {
uint64_t actual_bits;
ASSERT_EQ(Result::Error,
ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
}

} // end anonymous namespace

TEST(ParseFloat, NonCanonical) {
AssertHexFloatEquals(0x3f800000, "0x00000000000000000000001.0p0");
AssertHexFloatEquals(0x3f800000, "0x1.00000000000000000000000p0");
AssertHexFloatEquals(0x3f800000, "0x0.0000000000000000000001p88");
}

TEST(ParseFloat, Rounding) {
// |------- 23 bits -----| V-- extra bit
//
// 11111111111111111111101 0 ==> no rounding
AssertHexFloatEquals(0x7f7ffffd, "0x1.fffffap127");
// 11111111111111111111101 1 ==> round up
AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffbp127");
// 11111111111111111111110 0 ==> no rounding
AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffcp127");
// 11111111111111111111110 1 ==> round down
AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffdp127");
// 11111111111111111111111 0 ==> no rounding
AssertHexFloatEquals(0x7f7fffff, "0x1.fffffep127");
}

TEST(ParseFloat, OutOfRange) {
AssertHexFloatFails("0x1p128");
AssertHexFloatFails("-0x1p128");
AssertHexFloatFails("0x1.ffffffp127");
AssertHexFloatFails("-0x1.ffffffp127");
}

TEST(ParseDouble, NonCanonical) {
AssertHexDoubleEquals(0x3ff0000000000000, "0x00000000000000000000001.0p0");
AssertHexDoubleEquals(0x3ff0000000000000, "0x1.00000000000000000000000p0");
AssertHexDoubleEquals(0x3ff0000000000000, "0x0.0000000000000000000001p88");
}

TEST(ParseDouble, Rounding) {
// |-------------------- 52 bits ---------------------| V-- extra bit
//
// 1111111111111111111111111111111111111111111111111101 0 ==> no rounding
AssertHexDoubleEquals(0x7feffffffffffffd, "0x1.ffffffffffffd0p1023");
// 1111111111111111111111111111111111111111111111111101 1 ==> round up
AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffd8p1023");
// 1111111111111111111111111111111111111111111111111110 0 ==> no rounding
AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe0p1023");
// 1111111111111111111111111111111111111111111111111110 1 ==> round down
AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe8p1023");
// 1111111111111111111111111111111111111111111111111111 0 ==> no rounding
AssertHexDoubleEquals(0x7fefffffffffffff, "0x1.fffffffffffff0p1023");
}

TEST(ParseDouble, OutOfRange) {
AssertHexDoubleFails("0x1p1024");
AssertHexDoubleFails("-0x1p1024");
AssertHexDoubleFails("0x1.fffffffffffff8p1023");
AssertHexDoubleFails("-0x1.fffffffffffff8p1023");
}
@@ -58,21 +58,15 @@ TEST(utf8, valid_empty) {
}

TEST(utf8, valid_1_byte) {
FOR_RANGE(cu0, 0, 0x80) {
assert_is_valid_utf8(true, 1, cu0);
}
FOR_RANGE(cu0, 0, 0x80) { assert_is_valid_utf8(true, 1, cu0); }
}

TEST(utf8, invalid_continuation_bytes) {
FOR_RANGE(cu0, 0x80, 0xc0) {
assert_is_valid_utf8(false, 1, cu0);
}
FOR_RANGE(cu0, 0x80, 0xc0) { assert_is_valid_utf8(false, 1, cu0); }
}

TEST(utf8, invalid_2_byte) {
FOR_RANGE(cu0, 0xc0, 0xc2) {
assert_is_valid_utf8(false, 1, cu0);
}
FOR_RANGE(cu0, 0xc0, 0xc2) { assert_is_valid_utf8(false, 1, cu0); }
}

TEST(utf8, valid_2_bytes) {
@@ -69,6 +69,8 @@ const char* GetTokenTypeName(TokenType token_type) {
"ATOMIC_RMW",
"ATOMIC_RMW_CMPXCHG",
"ATOMIC_STORE",
"ATOMIC_WAIT",
"ATOMIC_WAKE",
"BINARY",
"block",
"br",
@@ -77,7 +79,6 @@ const char* GetTokenTypeName(TokenType token_type) {
"call",
"call_indirect",
"catch",
"catch_all",
"COMPARE",
"CONST",
"CONVERT",
@@ -89,6 +90,7 @@ const char* GetTokenTypeName(TokenType token_type) {
"get_local",
"grow_memory",
"if",
"if_except",
"LOAD",
"loop",
"nop",
@@ -99,12 +101,13 @@ const char* GetTokenTypeName(TokenType token_type) {
"set_local",
"STORE",
"tee_local",
"TERNARY",
"throw",
"try",
"UNARY",
"SIMDLANEOP",
"SIMDSHUFFLEOP",
"unreachable",
"WAIT",
"WAKE",

// String.
"align=",
@@ -96,7 +96,6 @@ enum class TokenType {
Call,
CallIndirect,
Catch,
CatchAll,
Compare,
Const,
Convert,
@@ -108,6 +107,7 @@ enum class TokenType {
GetLocal,
GrowMemory,
If,
IfExcept,
Load,
Loop,
Nop,
@@ -118,9 +118,12 @@ enum class TokenType {
SetLocal,
Store,
TeeLocal,
Ternary,
Throw,
Try,
Unary,
SimdLaneOp,
SimdShuffleOp,
Unreachable,
First_Opcode = AtomicLoad,
Last_Opcode = Unreachable,
@@ -196,9 +199,15 @@ struct Token {
return text_;
}

Type type() const { assert(HasType()); return type_; }
Type type() const {
assert(HasType());
return type_;
}

Opcode opcode() const { assert(HasOpcode()); return opcode_; }
Opcode opcode() const {
assert(HasOpcode());
return opcode_;
}

const Literal& literal() const {
assert(HasLiteral());
@@ -223,4 +232,4 @@ struct Token {

} // namespace wabt

#endif // WABT_TOKEN_H_
#endif // WABT_TOKEN_H_
@@ -222,6 +222,7 @@ class JSONParser {
wabt::Result ParseConst(TypedValue* out_value);
wabt::Result ParseConstVector(TypedValues* out_values);
wabt::Result ParseAction(Action* out_action);
wabt::Result ParseActionResult();
wabt::Result ParseModuleType(ModuleType* out_type);

std::string CreateModulePath(string_view filename);
@@ -553,6 +554,14 @@ wabt::Result JSONParser::ParseAction(Action* out_action) {
return wabt::Result::Ok;
}

wabt::Result JSONParser::ParseActionResult() {
// Not needed for wabt-interp, but useful for other parsers.
EXPECT_KEY("expected");
TypeVector expected;
CHECK_RESULT(ParseTypeVector(&expected));
return wabt::Result::Ok;
}

wabt::Result JSONParser::ParseModuleType(ModuleType* out_type) {
std::string module_type_str;

@@ -628,6 +637,8 @@ wabt::Result JSONParser::ParseCommand(CommandPtr* out_command) {
CHECK_RESULT(ParseLine(&command->line));
EXPECT(",");
CHECK_RESULT(ParseAction(&command->action));
EXPECT(",");
CHECK_RESULT(ParseActionResult());
*out_command = std::move(command);
} else if (Match("\"register\"")) {
auto command = MakeUnique<RegisterCommand>();
@@ -698,10 +709,7 @@ wabt::Result JSONParser::ParseCommand(CommandPtr* out_command) {
EXPECT(",");
CHECK_RESULT(ParseAction(&command->action));
EXPECT(",");
// Not needed for wabt-interp, but useful for other parsers.
EXPECT_KEY("expected");
TypeVector expected;
CHECK_RESULT(ParseTypeVector(&expected));
CHECK_RESULT(ParseActionResult());
*out_command = std::move(command);
} else if (Match("\"assert_return_arithmetic_nan\"")) {
auto command = MakeUnique<AssertReturnArithmeticNanCommand>();
@@ -710,10 +718,7 @@ wabt::Result JSONParser::ParseCommand(CommandPtr* out_command) {
EXPECT(",");
CHECK_RESULT(ParseAction(&command->action));
EXPECT(",");
// Not needed for wabt-interp, but useful for other parsers.
EXPECT_KEY("expected");
TypeVector expected;
CHECK_RESULT(ParseTypeVector(&expected));
CHECK_RESULT(ParseActionResult());
*out_command = std::move(command);
} else if (Match("\"assert_trap\"")) {
auto command = MakeUnique<AssertTrapCommand>();
@@ -723,13 +728,17 @@ wabt::Result JSONParser::ParseCommand(CommandPtr* out_command) {
CHECK_RESULT(ParseAction(&command->action));
EXPECT(",");
PARSE_KEY_STRING_VALUE("text", &command->text);
EXPECT(",");
CHECK_RESULT(ParseActionResult());
*out_command = std::move(command);
} else if (Match("\"assert_exhaustion\"")) {
auto command = MakeUnique<AssertExhaustionCommand>();
EXPECT(",");
CHECK_RESULT(ParseLine(&command->line));
EXPECT(",");
CHECK_RESULT(ParseAction(&command->action));
EXPECT(",");
CHECK_RESULT(ParseActionResult());
*out_command = std::move(command);
} else {
PrintError("unknown command type");
@@ -838,7 +847,11 @@ class SpectestHostImportDelegate : public HostImportDelegate {
interp::Func* func,
interp::FuncSignature* func_sig,
const ErrorCallback& callback) override {
if (import->field_name == "print") {
if (import->field_name == "print" || import->field_name == "print_i32" ||
import->field_name == "print_f32" ||
import->field_name == "print_f64" ||
import->field_name == "print_i32_f32" ||
import->field_name == "print_f64_f64") {
cast<HostFunc>(func)->callback = DefaultHostCallback;
return wabt::Result::Ok;
} else {
@@ -882,34 +895,23 @@ class SpectestHostImportDelegate : public HostImportDelegate {
wabt::Result ImportGlobal(interp::GlobalImport* import,
interp::Global* global,
const ErrorCallback& callback) override {
if (import->field_name == "global") {
switch (global->typed_value.type) {
case Type::I32:
global->typed_value.value.i32 = 666;
break;

case Type::F32: {
float value = 666.6f;
memcpy(&global->typed_value.value.f32_bits, &value, sizeof(value));
break;
}

case Type::I64:
global->typed_value.value.i64 = 666;
break;

case Type::F64: {
double value = 666.6;
memcpy(&global->typed_value.value.f64_bits, &value, sizeof(value));
break;
}

default:
PrintError(callback, "bad type for host global import " PRIimport,
PRINTF_IMPORT_ARG(*import));
return wabt::Result::Error;
}

if (import->field_name == "global_i32") {
global->typed_value.type = Type::I32;
global->typed_value.value.i32 = 666;
return wabt::Result::Ok;
} else if (import->field_name == "global_f32") {
global->typed_value.type = Type::F32;
float value = 666.6f;
memcpy(&global->typed_value.value.f32_bits, &value, sizeof(value));
return wabt::Result::Ok;
} else if (import->field_name == "global_i64") {
global->typed_value.type = Type::I64;
global->typed_value.value.i64 = 666;
return wabt::Result::Ok;
} else if (import->field_name == "global_f64") {
global->typed_value.type = Type::F64;
double value = 666.6;
memcpy(&global->typed_value.value.f64_bits, &value, sizeof(value));
return wabt::Result::Ok;
} else {
PrintError(callback, "unknown host global import " PRIimport,

This file was deleted.

@@ -0,0 +1,149 @@
/*
* Copyright 2017 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <cassert>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>

#include "src/apply-names.h"
#include "src/binary-reader.h"
#include "src/binary-reader-ir.h"
#include "src/error-handler.h"
#include "src/feature.h"
#include "src/generate-names.h"
#include "src/ir.h"
#include "src/option-parser.h"
#include "src/stream.h"
#include "src/validator.h"
#include "src/wast-lexer.h"

#include "src/c-writer.h"

using namespace wabt;

static int s_verbose;
static std::string s_infile;
static std::string s_outfile;
static Features s_features;
static WriteCOptions s_write_c_options;
static bool s_read_debug_names = true;
static std::unique_ptr<FileStream> s_log_stream;

static const char s_description[] =
R"( Read a file in the WebAssembly binary format, and convert it to
a C source file and header.
examples:
# parse binary file test.wasm and write test.c and test.h
$ wasm2c test.wasm -o test.c
# parse test.wasm, write test.c and test.h, but ignore the debug names, if any
$ wasm2c test.wasm --no-debug-names -o test.c
)";

static void ParseOptions(int argc, char** argv) {
OptionParser parser("wasm2c", s_description);

parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
s_verbose++;
s_log_stream = FileStream::CreateStdout();
});
parser.AddHelpOption();
parser.AddOption(
'o', "output", "FILENAME",
"Output file for the generated C source file, by default use stdout",
[](const char* argument) {
s_outfile = argument;
ConvertBackslashToSlash(&s_outfile);
});
s_features.AddOptions(&parser);
parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
[]() { s_read_debug_names = false; });
parser.AddArgument("filename", OptionParser::ArgumentCount::One,
[](const char* argument) {
s_infile = argument;
ConvertBackslashToSlash(&s_infile);
});
parser.Parse(argc, argv);
}

// TODO(binji): copied from binary-writer-spec.cc, probably should share.
static string_view strip_extension(string_view s) {
string_view ext = s.substr(s.find_last_of('.'));
string_view result = s;

if (ext == ".c")
result.remove_suffix(ext.length());
return result;
}

int ProgramMain(int argc, char** argv) {
Result result;

InitStdio();
ParseOptions(argc, argv);

std::vector<uint8_t> file_data;
result = ReadFile(s_infile.c_str(), &file_data);
if (Succeeded(result)) {
ErrorHandlerFile error_handler(Location::Type::Binary);
Module module;
const bool kStopOnFirstError = true;
ReadBinaryOptions options(s_features, s_log_stream.get(),
s_read_debug_names, kStopOnFirstError);
result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
&options, &error_handler, &module);
if (Succeeded(result)) {
if (Succeeded(result)) {
ValidateOptions options(s_features);
WastLexer* lexer = nullptr;
result = ValidateModule(lexer, &module, &error_handler, &options);
result |= GenerateNames(&module);
}

if (Succeeded(result)) {
/* TODO(binji): This shouldn't fail; if a name can't be applied
* (because the index is invalid, say) it should just be skipped. */
Result dummy_result = ApplyNames(&module);
WABT_USE(dummy_result);
}

if (Succeeded(result)) {
if (!s_outfile.empty()) {
std::string header_name =
strip_extension(s_outfile).to_string() + ".h";
FileStream c_stream(s_outfile.c_str());
FileStream h_stream(header_name);
result = WriteC(&c_stream, &h_stream, header_name.c_str(), &module,
&s_write_c_options);
} else {
FileStream stream(stdout);
result =
WriteC(&stream, &stream, "wasm.h", &module, &s_write_c_options);
}
}
}
}
return result != Result::Ok;
}

int main(int argc, char** argv) {
WABT_TRY
return ProgramMain(argc, argv);
WABT_CATCH_BAD_ALLOC_AND_EXIT
}

@@ -76,6 +76,8 @@ static void ParseOptions(int argc, char** argv) {
s_features.AddOptions(&parser);
parser.AddOption("inline-exports", "Write all exports inline",
[]() { s_write_wat_options.inline_export = true; });
parser.AddOption("inline-imports", "Write all imports inline",
[]() { s_write_wat_options.inline_import = true; });
parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
[]() { s_read_debug_names = false; });
parser.AddOption(
@@ -28,6 +28,7 @@
#include "src/common.h"
#include "src/error-handler.h"
#include "src/feature.h"
#include "src/filenames.h"
#include "src/ir.h"
#include "src/option-parser.h"
#include "src/resolve-names.h"
@@ -72,7 +73,7 @@ static void ParseOptions(int argc, char* argv[]) {
[](const char* argument) { s_outfile = argument; });
parser.AddOption(
'r', "relocatable",
"Create a relocatable wasm binary (suitable for linking with wasm-link)",
"Create a relocatable wasm binary (suitable for linking with e.g. lld)",
[]() { s_write_binary_options.relocatable = true; });
parser.AddOption(
"no-canonicalize-leb128s",
@@ -115,12 +116,23 @@ int ProgramMain(int argc, char** argv) {
}

if (Succeeded(result)) {
WriteBinarySpecOptions write_binary_spec_options;
write_binary_spec_options.log_stream = s_log_stream.get();
write_binary_spec_options.json_filename = s_outfile;
write_binary_spec_options.write_binary_options = s_write_binary_options;
result = WriteBinarySpecScript(script.get(), s_infile,
&write_binary_spec_options);
std::vector<FilenameMemoryStreamPair> module_streams;
MemoryStream json_stream;

std::string module_filename_noext =
StripExtension(s_outfile ? s_outfile : s_infile).to_string();
result = WriteBinarySpecScript(
&json_stream, script.get(), s_infile, module_filename_noext,
&s_write_binary_options, &module_streams, s_log_stream.get());

if (s_outfile) {
json_stream.WriteToFile(s_outfile);
}

for (auto iter = module_streams.begin(); iter != module_streams.end();
++iter) {
iter->stream->WriteToFile(iter->filename);
}
}
}

@@ -82,7 +82,7 @@ static void ParseOptions(int argc, char* argv[]) {
[](const char* argument) { s_outfile = argument; });
parser.AddOption(
'r', "relocatable",
"Create a relocatable wasm binary (suitable for linking with wasm-link)",
"Create a relocatable wasm binary (suitable for linking with e.g. lld)",
[]() { s_write_binary_options.relocatable = true; });
parser.AddOption(
"no-canonicalize-leb128s",
@@ -41,7 +41,7 @@ struct TraceScope {
WABT_DISALLOW_COPY_AND_ASSIGN(TraceScope);
TraceScope() = delete;
TraceScope(const char* method);
template<typename... Args>
template <typename... Args>
TraceScope(const char* method, const char* format, Args&&... args)
: method_(method) {
PrintEnter(method);
@@ -70,4 +70,4 @@ struct TraceScope {

} // end namespace wabt

#endif // WABT_TRACING_H_
#endif // WABT_TRACING_H_
@@ -16,6 +16,8 @@

#include "src/type-checker.h"

#include <cinttypes>

namespace wabt {

TypeChecker::Label::Label(LabelType label_type,
@@ -124,8 +126,9 @@ void TypeChecker::PushType(Type type) {
}

void TypeChecker::PushTypes(const TypeVector& types) {
for (Type type : types)
for (Type type : types) {
PushType(type);
}
}

Result TypeChecker::CheckTypeStackEnd(const char* desc) {
@@ -146,8 +149,9 @@ Result TypeChecker::CheckType(Type actual, Type expected) {

Result TypeChecker::CheckSignature(const TypeVector& sig) {
Result result = Result::Ok;
for (size_t i = 0; i < sig.size(); ++i)
for (size_t i = 0; i < sig.size(); ++i) {
result |= PeekAndCheckType(sig.size() - i - 1, sig[i]);
}
return result;
}

@@ -242,48 +246,49 @@ static std::string TypesToString(const TypeVector& types,
void TypeChecker::PrintStackIfFailed(Result result,
const char* desc,
const TypeVector& expected) {
if (Failed(result)) {
size_t limit = 0;
Label* label;
if (Succeeded(TopLabel(&label))) {
limit = label->type_stack_limit;
}

TypeVector actual;
size_t max_depth = type_stack_.size() - limit;

// In general we want to print as many values of the actual stack as were
// expected. However, if the stack was expected to be empty, we should
// print some amount of the actual stack.
size_t actual_size;
if (expected.size() == 0) {
// Don't print too many elements if the stack is really deep.
const size_t kMaxActualStackToPrint = 4;
actual_size = std::min(kMaxActualStackToPrint, max_depth);
} else {
actual_size = std::min(expected.size(), max_depth);
}
if (Succeeded(result)) {
return;
}

bool incomplete_actual_stack = actual_size != max_depth;
size_t limit = 0;
Label* label;
if (Succeeded(TopLabel(&label))) {
limit = label->type_stack_limit;
}

for (size_t i = 0; i < actual_size; ++i) {
Type type;
Result result = PeekType(actual_size - i - 1, &type);
WABT_USE(result);
assert(Succeeded(result));
actual.push_back(type);
}
TypeVector actual;
size_t max_depth = type_stack_.size() - limit;

// In general we want to print as many values of the actual stack as were
// expected. However, if the stack was expected to be empty, we should
// print some amount of the actual stack.
size_t actual_size;
if (expected.size() == 0) {
// Don't print too many elements if the stack is really deep.
const size_t kMaxActualStackToPrint = 4;
actual_size = std::min(kMaxActualStackToPrint, max_depth);
} else {
actual_size = std::min(expected.size(), max_depth);
}

std::string message = "type mismatch in ";
message += desc;
message += ", expected ";
message += TypesToString(expected);
message += " but got ";
message +=
TypesToString(actual, incomplete_actual_stack ? "... " : nullptr);
bool incomplete_actual_stack = actual_size != max_depth;

PrintError("%s", message.c_str());
for (size_t i = 0; i < actual_size; ++i) {
Type type;
Result result = PeekType(actual_size - i - 1, &type);
WABT_USE(result);
assert(Succeeded(result));
actual.push_back(type);
}

std::string message = "type mismatch in ";
message += desc;
message += ", expected ";
message += TypesToString(expected);
message += " but got ";
message += TypesToString(actual, incomplete_actual_stack ? "... " : nullptr);

PrintError("%s", message.c_str());
}

Result TypeChecker::BeginFunction(const TypeVector* sig) {
@@ -401,12 +406,7 @@ Result TypeChecker::OnCompare(Opcode opcode) {
return CheckOpcode2(opcode);
}

Result TypeChecker::OnCatch(const TypeVector* sig) {
PushTypes(*sig);
return Result::Ok;
}

Result TypeChecker::OnCatchBlock(const TypeVector* sig) {
Result TypeChecker::OnCatch() {
Result result = Result::Ok;
Label* label;
CHECK_RESULT(TopLabel(&label));
@@ -416,6 +416,7 @@ Result TypeChecker::OnCatchBlock(const TypeVector* sig) {
ResetTypeStackToLabel(label);
label->label_type = LabelType::Catch;
label->unreachable = false;
PushType(Type::ExceptRef);
return result;
}

@@ -467,14 +468,21 @@ Result TypeChecker::OnEnd(Label* label,

Result TypeChecker::OnEnd() {
Result result = Result::Ok;
static const char* s_label_type_name[] = {"function", "block", "loop", "if",
"if false branch", "try",
static const char* s_label_type_name[] = {"function",
"block",
"loop",
"if",
"if false branch",
"if_except",
"if_except false branch",
"try",
"try catch"};
WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_label_type_name) == kLabelTypeCount);
Label* label;
CHECK_RESULT(TopLabel(&label));
assert(static_cast<int>(label->label_type) < kLabelTypeCount);
if (label->label_type == LabelType::If) {
if (label->label_type == LabelType::If ||
label->label_type == LabelType::IfExcept) {
if (label->sig.size() != 0) {
PrintError("if without else cannot have type signature.");
result = Result::Error;
@@ -495,6 +503,14 @@ Result TypeChecker::OnIf(const TypeVector* sig) {
return result;
}

Result TypeChecker::OnIfExcept(const TypeVector* sig,
const TypeVector* except_sig) {
Result result = PopAndCheck1Type(Type::ExceptRef, "if_except");
PushLabel(LabelType::IfExcept, *sig);
PushTypes(*except_sig);
return result;
}

Result TypeChecker::OnGetGlobal(Type type) {
PushType(type);
return Result::Ok;
@@ -514,29 +530,8 @@ Result TypeChecker::OnLoop(const TypeVector* sig) {
return Result::Ok;
}

Result TypeChecker::OnRethrow(Index depth) {
Result result = Result::Ok;
Label* label;
CHECK_RESULT(GetLabel(depth, &label));
if (label->label_type != LabelType::Catch) {
std::string candidates;
size_t last = label_stack_.size() - 1;
for (size_t i = 0; i < label_stack_.size(); ++i) {
if (label_stack_[last - i].label_type == LabelType::Catch) {
if (!candidates.empty()) {
candidates.append(", ");
}
candidates.append(std::to_string(i));
}
}
if (candidates.empty()) {
PrintError("Rethrow not in try catch block");
} else {
PrintError("invalid rethrow depth: %" PRIindex " (catches: %s)", depth,
candidates.c_str());
}
result = Result::Error;
}
Result TypeChecker::OnRethrow() {
Result result = PopAndCheck1Type(Type::ExceptRef, "rethrow");
CHECK_RESULT(SetUnreachable());
return result;
}
@@ -581,7 +576,7 @@ Result TypeChecker::OnStore(Opcode opcode) {
return CheckOpcode2(opcode);
}

Result TypeChecker::OnTryBlock(const TypeVector* sig) {
Result TypeChecker::OnTry(const TypeVector* sig) {
PushLabel(LabelType::Try, *sig);
return Result::Ok;
}
@@ -597,6 +592,57 @@ Result TypeChecker::OnUnary(Opcode opcode) {
return CheckOpcode1(opcode);
}

Result TypeChecker::OnTernary(Opcode opcode) {
return CheckOpcode3(opcode);
}

Result TypeChecker::OnSimdLaneOp(Opcode opcode, uint64_t lane_idx) {
Result result = Result::Error;
uint32_t lane_count = opcode.GetSimdLaneCount();
if (lane_idx >= lane_count) {
PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count,
lane_idx);
}

switch (opcode) {
case Opcode::I8X16ExtractLaneS:
case Opcode::I8X16ExtractLaneU:
case Opcode::I16X8ExtractLaneS:
case Opcode::I16X8ExtractLaneU:
case Opcode::I32X4ExtractLane:
case Opcode::F32X4ExtractLane:
case Opcode::I64X2ExtractLane:
case Opcode::F64X2ExtractLane:
result = CheckOpcode1(opcode);
break;
case Opcode::I8X16ReplaceLane:
case Opcode::I16X8ReplaceLane:
case Opcode::I32X4ReplaceLane:
case Opcode::F32X4ReplaceLane:
case Opcode::I64X2ReplaceLane:
case Opcode::F64X2ReplaceLane:
result = CheckOpcode2(opcode);
break;
default:
WABT_UNREACHABLE;
}
return result;
}

Result TypeChecker::OnSimdShuffleOp(Opcode opcode, v128 lane_idx) {
Result result = Result::Error;
uint8_t simd_data[16];
memcpy(simd_data, &lane_idx, 16);
for (int i = 0; i < 16; i++) {
if (simd_data[i] >= 32) {
PrintError("lane index must be less than 32 (got %d)", simd_data[i]);
}
}

result = CheckOpcode2(opcode);
return result;
}

Result TypeChecker::OnUnreachable() {
return SetUnreachable();
}
@@ -67,8 +67,7 @@ class TypeChecker {
Result OnCall(const TypeVector* param_types, const TypeVector* result_types);
Result OnCallIndirect(const TypeVector* param_types,
const TypeVector* result_types);
Result OnCatch(const TypeVector* sig);
Result OnCatchBlock(const TypeVector* sig);
Result OnCatch();
Result OnCompare(Opcode);
Result OnConst(Type);
Result OnConvert(Opcode);
@@ -80,17 +79,21 @@ class TypeChecker {
Result OnGetLocal(Type);
Result OnGrowMemory();
Result OnIf(const TypeVector* sig);
Result OnIfExcept(const TypeVector* sig, const TypeVector* except_sig);
Result OnLoad(Opcode);
Result OnLoop(const TypeVector* sig);
Result OnRethrow(Index depth);
Result OnRethrow();
Result OnReturn();
Result OnSelect();
Result OnSetGlobal(Type);
Result OnSetLocal(Type);
Result OnSimdLaneOp(Opcode, uint64_t);
Result OnSimdShuffleOp(Opcode, v128);
Result OnStore(Opcode);
Result OnTeeLocal(Type);
Result OnTernary(Opcode);
Result OnThrow(const TypeVector* sig);
Result OnTryBlock(const TypeVector* sig);
Result OnTry(const TypeVector* sig);
Result OnUnary(Opcode);
Result OnUnreachable();
Result EndFunction();
@@ -25,4 +25,4 @@ bool IsValidUtf8(const char* s, size_t length);

} // namespace wabt

#endif // WABT_UTF8_H_
#endif // WABT_UTF8_H_

Large diffs are not rendered by default.

@@ -47,15 +47,15 @@ function allocateBuffer(buf) {
if (buf instanceof ArrayBuffer) {
size = buf.byteLength;
addr = malloc(size);
(new Uint8Array(Module.buffer, addr, size)).set(new Uint8Array(buf))
(new Uint8Array(HEAP8.buffer, addr, size)).set(new Uint8Array(buf))
} else if (ArrayBuffer.isView(buf)) {
size = buf.buffer.byteLength;
addr = malloc(size);
(new Uint8Array(Module.buffer, addr, size)).set(buf);
(new Uint8Array(HEAP8.buffer, addr, size)).set(buf);
} else if (typeof buf == 'string') {
size = buf.length;
addr = malloc(size);
Module.writeAsciiToMemory(buf, addr, true); // don't null-terminate
writeAsciiToMemory(buf, addr, true); // don't null-terminate
} else {
throw new Error('unknown buffer type: ' + buf);
}
@@ -65,7 +65,7 @@ function allocateBuffer(buf) {
function allocateCString(s) {
var size = s.length;
var addr = malloc(size);
Module.writeAsciiToMemory(s, addr);
writeAsciiToMemory(s, addr);
return {addr: addr, size: size};
}

@@ -111,7 +111,7 @@ OutputBuffer.prototype.toString = function() {

var addr = Module._wabt_output_buffer_get_data(this.addr);
var size = Module._wabt_output_buffer_get_size(this.addr);
return Module.Pointer_stringify(addr, size);
return Pointer_stringify(addr, size);
};

OutputBuffer.prototype.destroy = function() {
@@ -134,7 +134,7 @@ ErrorHandler.prototype = Object.create(Object.prototype);
ErrorHandler.prototype.getMessage = function() {
var addr = Module._wabt_error_handler_buffer_get_data(this.addr);
var size = Module._wabt_error_handler_buffer_get_size(this.addr);
return Module.Pointer_stringify(addr, size);
return Pointer_stringify(addr, size);
}

ErrorHandler.prototype.destroy = function() {

This file was deleted.

@@ -0,0 +1,149 @@
%%includes
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
%%declarations
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#define LIKELY(x) __builtin_expect(!!(x), 1)

#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)

#define FUNC_PROLOGUE \
if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \
TRAP(EXHAUSTION)

#define FUNC_EPILOGUE --wasm_rt_call_stack_depth

#define UNREACHABLE TRAP(UNREACHABLE)

#define CALL_INDIRECT(table, t, ft, x, ...) \
(LIKELY((x) < table.size && table.data[x].func && \
table.data[x].func_type == func_types[ft]) \
? ((t)table.data[x].func)(__VA_ARGS__) \
: TRAP(CALL_INDIRECT))

#define MEMCHECK(mem, a, t) \
if (UNLIKELY((a) + sizeof(t) > mem->size)) TRAP(OOB)

#define DEFINE_LOAD(name, t1, t2, t3) \
static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
MEMCHECK(mem, addr, t1); \
t1 result; \
memcpy(&result, &mem->data[addr], sizeof(t1)); \
return (t3)(t2)result; \
}

#define DEFINE_STORE(name, t1, t2) \
static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \
MEMCHECK(mem, addr, t1); \
t1 wrapped = (t1)value; \
memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \
}

DEFINE_LOAD(i32_load, u32, u32, u32);
DEFINE_LOAD(i64_load, u64, u64, u64);
DEFINE_LOAD(f32_load, f32, f32, f32);
DEFINE_LOAD(f64_load, f64, f64, f64);
DEFINE_LOAD(i32_load8_s, s8, s32, u32);
DEFINE_LOAD(i64_load8_s, s8, s64, u64);
DEFINE_LOAD(i32_load8_u, u8, u32, u32);
DEFINE_LOAD(i64_load8_u, u8, u64, u64);
DEFINE_LOAD(i32_load16_s, s16, s32, u32);
DEFINE_LOAD(i64_load16_s, s16, s64, u64);
DEFINE_LOAD(i32_load16_u, u16, u32, u32);
DEFINE_LOAD(i64_load16_u, u16, u64, u64);
DEFINE_LOAD(i64_load32_s, s32, s64, u64);
DEFINE_LOAD(i64_load32_u, u32, u64, u64);
DEFINE_STORE(i32_store, u32, u32);
DEFINE_STORE(i64_store, u64, u64);
DEFINE_STORE(f32_store, f32, f32);
DEFINE_STORE(f64_store, f64, f64);
DEFINE_STORE(i32_store8, u8, u32);
DEFINE_STORE(i32_store16, u16, u32);
DEFINE_STORE(i64_store8, u8, u64);
DEFINE_STORE(i64_store16, u16, u64);
DEFINE_STORE(i64_store32, u32, u64);

#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)
#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)
#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)
#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)
#define I32_POPCNT(x) (__builtin_popcount(x))
#define I64_POPCNT(x) (__builtin_popcountll(x))

#define DIV_S(ut, min, x, y) \
((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \
: (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \
: (ut)((x) / (y)))

#define REM_S(ut, min, x, y) \
((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \
: (UNLIKELY((x) == min && (y) == -1)) ? 0 \
: (ut)((x) % (y)))

#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y)
#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y)
#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y)
#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y)

#define DIVREM_U(op, x, y) \
((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x) op (y)))

#define DIV_U(x, y) DIVREM_U(/, x, y)
#define REM_U(x, y) DIVREM_U(%, x, y)

#define ROTL(x, y, mask) \
(((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask))))
#define ROTR(x, y, mask) \
(((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask))))

#define I32_ROTL(x, y) ROTL(x, y, 31)
#define I64_ROTL(x, y) ROTL(x, y, 63)
#define I32_ROTR(x, y) ROTR(x, y, 31)
#define I64_ROTR(x, y) ROTR(x, y, 63)

#define FMIN(x, y) \
((UNLIKELY((x) != (x))) ? NAN \
: (UNLIKELY((y) != (y))) ? NAN \
: (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \
: (x < y) ? x : y)

#define FMAX(x, y) \
((UNLIKELY((x) != (x))) ? NAN \
: (UNLIKELY((y) != (y))) ? NAN \
: (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \
: (x > y) ? x : y)

#define TRUNC_S(ut, st, ft, min, max, maxop, x) \
((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \
: (UNLIKELY((x) < (ft)(min) || (x) maxop (ft)(max))) ? TRAP(INT_OVERFLOW) \
: (ut)(st)(x))

#define I32_TRUNC_S_F32(x) TRUNC_S(u32, s32, f32, INT32_MIN, INT32_MAX, >=, x)
#define I64_TRUNC_S_F32(x) TRUNC_S(u64, s64, f32, INT64_MIN, INT64_MAX, >=, x)
#define I32_TRUNC_S_F64(x) TRUNC_S(u32, s32, f64, INT32_MIN, INT32_MAX, >, x)
#define I64_TRUNC_S_F64(x) TRUNC_S(u64, s64, f64, INT64_MIN, INT64_MAX, >=, x)

#define TRUNC_U(ut, ft, max, maxop, x) \
((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \
: (UNLIKELY((x) <= (ft)-1 || (x) maxop (ft)(max))) ? TRAP(INT_OVERFLOW) \
: (ut)(x))

#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, UINT32_MAX, >=, x)
#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, UINT64_MAX, >=, x)
#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, UINT32_MAX, >, x)
#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, UINT64_MAX, >=, x)

#define DEFINE_REINTERPRET(name, t1, t2) \
static inline t2 name(t1 x) { \
t2 result; \
memcpy(&result, &x, sizeof(result)); \
return result; \
}

DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)
DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32)
DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)
DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)

@@ -0,0 +1,36 @@
%%top
#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

#include "wasm-rt.h"

#ifndef WASM_RT_MODULE_PREFIX
#define WASM_RT_MODULE_PREFIX
#endif

#define WASM_RT_PASTE_(x, y) x ## y
#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)
#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)

#define WASM_RT_DEFINE_EXTERNAL(decl, target) decl = &target;

/* TODO(binji): only use stdint.h types in header */
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;
typedef int16_t s16;
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
typedef float f32;
typedef double f64;

extern void WASM_RT_ADD_PREFIX(init)(void);
%%bottom
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,80 @@
#!/usr/bin/env python
#
# Copyright 2018 WebAssembly Community Group participants
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from __future__ import print_function
import argparse
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import os
import sys

def EscapeCString(s):
out = ''
for b in bytearray(s.encode('utf-8')):
if b in (34, 92):
# " or \
out += '\\' + chr(b)
elif b == 10:
# newline
out += '\\n'
elif 32 <= b <= 127:
# printable char
out += chr(b)
else:
# non-printable; write as \xab
out += '\\x%02x' % b

return out


def main(args):
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('-o', '--output', metavar='PATH',
help='output file.')
arg_parser.add_argument('file', help='input file.')
options = arg_parser.parse_args(args)

section_name = None
output = StringIO()

output.write('/* Generated from \'%s\' by wasm2c_tmpl.py, do not edit! */\n' %
os.path.basename(options.file))

with open(options.file) as f:
for line in f.readlines():
if line.startswith('%%'):
if section_name is not None:
output.write(';\n\n');
section_name = line[2:-1]
output.write('const char SECTION_NAME(%s)[] =\n' % section_name)
else:
output.write('"%s"\n' % EscapeCString(line))

output.write(';\n');
if options.output:
with open(options.output, 'w') as outf:
outf.write(output.getvalue())
else:
sys.stdout.write(output.getvalue())

return 0


if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
@@ -104,16 +104,19 @@ WastLexer::~WastLexer() {

// static
std::unique_ptr<WastLexer> WastLexer::CreateFileLexer(string_view filename) {
std::unique_ptr<LexerSource> source(new LexerSourceFile(filename));
return std::unique_ptr<WastLexer>(new WastLexer(std::move(source), filename));
auto source = MakeUnique<LexerSourceFile>(filename);
if (!source->IsOpen()) {
return std::unique_ptr<WastLexer>();
}
return MakeUnique<WastLexer>(std::move(source), filename);
}

// static
std::unique_ptr<WastLexer> WastLexer::CreateBufferLexer(string_view filename,
const void* data,
size_t size) {
std::unique_ptr<LexerSource> source(new LexerSourceBuffer(data, size));
return std::unique_ptr<WastLexer>(new WastLexer(std::move(source), filename));
return MakeUnique<WastLexer>(MakeUnique<LexerSourceBuffer>(data, size),
filename);
}

Location WastLexer::GetLocation() {
@@ -514,12 +517,145 @@ Token WastLexer::GetToken(WastParser* parser) {
<i> "i64.atomic.rmw16_u.cmpxchg" { RETURN_OPCODE(AtomicRmwCmpxchg, I64AtomicRmw16UCmpxchg); }
<i> "i64.atomic.rmw32_u.cmpxchg" { RETURN_OPCODE(AtomicRmwCmpxchg, I64AtomicRmw32UCmpxchg); }
<i> "v128.const" { RETURN_OPCODE(Const, V128Const); }
<i> "v128.load" { RETURN_OPCODE(Load, V128Load); }
<i> "v128.store" { RETURN_OPCODE(Store, V128Store); }
<i> "i8x16.splat" { RETURN_OPCODE(Unary, I8X16Splat); }
<i> "i16x8.splat" { RETURN_OPCODE(Unary, I16X8Splat); }
<i> "i32x4.splat" { RETURN_OPCODE(Unary, I32X4Splat); }
<i> "i64x2.splat" { RETURN_OPCODE(Unary, I64X2Splat); }
<i> "f32x4.splat" { RETURN_OPCODE(Unary, F32X4Splat); }
<i> "f64x2.splat" { RETURN_OPCODE(Unary, F64X2Splat); }
<i> "i8x16.extract_lane_s" { RETURN_OPCODE(SimdLaneOp, I8X16ExtractLaneS); }
<i> "i8x16.extract_lane_u" { RETURN_OPCODE(SimdLaneOp, I8X16ExtractLaneU); }
<i> "i16x8.extract_lane_s" { RETURN_OPCODE(SimdLaneOp, I16X8ExtractLaneS); }
<i> "i16x8.extract_lane_u" { RETURN_OPCODE(SimdLaneOp, I16X8ExtractLaneU); }
<i> "i32x4.extract_lane" { RETURN_OPCODE(SimdLaneOp, I32X4ExtractLane); }
<i> "i64x2.extract_lane" { RETURN_OPCODE(SimdLaneOp, I64X2ExtractLane); }
<i> "f32x4.extract_lane" { RETURN_OPCODE(SimdLaneOp, F32X4ExtractLane); }
<i> "f64x2.extract_lane" { RETURN_OPCODE(SimdLaneOp, F64X2ExtractLane); }
<i> "i8x16.replace_lane" { RETURN_OPCODE(SimdLaneOp, I8X16ReplaceLane); }
<i> "i16x8.replace_lane" { RETURN_OPCODE(SimdLaneOp, I16X8ReplaceLane); }
<i> "i32x4.replace_lane" { RETURN_OPCODE(SimdLaneOp, I32X4ReplaceLane); }
<i> "i64x2.replace_lane" { RETURN_OPCODE(SimdLaneOp, I64X2ReplaceLane); }
<i> "f32x4.replace_lane" { RETURN_OPCODE(SimdLaneOp, F32X4ReplaceLane); }
<i> "f64x2.replace_lane" { RETURN_OPCODE(SimdLaneOp, F64X2ReplaceLane); }
<i> "v8x16.shuffle" { RETURN_OPCODE(SimdShuffleOp, V8X16Shuffle); }
<i> "i8x16.add" { RETURN_OPCODE(Binary, I8X16Add); }
<i> "i16x8.add" { RETURN_OPCODE(Binary, I16X8Add); }
<i> "i32x4.add" { RETURN_OPCODE(Binary, I32X4Add); }
<i> "i64x2.add" { RETURN_OPCODE(Binary, I64X2Add); }
<i> "i8x16.sub" { RETURN_OPCODE(Binary, I8X16Sub); }
<i> "i16x8.sub" { RETURN_OPCODE(Binary, I16X8Sub); }
<i> "i32x4.sub" { RETURN_OPCODE(Binary, I32X4Sub); }
<i> "i64x2.sub" { RETURN_OPCODE(Binary, I64X2Sub); }
<i> "i8x16.mul" { RETURN_OPCODE(Binary, I8X16Mul); }
<i> "i16x8.mul" { RETURN_OPCODE(Binary, I16X8Mul); }
<i> "i32x4.mul" { RETURN_OPCODE(Binary, I32X4Mul); }
<i> "i8x16.neg" { RETURN_OPCODE(Unary, I8X16Neg); }
<i> "i16x8.neg" { RETURN_OPCODE(Unary, I16X8Neg); }
<i> "i32x4.neg" { RETURN_OPCODE(Unary, I32X4Neg); }
<i> "i64x2.neg" { RETURN_OPCODE(Unary, I64X2Neg); }
<i> "i8x16.add_saturate_s" { RETURN_OPCODE(Binary, I8X16AddSaturateS); }
<i> "i8x16.add_saturate_u" { RETURN_OPCODE(Binary, I8X16AddSaturateU); }
<i> "i16x8.add_saturate_s" { RETURN_OPCODE(Binary, I16X8AddSaturateS); }
<i> "i16x8.add_saturate_u" { RETURN_OPCODE(Binary, I16X8AddSaturateU); }
<i> "i8x16.sub_saturate_s" { RETURN_OPCODE(Binary, I8X16SubSaturateS); }
<i> "i8x16.sub_saturate_u" { RETURN_OPCODE(Binary, I8X16SubSaturateU); }
<i> "i16x8.sub_saturate_s" { RETURN_OPCODE(Binary, I16X8SubSaturateS); }
<i> "i16x8.sub_saturate_u" { RETURN_OPCODE(Binary, I16X8SubSaturateU); }
<i> "i8x16.shl" { RETURN_OPCODE(Binary, I8X16Shl); }
<i> "i16x8.shl" { RETURN_OPCODE(Binary, I16X8Shl); }
<i> "i32x4.shl" { RETURN_OPCODE(Binary, I32X4Shl); }
<i> "i64x2.shl" { RETURN_OPCODE(Binary, I64X2Shl); }
<i> "i8x16.shr_s" { RETURN_OPCODE(Binary, I8X16ShrS); }
<i> "i8x16.shr_u" { RETURN_OPCODE(Binary, I8X16ShrU); }
<i> "i16x8.shr_s" { RETURN_OPCODE(Binary, I16X8ShrS); }
<i> "i16x8.shr_u" { RETURN_OPCODE(Binary, I16X8ShrU); }
<i> "i32x4.shr_s" { RETURN_OPCODE(Binary, I32X4ShrS); }
<i> "i32x4.shr_u" { RETURN_OPCODE(Binary, I32X4ShrU); }
<i> "i64x2.shr_s" { RETURN_OPCODE(Binary, I64X2ShrS); }
<i> "i64x2.shr_u" { RETURN_OPCODE(Binary, I64X2ShrU); }
<i> "v128.and" { RETURN_OPCODE(Binary, V128And); }
<i> "v128.or" { RETURN_OPCODE(Binary, V128Or); }
<i> "v128.xor" { RETURN_OPCODE(Binary, V128Xor); }
<i> "v128.not" { RETURN_OPCODE(Unary, V128Not); }
<i> "v128.bitselect" { RETURN_OPCODE(Ternary, V128BitSelect); }
<i> "i8x16.any_true" { RETURN_OPCODE(Unary, I8X16AnyTrue); }
<i> "i16x8.any_true" { RETURN_OPCODE(Unary, I16X8AnyTrue); }
<i> "i32x4.any_true" { RETURN_OPCODE(Unary, I32X4AnyTrue); }
<i> "i64x2.any_true" { RETURN_OPCODE(Unary, I64X2AnyTrue); }
<i> "i8x16.all_true" { RETURN_OPCODE(Unary, I8X16AllTrue); }
<i> "i16x8.all_true" { RETURN_OPCODE(Unary, I16X8AllTrue); }
<i> "i32x4.all_true" { RETURN_OPCODE(Unary, I32X4AllTrue); }
<i> "i64x2.all_true" { RETURN_OPCODE(Unary, I64X2AllTrue); }
<i> "i8x16.eq" { RETURN_OPCODE(Compare, I8X16Eq); }
<i> "i16x8.eq" { RETURN_OPCODE(Compare, I16X8Eq); }
<i> "i32x4.eq" { RETURN_OPCODE(Compare, I32X4Eq); }
<i> "f32x4.eq" { RETURN_OPCODE(Compare, F32X4Eq); }
<i> "f64x2.eq" { RETURN_OPCODE(Compare, F64X2Eq); }
<i> "i8x16.ne" { RETURN_OPCODE(Compare, I8X16Ne); }
<i> "i16x8.ne" { RETURN_OPCODE(Compare, I16X8Ne); }
<i> "i32x4.ne" { RETURN_OPCODE(Compare, I32X4Ne); }
<i> "f32x4.ne" { RETURN_OPCODE(Compare, F32X4Ne); }
<i> "f64x2.ne" { RETURN_OPCODE(Compare, F64X2Ne); }
<i> "i8x16.lt_s" { RETURN_OPCODE(Compare, I8X16LtS); }
<i> "i8x16.lt_u" { RETURN_OPCODE(Compare, I8X16LtU); }
<i> "i16x8.lt_s" { RETURN_OPCODE(Compare, I16X8LtS); }
<i> "i16x8.lt_u" { RETURN_OPCODE(Compare, I16X8LtU); }
<i> "i32x4.lt_s" { RETURN_OPCODE(Compare, I32X4LtS); }
<i> "i32x4.lt_u" { RETURN_OPCODE(Compare, I32X4LtU); }
<i> "f32x4.lt" { RETURN_OPCODE(Compare, F32X4Lt); }
<i> "f64x2.lt" { RETURN_OPCODE(Compare, F64X2Lt); }
<i> "i8x16.le_s" { RETURN_OPCODE(Compare, I8X16LeS); }
<i> "i8x16.le_u" { RETURN_OPCODE(Compare, I8X16LeU); }
<i> "i16x8.le_s" { RETURN_OPCODE(Compare, I16X8LeS); }
<i> "i16x8.le_u" { RETURN_OPCODE(Compare, I16X8LeU); }
<i> "i32x4.le_s" { RETURN_OPCODE(Compare, I32X4LeS); }
<i> "i32x4.le_u" { RETURN_OPCODE(Compare, I32X4LeU); }
<i> "f32x4.le" { RETURN_OPCODE(Compare, F32X4Le); }
<i> "f64x2.le" { RETURN_OPCODE(Compare, F64X2Le); }
<i> "i8x16.gt_s" { RETURN_OPCODE(Compare, I8X16GtS); }
<i> "i8x16.gt_u" { RETURN_OPCODE(Compare, I8X16GtU); }
<i> "i16x8.gt_s" { RETURN_OPCODE(Compare, I16X8GtS); }
<i> "i16x8.gt_u" { RETURN_OPCODE(Compare, I16X8GtU); }
<i> "i32x4.gt_s" { RETURN_OPCODE(Compare, I32X4GtS); }
<i> "i32x4.gt_u" { RETURN_OPCODE(Compare, I32X4GtU); }
<i> "f32x4.gt" { RETURN_OPCODE(Compare, F32X4Gt); }
<i> "f64x2.gt" { RETURN_OPCODE(Compare, F64X2Gt); }
<i> "i8x16.ge_s" { RETURN_OPCODE(Compare, I8X16GeS); }
<i> "i8x16.ge_u" { RETURN_OPCODE(Compare, I8X16GeU); }
<i> "i16x8.ge_s" { RETURN_OPCODE(Compare, I16X8GeS); }
<i> "i16x8.ge_u" { RETURN_OPCODE(Compare, I16X8GeU); }
<i> "i32x4.ge_s" { RETURN_OPCODE(Compare, I32X4GeS); }
<i> "i32x4.ge_u" { RETURN_OPCODE(Compare, I32X4GeU); }
<i> "f32x4.ge" { RETURN_OPCODE(Compare, F32X4Ge); }
<i> "f64x2.ge" { RETURN_OPCODE(Compare, F64X2Ge); }
<i> "f32x4.neg" { RETURN_OPCODE(Unary, F32X4Neg); }
<i> "f64x2.neg" { RETURN_OPCODE(Unary, F64X2Neg); }
<i> "f32x4.abs" { RETURN_OPCODE(Unary, F32X4Abs); }
<i> "f64x2.abs" { RETURN_OPCODE(Unary, F64X2Abs); }
<i> "f32x4.min" { RETURN_OPCODE(Binary, F32X4Min); }
<i> "f64x2.min" { RETURN_OPCODE(Binary, F64X2Min); }
<i> "f32x4.max" { RETURN_OPCODE(Binary, F32X4Max); }
<i> "f64x2.max" { RETURN_OPCODE(Binary, F64X2Max); }
<i> "f32x4.add" { RETURN_OPCODE(Binary, F32X4Add); }
<i> "f64x2.add" { RETURN_OPCODE(Binary, F64X2Add); }
<i> "f32x4.sub" { RETURN_OPCODE(Binary, F32X4Sub); }
<i> "f64x2.sub" { RETURN_OPCODE(Binary, F64X2Sub); }
<i> "f32x4.div" { RETURN_OPCODE(Binary, F32X4Div); }
<i> "f64x2.div" { RETURN_OPCODE(Binary, F64X2Div); }
<i> "f32x4.mul" { RETURN_OPCODE(Binary, F32X4Mul); }
<i> "f64x2.mul" { RETURN_OPCODE(Binary, F64X2Mul); }
<i> "f32x4.sqrt" { RETURN_OPCODE(Unary, F32X4Sqrt); }
<i> "f64x2.sqrt" { RETURN_OPCODE(Unary, F64X2Sqrt); }
<i> "f32x4.convert_s/i32x4" { RETURN_OPCODE(Unary, F32X4ConvertSI32X4); }
<i> "f32x4.convert_u/i32x4" { RETURN_OPCODE(Unary, F32X4ConvertUI32X4); }
<i> "f64x2.convert_s/i64x2" { RETURN_OPCODE(Unary, F64X2ConvertSI64X2); }
<i> "f64x2.convert_u/i64x2" { RETURN_OPCODE(Unary, F64X2ConvertUI64X2); }
<i> "i32x4.trunc_s/f32x4:sat" { RETURN_OPCODE(Unary, I32X4TruncSF32X4Sat); }
<i> "i32x4.trunc_u/f32x4:sat" { RETURN_OPCODE(Unary, I32X4TruncUF32X4Sat); }
<i> "i64x2.trunc_s/f64x2:sat" { RETURN_OPCODE(Unary, I64X2TruncSF64X2Sat); }
<i> "i64x2.trunc_u/f64x2:sat" { RETURN_OPCODE(Unary, I64X2TruncUF64X2Sat); }
<i> "type" { RETURN(Type); }
<i> "func" { RETURN(Func); }
@@ -552,9 +688,9 @@ Token WastLexer::GetToken(WastParser* parser) {
<i> "assert_exhaustion" { RETURN(AssertExhaustion); }
<i> "try" { RETURN_OPCODE0(Try); }
<i> "catch" { RETURN_OPCODE0(Catch); }
<i> "catch_all" { RETURN_OPCODE0(CatchAll); }
<i> "throw" { RETURN_OPCODE0(Throw); }
<i> "rethrow" { RETURN_OPCODE0(Rethrow); }
<i> "if_except" { RETURN_OPCODE0(IfExcept); }
<i> name { RETURN_TEXT(Var); }
<i> "shared" { RETURN(Shared); }
@@ -62,8 +62,8 @@ class WastLexer {
std::string filename_;
int line_;
int comment_nesting_;
size_t buffer_file_offset_; // File offset of the start of the buffer.
size_t line_file_offset_; // File offset of the start of the current line.
size_t buffer_file_offset_; // File offset of the start of the buffer.
size_t line_file_offset_; // File offset of the start of the current line.

// Lexing data needed by re2c.
bool eof_;
@@ -16,11 +16,11 @@

#include "src/wast-parser.h"

#include "src/binary-reader.h"
#include "src/binary-reader-ir.h"
#include "src/binary-reader.h"
#include "src/cast.h"
#include "src/expr-visitor.h"
#include "src/error-handler.h"
#include "src/expr-visitor.h"
#include "src/make-unique.h"
#include "src/utf8.h"
#include "src/wast-parser-lexer-shared.h"
@@ -172,6 +172,9 @@ bool IsPlainInstr(TokenType token_type) {
case TokenType::AtomicRmwCmpxchg:
case TokenType::AtomicWake:
case TokenType::AtomicWait:
case TokenType::Ternary:
case TokenType::SimdLaneOp:
case TokenType::SimdShuffleOp:
return true;
default:
return false;
@@ -183,6 +186,7 @@ bool IsBlockInstr(TokenType token_type) {
case TokenType::Block:
case TokenType::Loop:
case TokenType::If:
case TokenType::IfExcept:
case TokenType::Try:
return true;
default:
@@ -202,10 +206,6 @@ bool IsInstr(TokenTypePair pair) {
return IsPlainOrBlockInstr(pair[0]) || IsExpr(pair);
}

bool IsCatch(TokenType token_type) {
return token_type == TokenType::Catch || token_type == TokenType::CatchAll;
}

bool IsModuleField(TokenTypePair pair) {
if (pair[0] != TokenType::Lpar) {
return false;
@@ -862,8 +862,10 @@ Result WastParser::ParseFuncModuleField(Module* module) {
Func& func = field->func;
CHECK_RESULT(ParseTypeUseOpt(&func.decl));
CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.param_bindings));
CHECK_RESULT(ParseBoundValueTypeList(TokenType::Local, &func.local_types,
TypeVector local_types;
CHECK_RESULT(ParseBoundValueTypeList(TokenType::Local, &local_types,
&func.local_bindings));
func.local_types.Set(local_types);
CHECK_RESULT(ParseTerminatingInstrList(&func.exprs));
module->AppendField(std::move(field));
}
@@ -1294,7 +1296,6 @@ Result WastParser::ParsePlainLoadStoreInstr(Location loc,
return Result::Ok;
}


Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
WABT_TRACE(ParsePlainInstr);
Location loc = GetLocation();
@@ -1439,7 +1440,7 @@ Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {

case TokenType::Rethrow:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParsePlainInstrVar<RethrowExpr>(loc, out_expr));
out_expr->reset(new RethrowExpr(loc));
break;

case TokenType::AtomicWake: {
@@ -1490,6 +1491,32 @@ Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
break;
}

case TokenType::Ternary: {
Token token = Consume();
ErrorUnlessOpcodeEnabled(token);
out_expr->reset(new TernaryExpr(token.opcode(), loc));
break;
}

case TokenType::SimdLaneOp: {
Token token = Consume();
ErrorUnlessOpcodeEnabled(token);
uint64_t lane_idx;
CHECK_RESULT(ParseNat(&lane_idx));
out_expr->reset(new SimdLaneOpExpr(token.opcode(), lane_idx, loc));
break;
}

case TokenType::SimdShuffleOp: {
Token token = Consume();
ErrorUnlessOpcodeEnabled(token);
Const const_;
CHECK_RESULT((ParseSimdConst(&const_, Type::I32, sizeof(v128))));
out_expr->reset(
new SimdShuffleOpExpr(token.opcode(), const_.v128_bits, loc));
break;
}

default:
assert(
!"ParsePlainInstr should only be called when IsPlainInstr() is true");
@@ -1502,16 +1529,18 @@ Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
// Current Simd const type is V128 const only.
// The current expected V128 const lists is:
// i32 0xXXXXXXXX 0xXXXXXXXX 0xXXXXXXXX 0xXXXXXXXX
Result WastParser::ParseSimdConst(Const* const_, Type in_type, int32_t nSimdConstBytes) {
Result WastParser::ParseSimdConst(Const* const_,
Type in_type,
int32_t nSimdConstBytes) {
WABT_TRACE(ParseSimdConst);

// Parse the Simd Consts according to input data type.
switch (in_type) {
case Type::I32: {
const_->loc = GetLocation();
int Count = nSimdConstBytes/sizeof(uint32_t);
int Count = nSimdConstBytes / sizeof(uint32_t);
// Meet expected "i32" token. start parse 4 i32 consts
for(int i=0; i<Count; i++) {
for (int i = 0; i < Count; i++) {
Location loc = GetLocation();

// Expected one 0xXXXXXXXX number
@@ -1525,8 +1554,8 @@ Result WastParser::ParseSimdConst(Const* const_, Type in_type, int32_t nSimdCons
const char* end = sv.end();
Result result;

result =
ParseInt32(s, end, &(const_->v128_bits.v[i]), ParseIntType::SignedAndUnsigned);
result = ParseInt32(s, end, &(const_->v128_bits.v[i]),
ParseIntType::SignedAndUnsigned);

if (Failed(result)) {
Error(loc, "invalid literal \"%s\"", literal.text.c_str());
@@ -1568,8 +1597,9 @@ Result WastParser::ParseConst(Const* const_) {
}
case TokenType::ValueType: {
// ValueType token is valid here only when after a Simd const opcode.
if(opcode != Opcode::V128Const) {
return ErrorExpected({"a numeric literal for non-simd const opcode"}, "123, -45, 6.7e8");
if (opcode != Opcode::V128Const) {
return ErrorExpected({"a numeric literal for non-simd const opcode"},
"123, -45, 6.7e8");
}
// Get Simd Const input type.
in_type = Consume().type();
@@ -1608,8 +1638,11 @@ Result WastParser::ParseConst(Const* const_) {
const_->type = Type::V128;
// Parse V128 Simd Const (16 bytes).
result = ParseSimdConst(const_, in_type, sizeof(v128));
// ParseSimdConst report error already, just return here if parser get errors.
if (Failed(result)) return Result::Error;
// ParseSimdConst report error already, just return here if parser get
// errors.
if (Failed(result)) {
return Result::Error;
}
break;

default:
@@ -1681,14 +1714,29 @@ Result WastParser::ParseBlockInstr(std::unique_ptr<Expr>* out_expr) {
break;
}

case TokenType::IfExcept: {
ErrorUnlessOpcodeEnabled(Consume());
auto expr = MakeUnique<IfExceptExpr>(loc);
CHECK_RESULT(ParseIfExceptHeader(expr.get()));
CHECK_RESULT(ParseInstrList(&expr->true_.exprs));
if (Match(TokenType::Else)) {
CHECK_RESULT(ParseEndLabelOpt(expr->true_.label));
CHECK_RESULT(ParseTerminatingInstrList(&expr->false_));
}
EXPECT(End);
CHECK_RESULT(ParseEndLabelOpt(expr->true_.label));
*out_expr = std::move(expr);
break;
}

case TokenType::Try: {
ErrorUnlessOpcodeEnabled(Consume());
auto expr = MakeUnique<TryExpr>(loc);
CatchVector catches;
CHECK_RESULT(ParseLabelOpt(&expr->block.label));
CHECK_RESULT(ParseBlock(&expr->block));
CHECK_RESULT(ParseCatchInstrList(&expr->catches));
CHECK_RESULT(ErrorIfLpar({"a catch expr"}));
EXPECT(Catch);
CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
CHECK_RESULT(ParseTerminatingInstrList(&expr->catch_));
EXPECT(End);
CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
*out_expr = std::move(expr);
@@ -1737,6 +1785,51 @@ Result WastParser::ParseBlock(Block* block) {
return Result::Ok;
}

Result WastParser::ParseIfExceptHeader(IfExceptExpr* expr) {
WABT_TRACE(ParseIfExceptHeader);
// if_except has the syntax:
//
// if_except label_opt block_type except_index
//
// This means that it can have a few different forms:
//
// 1. if_except <num> ...
// 2. if_except $except ...
// 3. if_except $label $except/<num> ...
// 4. if_except (result...) $except/<num> ...
// 5. if_except $label (result...) $except/<num> ...

if (PeekMatchLpar(TokenType::Result)) {
// Case 4.
CHECK_RESULT(ParseResultList(&expr->true_.sig));
CHECK_RESULT(ParseVar(&expr->except_var));
} else if (PeekMatch(TokenType::Nat)) {
// Case 1.
CHECK_RESULT(ParseVar(&expr->except_var));
} else if (PeekMatch(TokenType::Var)) {
// Cases 2, 3, 5.
Var var;
CHECK_RESULT(ParseVar(&var));
if (PeekMatchLpar(TokenType::Result)) {
// Case 5.
expr->true_.label = var.name();
CHECK_RESULT(ParseResultList(&expr->true_.sig));
CHECK_RESULT(ParseVar(&expr->except_var));
} else if (ParseVarOpt(&expr->except_var, Var())) {
// Case 3.
expr->true_.label = var.name();
} else {
// Case 2.
expr->except_var = var;
}
} else {
return ErrorExpected({"a var", "a block type"},
"12 or $foo or (result ...)");
}

return Result::Ok;
}

Result WastParser::ParseExprList(ExprList* exprs) {
WABT_TRACE(ParseExprList);
ExprList new_exprs;
@@ -1780,7 +1873,7 @@ Result WastParser::ParseExpr(ExprList* exprs) {
case TokenType::Loop: {
Consume();
Consume();
auto expr = MakeUnique<LoopExpr>();
auto expr = MakeUnique<LoopExpr>(loc);
CHECK_RESULT(ParseLabelOpt(&expr->block.label));
CHECK_RESULT(ParseBlock(&expr->block));
exprs->push_back(std::move(expr));
@@ -1825,6 +1918,43 @@ Result WastParser::ParseExpr(ExprList* exprs) {
break;
}

case TokenType::IfExcept: {
Consume();
ErrorUnlessOpcodeEnabled(Consume());
auto expr = MakeUnique<IfExceptExpr>(loc);

CHECK_RESULT(ParseIfExceptHeader(expr.get()));

if (PeekMatchExpr()) {
ExprList cond;
CHECK_RESULT(ParseExpr(&cond));
exprs->splice(exprs->end(), cond);
}

if (MatchLpar(TokenType::Then)) {
CHECK_RESULT(ParseTerminatingInstrList(&expr->true_.exprs));
EXPECT(Rpar);

if (MatchLpar(TokenType::Else)) {
CHECK_RESULT(ParseTerminatingInstrList(&expr->false_));
EXPECT(Rpar);
} else if (PeekMatchExpr()) {
CHECK_RESULT(ParseExpr(&expr->false_));
}
} else if (PeekMatchExpr()) {
CHECK_RESULT(ParseExpr(&expr->true_.exprs));
if (PeekMatchExpr()) {
CHECK_RESULT(ParseExpr(&expr->false_));
}
} else {
ConsumeIfLpar();
return ErrorExpected({"then block"}, "(then ...)");
}

exprs->push_back(std::move(expr));
break;
}

case TokenType::Try: {
Consume();
ErrorUnlessOpcodeEnabled(Consume());
@@ -1833,9 +1963,10 @@ Result WastParser::ParseExpr(ExprList* exprs) {
CHECK_RESULT(ParseLabelOpt(&expr->block.label));
CHECK_RESULT(ParseResultList(&expr->block.sig));
CHECK_RESULT(ParseInstrList(&expr->block.exprs));
CHECK_RESULT(ParseCatchExprList(&expr->catches));
CHECK_RESULT(ErrorIfLpar({"a catch expr"}));

EXPECT(Lpar);
EXPECT(Catch);
CHECK_RESULT(ParseTerminatingInstrList(&expr->catch_));
EXPECT(Rpar);
exprs->push_back(std::move(expr));
break;
}
@@ -1850,40 +1981,6 @@ Result WastParser::ParseExpr(ExprList* exprs) {
return Result::Ok;
}

Result WastParser::ParseCatchInstrList(CatchVector* catches) {
WABT_TRACE(ParseCatchInstrList);
while (IsCatch(Peek())) {
Catch catch_(GetLocation());

if (Consume().token_type() == TokenType::Catch) {
CHECK_RESULT(ParseVar(&catch_.var));
}

CHECK_RESULT(ParseInstrList(&catch_.exprs));
catches->push_back(std::move(catch_));
}

return Result::Ok;
}

Result WastParser::ParseCatchExprList(CatchVector* catches) {
WABT_TRACE(ParseCatchExprList);
while (PeekMatch(TokenType::Lpar) && IsCatch(Peek(1))) {
Consume();
Catch catch_(GetLocation());

if (Consume().token_type() == TokenType::Catch) {
CHECK_RESULT(ParseVar(&catch_.var));
}

CHECK_RESULT(ParseTerminatingInstrList(&catch_.exprs));
EXPECT(Rpar);
catches->push_back(std::move(catch_));
}

return Result::Ok;
}

Result WastParser::ParseGlobalType(Global* global) {
WABT_TRACE(ParseGlobalType);
if (MatchLpar(TokenType::Mut)) {
@@ -2231,15 +2328,14 @@ void WastParser::CheckImportOrdering(Module* module) {
}

Result ParseWatModule(WastLexer* lexer,
std::unique_ptr<Module>* out_module,
ErrorHandler* error_handler,
WastParseOptions* options) {
std::unique_ptr<Module>* out_module,
ErrorHandler* error_handler,
WastParseOptions* options) {
assert(out_module != nullptr);
WastParser parser(lexer, error_handler, options);
return parser.ParseModule(out_module);
}


Result ParseWastScript(WastLexer* lexer,
std::unique_ptr<Script>* out_script,
ErrorHandler* error_handler,
@@ -21,8 +21,8 @@

#include "src/circular-array.h"
#include "src/feature.h"
#include "src/ir.h"
#include "src/intrusive-list.h"
#include "src/ir.h"
#include "src/wast-lexer.h"

namespace wabt {
@@ -163,10 +163,9 @@ class WastParser {
Result ParseLabelOpt(std::string*);
Result ParseEndLabelOpt(const std::string&);
Result ParseBlock(Block*);
Result ParseIfExceptHeader(IfExceptExpr*);
Result ParseExprList(ExprList*);
Result ParseExpr(ExprList*);
Result ParseCatchInstrList(CatchVector* catches);
Result ParseCatchExprList(CatchVector* catches);
Result ParseGlobalType(Global*);

template <typename T>