зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1253137 - Baldr: switch to bottom-up validation in Wasm.cpp (r=sunfish)
MozReview-Commit-ID: 8Ml0V6RcoSR
This commit is contained in:
Родитель
fd73ec9670
Коммит
08ad2796af
|
@ -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"));
|
||||
|
|
Загрузка…
Ссылка в новой задаче