Bug 1253137 - Baldr: switch to bottom-up validation in Wasm.cpp (r=sunfish)

MozReview-Commit-ID: 8Ml0V6RcoSR
This commit is contained in:
Luke Wagner 2016-03-04 18:42:57 -06:00
Родитель fd73ec9670
Коммит 08ad2796af
4 изменённых файлов: 312 добавлений и 170 удалений

Просмотреть файл

@ -59,7 +59,25 @@ Fail(JSContext* cx, Decoder& d, const char* str)
}
/*****************************************************************************/
// wasm function body validation
// wasm validation type lattice
// ExprType::Limit is an out-of-band value and has no wasm-semantic meaning. For
// the purpose of recursive validation, we use this value to represent the type
// of branch/return instructions that don't actually return to the parent
// expression and can thus be used in any context.
static const ExprType AnyType = ExprType::Limit;
static ExprType
Unify(ExprType one, ExprType two)
{
if (one == AnyType)
return two;
if (two == AnyType)
return one;
if (one == two)
return one;
return ExprType::Void;
}
class FunctionDecoder
{
@ -68,12 +86,12 @@ class FunctionDecoder
ModuleGenerator& mg_;
FunctionGenerator& fg_;
uint32_t funcIndex_;
uint32_t blockDepth_;
Vector<ExprType> blocks_;
public:
FunctionDecoder(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
uint32_t funcIndex)
: cx_(cx), d_(d), mg_(mg), fg_(fg), funcIndex_(funcIndex), blockDepth_(0)
: cx_(cx), d_(d), mg_(mg), fg_(fg), funcIndex_(funcIndex), blocks_(cx)
{}
JSContext* cx() const { return cx_; }
Decoder& d() const { return d_; }
@ -86,23 +104,25 @@ class FunctionDecoder
return Fail(cx_, d_, str);
}
MOZ_WARN_UNUSED_RESULT bool pushLabel() {
++blockDepth_;
return blockDepth_ != 0;
MOZ_WARN_UNUSED_RESULT bool pushBlock() {
return blocks_.append(AnyType);
}
void popLabel() {
MOZ_ASSERT(blockDepth_ != 0);
--blockDepth_;
ExprType popBlock() {
return blocks_.popCopy();
}
MOZ_WARN_UNUSED_RESULT bool isLabelInBounds(uint32_t depth) {
return depth < blockDepth_;
MOZ_WARN_UNUSED_RESULT bool branchWithType(uint32_t depth, ExprType type) {
if (depth >= blocks_.length())
return false;
uint32_t absolute = blocks_.length() - 1 - depth;
blocks_[absolute] = Unify(blocks_[absolute], type);
return true;
}
};
static bool
CheckType(FunctionDecoder& f, ExprType actual, ExprType expected)
CheckType(FunctionDecoder& f, ExprType actual, ValType expected)
{
if (actual == expected || expected == ExprType::Void)
if (actual == AnyType || actual == ToExprType(expected))
return true;
UniqueChars error(JS_smprintf("type mismatch: expression has type %s but expected %s",
@ -114,7 +134,15 @@ CheckType(FunctionDecoder& f, ExprType actual, ExprType expected)
}
static bool
DecodeExpr(FunctionDecoder& f, ExprType expected);
CheckType(FunctionDecoder& f, ExprType actual, ExprType expected)
{
MOZ_ASSERT(expected != AnyType);
return expected == ExprType::Void ||
CheckType(f, actual, NonVoidToValType(expected));
}
static bool
DecodeExpr(FunctionDecoder& f, ExprType* type);
static bool
DecodeValType(JSContext* cx, Decoder& d, ValType *type)
@ -172,18 +200,30 @@ DecodeExprType(JSContext* cx, Decoder& d, ExprType *type)
}
static bool
DecodeCallWithSig(FunctionDecoder& f, const Sig& sig, ExprType expected)
DecodeNop(FunctionDecoder& f, ExprType* type)
{
for (ValType argType : sig.args()) {
if (!DecodeExpr(f, ToExprType(argType)))
return false;
}
return CheckType(f, sig.ret(), expected);
*type = ExprType::Void;
return true;
}
static bool
DecodeCall(FunctionDecoder& f, ExprType expected)
DecodeCallWithSig(FunctionDecoder& f, const Sig& sig, ExprType* type)
{
for (ValType argType : sig.args()) {
ExprType exprType;
if (!DecodeExpr(f, &exprType))
return false;
if (!CheckType(f, exprType, argType))
return false;
}
*type = sig.ret();
return true;
}
static bool
DecodeCall(FunctionDecoder& f, ExprType* type)
{
uint32_t funcIndex;
if (!f.d().readVarU32(&funcIndex))
@ -192,11 +232,11 @@ DecodeCall(FunctionDecoder& f, ExprType expected)
if (funcIndex >= f.mg().numFuncSigs())
return f.fail("callee index out of range");
return DecodeCallWithSig(f, f.mg().funcSig(funcIndex), expected);
return DecodeCallWithSig(f, f.mg().funcSig(funcIndex), type);
}
static bool
DecodeCallImport(FunctionDecoder& f, ExprType expected)
DecodeCallImport(FunctionDecoder& f, ExprType* type)
{
uint32_t importIndex;
if (!f.d().readVarU32(&importIndex))
@ -205,11 +245,11 @@ DecodeCallImport(FunctionDecoder& f, ExprType expected)
if (importIndex >= f.mg().numImports())
return f.fail("import index out of range");
return DecodeCallWithSig(f, *f.mg().import(importIndex).sig, expected);
return DecodeCallWithSig(f, *f.mg().import(importIndex).sig, type);
}
static bool
DecodeCallIndirect(FunctionDecoder& f, ExprType expected)
DecodeCallIndirect(FunctionDecoder& f, ExprType* type)
{
uint32_t sigIndex;
if (!f.d().readVarU32(&sigIndex))
@ -218,62 +258,72 @@ DecodeCallIndirect(FunctionDecoder& f, ExprType expected)
if (sigIndex >= f.mg().numSigs())
return f.fail("signature index out of range");
if (!DecodeExpr(f, ExprType::I32))
ExprType indexType;
if (!DecodeExpr(f, &indexType))
return false;
return DecodeCallWithSig(f, f.mg().sig(sigIndex), expected);
if (!CheckType(f, indexType, ValType::I32))
return false;
return DecodeCallWithSig(f, f.mg().sig(sigIndex), type);
}
static bool
DecodeConstI32(FunctionDecoder& f, ExprType expected)
DecodeConstI32(FunctionDecoder& f, ExprType* type)
{
if (!f.d().readVarU32())
return f.fail("unable to read i32.const immediate");
return CheckType(f, ExprType::I32, expected);
*type = ExprType::I32;
return true;
}
static bool
DecodeConstI64(FunctionDecoder& f, ExprType expected)
DecodeConstI64(FunctionDecoder& f, ExprType* type)
{
if (!f.d().readVarU64())
return f.fail("unable to read i64.const immediate");
return CheckType(f, ExprType::I64, expected);
*type = ExprType::I64;
return true;
}
static bool
DecodeConstF32(FunctionDecoder& f, ExprType expected)
DecodeConstF32(FunctionDecoder& f, ExprType* type)
{
float value;
if (!f.d().readFixedF32(&value))
return f.fail("unable to read f32.const immediate");
if (IsNaN(value)) {
const float jsNaN = (float)JS::GenericNaN();
if (memcmp(&value, &jsNaN, sizeof(value)) != 0)
return f.fail("NYI: NaN literals with custom payloads");
}
return CheckType(f, ExprType::F32, expected);
*type = ExprType::F32;
return true;
}
static bool
DecodeConstF64(FunctionDecoder& f, ExprType expected)
DecodeConstF64(FunctionDecoder& f, ExprType* type)
{
double value;
if (!f.d().readFixedF64(&value))
return f.fail("unable to read f64.const immediate");
if (IsNaN(value)) {
const double jsNaN = JS::GenericNaN();
if (memcmp(&value, &jsNaN, sizeof(value)) != 0)
return f.fail("NYI: NaN literals with custom payloads");
}
return CheckType(f, ExprType::F64, expected);
*type = ExprType::F64;
return true;
}
static bool
DecodeGetLocal(FunctionDecoder& f, ExprType expected)
DecodeGetLocal(FunctionDecoder& f, ExprType* type)
{
uint32_t localIndex;
if (!f.d().readVarU32(&localIndex))
@ -282,11 +332,12 @@ DecodeGetLocal(FunctionDecoder& f, ExprType expected)
if (localIndex >= f.fg().locals().length())
return f.fail("get_local index out of range");
return CheckType(f, ToExprType(f.fg().locals()[localIndex]), expected);
*type = ToExprType(f.fg().locals()[localIndex]);
return true;
}
static bool
DecodeSetLocal(FunctionDecoder& f, ExprType expected)
DecodeSetLocal(FunctionDecoder& f, ExprType* type)
{
uint32_t localIndex;
if (!f.d().readVarU32(&localIndex))
@ -295,22 +346,23 @@ DecodeSetLocal(FunctionDecoder& f, ExprType expected)
if (localIndex >= f.fg().locals().length())
return f.fail("set_local index out of range");
ExprType localType = ToExprType(f.fg().locals()[localIndex]);
*type = ToExprType(f.fg().locals()[localIndex]);
if (!DecodeExpr(f, localType))
ExprType rhsType;
if (!DecodeExpr(f, &rhsType))
return false;
return CheckType(f, localType, expected);
return CheckType(f, rhsType, *type);
}
static bool
DecodeBlock(FunctionDecoder& f, bool isLoop, ExprType expected)
DecodeBlock(FunctionDecoder& f, bool isLoop, ExprType* type)
{
if (!f.pushLabel())
if (!f.pushBlock())
return f.fail("nesting overflow");
if (isLoop) {
if (!f.pushLabel())
if (!f.pushBlock())
return f.fail("nesting overflow");
}
@ -318,65 +370,116 @@ DecodeBlock(FunctionDecoder& f, bool isLoop, ExprType expected)
if (!f.d().readVarU32(&numExprs))
return f.fail("unable to read block's number of expressions");
if (numExprs) {
for (uint32_t i = 0; i < numExprs - 1; i++) {
if (!DecodeExpr(f, ExprType::Void))
return false;
}
if (!DecodeExpr(f, expected))
return false;
} else {
if (!CheckType(f, ExprType::Void, expected))
ExprType exprType = ExprType::Void;
for (uint32_t i = 0; i < numExprs; i++) {
if (!DecodeExpr(f, &exprType))
return false;
}
if (isLoop)
f.popLabel();
f.popLabel();
f.popBlock();
ExprType branchType = f.popBlock();
*type = Unify(branchType, exprType);
return true;
}
static bool
DecodeUnaryOperator(FunctionDecoder& f, ExprType expected, ExprType type)
DecodeUnaryOperator(FunctionDecoder& f, ValType argType, ExprType *type)
{
return CheckType(f, type, expected) &&
DecodeExpr(f, type);
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, argType))
return false;
*type = ToExprType(argType);
return true;
}
static bool
DecodeBinaryOperator(FunctionDecoder& f, ExprType expected, ExprType type)
DecodeBinaryOperator(FunctionDecoder& f, ValType argType, ExprType* type)
{
return CheckType(f, type, expected) &&
DecodeExpr(f, type) &&
DecodeExpr(f, type);
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, argType))
return false;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, argType))
return false;
*type = ToExprType(argType);
return true;
}
static bool
DecodeComparisonOperator(FunctionDecoder& f, ExprType expected, ExprType type)
DecodeComparisonOperator(FunctionDecoder& f, ValType argType, ExprType* type)
{
return CheckType(f, ExprType::I32, expected) &&
DecodeExpr(f, type) &&
DecodeExpr(f, type);
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, argType))
return false;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, argType))
return false;
*type = ExprType::I32;
return true;
}
static bool
DecodeConversionOperator(FunctionDecoder& f, ExprType expected,
ExprType dstType, ExprType srcType)
DecodeConversionOperator(FunctionDecoder& f, ValType to, ValType argType, ExprType* type)
{
return CheckType(f, dstType, expected) &&
DecodeExpr(f, srcType);
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, argType))
return false;
*type = ToExprType(to);
return true;
}
static bool
DecodeIfElse(FunctionDecoder& f, bool hasElse, ExprType expected)
DecodeIfElse(FunctionDecoder& f, bool hasElse, ExprType* type)
{
return DecodeExpr(f, ExprType::I32) &&
DecodeExpr(f, expected) &&
(hasElse
? DecodeExpr(f, expected)
: CheckType(f, ExprType::Void, expected));
ExprType condType;
if (!DecodeExpr(f, &condType))
return false;
if (!CheckType(f, condType, ValType::I32))
return false;
ExprType thenType;
if (!DecodeExpr(f, &thenType))
return false;
if (hasElse) {
ExprType elseType;
if (!DecodeExpr(f, &elseType))
return false;
*type = Unify(thenType, elseType);
} else {
*type = ExprType::Void;
}
return true;
}
static bool
@ -393,57 +496,82 @@ DecodeLoadStoreAddress(FunctionDecoder &f)
if (!mozilla::IsPowerOfTwo(align))
return f.fail("memory access alignment must be a power of two");
return DecodeExpr(f, ExprType::I32);
ExprType baseType;
if (!DecodeExpr(f, &baseType))
return false;
return CheckType(f, baseType, ExprType::I32);
}
static bool
DecodeLoad(FunctionDecoder& f, ExprType expected, ExprType type)
DecodeLoad(FunctionDecoder& f, ValType loadType, ExprType* type)
{
return DecodeLoadStoreAddress(f) &&
CheckType(f, type, expected);
if (!DecodeLoadStoreAddress(f))
return false;
*type = ToExprType(loadType);
return true;
}
static bool
DecodeStore(FunctionDecoder& f, ExprType expected, ExprType type)
DecodeStore(FunctionDecoder& f, ValType storeType, ExprType* type)
{
return DecodeLoadStoreAddress(f) &&
DecodeExpr(f, type) &&
CheckType(f, type, expected);
if (!DecodeLoadStoreAddress(f))
return false;
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, storeType))
return false;
*type = ToExprType(storeType);
return true;
}
static bool
DecodeBr(FunctionDecoder& f, ExprType expected)
DecodeBr(FunctionDecoder& f, ExprType* type)
{
uint32_t relativeDepth;
if (!f.d().readVarU32(&relativeDepth))
return f.fail("expected relative depth");
if (!f.isLabelInBounds(relativeDepth))
if (!f.branchWithType(relativeDepth, ExprType::Void))
return f.fail("branch depth exceeds current nesting level");
return CheckType(f, ExprType::Void, expected);
*type = AnyType;
return true;
}
static bool
DecodeBrIf(FunctionDecoder& f, ExprType expected)
DecodeBrIf(FunctionDecoder& f, ExprType* type)
{
uint32_t relativeDepth;
if (!f.d().readVarU32(&relativeDepth))
return f.fail("expected relative depth");
if (!f.isLabelInBounds(relativeDepth))
if (!f.branchWithType(relativeDepth, ExprType::Void))
return f.fail("branch depth exceeds current nesting level");
return CheckType(f, ExprType::Void, expected) &&
DecodeExpr(f, ExprType::I32);
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, ValType::I32))
return false;
*type = ExprType::Void;
return true;
}
static bool
DecodeBrTable(FunctionDecoder& f)
DecodeBrTable(FunctionDecoder& f, ExprType* type)
{
uint32_t tableLength;
if (!f.d().readVarU32(&tableLength))
return false;
if (tableLength > MaxBrTableElems)
return f.fail("too many br_table entries");
@ -451,28 +579,47 @@ DecodeBrTable(FunctionDecoder& f)
uint32_t depth;
if (!f.d().readVarU32(&depth))
return f.fail("missing br_table entry");
if (!f.isLabelInBounds(depth))
if (!f.branchWithType(depth, ExprType::Void))
return f.fail("branch depth exceeds current nesting level");
}
uint32_t defaultDepth;
if (!f.d().readVarU32(&defaultDepth))
return f.fail("expected default relative depth");
if (!f.isLabelInBounds(defaultDepth))
if (!f.branchWithType(defaultDepth, ExprType::Void))
return f.fail("branch depth exceeds current nesting level");
return DecodeExpr(f, ExprType::I32);
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, ExprType::I32))
return false;
*type = AnyType;
return true;
}
static bool
DecodeReturn(FunctionDecoder& f)
DecodeReturn(FunctionDecoder& f, ExprType* type)
{
return f.ret() == ExprType::Void ||
DecodeExpr(f, f.ret());
if (f.ret() != ExprType::Void) {
ExprType actual;
if (!DecodeExpr(f, &actual))
return false;
if (!CheckType(f, actual, f.ret()))
return false;
}
*type = AnyType;
return true;
}
static bool
DecodeExpr(FunctionDecoder& f, ExprType expected)
DecodeExpr(FunctionDecoder& f, ExprType* type)
{
Expr expr;
if (!f.d().readExpr(&expr))
@ -480,48 +627,48 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
switch (expr) {
case Expr::Nop:
return CheckType(f, ExprType::Void, expected);
return DecodeNop(f, type);
case Expr::Call:
return DecodeCall(f, expected);
return DecodeCall(f, type);
case Expr::CallImport:
return DecodeCallImport(f, expected);
return DecodeCallImport(f, type);
case Expr::CallIndirect:
return DecodeCallIndirect(f, expected);
return DecodeCallIndirect(f, type);
case Expr::I32Const:
return DecodeConstI32(f, expected);
return DecodeConstI32(f, type);
case Expr::I64Const:
return DecodeConstI64(f, expected);
return DecodeConstI64(f, type);
case Expr::F32Const:
return DecodeConstF32(f, expected);
return DecodeConstF32(f, type);
case Expr::F64Const:
return DecodeConstF64(f, expected);
return DecodeConstF64(f, type);
case Expr::GetLocal:
return DecodeGetLocal(f, expected);
return DecodeGetLocal(f, type);
case Expr::SetLocal:
return DecodeSetLocal(f, expected);
return DecodeSetLocal(f, type);
case Expr::Block:
return DecodeBlock(f, /* isLoop */ false, expected);
return DecodeBlock(f, /* isLoop */ false, type);
case Expr::Loop:
return DecodeBlock(f, /* isLoop */ true, expected);
return DecodeBlock(f, /* isLoop */ true, type);
case Expr::If:
return DecodeIfElse(f, /* hasElse */ false, expected);
return DecodeIfElse(f, /* hasElse */ false, type);
case Expr::IfElse:
return DecodeIfElse(f, /* hasElse */ true, expected);
return DecodeIfElse(f, /* hasElse */ true, type);
case Expr::I32Clz:
case Expr::I32Ctz:
case Expr::I32Popcnt:
return DecodeUnaryOperator(f, expected, ExprType::I32);
return DecodeUnaryOperator(f, ValType::I32, type);
case Expr::I64Clz:
case Expr::I64Ctz:
case Expr::I64Popcnt:
return f.fail("NYI: i64") &&
DecodeUnaryOperator(f, expected, ExprType::I64);
DecodeUnaryOperator(f, ValType::I64, type);
case Expr::F32Abs:
case Expr::F32Neg:
case Expr::F32Ceil:
case Expr::F32Floor:
case Expr::F32Sqrt:
return DecodeUnaryOperator(f, expected, ExprType::F32);
return DecodeUnaryOperator(f, ValType::F32, type);
case Expr::F32Trunc:
return f.fail("NYI: trunc");
case Expr::F32Nearest:
@ -531,7 +678,7 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::F64Ceil:
case Expr::F64Floor:
case Expr::F64Sqrt:
return DecodeUnaryOperator(f, expected, ExprType::F64);
return DecodeUnaryOperator(f, ValType::F64, type);
case Expr::F64Trunc:
return f.fail("NYI: trunc");
case Expr::F64Nearest:
@ -549,7 +696,7 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::I32Shl:
case Expr::I32ShrS:
case Expr::I32ShrU:
return DecodeBinaryOperator(f, expected, ExprType::I32);
return DecodeBinaryOperator(f, ValType::I32, type);
case Expr::I64Add:
case Expr::I64Sub:
case Expr::I64Mul:
@ -563,14 +710,14 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::I64Shl:
case Expr::I64ShrS:
case Expr::I64ShrU:
return DecodeBinaryOperator(f, expected, ExprType::I64);
return DecodeBinaryOperator(f, ValType::I64, type);
case Expr::F32Add:
case Expr::F32Sub:
case Expr::F32Mul:
case Expr::F32Div:
case Expr::F32Min:
case Expr::F32Max:
return DecodeBinaryOperator(f, expected, ExprType::F32);
return DecodeBinaryOperator(f, ValType::F32, type);
case Expr::F32CopySign:
return f.fail("NYI: copysign");
case Expr::F64Add:
@ -579,7 +726,7 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::F64Div:
case Expr::F64Min:
case Expr::F64Max:
return DecodeBinaryOperator(f, expected, ExprType::F64);
return DecodeBinaryOperator(f, ValType::F64, type);
case Expr::F64CopySign:
return f.fail("NYI: copysign");
case Expr::I32Eq:
@ -592,7 +739,7 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::I32GtU:
case Expr::I32GeS:
case Expr::I32GeU:
return DecodeComparisonOperator(f, expected, ExprType::I32);
return DecodeComparisonOperator(f, ValType::I32, type);
case Expr::I64Eq:
case Expr::I64Ne:
case Expr::I64LtS:
@ -603,69 +750,69 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::I64GtU:
case Expr::I64GeS:
case Expr::I64GeU:
return DecodeComparisonOperator(f, expected, ExprType::I64);
return DecodeComparisonOperator(f, ValType::I64, type);
case Expr::F32Eq:
case Expr::F32Ne:
case Expr::F32Lt:
case Expr::F32Le:
case Expr::F32Gt:
case Expr::F32Ge:
return DecodeComparisonOperator(f, expected, ExprType::F32);
return DecodeComparisonOperator(f, ValType::F32, type);
case Expr::F64Eq:
case Expr::F64Ne:
case Expr::F64Lt:
case Expr::F64Le:
case Expr::F64Gt:
case Expr::F64Ge:
return DecodeComparisonOperator(f, expected, ExprType::F64);
return DecodeComparisonOperator(f, ValType::F64, type);
case Expr::I32WrapI64:
return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::I64);
return DecodeConversionOperator(f, ValType::I32, ValType::I64, type);
case Expr::I32TruncSF32:
case Expr::I32TruncUF32:
return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F32);
return DecodeConversionOperator(f, ValType::I32, ValType::F32, type);
case Expr::I32ReinterpretF32:
return f.fail("NYI: reinterpret");
case Expr::I32TruncSF64:
case Expr::I32TruncUF64:
return DecodeConversionOperator(f, expected, ExprType::I32, ExprType::F64);
return DecodeConversionOperator(f, ValType::I32, ValType::F64, type);
case Expr::I64ExtendSI32:
case Expr::I64ExtendUI32:
return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::I32);
return DecodeConversionOperator(f, ValType::I64, ValType::I32, type);
case Expr::I64TruncSF32:
case Expr::I64TruncUF32:
return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F32);
return DecodeConversionOperator(f, ValType::I64, ValType::F32, type);
case Expr::I64TruncSF64:
case Expr::I64TruncUF64:
return DecodeConversionOperator(f, expected, ExprType::I64, ExprType::F64);
return DecodeConversionOperator(f, ValType::I64, ValType::F64, type);
case Expr::I64ReinterpretF64:
return f.fail("NYI: i64");
case Expr::F32ConvertSI32:
case Expr::F32ConvertUI32:
return DecodeConversionOperator(f, expected, ExprType::F32, ExprType::I32);
return DecodeConversionOperator(f, ValType::F32, ValType::I32, type);
case Expr::F32ReinterpretI32:
return f.fail("NYI: reinterpret");
case Expr::F32ConvertSI64:
case Expr::F32ConvertUI64:
return f.fail("NYI: i64") &&
DecodeConversionOperator(f, expected, ExprType::F32, ExprType::I64);
DecodeConversionOperator(f, ValType::F32, ValType::I64, type);
case Expr::F32DemoteF64:
return DecodeConversionOperator(f, expected, ExprType::F32, ExprType::F64);
return DecodeConversionOperator(f, ValType::F32, ValType::F64, type);
case Expr::F64ConvertSI32:
case Expr::F64ConvertUI32:
return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I32);
return DecodeConversionOperator(f, ValType::F64, ValType::I32, type);
case Expr::F64ConvertSI64:
case Expr::F64ConvertUI64:
case Expr::F64ReinterpretI64:
return f.fail("NYI: i64") &&
DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I64);
DecodeConversionOperator(f, ValType::F64, ValType::I64, type);
case Expr::F64PromoteF32:
return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::F32);
return DecodeConversionOperator(f, ValType::F64, ValType::F32, type);
case Expr::I32LoadMem:
case Expr::I32LoadMem8S:
case Expr::I32LoadMem8U:
case Expr::I32LoadMem16S:
case Expr::I32LoadMem16U:
return DecodeLoad(f, expected, ExprType::I32);
return DecodeLoad(f, ValType::I32, type);
case Expr::I64LoadMem:
case Expr::I64LoadMem8S:
case Expr::I64LoadMem8U:
@ -674,33 +821,33 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::I64LoadMem32S:
case Expr::I64LoadMem32U:
return f.fail("NYI: i64") &&
DecodeLoad(f, expected, ExprType::I64);
DecodeLoad(f, ValType::I64, type);
case Expr::F32LoadMem:
return DecodeLoad(f, expected, ExprType::F32);
return DecodeLoad(f, ValType::F32, type);
case Expr::F64LoadMem:
return DecodeLoad(f, expected, ExprType::F64);
return DecodeLoad(f, ValType::F64, type);
case Expr::I32StoreMem:
case Expr::I32StoreMem8:
case Expr::I32StoreMem16:
return DecodeStore(f, expected, ExprType::I32);
return DecodeStore(f, ValType::I32, type);
case Expr::I64StoreMem:
case Expr::I64StoreMem8:
case Expr::I64StoreMem16:
case Expr::I64StoreMem32:
return f.fail("NYI: i64") &&
DecodeStore(f, expected, ExprType::I64);
DecodeStore(f, ValType::I64, type);
case Expr::F32StoreMem:
return DecodeStore(f, expected, ExprType::F32);
return DecodeStore(f, ValType::F32, type);
case Expr::F64StoreMem:
return DecodeStore(f, expected, ExprType::F64);
return DecodeStore(f, ValType::F64, type);
case Expr::Br:
return DecodeBr(f, expected);
return DecodeBr(f, type);
case Expr::BrIf:
return DecodeBrIf(f, expected);
return DecodeBrIf(f, type);
case Expr::BrTable:
return DecodeBrTable(f);
return DecodeBrTable(f, type);
case Expr::Return:
return DecodeReturn(f);
return DecodeReturn(f, type);
default:
break;
}
@ -1123,20 +1270,15 @@ DecodeFunctionBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t func
if (!d.readVarU32(&numExprs))
return Fail(cx, d, "expected number of function body expressions");
if (numExprs) {
for (size_t i = 0; i < numExprs - 1; i++) {
if (!DecodeExpr(f, ExprType::Void))
return false;
}
ExprType type = ExprType::Void;
if (!DecodeExpr(f, f.ret()))
return false;
} else {
if (!CheckType(f, ExprType::Void, f.ret()))
for (size_t i = 0; i < numExprs; i++) {
if (!DecodeExpr(f, &type))
return false;
}
MOZ_ASSERT(!f.isLabelInBounds(0));
if (!CheckType(f, type, f.ret()))
return false;
const uint8_t* bodyEnd = d.currentPosition();
uintptr_t bodyLength = bodyEnd - bodyBegin;

Просмотреть файл

@ -11,8 +11,8 @@ wasmEvalText('(module (func (local i32) (if (get_local 0) (nop))) (export "" 0))
wasmEvalText('(module (func (local i32) (if_else (get_local 0) (nop) (nop))) (export "" 0))');
// Expression values types are consistent
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (local f32) (if_else (i32.const 42) (get_local 0) (i32.const 0))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (local f64) (if_else (i32.const 42) (i32.const 0) (get_local 0))))'), TypeError, mismatchError("f64", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (local f32) (if_else (i32.const 42) (get_local 0) (i32.const 0))))'), TypeError, mismatchError("void", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (local f64) (if_else (i32.const 42) (i32.const 0) (get_local 0))))'), TypeError, mismatchError("void", "i32"));
assertEq(wasmEvalText('(module (func (result i32) (if_else (i32.const 42) (i32.const 1) (i32.const 2))) (export "" 0))')(), 1);
assertEq(wasmEvalText('(module (func (result i32) (if_else (i32.const 0) (i32.const 1) (i32.const 2))) (export "" 0))')(), 2);
@ -170,7 +170,7 @@ assertErrorMessage(() => wasmEvalText(`(module (func (result i32)
)
) (export "" 0))`), TypeError, mismatchError("void", "i32"));
assertErrorMessage(() => wasmEvalText(`(module (func (block $out (br_if $out (br 0)))) (export "" 0))`), TypeError, mismatchError("void", "i32"));
assertEq(wasmEvalText(`(module (func (block $out (br_if $out (br 0)))) (export "" 0))`)(), undefined);
assertEq(wasmEvalText('(module (func (block (br 0))) (export "" 0))')(), undefined);
assertEq(wasmEvalText('(module (func (block $l (br $l))) (export "" 0))')(), undefined);

Просмотреть файл

@ -73,20 +73,20 @@ testComparison('f64', 'ge', 40, 40, 1);
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result f32) (f32.sqrt (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result i32) (f32.sqrt (get_local 0))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i32) (f32.sqrt (get_local 0))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i32) (f32.sqrt (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result f64) (f64.sqrt (get_local 0))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (func (param f64) (result i32) (f64.sqrt (get_local 0))))'), TypeError, mismatchError("f64", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i32) (f64.sqrt (get_local 0))))'), TypeError, mismatchError("f64", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i32) (f64.sqrt (get_local 0))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (func (f32.sqrt (nop))))'), TypeError, mismatchError("void", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result f32) (f32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result f32) (f32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param f32) (result i32) (f32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result i32) (f32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result i32) (f32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f64) (result f64) (f64.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (func (param f64) (param i32) (result f64) (f64.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (func (param f64) (param f64) (result i32) (f64.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f64", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result i32) (f64.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f64", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result i32) (f64.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f64"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result f32) (f32.eq (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result f32) (f32.eq (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));

Просмотреть файл

@ -169,12 +169,12 @@ if (getBuildConfiguration().x64) {
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result i32) (i32.clz (get_local 0))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result f32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param f32) (result f32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param f32) (result f32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result i32) (i32.eq (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result i32) (i32.eq (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));