Bug 1242804 - Baldr: check enum limits (r=bbouvier)

This commit is contained in:
Luke Wagner 2016-01-26 13:05:03 -06:00
parent 3416300355
commit 4bb80c9a77
8 changed files with 192 additions and 93 deletions

View File

@ -1099,6 +1099,7 @@ class Type
case ValType::I32x4: return Int32x4;
case ValType::F32x4: return Float32x4;
case ValType::B32x4: return Bool32x4;
case ValType::Limit: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
}
@ -1113,6 +1114,7 @@ class Type
case ExprType::I32x4: return Int32x4;
case ExprType::F32x4: return Float32x4;
case ExprType::B32x4: return Bool32x4;
case ExprType::Limit: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
}
@ -1161,6 +1163,7 @@ class Type
case ValType::I32x4: return isInt32x4();
case ValType::F32x4: return isFloat32x4();
case ValType::B32x4: return isBool32x4();
case ValType::Limit: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected rhs type");
}
@ -4639,6 +4642,7 @@ CheckCoercionArg(FunctionValidator& f, ParseNode* arg, ValType expected, Type* t
break;
case ValType::I32:
case ValType::F64:
case ValType::Limit:
MOZ_CRASH("not call coercions");
}
@ -5457,6 +5461,8 @@ CoerceResult(FunctionValidator& f, ParseNode* expr, ExprType expected, Type actu
return f.failf(expr, "%s is not a subtype of bool32x4", actual.toChars());
f.patchOp(patchAt, Expr::Id);
break;
case ExprType::Limit:
MOZ_CRASH("Limit");
}
*type = Type::ret(expected);
@ -7079,6 +7085,8 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, uint8_t* global
case ValType::F32x4:
memcpy(datum, v.f32x4(), Simd128DataSize);
break;
case ValType::Limit:
MOZ_CRASH("Limit");
}
break;
}
@ -7129,6 +7137,8 @@ ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, uint8_t* global
memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
break;
}
case ValType::Limit:
MOZ_CRASH("Limit");
}
break;
}

View File

@ -269,7 +269,9 @@ enum class Expr : uint16_t
F64FromS32,
F64FromU32,
F64StoreMemF32
F64StoreMemF32,
Limit
};
enum NeedsBoundsCheck : uint8_t
@ -296,7 +298,7 @@ class Encoder
return bytecode_.append(reinterpret_cast<uint8_t*>(&v), sizeof(T));
}
template <class T>
template <class IntT, class T>
MOZ_WARN_UNUSED_RESULT
bool writeEnum(T v, size_t* offset) {
// For now, just write a u16 instead of a variable-length integer.
@ -304,16 +306,17 @@ class Encoder
// pre-order encoding and back-patching; let's see if we switch to
// post-order first.
static_assert(mozilla::IsEnum<T>::value, "is an enum");
MOZ_ASSERT(uint64_t(v) < UINT16_MAX);
return write<uint16_t>(uint16_t(v), offset);
MOZ_ASSERT(uint64_t(v) < IntT(-1));
MOZ_ASSERT(v != T::Limit);
return write<IntT>(IntT(v), offset);
}
template <class T>
template <class IntT, class T>
void patchEnum(size_t pc, T v) {
// See writeEnum comment.
static_assert(mozilla::IsEnum<T>::value, "is an enum");
MOZ_ASSERT(uint64_t(v) < UINT16_MAX);
memcpy(&bytecode_[pc], &v, sizeof(uint16_t));
memcpy(&bytecode_[pc], &v, sizeof(IntT));
}
template <class T>
@ -349,11 +352,11 @@ class Encoder
}
MOZ_WARN_UNUSED_RESULT bool
writeExpr(Expr expr, size_t* offset = nullptr) { return writeEnum(expr, offset); }
writeExpr(Expr expr, size_t* offset = nullptr) { return writeEnum<uint16_t>(expr, offset); }
MOZ_WARN_UNUSED_RESULT bool
writeValType(ValType type, size_t* offset = nullptr) { return writeEnum(type, offset); }
writeValType(ValType type, size_t* offset = nullptr) { return writeEnum<uint8_t>(type, offset); }
MOZ_WARN_UNUSED_RESULT bool
writeExprType(ExprType type, size_t* offset = nullptr) { return writeEnum(type, offset); }
writeExprType(ExprType type, size_t* offset = nullptr) { return writeEnum<uint8_t>(type, offset); }
MOZ_WARN_UNUSED_RESULT bool
writeU8(uint8_t i, size_t* offset = nullptr) { return write<uint8_t>(i, offset); }
@ -421,7 +424,7 @@ class Encoder
void patchExpr(size_t pc, Expr expr) {
// See comment in writeEnum
MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint16_t)));
patchEnum(pc, expr);
patchEnum<uint16_t>(pc, expr);
}
template<class T>
void patch32(size_t pc, T i) {
@ -449,16 +452,16 @@ class Decoder
return true;
}
template <class T>
template <class IntT, class T>
MOZ_WARN_UNUSED_RESULT bool
readEnum(T* out) {
static_assert(mozilla::IsEnum<T>::value, "is an enum");
// See Encoder::writeEnum.
uint16_t u16;
if (!read(&u16))
IntT i;
if (!read(&i) || i >= IntT(T::Limit))
return false;
if (out)
*out = T(u16);
*out = T(i);
return true;
}
@ -470,11 +473,11 @@ class Decoder
return ret;
}
template <class T>
template <class IntT, class T>
T uncheckedPeekEnum() const {
// See Encoder::writeEnum.
static_assert(mozilla::IsEnum<T>::value, "is an enum");
return (T)uncheckedPeek<uint16_t>();
return (T)uncheckedPeek<IntT>();
}
template <class T>
@ -484,11 +487,11 @@ class Decoder
return ret;
}
template <class T>
template <class IntT, class T>
T uncheckedReadEnum() {
// See Encoder::writeEnum.
static_assert(mozilla::IsEnum<T>::value, "is an enum");
return (T)uncheckedRead<uint16_t>();
return (T)uncheckedRead<IntT>();
}
public:
@ -571,13 +574,13 @@ class Decoder
return true;
}
MOZ_WARN_UNUSED_RESULT bool readExpr(Expr* expr = nullptr) {
return readEnum(expr);
return readEnum<uint16_t>(expr);
}
MOZ_WARN_UNUSED_RESULT bool readValType(ValType* type = nullptr) {
return readEnum(type);
return readEnum<uint8_t>(type);
}
MOZ_WARN_UNUSED_RESULT bool readExprType(ExprType* type = nullptr) {
return readEnum(type);
return readEnum<uint8_t>(type);
}
MOZ_WARN_UNUSED_RESULT bool readCString(const char** cstr = nullptr) {
@ -666,10 +669,10 @@ class Decoder
return decoded;
}
Expr uncheckedReadExpr() {
return uncheckedReadEnum<Expr>();
return uncheckedReadEnum<uint16_t, Expr>();
}
Expr uncheckedPeekExpr() const {
return uncheckedPeekEnum<Expr>();
return uncheckedPeekEnum<uint16_t, Expr>();
}
};

View File

@ -238,19 +238,22 @@ ModuleGenerator::allocateGlobalVar(ValType type, bool isConst, uint32_t* index)
MOZ_ASSERT(!startedFuncDefs());
unsigned width = 0;
switch (type) {
case wasm::ValType::I32:
case wasm::ValType::F32:
case ValType::I32:
case ValType::F32:
width = 4;
break;
case wasm::ValType::I64:
case wasm::ValType::F64:
case ValType::I64:
case ValType::F64:
width = 8;
break;
case wasm::ValType::I32x4:
case wasm::ValType::F32x4:
case wasm::ValType::B32x4:
case ValType::I32x4:
case ValType::F32x4:
case ValType::B32x4:
width = 16;
break;
case ValType::Limit:
MOZ_CRASH("Limit");
break;
}
uint32_t offset;

View File

@ -135,6 +135,8 @@ class FunctionCompiler
// Bool32x4 uses the same data layout as Int32x4.
ins = MSimdConstant::New(alloc(), SimdConstant::SplatX4(0), MIRType_Bool32x4);
break;
case ValType::Limit:
MOZ_CRASH("Limit");
}
curBlock_->add(ins);
@ -1292,24 +1294,6 @@ class FunctionCompiler
}
};
// A Type or Undefined, implicitly constructed from an ExprType and usabled as
// an ExprType. Has two functions:
// - in debug, will ensure that the expected type and the actual type of some
// expressions match.
// - provides a way to mean "no type" in the context of expression statements,
// and will provoke assertions if we're trying to use an expected type when we
// don't have one.
class MaybeType
{
Maybe<ExprType> maybe_;
MaybeType() {}
public:
MOZ_IMPLICIT MaybeType(ExprType t) : maybe_() { maybe_.emplace(t); }
static MaybeType Undefined() { return MaybeType(); }
explicit operator bool() { return maybe_.isSome(); }
MOZ_IMPLICIT operator ExprType() { return maybe_.value(); }
};
static bool
EmitLiteral(FunctionCompiler& f, ExprType type, MDefinition** def)
{
@ -1348,7 +1332,8 @@ EmitLiteral(FunctionCompiler& f, ExprType type, MDefinition** def)
*def = f.constant(lit, MIRType_Bool32x4);
return true;
}
case ExprType::Void: {
case ExprType::Void:
case ExprType::Limit: {
break;
}
}
@ -1356,25 +1341,25 @@ EmitLiteral(FunctionCompiler& f, ExprType type, MDefinition** def)
}
static bool
EmitGetLocal(FunctionCompiler& f, MaybeType type, MDefinition** def)
EmitGetLocal(FunctionCompiler& f, ExprType type, MDefinition** def)
{
uint32_t slot = f.readVarU32();
*def = f.getLocalDef(slot);
MOZ_ASSERT_IF(type, f.localType(slot) == type);
MOZ_ASSERT_IF(type != ExprType::Limit, f.localType(slot) == type);
return true;
}
static bool
EmitLoadGlobal(FunctionCompiler& f, MaybeType type, MDefinition** def)
EmitLoadGlobal(FunctionCompiler& f, ExprType type, MDefinition** def)
{
uint32_t index = f.readVarU32();
const AsmJSGlobalVariable& global = f.mg().globalVar(index);
*def = f.loadGlobalVar(global.globalDataOffset, global.isConst, ToMIRType(global.type));
MOZ_ASSERT_IF(type, global.type == type);
MOZ_ASSERT_IF(type != ExprType::Limit, global.type == type);
return true;
}
static bool EmitExpr(FunctionCompiler&, MaybeType, MDefinition**, LabelVector* = nullptr);
static bool EmitExpr(FunctionCompiler&, ExprType, MDefinition**, LabelVector* = nullptr);
static bool EmitExprStmt(FunctionCompiler&, MDefinition**, LabelVector* = nullptr);
static bool
@ -1450,12 +1435,12 @@ EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type vi
}
static bool
EmitSetLocal(FunctionCompiler& f, MaybeType expected, MDefinition** def)
EmitSetLocal(FunctionCompiler& f, ExprType expected, MDefinition** def)
{
uint32_t slot = f.readVarU32();
MDefinition* expr;
ExprType actual = f.localType(slot);
MOZ_ASSERT_IF(expected, actual == expected);
MOZ_ASSERT_IF(expected != ExprType::Limit, actual == expected);
if (!EmitExpr(f, actual, &expr))
return false;
f.assign(slot, expr);
@ -1464,11 +1449,11 @@ EmitSetLocal(FunctionCompiler& f, MaybeType expected, MDefinition** def)
}
static bool
EmitStoreGlobal(FunctionCompiler& f, MaybeType type, MDefinition**def)
EmitStoreGlobal(FunctionCompiler& f, ExprType type, MDefinition**def)
{
uint32_t index = f.readVarU32();
const AsmJSGlobalVariable& global = f.mg().globalVar(index);
MOZ_ASSERT_IF(type, global.type == type);
MOZ_ASSERT_IF(type != ExprType::Limit, global.type == type);
MDefinition* expr;
if (!EmitExpr(f, global.type, &expr))
return false;
@ -1589,6 +1574,7 @@ EmitCallArgs(FunctionCompiler& f, const Sig& sig, FunctionCompiler::Call* call)
case ValType::I32x4: if (!EmitExpr(f, ExprType::I32x4, &arg)) return false; break;
case ValType::F32x4: if (!EmitExpr(f, ExprType::F32x4, &arg)) return false; break;
case ValType::B32x4: if (!EmitExpr(f, ExprType::B32x4, &arg)) return false; break;
case ValType::Limit: MOZ_CRASH("Limit");
}
if (!f.passArg(arg, sig.arg(i), call))
return false;
@ -1598,12 +1584,12 @@ EmitCallArgs(FunctionCompiler& f, const Sig& sig, FunctionCompiler::Call* call)
}
static bool
EmitInternalCall(FunctionCompiler& f, MaybeType ret, MDefinition** def)
EmitInternalCall(FunctionCompiler& f, ExprType ret, MDefinition** def)
{
uint32_t funcIndex = f.readU32();
const Sig& sig = f.mg().funcSig(funcIndex);
MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret, sig.ret() == ret);
MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret != ExprType::Limit, sig.ret() == ret);
uint32_t lineno, column;
f.readCallLineCol(&lineno, &column);
@ -1616,14 +1602,14 @@ EmitInternalCall(FunctionCompiler& f, MaybeType ret, MDefinition** def)
}
static bool
EmitFuncPtrCall(FunctionCompiler& f, MaybeType ret, MDefinition** def)
EmitFuncPtrCall(FunctionCompiler& f, ExprType ret, MDefinition** def)
{
uint32_t mask = f.readU32();
uint32_t globalDataOffset = f.readU32();
uint32_t sigIndex = f.readU32();
const Sig& sig = f.mg().sig(sigIndex);
MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret, sig.ret() == ret);
MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret != ExprType::Limit, sig.ret() == ret);
uint32_t lineno, column;
f.readCallLineCol(&lineno, &column);
@ -1640,7 +1626,7 @@ EmitFuncPtrCall(FunctionCompiler& f, MaybeType ret, MDefinition** def)
}
static bool
EmitFFICall(FunctionCompiler& f, MaybeType ret, MDefinition** def)
EmitFFICall(FunctionCompiler& f, ExprType ret, MDefinition** def)
{
uint32_t importIndex = f.readU32();
@ -1649,7 +1635,7 @@ EmitFFICall(FunctionCompiler& f, MaybeType ret, MDefinition** def)
const ModuleImportGeneratorData& import = f.mg().import(importIndex);
const Sig& sig = *import.sig;
MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret, sig.ret() == ret);
MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret != ExprType::Limit, sig.ret() == ret);
FunctionCompiler::Call call(f, lineno, column);
if (!EmitCallArgs(f, sig, &call))
@ -1805,7 +1791,8 @@ SimdToLaneType(ExprType type)
case ExprType::I64:
case ExprType::F32:
case ExprType::F64:
case ExprType::Void:;
case ExprType::Void:
case ExprType::Limit:;
}
MOZ_CRASH("bad simd type");
}
@ -2065,6 +2052,7 @@ EmitSimdCtor(FunctionCompiler& f, ExprType type, MDefinition** def)
case ExprType::F32:
case ExprType::F64:
case ExprType::Void:
case ExprType::Limit:
break;
}
MOZ_CRASH("unexpected SIMD type");
@ -2093,7 +2081,7 @@ EmitUnaryMir(FunctionCompiler& f, ExprType type, MDefinition** def)
}
static bool
EmitTernary(FunctionCompiler& f, MaybeType type, MDefinition** def)
EmitTernary(FunctionCompiler& f, ExprType type, MDefinition** def)
{
MDefinition* cond;
if (!EmitExpr(f, ExprType::I32, &cond))
@ -2645,7 +2633,7 @@ EmitRet(FunctionCompiler& f)
}
static bool
EmitBlock(FunctionCompiler& f, MaybeType type, MDefinition** def)
EmitBlock(FunctionCompiler& f, ExprType type, MDefinition** def)
{
size_t numStmt = f.readU32();
for (size_t i = 1; i < numStmt; i++) {
@ -2680,7 +2668,7 @@ EmitBreak(FunctionCompiler& f, bool hasLabel)
}
static bool
EmitExpr(FunctionCompiler& f, MaybeType type, MDefinition** def, LabelVector* maybeLabels)
EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* maybeLabels)
{
if (!f.mirGen().ensureBallast())
return false;
@ -2982,6 +2970,8 @@ EmitExpr(FunctionCompiler& f, MaybeType type, MDefinition** def, LabelVector* ma
case Expr::DebugCheckPoint:
case Expr::Unreachable:
break;
case Expr::Limit:
MOZ_CRASH("Limit");
}
MOZ_CRASH("unexpected wasm opcode");
@ -2990,7 +2980,7 @@ EmitExpr(FunctionCompiler& f, MaybeType type, MDefinition** def, LabelVector* ma
static bool
EmitExprStmt(FunctionCompiler& f, MDefinition** def, LabelVector* maybeLabels)
{
return EmitExpr(f, MaybeType::Undefined(), def, maybeLabels);
return EmitExpr(f, ExprType::Limit, def, maybeLabels);
}
bool

View File

@ -1287,6 +1287,8 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
break;
}
case ValType::Limit:
MOZ_CRASH("Limit");
}
}
@ -1349,6 +1351,8 @@ Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
return false;
args.rval().set(ObjectValue(*simdObj));
break;
case ExprType::Limit:
MOZ_CRASH("Limit");
}
return true;
@ -1412,6 +1416,7 @@ Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Val
case ValType::I32x4: MOZ_CRASH("NYI");
case ValType::F32x4: MOZ_CRASH("NYI");
case ValType::B32x4: MOZ_CRASH("NYI");
case ValType::Limit: MOZ_CRASH("Limit");
}
if (!TypeScript::ArgTypes(script, i)->hasType(type))
return true;

View File

@ -272,6 +272,8 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
// We don't have control on argv alignment, do an unaligned access.
masm.storeUnalignedFloat32x4(ReturnSimd128Reg, Address(argv, 0));
break;
case ExprType::Limit:
MOZ_CRASH("Limit");
}
// Restore clobbered non-volatile registers of the caller.
@ -416,6 +418,8 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLa
case ExprType::F32x4:
case ExprType::B32x4:
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
case ExprType::Limit:
MOZ_CRASH("Limit");
}
GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets);
@ -663,6 +667,8 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
case ExprType::F32x4:
case ExprType::B32x4:
MOZ_CRASH("SIMD types shouldn't be returned from an import");
case ExprType::Limit:
MOZ_CRASH("Limit");
}
Label done;

View File

@ -54,7 +54,9 @@ enum class ValType
F64,
I32x4,
F32x4,
B32x4
B32x4,
Limit
};
typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
@ -76,6 +78,7 @@ ToMIRType(ValType vt)
case ValType::I32x4: return jit::MIRType_Int32x4;
case ValType::F32x4: return jit::MIRType_Float32x4;
case ValType::B32x4: return jit::MIRType_Bool32x4;
case ValType::Limit: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
}
@ -147,7 +150,9 @@ enum class ExprType : uint16_t
I32x4 = uint8_t(ValType::I32x4),
F32x4 = uint8_t(ValType::F32x4),
B32x4 = uint8_t(ValType::B32x4),
Void
Void,
Limit
};
static inline bool

View File

@ -4,30 +4,107 @@ if (!this.wasmEval)
quit();
// MagicNumber = 0x4d534100
var magic0 = 0;
var magic1 = 97; // 'a'
var magic2 = 115; // 's'
var magic3 = 109; // 'm'
const magic0 = 0;
const magic1 = 97; // 'a'
const magic2 = 115; // 's'
const magic3 = 109; // 'm'
// EncodingVersion = -1
var ver0 = 0xff;
var ver1 = 0xff;
var ver2 = 0xff;
var ver3 = 0xff;
// EncodingVersion = -1 (to be changed to 1 at some point in the future)
const ver0 = 0xff;
const ver1 = 0xff;
const ver2 = 0xff;
const ver3 = 0xff;
var magicError = /failed to match magic number/;
var versionError = /failed to match binary version/;
var extraError = /failed to consume all bytes of module/;
// Section names
const sigSectionStr = "sig";
const declSectionStr = "decl";
const exportSectionStr = "export";
const codeSectionStr = "code";
assertErrorMessage(() => wasmEval(Uint8Array.of().buffer), Error, magicError);
assertErrorMessage(() => wasmEval(Uint8Array.of(42).buffer), Error, magicError);
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2).buffer), Error, magicError);
assertErrorMessage(() => wasmEval(Uint8Array.of(1,2,3,4).buffer), Error, magicError);
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3).buffer), Error, versionError);
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, 1).buffer), Error, versionError);
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0).buffer), Error, versionError);
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0, ver1, ver2).buffer), Error, versionError);
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, 0, 1).buffer), Error, extraError);
const magicError = /failed to match magic number/;
const versionError = /failed to match binary version/;
const extraError = /failed to consume all bytes of module/;
const sectionError = /failed to read section name/;
var o = wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, 0).buffer);
const I32Code = 0;
const I64Code = 1;
const F32Code = 2;
const F64Code = 3;
const I32x4Code = 4;
const F32x4Code = 5;
const B32x4Code = 6;
const VoidCode = 7;
function toBuf(array) {
for (var b of array)
assertEq(b < 256, true);
return Uint8Array.from(array).buffer;
}
function varU32(u32) {
// TODO
assertEq(u32 < 128, true);
return [u32];
}
function moduleHeaderThen(...rest) {
return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
}
assertErrorMessage(() => wasmEval(toBuf([])), Error, magicError);
assertErrorMessage(() => wasmEval(toBuf([42])), Error, magicError);
assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2])), Error, magicError);
assertErrorMessage(() => wasmEval(toBuf([1,2,3,4])), Error, magicError);
assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3])), Error, versionError);
assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3, 1])), Error, versionError);
assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3, ver0])), Error, versionError);
assertErrorMessage(() => wasmEval(toBuf([magic0, magic1, magic2, magic3, ver0, ver1, ver2])), Error, versionError);
var o = wasmEval(toBuf(moduleHeaderThen(0)));
assertEq(Object.getOwnPropertyNames(o).length, 0);
assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(1))), Error, sectionError);
assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(0, 1))), Error, extraError);
function sectionName(name) {
return (name + '\0').split('').map(c => c.charCodeAt(0));
}
function sectionLength(length) {
var i32 = new Uint32Array(1);
i32[0] = length;
return new Uint8Array(i32.buffer);
}
function moduleWithSections(sectionArray) {
var bytes = moduleHeaderThen();
for (section of sectionArray) {
bytes.push(...sectionName(section.name));
bytes.push(...sectionLength(section.body.length));
bytes.push(...section.body);
}
bytes.push(0);
return bytes;
}
function sigSection(sigs) {
var body = [];
body.push(...varU32(sigs.length));
for (var sig of sigs) {
body.push(...varU32(sig.args.length));
body.push(...varU32(sig.ret));
for (var arg of sig.args)
body.push(...varU32(arg));
}
return { name: sigSectionStr, body };
}
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1]}]))), Error);
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1, 1, 0]}]))), Error);
wasmEval(toBuf(moduleWithSections([sigSection([])])));
wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}])])));
wasmEval(toBuf(moduleWithSections([sigSection([{args:[I32Code], ret:VoidCode}])])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:100}])]))), Error, /bad result type/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])]))), Error, /bad arg type/);