diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index 0987db301f81..10cd61bc8a6a 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -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 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; diff --git a/js/src/jit-test/tests/wasm/basic-control-flow.js b/js/src/jit-test/tests/wasm/basic-control-flow.js index a9247fef8abd..87e47dbeb963 100644 --- a/js/src/jit-test/tests/wasm/basic-control-flow.js +++ b/js/src/jit-test/tests/wasm/basic-control-flow.js @@ -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); diff --git a/js/src/jit-test/tests/wasm/basic-float.js b/js/src/jit-test/tests/wasm/basic-float.js index bdfdba7dae70..4d57e0dda8bb 100644 --- a/js/src/jit-test/tests/wasm/basic-float.js +++ b/js/src/jit-test/tests/wasm/basic-float.js @@ -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")); diff --git a/js/src/jit-test/tests/wasm/basic-integer.js b/js/src/jit-test/tests/wasm/basic-integer.js index 4dcabf561cfa..c65ad4bb758f 100644 --- a/js/src/jit-test/tests/wasm/basic-integer.js +++ b/js/src/jit-test/tests/wasm/basic-integer.js @@ -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"));