From e36d9e52e30cbba3e990c5566a1337c258a0ad5e Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Sun, 17 Sep 2023 20:15:39 -0400 Subject: [PATCH 1/4] WIP: refactor validation of initialization expressions --- src/engine/BinParser.v3 | 14 ++++++ src/engine/CodeValidator.v3 | 98 ++++++++++++++++++++++++++++--------- src/util/ErrorGen.v3 | 3 ++ 3 files changed, 92 insertions(+), 23 deletions(-) diff --git a/src/engine/BinParser.v3 b/src/engine/BinParser.v3 index e81c21ec..ff3a8943 100644 --- a/src/engine/BinParser.v3 +++ b/src/engine/BinParser.v3 @@ -680,6 +680,20 @@ class WasmParser(extensions: Extension.set, limits: Limits, module: Module, return readInitExpr(quantity, ValueType.I32); } def readInitExpr(quantity: string, expected: ValueType) -> InitExpr { + var prev_pos = decoder.pos; + var validator = CodeValidator.new(extensions, limits, module, err); // TODO: cache + var sig: SigDecl; + match (expected) { + I32 => sig = SigCache.v_i; + I64 => sig = SigCache.v_l; + F32 => sig = SigCache.v_f; + F64 => sig = SigCache.v_d; + V128 => sig = SigCache.v_s; + _ => sig = SigDecl.new(true, ValueTypes.NO_HEAPTYPES, SigCache.arr_v, [expected]); + } + var r = validator.validateInitExpr(sig, decoder); + decoder.at(prev_pos); + // TODO: reduce duplication with CodeValidator if (init_stack == null) init_stack = ArrayStack.new(); else init_stack.clear(); diff --git a/src/engine/CodeValidator.v3 b/src/engine/CodeValidator.v3 index 3a5af0a2..9a097748 100644 --- a/src/engine/CodeValidator.v3 +++ b/src/engine/CodeValidator.v3 @@ -18,6 +18,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e var func: FuncDecl; var sig: SigDecl; var opcode_pos: int; + var validatingInitExpr = false; new() { codeptr.onError = err.onDataReaderError; @@ -28,28 +29,17 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e return validate(f, codeptr); } def validate(func: FuncDecl, d: DataReader) -> CodeValidatorResult { - return Metrics.validate_time_us.run(validate0, (func, d)); + return Metrics.validate_time_us.run(validateFunc0, (func, d)); } - private def validate0(func: FuncDecl, d: DataReader) -> CodeValidatorResult { + def validateInitExpr(expected: SigDecl, d: DataReader) -> CodeValidatorResult { + return Metrics.validate_time_us.run(validateExpr0, (expected, d)); + } + private def validateFunc0(func: FuncDecl, d: DataReader) -> CodeValidatorResult { // Reset internal state. + if (Trace.validation) OUT.put2("validate(func #%d: %q)", func.func_index, func.sig.render).outln(); this.func = func; - sig = func.sig; - if (Trace.validation) OUT.put2("validate(func #%d: %q)", func.func_index, sig.render).outln(); - codeptr.reset(d.data, d.pos, d.limit); - parser.reset(codeptr); - func_start_pos = d.pos; - err.section = BpSection.Code; - err.index = func.func_index; - ctl_stack.clear(); - val_stack.clear(); - ctlxfer.reset(codeptr.pos); - ex_handlers.resize(0); - - // Setup params. - locals.resize(0); - init_status.resize(0); - locals.puta(sig.params); - init_status.putn(InitStatus.INIT, sig.params.length); + resetSig(func.sig); + resetCode(d); // Read and initialize locals. if (!readLocals(locals)) return reterr(); @@ -83,6 +73,43 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } return reterr(); } + private def validateExpr0(sig: SigDecl, d: DataReader) -> CodeValidatorResult { + if (Trace.validation) OUT.put1("validate(expr: %q)", sig.render).outln(); + resetSig(sig); + resetCode(d); + opcode_pos = codeptr.pos; + pushControl(Opcode.UNREACHABLE.code, sig.params, sig.results, 0); + + // Run validation. + validatingInitExpr = true; + validateCode(); + if (ctl_stack.top != 0 && err.ok()) { + err.rel(codeptr, codeptr.pos).UnterminatedInitExpr(); + } + if (err.ok()) { + // Metric collection. + Metrics.validate_bytes.val += u32.view(d.pos - func_start_pos); + // Return success. + return CodeValidatorResult.Ok; + } + return reterr(); + } + private def resetSig(sig: SigDecl) { + this.sig = sig; + val_stack.clear(); + locals.resize(0); + init_status.resize(0); + locals.puta(sig.params); + init_status.putn(InitStatus.INIT, sig.params.length); + } + private def resetCode(d: DataReader) { + codeptr.reset(d.data, d.pos, d.limit); + parser.reset(codeptr); + func_start_pos = d.pos; + ctl_stack.clear(); + ctlxfer.reset(codeptr.pos); + ex_handlers.resize(0); + } def readLocals(vec: Vector) -> bool { var start = vec.length, max = limits.max_num_locals; var dcount = parser.readU32("local decl count", max); @@ -118,12 +145,12 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } def validateCode() { while (codeptr.pos < codeptr.limit) { + var constExpr = false; opcode_pos = codeptr.pos; - var b = codeptr.peek1(); var opcode = codeptr.read_opcode(); if (Trace.validation) { traceOpcode(); traceStack(true); } // FAST: Handle short operators (predictable direct branch) - if (Opcodes.attributes[b].SHORT_OP) { + if (Opcodes.attributes[opcode.prefix].SHORT_OP) { checkSignature(opcode.sig); if (Trace.validation) traceStack(false); continue; @@ -215,6 +242,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e setUnreachable(); } END => { + constExpr = true; if (ctl_stack.top == 0) return err_atpc().EmptyControlStack(); checkArgsAndTransfer(); var ctl = ctl_stack.peek(); @@ -238,7 +266,11 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } resetInit(); ctl_stack.pop(); - ctl_top = ctl_stack.peek(); + if (validatingInitExpr && ctl_stack.empty()) { // END finished the init expr + codeptr.reset(codeptr.data, codeptr.pos, codeptr.pos); + } else { + ctl_top = ctl_stack.peek(); + } } BR => { var depth = parser.readLabel(); @@ -398,6 +430,10 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e GLOBAL_GET => { var g = parser.readGlobalRef(); if (g == null) return; + if (validatingInitExpr) { + if (g.mutable) err_atpc().ExpectedImmutableGlobalInInit(g); + else constExpr = true; + } push(g.valtype); } GLOBAL_SET => { @@ -454,22 +490,27 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e checkSignature(opcode.sig); } I32_CONST => { + constExpr = true; codeptr.read_sleb32(); push(ValueType.I32); } I64_CONST => { + constExpr = true; codeptr.read_sleb64(); push(ValueType.I64); } F32_CONST => { + constExpr = true; codeptr.skipN(4); push(ValueType.F32); } F64_CONST => { + constExpr = true; codeptr.skipN(8); push(ValueType.F64); } REF_NULL => { + constExpr = true; var ht = parser.readHeapType(); push(ValueType.Ref(true, ht)); } @@ -478,9 +519,10 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e push(ValueType.I32); } REF_FUNC => { + constExpr = true; var func = parser.readFuncRef(); if (func == null) return; - if (!func.reffed) err_atpc().IllegalFuncRef(func); + if (!func.reffed && !validatingInitExpr) err_atpc().IllegalFuncRef(func); var ftype = if(extensions.FUNCTION_REFERENCES, ValueTypes.RefFunc(false, func.sig), ValueTypes.FUNCREF); @@ -520,6 +562,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } STRUCT_NEW => { if (noGC(opcode)) return; + constExpr = true; // TODO: only if fields are immutable? var st = parser.readStructType(); if (st == null) return; checkAndPopFields(st.field_types); @@ -527,6 +570,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } STRUCT_NEW_DEFAULT => { if (noGC(opcode)) return; + constExpr = true; // TODO: only if fields are immutable? var st = parser.readStructType(); if (st == null) return; var stt = ValueTypes.RefStruct(false, st); @@ -569,6 +613,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } ARRAY_NEW => { if (noGC(opcode)) return; + constExpr = true; // TODO: only if fields are immutable? var at = parser.readArrayType(); if (at == null) return; popE(ValueType.I32); @@ -577,6 +622,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } ARRAY_NEW_DEFAULT => { if (noGC(opcode)) return; + constExpr = true; // TODO: only if fields are immutable? var at = parser.readArrayType(); if (at == null) return; var att = ValueTypes.RefArray(false, at); @@ -670,6 +716,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } ARRAY_NEW_FIXED => { if (noGC(opcode)) return; + constExpr = true; // TODO: only if elem is immutable? var at = parser.readArrayType(); if (at == null) return; var size = codeptr.read_uleb32(); @@ -678,6 +725,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } ARRAY_NEW_DATA => { if (noGC(opcode)) return; + constExpr = true; // TODO: only if elem is immutable? var at = parser.readArrayType(); if (at == null) return; var index = parser.readDataIndex(); @@ -688,6 +736,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } ARRAY_NEW_ELEM => { if (noGC(opcode)) return; + constExpr = true; // TODO: only if elem is immutable? var at = parser.readArrayType(); if (at == null) return; var elem = parser.readElemRef(); @@ -701,6 +750,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } I31_NEW => { if (noGC(opcode)) return; + constExpr = true; popE(ValueType.I32); push(ValueTypes.I31REF_NONNULL); } @@ -857,6 +907,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e V128_LOAD_64_LANE => { checkLaneLoad(opcode, 3); checkLane(1); } V128_STORE_64_LANE => { checkLaneStore(opcode, 3); checkLane(1); } V128_CONST => { + constExpr = true; codeptr.skipN(16); push(ValueType.V128); } @@ -957,6 +1008,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } } if (Trace.validation) traceStack(false); + if (validatingInitExpr && !constExpr) err_atpc().UnexpectedOpcodeInInit(opcode.prefix, opcode.code); } } def startExHandler() -> (int, int) { diff --git a/src/util/ErrorGen.v3 b/src/util/ErrorGen.v3 index 7a211dfc..bfe6ff33 100644 --- a/src/util/ErrorGen.v3 +++ b/src/util/ErrorGen.v3 @@ -335,6 +335,9 @@ class ErrorGen(filename: string) { def UnterminatedFunctionBody() { setc(WasmError.UNTERMINATED_BODY, "unterminated function body"); } + def UnterminatedInitExpr() { + setc(WasmError.UNTERMINATED_BODY, "unterminated init expression"); + } def MismatchedElse() { setc(WasmError.MISMATCHED_ELSE, "mismatched else"); } From d91be51ffb9de0166648fd2de14819e717287898 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Fri, 6 Oct 2023 15:29:34 -0400 Subject: [PATCH 2/4] WIP: add init_stack to CodeValidator --- src/engine/BinParser.v3 | 136 +----------------------------------- src/engine/CodeValidator.v3 | 65 +++++++++++++++-- src/engine/Instance.v3 | 10 +++ src/engine/Module.v3 | 2 + 4 files changed, 74 insertions(+), 139 deletions(-) diff --git a/src/engine/BinParser.v3 b/src/engine/BinParser.v3 index ff3a8943..fa6bb5e1 100644 --- a/src/engine/BinParser.v3 +++ b/src/engine/BinParser.v3 @@ -692,139 +692,9 @@ class WasmParser(extensions: Extension.set, limits: Limits, module: Module, _ => sig = SigDecl.new(true, ValueTypes.NO_HEAPTYPES, SigCache.arr_v, [expected]); } var r = validator.validateInitExpr(sig, decoder); - decoder.at(prev_pos); - - // TODO: reduce duplication with CodeValidator - if (init_stack == null) init_stack = ArrayStack.new(); - else init_stack.clear(); - var wasEnd = false; - var pt = decoder.pos; - var last: byte; - while (decoder.pos < decoder.limit) { - pt = decoder.pos; - last = readByte("opcode", Opcodes.render); - match (last) { - Opcode.I32_CONST.code => { - var val = readI32(); - init_stack.push(ValueType.I32, InitExpr.I32(int.view(val))); - } - Opcode.I64_CONST.code => { - var val = readI64(); - init_stack.push(ValueType.I64, InitExpr.I64(long.view(val))); - } - Opcode.F32_CONST.code => { - var val = decoder.read_u32(); - init_stack.push(ValueType.F32, InitExpr.F32(val)); - } - Opcode.F64_CONST.code => { - var val = decoder.read_u64(); - init_stack.push(ValueType.F64, InitExpr.F64(val)); - } - Opcode.REF_NULL.code => { - init_stack.push(ValueType.Ref(true, readHeapType()), InitExpr.ExternRefNull); - } - Opcode.REF_FUNC.code => { - var f = readFuncRef(); - if (f != null) { - f.reffed = true; - init_stack.push(ValueTypes.RefFunc(false, f.sig), InitExpr.FuncRef(f.func_index, f)); - } - } - Opcode.GLOBAL_GET.code => { - var g = readGlobalRef(); - if (g != null) { - if (g.mutable) err.rel(decoder, pt).ExpectedImmutableGlobalInInit(g); - if (g.imp == null) err.rel(decoder, pt).ExpectedImportedGlobalInInit(g); - init_stack.push(g.valtype, InitExpr.Global(g.global_index, g)); - } - } - Opcode.END.code => { - break; - } - 0xFB => if (extensions.GC) { - var b2 = readU32("opcode", 1024); - match (b2) { - Opcode.STRUCT_NEW.code => { - var st = readStructType(); - if (st == null) break; - var vals = Array.new(st.field_types.length); - for (i = vals.length - 1; i >= 0; i--) vals[i] = init_stack.pop().1; // TODO: typecheck - var ht = HeapType.Struct(st); - init_stack.push(ValueType.Ref(false, ht), InitExpr.Struct(ht, vals)); - } - Opcode.STRUCT_NEW_DEFAULT.code => { - var st = readStructType(); - if (st == null) break; - var vals = Array.new(st.field_types.length); // TODO: proper default values - var ht = HeapType.Struct(st); - init_stack.push(ValueType.Ref(false, ht), InitExpr.Struct(ht, vals)); - } - Opcode.ARRAY_NEW.code => { - var at = readArrayType(); - if (at == null) break; - var len = init_stack.pop().1; // TODO: typecheck - var elem = init_stack.pop().1; // TODO: typecheck - var ht = HeapType.Array(at); - init_stack.push(ValueType.Ref(false, ht), InitExpr.Array(ht, len, elem)); - } - Opcode.ARRAY_NEW_DEFAULT.code => { - var at = readArrayType(); - if (at == null) break; - var len = init_stack.pop().1; // TODO: typecheck - var elem: InitExpr; - match (at.elem_types[0].valtype) { - I32 => elem = InitExpr.I32(0); - I64 => elem = InitExpr.I64(0); - F32 => elem = InitExpr.F32(0); - F64 => elem = InitExpr.F64(0); - V128 => elem = InitExpr.V128(0, 0); - BOTTOM, Ref, Abstract, Host => elem = InitExpr.ExternRefNull; - } - var ht = HeapType.Array(at); - init_stack.push(ValueType.Ref(false, ht), InitExpr.Array(ht, len, elem)); - } - Opcode.ARRAY_NEW_FIXED.code => { - var at = readArrayType(); - var length = readU32_i("array length", limits.max_array_length); - var vals = Array.new(length); - for (i = vals.length - 1; i >= 0; i--) vals[i] = init_stack.pop().1; - var ht = HeapType.Array(at); - init_stack.push(ValueType.Ref(false, ht), InitExpr.FixedArray(ht, vals)); - } - Opcode.I31_NEW.code => { - var e = init_stack.pop(); - if (e.0 == ValueType.I32) { - init_stack.push(ValueTypes.I31REF_NONNULL, InitExpr.I31(e.1)); - } else { - err.rel(decoder, pt).TypeMismatchIn(Strings.format1("i31.new in %s", quantity), ValueType.I32, e.0); - } - } - _ => err.rel(decoder, pt).UnexpectedOpcodeInInit(last, b2); - } - } - 0xFD => { - var b2 = decoder.read_uleb32(); - match (Opcodes.page_FD[b2]) { - V128_CONST => { - decoder.skipN(16); // TODO: read v128 const initializer - init_stack.push(ValueType.V128, InitExpr.V128(0, 0)); - } - _ => err.rel(decoder, pt).UnexpectedOpcodeInInit(last, b2); - } - } - _ => { - err.rel(decoder, pt).UnexpectedOpcodeInInit(0, last); - } - } - } - if (last != Opcode.END.code) err.rel(decoder, pt).ExpectedEndInInit(0); - if (init_stack.top != 1) { - err.rel(decoder, decoder.pos).TypeMismatchIn(quantity, expected, ValueType.BOTTOM); // TODO: proper msg for multiple - return InitExpr.ExternRefNull; - } else { - var t = init_stack.pop(); - if (!ValueTypes.isAssignable(t.0, expected)) err.rel(decoder, decoder.pos).TypeMismatchIn(quantity, expected, t.0); - return t.1; + match (r) { + Ok => return validator.init_stack.pop(); + _ => return InitExpr.I32(0); } } def readDefType(index: int) { diff --git a/src/engine/CodeValidator.v3 b/src/engine/CodeValidator.v3 index 9a097748..a8cef32f 100644 --- a/src/engine/CodeValidator.v3 +++ b/src/engine/CodeValidator.v3 @@ -19,6 +19,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e var sig: SigDecl; var opcode_pos: int; var validatingInitExpr = false; + var init_stack: ArrayStack; new() { codeptr.onError = err.onDataReaderError; @@ -79,10 +80,13 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e resetCode(d); opcode_pos = codeptr.pos; pushControl(Opcode.UNREACHABLE.code, sig.params, sig.results, 0); + if (init_stack == null) init_stack = ArrayStack.new(); + else init_stack.top = 0; // Run validation. validatingInitExpr = true; validateCode(); + d.reset(codeptr.data, codeptr.pos, codeptr.limit); if (ctl_stack.top != 0 && err.ok()) { err.rel(codeptr, codeptr.pos).UnterminatedInitExpr(); } @@ -433,6 +437,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e if (validatingInitExpr) { if (g.mutable) err_atpc().ExpectedImmutableGlobalInInit(g); else constExpr = true; + init_stack.push(InitExpr.Global(g.global_index, g)); } push(g.valtype); } @@ -491,28 +496,33 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e } I32_CONST => { constExpr = true; - codeptr.read_sleb32(); + var val = codeptr.read_sleb32(); push(ValueType.I32); + if (validatingInitExpr) init_stack.push(InitExpr.I32(val)); } I64_CONST => { constExpr = true; - codeptr.read_sleb64(); + var val = codeptr.read_sleb64(); push(ValueType.I64); + if (validatingInitExpr) init_stack.push(InitExpr.I64(val)); } F32_CONST => { constExpr = true; - codeptr.skipN(4); + var val = codeptr.read_u32(); push(ValueType.F32); + if (validatingInitExpr) init_stack.push(InitExpr.F32(val)); } F64_CONST => { constExpr = true; - codeptr.skipN(8); + var val = codeptr.read_u64(); push(ValueType.F64); + if (validatingInitExpr) init_stack.push(InitExpr.F64(val)); } REF_NULL => { constExpr = true; var ht = parser.readHeapType(); push(ValueType.Ref(true, ht)); + if (validatingInitExpr) init_stack.push(InitExpr.ExternRefNull); // TODO: add heap type } REF_IS_NULL => { popRef(); @@ -527,6 +537,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e ValueTypes.RefFunc(false, func.sig), ValueTypes.FUNCREF); push(ftype); + if (validatingInitExpr) init_stack.push(InitExpr.FuncRef(func.func_index, func)); } REF_AS_NON_NULL => { if (!checkExtension(Extension.FUNCTION_REFERENCES, opcode)) return; @@ -567,6 +578,12 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e if (st == null) return; checkAndPopFields(st.field_types); push(ValueTypes.RefStruct(false, st)); + if (validatingInitExpr) { + var vals = Array.new(st.field_types.length); + for (i = vals.length - 1; i >= 0; i--) vals[i] = init_stack.pop(); + var ht = HeapType.Struct(st); + init_stack.push(InitExpr.Struct(ht, vals)); + } } STRUCT_NEW_DEFAULT => { if (noGC(opcode)) return; @@ -576,6 +593,12 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e var stt = ValueTypes.RefStruct(false, st); if (!st.defaultable) err_atpc().ExpectedDefaultableHeapType(stt); push(stt); + if (validatingInitExpr) { + var vals = Array.new(st.field_types.length); + for (i < vals.length) vals[i] = InitExpr.Const(Values.default(st.field_types[i].valtype)); + var ht = HeapType.Struct(st); + init_stack.push(InitExpr.Struct(ht, vals)); + } } STRUCT_GET => { if (noGC(opcode)) return; @@ -619,6 +642,12 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e popE(ValueType.I32); checkAndPopFields(at.elem_types); push(ValueTypes.RefArray(false, at)); + if (validatingInitExpr) { + var len = init_stack.pop(); + var elem = init_stack.pop(); + var ht = HeapType.Array(at); + init_stack.push(InitExpr.Array(ht, len, elem)); + } } ARRAY_NEW_DEFAULT => { if (noGC(opcode)) return; @@ -629,6 +658,12 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e if (!at.defaultable) err_atpc().ExpectedDefaultableHeapType(att); popE(ValueType.I32); push(att); + if (validatingInitExpr) { + var len = init_stack.pop(); + var elem = InitExpr.Const(Values.default(at.elem_types[0].valtype)); + var ht = HeapType.Array(at); + init_stack.push(InitExpr.Array(ht, len, elem)); + } } ARRAY_GET => { if (noGC(opcode)) return; @@ -719,9 +754,14 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e constExpr = true; // TODO: only if elem is immutable? var at = parser.readArrayType(); if (at == null) return; - var size = codeptr.read_uleb32(); + var size = codeptr.read_uleb31(); for (i < size) checkAndPopFields(at.elem_types); push(ValueTypes.RefArray(false, at)); + if (validatingInitExpr) { + var vals = Array.new(size); + for (i = vals.length - 1; i >= 0; i--) vals[i] = init_stack.pop(); + init_stack.push(InitExpr.FixedArray(HeapType.Array(at), vals)); + } } ARRAY_NEW_DATA => { if (noGC(opcode)) return; @@ -733,6 +773,11 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e popE(ValueType.I32); popE(ValueType.I32); push(ValueTypes.RefArray(false, at)); + if (validatingInitExpr) { + var len = init_stack.pop(); + var offset = init_stack.pop(); + init_stack.push(InitExpr.ArrayNewData(HeapType.Array(at), index, offset, len)); + } } ARRAY_NEW_ELEM => { if (noGC(opcode)) return; @@ -747,12 +792,18 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e popE(ValueType.I32); popE(ValueType.I32); push(ValueTypes.RefArray(false, at)); + if (validatingInitExpr) { + var len = init_stack.pop(); + var offset = init_stack.pop(); + init_stack.push(InitExpr.ArrayNewElem(HeapType.Array(at), elem.elem_index, offset, len)); + } } I31_NEW => { if (noGC(opcode)) return; constExpr = true; popE(ValueType.I32); push(ValueTypes.I31REF_NONNULL); + if (validatingInitExpr) init_stack.push(InitExpr.I31(init_stack.pop())); } I31_GET_S => { if (noGC(opcode)) return; @@ -908,8 +959,10 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e V128_STORE_64_LANE => { checkLaneStore(opcode, 3); checkLane(1); } V128_CONST => { constExpr = true; - codeptr.skipN(16); + var low = codeptr.read_u64(); + var high = codeptr.read_u64(); push(ValueType.V128); + if (validatingInitExpr) init_stack.push(InitExpr.V128(low, high)); } I8X16_SHUFFLE => { checkLanes(31, 16); diff --git a/src/engine/Instance.v3 b/src/engine/Instance.v3 index 4b322a93..46ceb47d 100644 --- a/src/engine/Instance.v3 +++ b/src/engine/Instance.v3 @@ -95,6 +95,16 @@ class Instance(module: Module, imports: Array) { var vvals = Arrays.map(vals, evalInitExpr); return Value.Ref(HeapStruct.new(t.sdecl, vvals)); } + ArrayNewData(t, data_index, offset, len) => { + var voffset = evalInitExpr(offset); + var vlen = evalInitExpr(len); + return Values.I32_0; // TODO + } + ArrayNewElem(t, elem_index, offset, len) => { + var voffset = evalInitExpr(offset); + var vlen = evalInitExpr(len); + return Values.I32_0; // TODO + } } } } diff --git a/src/engine/Module.v3 b/src/engine/Module.v3 index bc041b8c..9bd6d97a 100644 --- a/src/engine/Module.v3 +++ b/src/engine/Module.v3 @@ -287,6 +287,8 @@ type InitExpr { case Array(t: HeapType.Array, len: InitExpr, elem: InitExpr); case FixedArray(t: HeapType.Array, vals: Array); case Struct(t: HeapType.Struct, vals: Array); + case ArrayNewData(t: HeapType.Array, data_index: int, offset: InitExpr, len: InitExpr); + case ArrayNewElem(t: HeapType.Array, elem_index: int, offset: InitExpr, len: InitExpr); } // Optional maximum for a table or memory. From a767bc12f22e0fc0da90ad456831cee733c6bc72 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Fri, 6 Oct 2023 16:26:41 -0400 Subject: [PATCH 3/4] Fix imported global check --- src/engine/BinParser.v3 | 3 +-- src/engine/CodeValidator.v3 | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/BinParser.v3 b/src/engine/BinParser.v3 index fa6bb5e1..817cd4b5 100644 --- a/src/engine/BinParser.v3 +++ b/src/engine/BinParser.v3 @@ -214,7 +214,6 @@ class WasmParser(extensions: Extension.set, limits: Limits, module: Module, err: ErrorGen, var decoder: DataReader) { def cache = Canon.globalCache; var eof = false; - var init_stack: ArrayStack<(ValueType, InitExpr)>; var max_fw_index = if(module != null, module.heaptypes.length); var max_legal_index = max_fw_index; var subtype_list: Vector<(int, int)>; @@ -694,7 +693,7 @@ class WasmParser(extensions: Extension.set, limits: Limits, module: Module, var r = validator.validateInitExpr(sig, decoder); match (r) { Ok => return validator.init_stack.pop(); - _ => return InitExpr.I32(0); + _ => return InitExpr.I32(0); // TODO: return invalid initexpr? } } def readDefType(index: int) { diff --git a/src/engine/CodeValidator.v3 b/src/engine/CodeValidator.v3 index a8cef32f..ffe649f1 100644 --- a/src/engine/CodeValidator.v3 +++ b/src/engine/CodeValidator.v3 @@ -86,7 +86,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e // Run validation. validatingInitExpr = true; validateCode(); - d.reset(codeptr.data, codeptr.pos, codeptr.limit); + d.reset(codeptr.data, codeptr.pos, d.limit); if (ctl_stack.top != 0 && err.ok()) { err.rel(codeptr, codeptr.pos).UnterminatedInitExpr(); } @@ -436,6 +436,7 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e if (g == null) return; if (validatingInitExpr) { if (g.mutable) err_atpc().ExpectedImmutableGlobalInInit(g); + else if (g.imp == null) err_atpc().ExpectedImportedGlobalInInit(g); else constExpr = true; init_stack.push(InitExpr.Global(g.global_index, g)); } From 7f6e9d7854308444e64c1af7699acbe61d664913 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Fri, 6 Oct 2023 16:28:17 -0400 Subject: [PATCH 4/4] Fix objdump --- src/objdump.main.v3 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/objdump.main.v3 b/src/objdump.main.v3 index a927842a..5a03a7c3 100644 --- a/src/objdump.main.v3 +++ b/src/objdump.main.v3 @@ -100,6 +100,8 @@ def parseAndDump(engine: Engine, path: string) -> int { Array => OUT.puts("Array"); FixedArray => OUT.puts("FixedArray"); Struct => OUT.puts("Struct"); + ArrayNewData => OUT.puts("ArrayNewData"); + ArrayNewElem => OUT.puts("ArrayNewElem"); } OUT.outln(); }