From 26f49b7e27076e689541b9e13a1b54f60a4ee5c2 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Thu, 11 Jul 2024 02:57:23 +0900 Subject: feat: organize namespaces --- examples/php-on-wasm/php-wasm.php | 22 +- examples/rubyvm-on-php-on-wasm/php-wasm.php | 22 +- src/BinaryFormat/Decoder.php | 1173 --------- src/BinaryFormat/Internal/Code.php | 23 - src/BinaryFormat/Internal/Locals.php | 22 - src/BinaryFormat/Internal/SectionId.php | 25 - src/BinaryFormat/InvalidBinaryFormatException.php | 11 - src/Debug/Debug.php | 112 - src/Execution/Allocator.php | 165 -- src/Execution/DataInst.php | 16 - src/Execution/ElemInst.php | 19 - src/Execution/ExportInst.php | 17 - src/Execution/Extern.php | 28 - src/Execution/ExternVal.php | 28 - src/Execution/ExternVals/Func.php | 15 - src/Execution/ExternVals/Global_.php | 15 - src/Execution/ExternVals/Mem.php | 15 - src/Execution/ExternVals/Table.php | 15 - src/Execution/Externs/Func.php | 16 - src/Execution/Externs/Global_.php | 16 - src/Execution/Externs/Mem.php | 16 - src/Execution/Externs/Table.php | 16 - src/Execution/Frame.php | 20 - src/Execution/FuncInst.php | 21 - src/Execution/FuncInsts/Host.php | 20 - src/Execution/FuncInsts/Wasm.php | 20 - src/Execution/GlobalInst.php | 16 - src/Execution/Label.php | 16 - src/Execution/MemInst.php | 662 ----- src/Execution/ModuleInst.php | 32 - src/Execution/NumericOps.php | 193 -- src/Execution/Ref.php | 25 - src/Execution/Refs/RefExtern.php | 15 - src/Execution/Refs/RefFunc.php | 15 - src/Execution/Refs/RefNull.php | 16 - src/Execution/Result.php | 21 - src/Execution/Results/Trap.php | 11 - src/Execution/Results/Values.php | 19 - src/Execution/Runtime.php | 2668 -------------------- src/Execution/Stack.php | 212 -- src/Execution/StackOverflowException.php | 19 - src/Execution/Store.php | 52 - src/Execution/TableInst.php | 19 - src/Execution/TrapException.php | 28 - src/Execution/TrapKind.php | 19 - src/Structure/Instructions/Instr.php | 971 ------- .../Instructions/Instrs/Control/Block.php | 24 - .../Instructions/Instrs/Control/BlockType.php | 25 - .../Instrs/Control/BlockTypes/TypeIdx.php | 14 - .../Instrs/Control/BlockTypes/ValType.php | 20 - src/Structure/Instructions/Instrs/Control/Br.php | 20 - src/Structure/Instructions/Instrs/Control/BrIf.php | 20 - .../Instructions/Instrs/Control/BrTable.php | 24 - src/Structure/Instructions/Instrs/Control/Call.php | 20 - .../Instructions/Instrs/Control/CallIndirect.php | 21 - .../Instructions/Instrs/Control/Else_.php | 15 - src/Structure/Instructions/Instrs/Control/End.php | 15 - src/Structure/Instructions/Instrs/Control/If_.php | 26 - src/Structure/Instructions/Instrs/Control/Loop.php | 24 - src/Structure/Instructions/Instrs/Control/Nop.php | 15 - .../Instructions/Instrs/Control/Return_.php | 15 - .../Instructions/Instrs/Control/Unreachable.php | 15 - .../Instructions/Instrs/Memory/DataDrop.php | 20 - .../Instructions/Instrs/Memory/F32Load.php | 25 - .../Instructions/Instrs/Memory/F32Store.php | 25 - .../Instructions/Instrs/Memory/F64Load.php | 25 - .../Instructions/Instrs/Memory/F64Store.php | 25 - .../Instructions/Instrs/Memory/I32Load.php | 25 - .../Instructions/Instrs/Memory/I32Load16S.php | 25 - .../Instructions/Instrs/Memory/I32Load16U.php | 25 - .../Instructions/Instrs/Memory/I32Load8S.php | 25 - .../Instructions/Instrs/Memory/I32Load8U.php | 25 - .../Instructions/Instrs/Memory/I32Store.php | 25 - .../Instructions/Instrs/Memory/I32Store16.php | 25 - .../Instructions/Instrs/Memory/I32Store8.php | 25 - .../Instructions/Instrs/Memory/I64Load.php | 25 - .../Instructions/Instrs/Memory/I64Load16S.php | 25 - .../Instructions/Instrs/Memory/I64Load16U.php | 25 - .../Instructions/Instrs/Memory/I64Load32S.php | 25 - .../Instructions/Instrs/Memory/I64Load32U.php | 25 - .../Instructions/Instrs/Memory/I64Load8S.php | 25 - .../Instructions/Instrs/Memory/I64Load8U.php | 25 - .../Instructions/Instrs/Memory/I64Store.php | 25 - .../Instructions/Instrs/Memory/I64Store16.php | 25 - .../Instructions/Instrs/Memory/I64Store32.php | 25 - .../Instructions/Instrs/Memory/I64Store8.php | 25 - .../Instructions/Instrs/Memory/MemoryCopy.php | 15 - .../Instructions/Instrs/Memory/MemoryFill.php | 15 - .../Instructions/Instrs/Memory/MemoryGrow.php | 15 - .../Instructions/Instrs/Memory/MemoryInit.php | 20 - .../Instructions/Instrs/Memory/MemorySize.php | 15 - .../Instructions/Instrs/Numeric/F32Abs.php | 15 - .../Instructions/Instrs/Numeric/F32Add.php | 15 - .../Instructions/Instrs/Numeric/F32Ceil.php | 15 - .../Instructions/Instrs/Numeric/F32Const.php | 23 - .../Instructions/Instrs/Numeric/F32ConvertI32S.php | 15 - .../Instructions/Instrs/Numeric/F32ConvertI32U.php | 15 - .../Instructions/Instrs/Numeric/F32ConvertI64S.php | 15 - .../Instructions/Instrs/Numeric/F32ConvertI64U.php | 15 - .../Instructions/Instrs/Numeric/F32CopySign.php | 15 - .../Instructions/Instrs/Numeric/F32DemoteF64.php | 15 - .../Instructions/Instrs/Numeric/F32Div.php | 15 - .../Instructions/Instrs/Numeric/F32Eq.php | 15 - .../Instructions/Instrs/Numeric/F32Floor.php | 15 - .../Instructions/Instrs/Numeric/F32Ge.php | 15 - .../Instructions/Instrs/Numeric/F32Gt.php | 15 - .../Instructions/Instrs/Numeric/F32Le.php | 15 - .../Instructions/Instrs/Numeric/F32Lt.php | 15 - .../Instructions/Instrs/Numeric/F32Max.php | 15 - .../Instructions/Instrs/Numeric/F32Min.php | 15 - .../Instructions/Instrs/Numeric/F32Mul.php | 15 - .../Instructions/Instrs/Numeric/F32Ne.php | 15 - .../Instructions/Instrs/Numeric/F32Nearest.php | 15 - .../Instructions/Instrs/Numeric/F32Neg.php | 15 - .../Instrs/Numeric/F32ReinterpretI32.php | 15 - .../Instrs/Numeric/F32ReinterpretI64.php | 15 - .../Instructions/Instrs/Numeric/F32Sqrt.php | 15 - .../Instructions/Instrs/Numeric/F32Sub.php | 15 - .../Instructions/Instrs/Numeric/F32Trunc.php | 15 - .../Instructions/Instrs/Numeric/F64Abs.php | 15 - .../Instructions/Instrs/Numeric/F64Add.php | 15 - .../Instructions/Instrs/Numeric/F64Ceil.php | 15 - .../Instructions/Instrs/Numeric/F64Const.php | 23 - .../Instructions/Instrs/Numeric/F64ConvertI32S.php | 15 - .../Instructions/Instrs/Numeric/F64ConvertI32U.php | 15 - .../Instructions/Instrs/Numeric/F64ConvertI64S.php | 15 - .../Instructions/Instrs/Numeric/F64ConvertI64U.php | 15 - .../Instructions/Instrs/Numeric/F64CopySign.php | 15 - .../Instructions/Instrs/Numeric/F64Div.php | 15 - .../Instructions/Instrs/Numeric/F64Eq.php | 15 - .../Instructions/Instrs/Numeric/F64Floor.php | 15 - .../Instructions/Instrs/Numeric/F64Ge.php | 15 - .../Instructions/Instrs/Numeric/F64Gt.php | 15 - .../Instructions/Instrs/Numeric/F64Le.php | 15 - .../Instructions/Instrs/Numeric/F64Lt.php | 15 - .../Instructions/Instrs/Numeric/F64Max.php | 15 - .../Instructions/Instrs/Numeric/F64Min.php | 15 - .../Instructions/Instrs/Numeric/F64Mul.php | 15 - .../Instructions/Instrs/Numeric/F64Ne.php | 15 - .../Instructions/Instrs/Numeric/F64Nearest.php | 15 - .../Instructions/Instrs/Numeric/F64Neg.php | 15 - .../Instructions/Instrs/Numeric/F64PromoteF32.php | 15 - .../Instrs/Numeric/F64ReinterpretI32.php | 15 - .../Instrs/Numeric/F64ReinterpretI64.php | 15 - .../Instructions/Instrs/Numeric/F64Sqrt.php | 15 - .../Instructions/Instrs/Numeric/F64Sub.php | 15 - .../Instructions/Instrs/Numeric/F64Trunc.php | 15 - .../Instructions/Instrs/Numeric/I32Add.php | 15 - .../Instructions/Instrs/Numeric/I32And.php | 15 - .../Instructions/Instrs/Numeric/I32Clz.php | 15 - .../Instructions/Instrs/Numeric/I32Const.php | 23 - .../Instructions/Instrs/Numeric/I32Ctz.php | 15 - .../Instructions/Instrs/Numeric/I32DivS.php | 15 - .../Instructions/Instrs/Numeric/I32DivU.php | 15 - .../Instructions/Instrs/Numeric/I32Eq.php | 15 - .../Instructions/Instrs/Numeric/I32Eqz.php | 15 - .../Instructions/Instrs/Numeric/I32Extend16S.php | 15 - .../Instructions/Instrs/Numeric/I32Extend8S.php | 15 - .../Instructions/Instrs/Numeric/I32GeS.php | 15 - .../Instructions/Instrs/Numeric/I32GeU.php | 15 - .../Instructions/Instrs/Numeric/I32GtS.php | 15 - .../Instructions/Instrs/Numeric/I32GtU.php | 15 - .../Instructions/Instrs/Numeric/I32LeS.php | 15 - .../Instructions/Instrs/Numeric/I32LeU.php | 15 - .../Instructions/Instrs/Numeric/I32LtS.php | 15 - .../Instructions/Instrs/Numeric/I32LtU.php | 15 - .../Instructions/Instrs/Numeric/I32Mul.php | 15 - .../Instructions/Instrs/Numeric/I32Ne.php | 15 - .../Instructions/Instrs/Numeric/I32Or.php | 15 - .../Instructions/Instrs/Numeric/I32Popcnt.php | 15 - .../Instrs/Numeric/I32ReinterpretF32.php | 15 - .../Instrs/Numeric/I32ReinterpretF64.php | 15 - .../Instructions/Instrs/Numeric/I32RemS.php | 15 - .../Instructions/Instrs/Numeric/I32RemU.php | 15 - .../Instructions/Instrs/Numeric/I32RotL.php | 15 - .../Instructions/Instrs/Numeric/I32RotR.php | 15 - .../Instructions/Instrs/Numeric/I32Shl.php | 15 - .../Instructions/Instrs/Numeric/I32ShrS.php | 15 - .../Instructions/Instrs/Numeric/I32ShrU.php | 15 - .../Instructions/Instrs/Numeric/I32Sub.php | 15 - .../Instructions/Instrs/Numeric/I32TruncF32S.php | 15 - .../Instructions/Instrs/Numeric/I32TruncF32U.php | 15 - .../Instructions/Instrs/Numeric/I32TruncF64S.php | 15 - .../Instructions/Instrs/Numeric/I32TruncF64U.php | 15 - .../Instrs/Numeric/I32TruncSatF32S.php | 15 - .../Instrs/Numeric/I32TruncSatF32U.php | 15 - .../Instrs/Numeric/I32TruncSatF64S.php | 15 - .../Instrs/Numeric/I32TruncSatF64U.php | 15 - .../Instructions/Instrs/Numeric/I32WrapI64.php | 15 - .../Instructions/Instrs/Numeric/I32Xor.php | 15 - .../Instructions/Instrs/Numeric/I64Add.php | 15 - .../Instructions/Instrs/Numeric/I64And.php | 15 - .../Instructions/Instrs/Numeric/I64Clz.php | 15 - .../Instructions/Instrs/Numeric/I64Const.php | 23 - .../Instructions/Instrs/Numeric/I64Ctz.php | 15 - .../Instructions/Instrs/Numeric/I64DivS.php | 15 - .../Instructions/Instrs/Numeric/I64DivU.php | 15 - .../Instructions/Instrs/Numeric/I64Eq.php | 15 - .../Instructions/Instrs/Numeric/I64Eqz.php | 15 - .../Instructions/Instrs/Numeric/I64Extend16S.php | 15 - .../Instructions/Instrs/Numeric/I64Extend32S.php | 15 - .../Instructions/Instrs/Numeric/I64Extend8S.php | 15 - .../Instructions/Instrs/Numeric/I64ExtendI32S.php | 15 - .../Instructions/Instrs/Numeric/I64ExtendI32U.php | 15 - .../Instructions/Instrs/Numeric/I64GeS.php | 15 - .../Instructions/Instrs/Numeric/I64GeU.php | 15 - .../Instructions/Instrs/Numeric/I64GtS.php | 15 - .../Instructions/Instrs/Numeric/I64GtU.php | 15 - .../Instructions/Instrs/Numeric/I64LeS.php | 15 - .../Instructions/Instrs/Numeric/I64LeU.php | 15 - .../Instructions/Instrs/Numeric/I64LtS.php | 15 - .../Instructions/Instrs/Numeric/I64LtU.php | 15 - .../Instructions/Instrs/Numeric/I64Mul.php | 15 - .../Instructions/Instrs/Numeric/I64Ne.php | 15 - .../Instructions/Instrs/Numeric/I64Or.php | 15 - .../Instructions/Instrs/Numeric/I64Popcnt.php | 15 - .../Instrs/Numeric/I64ReinterpretF32.php | 15 - .../Instrs/Numeric/I64ReinterpretF64.php | 15 - .../Instructions/Instrs/Numeric/I64RemS.php | 15 - .../Instructions/Instrs/Numeric/I64RemU.php | 15 - .../Instructions/Instrs/Numeric/I64RotL.php | 15 - .../Instructions/Instrs/Numeric/I64RotR.php | 15 - .../Instructions/Instrs/Numeric/I64Shl.php | 15 - .../Instructions/Instrs/Numeric/I64ShrS.php | 15 - .../Instructions/Instrs/Numeric/I64ShrU.php | 15 - .../Instructions/Instrs/Numeric/I64Sub.php | 15 - .../Instructions/Instrs/Numeric/I64TruncF32S.php | 15 - .../Instructions/Instrs/Numeric/I64TruncF32U.php | 15 - .../Instructions/Instrs/Numeric/I64TruncF64S.php | 15 - .../Instructions/Instrs/Numeric/I64TruncF64U.php | 15 - .../Instrs/Numeric/I64TruncSatF32S.php | 15 - .../Instrs/Numeric/I64TruncSatF32U.php | 15 - .../Instrs/Numeric/I64TruncSatF64S.php | 15 - .../Instrs/Numeric/I64TruncSatF64U.php | 15 - .../Instructions/Instrs/Numeric/I64Xor.php | 15 - .../Instructions/Instrs/Parametric/Drop.php | 15 - .../Instructions/Instrs/Parametric/Select.php | 24 - .../Instructions/Instrs/Reference/RefFunc.php | 20 - .../Instructions/Instrs/Reference/RefIsNull.php | 15 - .../Instructions/Instrs/Reference/RefNull.php | 21 - .../Instructions/Instrs/Table/ElemDrop.php | 20 - .../Instructions/Instrs/Table/TableCopy.php | 21 - .../Instructions/Instrs/Table/TableFill.php | 20 - .../Instructions/Instrs/Table/TableGet.php | 20 - .../Instructions/Instrs/Table/TableGrow.php | 20 - .../Instructions/Instrs/Table/TableInit.php | 21 - .../Instructions/Instrs/Table/TableSet.php | 20 - .../Instructions/Instrs/Table/TableSize.php | 20 - .../Instructions/Instrs/Variable/GlobalGet.php | 20 - .../Instructions/Instrs/Variable/GlobalSet.php | 20 - .../Instructions/Instrs/Variable/LocalGet.php | 20 - .../Instructions/Instrs/Variable/LocalSet.php | 20 - .../Instructions/Instrs/Variable/LocalTee.php | 20 - src/Structure/Modules/Data.php | 17 - src/Structure/Modules/DataMode.php | 25 - src/Structure/Modules/DataModes/Active.php | 20 - src/Structure/Modules/DataModes/Passive.php | 14 - src/Structure/Modules/Elem.php | 21 - src/Structure/Modules/ElemMode.php | 30 - src/Structure/Modules/ElemModes/Active.php | 20 - src/Structure/Modules/ElemModes/Declarative.php | 14 - src/Structure/Modules/ElemModes/Passive.php | 14 - src/Structure/Modules/Export.php | 17 - src/Structure/Modules/ExportDesc.php | 28 - src/Structure/Modules/ExportDescs/Func.php | 15 - src/Structure/Modules/ExportDescs/Global_.php | 15 - src/Structure/Modules/ExportDescs/Mem.php | 15 - src/Structure/Modules/ExportDescs/Table.php | 15 - src/Structure/Modules/Func.php | 21 - src/Structure/Modules/Global_.php | 20 - src/Structure/Modules/Import.php | 19 - src/Structure/Modules/ImportDesc.php | 32 - src/Structure/Modules/ImportDescs/Func.php | 15 - src/Structure/Modules/ImportDescs/Global_.php | 16 - src/Structure/Modules/ImportDescs/Mem.php | 16 - src/Structure/Modules/ImportDescs/Table.php | 16 - src/Structure/Modules/Local.php | 15 - src/Structure/Modules/Mem.php | 15 - src/Structure/Modules/Module.php | 35 - src/Structure/Modules/Start.php | 13 - src/Structure/Modules/Table.php | 15 - src/Structure/Types/ExternType.php | 28 - src/Structure/Types/ExternTypes/Func.php | 16 - src/Structure/Types/ExternTypes/Global_.php | 16 - src/Structure/Types/ExternTypes/Mem.php | 16 - src/Structure/Types/ExternTypes/Table.php | 16 - src/Structure/Types/FuncType.php | 20 - src/Structure/Types/GlobalType.php | 14 - src/Structure/Types/Limits.php | 24 - src/Structure/Types/MemType.php | 13 - src/Structure/Types/Mut.php | 11 - src/Structure/Types/NumType.php | 13 - src/Structure/Types/RefType.php | 11 - src/Structure/Types/ResultType.php | 31 - src/Structure/Types/TableType.php | 14 - src/Structure/Types/ValType.php | 25 - src/Structure/Types/ValTypes/NumType.php | 21 - src/Structure/Types/ValTypes/RefType.php | 21 - src/Structure/Types/ValTypes/VecType.php | 21 - src/Structure/Types/VecType.php | 10 - src/WebAssembly/BinaryFormat/Decoder.php | 1173 +++++++++ src/WebAssembly/BinaryFormat/Internal/Code.php | 23 + src/WebAssembly/BinaryFormat/Internal/Locals.php | 22 + .../BinaryFormat/Internal/SectionId.php | 25 + .../BinaryFormat/InvalidBinaryFormatException.php | 11 + src/WebAssembly/Debug/Debug.php | 112 + src/WebAssembly/Execution/Allocator.php | 165 ++ src/WebAssembly/Execution/DataInst.php | 16 + src/WebAssembly/Execution/ElemInst.php | 19 + src/WebAssembly/Execution/ExportInst.php | 17 + src/WebAssembly/Execution/Extern.php | 28 + src/WebAssembly/Execution/ExternVal.php | 28 + src/WebAssembly/Execution/ExternVals/Func.php | 15 + src/WebAssembly/Execution/ExternVals/Global_.php | 15 + src/WebAssembly/Execution/ExternVals/Mem.php | 15 + src/WebAssembly/Execution/ExternVals/Table.php | 15 + src/WebAssembly/Execution/Externs/Func.php | 16 + src/WebAssembly/Execution/Externs/Global_.php | 16 + src/WebAssembly/Execution/Externs/Mem.php | 16 + src/WebAssembly/Execution/Externs/Table.php | 16 + src/WebAssembly/Execution/Frame.php | 20 + src/WebAssembly/Execution/FuncInst.php | 21 + src/WebAssembly/Execution/FuncInsts/Host.php | 20 + src/WebAssembly/Execution/FuncInsts/Wasm.php | 20 + src/WebAssembly/Execution/GlobalInst.php | 16 + src/WebAssembly/Execution/Label.php | 16 + src/WebAssembly/Execution/MemInst.php | 662 +++++ src/WebAssembly/Execution/ModuleInst.php | 32 + src/WebAssembly/Execution/NumericOps.php | 193 ++ src/WebAssembly/Execution/Ref.php | 25 + src/WebAssembly/Execution/Refs/RefExtern.php | 15 + src/WebAssembly/Execution/Refs/RefFunc.php | 15 + src/WebAssembly/Execution/Refs/RefNull.php | 16 + src/WebAssembly/Execution/Result.php | 21 + src/WebAssembly/Execution/Results/Trap.php | 11 + src/WebAssembly/Execution/Results/Values.php | 19 + src/WebAssembly/Execution/Runtime.php | 2668 ++++++++++++++++++++ src/WebAssembly/Execution/Stack.php | 212 ++ .../Execution/StackOverflowException.php | 19 + src/WebAssembly/Execution/Store.php | 52 + src/WebAssembly/Execution/TableInst.php | 19 + src/WebAssembly/Execution/TrapException.php | 28 + src/WebAssembly/Execution/TrapKind.php | 19 + src/WebAssembly/Structure/Instructions/Instr.php | 971 +++++++ .../Instructions/Instrs/Control/Block.php | 24 + .../Instructions/Instrs/Control/BlockType.php | 25 + .../Instrs/Control/BlockTypes/TypeIdx.php | 14 + .../Instrs/Control/BlockTypes/ValType.php | 20 + .../Structure/Instructions/Instrs/Control/Br.php | 20 + .../Structure/Instructions/Instrs/Control/BrIf.php | 20 + .../Instructions/Instrs/Control/BrTable.php | 24 + .../Structure/Instructions/Instrs/Control/Call.php | 20 + .../Instructions/Instrs/Control/CallIndirect.php | 21 + .../Instructions/Instrs/Control/Else_.php | 15 + .../Structure/Instructions/Instrs/Control/End.php | 15 + .../Structure/Instructions/Instrs/Control/If_.php | 26 + .../Structure/Instructions/Instrs/Control/Loop.php | 24 + .../Structure/Instructions/Instrs/Control/Nop.php | 15 + .../Instructions/Instrs/Control/Return_.php | 15 + .../Instructions/Instrs/Control/Unreachable.php | 15 + .../Instructions/Instrs/Memory/DataDrop.php | 20 + .../Instructions/Instrs/Memory/F32Load.php | 25 + .../Instructions/Instrs/Memory/F32Store.php | 25 + .../Instructions/Instrs/Memory/F64Load.php | 25 + .../Instructions/Instrs/Memory/F64Store.php | 25 + .../Instructions/Instrs/Memory/I32Load.php | 25 + .../Instructions/Instrs/Memory/I32Load16S.php | 25 + .../Instructions/Instrs/Memory/I32Load16U.php | 25 + .../Instructions/Instrs/Memory/I32Load8S.php | 25 + .../Instructions/Instrs/Memory/I32Load8U.php | 25 + .../Instructions/Instrs/Memory/I32Store.php | 25 + .../Instructions/Instrs/Memory/I32Store16.php | 25 + .../Instructions/Instrs/Memory/I32Store8.php | 25 + .../Instructions/Instrs/Memory/I64Load.php | 25 + .../Instructions/Instrs/Memory/I64Load16S.php | 25 + .../Instructions/Instrs/Memory/I64Load16U.php | 25 + .../Instructions/Instrs/Memory/I64Load32S.php | 25 + .../Instructions/Instrs/Memory/I64Load32U.php | 25 + .../Instructions/Instrs/Memory/I64Load8S.php | 25 + .../Instructions/Instrs/Memory/I64Load8U.php | 25 + .../Instructions/Instrs/Memory/I64Store.php | 25 + .../Instructions/Instrs/Memory/I64Store16.php | 25 + .../Instructions/Instrs/Memory/I64Store32.php | 25 + .../Instructions/Instrs/Memory/I64Store8.php | 25 + .../Instructions/Instrs/Memory/MemoryCopy.php | 15 + .../Instructions/Instrs/Memory/MemoryFill.php | 15 + .../Instructions/Instrs/Memory/MemoryGrow.php | 15 + .../Instructions/Instrs/Memory/MemoryInit.php | 20 + .../Instructions/Instrs/Memory/MemorySize.php | 15 + .../Instructions/Instrs/Numeric/F32Abs.php | 15 + .../Instructions/Instrs/Numeric/F32Add.php | 15 + .../Instructions/Instrs/Numeric/F32Ceil.php | 15 + .../Instructions/Instrs/Numeric/F32Const.php | 23 + .../Instructions/Instrs/Numeric/F32ConvertI32S.php | 15 + .../Instructions/Instrs/Numeric/F32ConvertI32U.php | 15 + .../Instructions/Instrs/Numeric/F32ConvertI64S.php | 15 + .../Instructions/Instrs/Numeric/F32ConvertI64U.php | 15 + .../Instructions/Instrs/Numeric/F32CopySign.php | 15 + .../Instructions/Instrs/Numeric/F32DemoteF64.php | 15 + .../Instructions/Instrs/Numeric/F32Div.php | 15 + .../Instructions/Instrs/Numeric/F32Eq.php | 15 + .../Instructions/Instrs/Numeric/F32Floor.php | 15 + .../Instructions/Instrs/Numeric/F32Ge.php | 15 + .../Instructions/Instrs/Numeric/F32Gt.php | 15 + .../Instructions/Instrs/Numeric/F32Le.php | 15 + .../Instructions/Instrs/Numeric/F32Lt.php | 15 + .../Instructions/Instrs/Numeric/F32Max.php | 15 + .../Instructions/Instrs/Numeric/F32Min.php | 15 + .../Instructions/Instrs/Numeric/F32Mul.php | 15 + .../Instructions/Instrs/Numeric/F32Ne.php | 15 + .../Instructions/Instrs/Numeric/F32Nearest.php | 15 + .../Instructions/Instrs/Numeric/F32Neg.php | 15 + .../Instrs/Numeric/F32ReinterpretI32.php | 15 + .../Instrs/Numeric/F32ReinterpretI64.php | 15 + .../Instructions/Instrs/Numeric/F32Sqrt.php | 15 + .../Instructions/Instrs/Numeric/F32Sub.php | 15 + .../Instructions/Instrs/Numeric/F32Trunc.php | 15 + .../Instructions/Instrs/Numeric/F64Abs.php | 15 + .../Instructions/Instrs/Numeric/F64Add.php | 15 + .../Instructions/Instrs/Numeric/F64Ceil.php | 15 + .../Instructions/Instrs/Numeric/F64Const.php | 23 + .../Instructions/Instrs/Numeric/F64ConvertI32S.php | 15 + .../Instructions/Instrs/Numeric/F64ConvertI32U.php | 15 + .../Instructions/Instrs/Numeric/F64ConvertI64S.php | 15 + .../Instructions/Instrs/Numeric/F64ConvertI64U.php | 15 + .../Instructions/Instrs/Numeric/F64CopySign.php | 15 + .../Instructions/Instrs/Numeric/F64Div.php | 15 + .../Instructions/Instrs/Numeric/F64Eq.php | 15 + .../Instructions/Instrs/Numeric/F64Floor.php | 15 + .../Instructions/Instrs/Numeric/F64Ge.php | 15 + .../Instructions/Instrs/Numeric/F64Gt.php | 15 + .../Instructions/Instrs/Numeric/F64Le.php | 15 + .../Instructions/Instrs/Numeric/F64Lt.php | 15 + .../Instructions/Instrs/Numeric/F64Max.php | 15 + .../Instructions/Instrs/Numeric/F64Min.php | 15 + .../Instructions/Instrs/Numeric/F64Mul.php | 15 + .../Instructions/Instrs/Numeric/F64Ne.php | 15 + .../Instructions/Instrs/Numeric/F64Nearest.php | 15 + .../Instructions/Instrs/Numeric/F64Neg.php | 15 + .../Instructions/Instrs/Numeric/F64PromoteF32.php | 15 + .../Instrs/Numeric/F64ReinterpretI32.php | 15 + .../Instrs/Numeric/F64ReinterpretI64.php | 15 + .../Instructions/Instrs/Numeric/F64Sqrt.php | 15 + .../Instructions/Instrs/Numeric/F64Sub.php | 15 + .../Instructions/Instrs/Numeric/F64Trunc.php | 15 + .../Instructions/Instrs/Numeric/I32Add.php | 15 + .../Instructions/Instrs/Numeric/I32And.php | 15 + .../Instructions/Instrs/Numeric/I32Clz.php | 15 + .../Instructions/Instrs/Numeric/I32Const.php | 23 + .../Instructions/Instrs/Numeric/I32Ctz.php | 15 + .../Instructions/Instrs/Numeric/I32DivS.php | 15 + .../Instructions/Instrs/Numeric/I32DivU.php | 15 + .../Instructions/Instrs/Numeric/I32Eq.php | 15 + .../Instructions/Instrs/Numeric/I32Eqz.php | 15 + .../Instructions/Instrs/Numeric/I32Extend16S.php | 15 + .../Instructions/Instrs/Numeric/I32Extend8S.php | 15 + .../Instructions/Instrs/Numeric/I32GeS.php | 15 + .../Instructions/Instrs/Numeric/I32GeU.php | 15 + .../Instructions/Instrs/Numeric/I32GtS.php | 15 + .../Instructions/Instrs/Numeric/I32GtU.php | 15 + .../Instructions/Instrs/Numeric/I32LeS.php | 15 + .../Instructions/Instrs/Numeric/I32LeU.php | 15 + .../Instructions/Instrs/Numeric/I32LtS.php | 15 + .../Instructions/Instrs/Numeric/I32LtU.php | 15 + .../Instructions/Instrs/Numeric/I32Mul.php | 15 + .../Instructions/Instrs/Numeric/I32Ne.php | 15 + .../Instructions/Instrs/Numeric/I32Or.php | 15 + .../Instructions/Instrs/Numeric/I32Popcnt.php | 15 + .../Instrs/Numeric/I32ReinterpretF32.php | 15 + .../Instrs/Numeric/I32ReinterpretF64.php | 15 + .../Instructions/Instrs/Numeric/I32RemS.php | 15 + .../Instructions/Instrs/Numeric/I32RemU.php | 15 + .../Instructions/Instrs/Numeric/I32RotL.php | 15 + .../Instructions/Instrs/Numeric/I32RotR.php | 15 + .../Instructions/Instrs/Numeric/I32Shl.php | 15 + .../Instructions/Instrs/Numeric/I32ShrS.php | 15 + .../Instructions/Instrs/Numeric/I32ShrU.php | 15 + .../Instructions/Instrs/Numeric/I32Sub.php | 15 + .../Instructions/Instrs/Numeric/I32TruncF32S.php | 15 + .../Instructions/Instrs/Numeric/I32TruncF32U.php | 15 + .../Instructions/Instrs/Numeric/I32TruncF64S.php | 15 + .../Instructions/Instrs/Numeric/I32TruncF64U.php | 15 + .../Instrs/Numeric/I32TruncSatF32S.php | 15 + .../Instrs/Numeric/I32TruncSatF32U.php | 15 + .../Instrs/Numeric/I32TruncSatF64S.php | 15 + .../Instrs/Numeric/I32TruncSatF64U.php | 15 + .../Instructions/Instrs/Numeric/I32WrapI64.php | 15 + .../Instructions/Instrs/Numeric/I32Xor.php | 15 + .../Instructions/Instrs/Numeric/I64Add.php | 15 + .../Instructions/Instrs/Numeric/I64And.php | 15 + .../Instructions/Instrs/Numeric/I64Clz.php | 15 + .../Instructions/Instrs/Numeric/I64Const.php | 23 + .../Instructions/Instrs/Numeric/I64Ctz.php | 15 + .../Instructions/Instrs/Numeric/I64DivS.php | 15 + .../Instructions/Instrs/Numeric/I64DivU.php | 15 + .../Instructions/Instrs/Numeric/I64Eq.php | 15 + .../Instructions/Instrs/Numeric/I64Eqz.php | 15 + .../Instructions/Instrs/Numeric/I64Extend16S.php | 15 + .../Instructions/Instrs/Numeric/I64Extend32S.php | 15 + .../Instructions/Instrs/Numeric/I64Extend8S.php | 15 + .../Instructions/Instrs/Numeric/I64ExtendI32S.php | 15 + .../Instructions/Instrs/Numeric/I64ExtendI32U.php | 15 + .../Instructions/Instrs/Numeric/I64GeS.php | 15 + .../Instructions/Instrs/Numeric/I64GeU.php | 15 + .../Instructions/Instrs/Numeric/I64GtS.php | 15 + .../Instructions/Instrs/Numeric/I64GtU.php | 15 + .../Instructions/Instrs/Numeric/I64LeS.php | 15 + .../Instructions/Instrs/Numeric/I64LeU.php | 15 + .../Instructions/Instrs/Numeric/I64LtS.php | 15 + .../Instructions/Instrs/Numeric/I64LtU.php | 15 + .../Instructions/Instrs/Numeric/I64Mul.php | 15 + .../Instructions/Instrs/Numeric/I64Ne.php | 15 + .../Instructions/Instrs/Numeric/I64Or.php | 15 + .../Instructions/Instrs/Numeric/I64Popcnt.php | 15 + .../Instrs/Numeric/I64ReinterpretF32.php | 15 + .../Instrs/Numeric/I64ReinterpretF64.php | 15 + .../Instructions/Instrs/Numeric/I64RemS.php | 15 + .../Instructions/Instrs/Numeric/I64RemU.php | 15 + .../Instructions/Instrs/Numeric/I64RotL.php | 15 + .../Instructions/Instrs/Numeric/I64RotR.php | 15 + .../Instructions/Instrs/Numeric/I64Shl.php | 15 + .../Instructions/Instrs/Numeric/I64ShrS.php | 15 + .../Instructions/Instrs/Numeric/I64ShrU.php | 15 + .../Instructions/Instrs/Numeric/I64Sub.php | 15 + .../Instructions/Instrs/Numeric/I64TruncF32S.php | 15 + .../Instructions/Instrs/Numeric/I64TruncF32U.php | 15 + .../Instructions/Instrs/Numeric/I64TruncF64S.php | 15 + .../Instructions/Instrs/Numeric/I64TruncF64U.php | 15 + .../Instrs/Numeric/I64TruncSatF32S.php | 15 + .../Instrs/Numeric/I64TruncSatF32U.php | 15 + .../Instrs/Numeric/I64TruncSatF64S.php | 15 + .../Instrs/Numeric/I64TruncSatF64U.php | 15 + .../Instructions/Instrs/Numeric/I64Xor.php | 15 + .../Instructions/Instrs/Parametric/Drop.php | 15 + .../Instructions/Instrs/Parametric/Select.php | 24 + .../Instructions/Instrs/Reference/RefFunc.php | 20 + .../Instructions/Instrs/Reference/RefIsNull.php | 15 + .../Instructions/Instrs/Reference/RefNull.php | 21 + .../Instructions/Instrs/Table/ElemDrop.php | 20 + .../Instructions/Instrs/Table/TableCopy.php | 21 + .../Instructions/Instrs/Table/TableFill.php | 20 + .../Instructions/Instrs/Table/TableGet.php | 20 + .../Instructions/Instrs/Table/TableGrow.php | 20 + .../Instructions/Instrs/Table/TableInit.php | 21 + .../Instructions/Instrs/Table/TableSet.php | 20 + .../Instructions/Instrs/Table/TableSize.php | 20 + .../Instructions/Instrs/Variable/GlobalGet.php | 20 + .../Instructions/Instrs/Variable/GlobalSet.php | 20 + .../Instructions/Instrs/Variable/LocalGet.php | 20 + .../Instructions/Instrs/Variable/LocalSet.php | 20 + .../Instructions/Instrs/Variable/LocalTee.php | 20 + src/WebAssembly/Structure/Modules/Data.php | 17 + src/WebAssembly/Structure/Modules/DataMode.php | 25 + .../Structure/Modules/DataModes/Active.php | 20 + .../Structure/Modules/DataModes/Passive.php | 14 + src/WebAssembly/Structure/Modules/Elem.php | 21 + src/WebAssembly/Structure/Modules/ElemMode.php | 30 + .../Structure/Modules/ElemModes/Active.php | 20 + .../Structure/Modules/ElemModes/Declarative.php | 14 + .../Structure/Modules/ElemModes/Passive.php | 14 + src/WebAssembly/Structure/Modules/Export.php | 17 + src/WebAssembly/Structure/Modules/ExportDesc.php | 28 + .../Structure/Modules/ExportDescs/Func.php | 15 + .../Structure/Modules/ExportDescs/Global_.php | 15 + .../Structure/Modules/ExportDescs/Mem.php | 15 + .../Structure/Modules/ExportDescs/Table.php | 15 + src/WebAssembly/Structure/Modules/Func.php | 21 + src/WebAssembly/Structure/Modules/Global_.php | 20 + src/WebAssembly/Structure/Modules/Import.php | 19 + src/WebAssembly/Structure/Modules/ImportDesc.php | 32 + .../Structure/Modules/ImportDescs/Func.php | 15 + .../Structure/Modules/ImportDescs/Global_.php | 16 + .../Structure/Modules/ImportDescs/Mem.php | 16 + .../Structure/Modules/ImportDescs/Table.php | 16 + src/WebAssembly/Structure/Modules/Local.php | 15 + src/WebAssembly/Structure/Modules/Mem.php | 15 + src/WebAssembly/Structure/Modules/Module.php | 35 + src/WebAssembly/Structure/Modules/Start.php | 13 + src/WebAssembly/Structure/Modules/Table.php | 15 + src/WebAssembly/Structure/Types/ExternType.php | 28 + .../Structure/Types/ExternTypes/Func.php | 16 + .../Structure/Types/ExternTypes/Global_.php | 16 + .../Structure/Types/ExternTypes/Mem.php | 16 + .../Structure/Types/ExternTypes/Table.php | 16 + src/WebAssembly/Structure/Types/FuncType.php | 20 + src/WebAssembly/Structure/Types/GlobalType.php | 14 + src/WebAssembly/Structure/Types/Limits.php | 24 + src/WebAssembly/Structure/Types/MemType.php | 13 + src/WebAssembly/Structure/Types/Mut.php | 11 + src/WebAssembly/Structure/Types/NumType.php | 13 + src/WebAssembly/Structure/Types/RefType.php | 11 + src/WebAssembly/Structure/Types/ResultType.php | 31 + src/WebAssembly/Structure/Types/TableType.php | 14 + src/WebAssembly/Structure/Types/ValType.php | 25 + .../Structure/Types/ValTypes/NumType.php | 21 + .../Structure/Types/ValTypes/RefType.php | 21 + .../Structure/Types/ValTypes/VecType.php | 21 + src/WebAssembly/Structure/Types/VecType.php | 10 + tests/src/SpecTestsuites/SpecTestsuiteBase.php | 46 +- 599 files changed, 11324 insertions(+), 11324 deletions(-) delete mode 100644 src/BinaryFormat/Decoder.php delete mode 100644 src/BinaryFormat/Internal/Code.php delete mode 100644 src/BinaryFormat/Internal/Locals.php delete mode 100644 src/BinaryFormat/Internal/SectionId.php delete mode 100644 src/BinaryFormat/InvalidBinaryFormatException.php delete mode 100644 src/Debug/Debug.php delete mode 100644 src/Execution/Allocator.php delete mode 100644 src/Execution/DataInst.php delete mode 100644 src/Execution/ElemInst.php delete mode 100644 src/Execution/ExportInst.php delete mode 100644 src/Execution/Extern.php delete mode 100644 src/Execution/ExternVal.php delete mode 100644 src/Execution/ExternVals/Func.php delete mode 100644 src/Execution/ExternVals/Global_.php delete mode 100644 src/Execution/ExternVals/Mem.php delete mode 100644 src/Execution/ExternVals/Table.php delete mode 100644 src/Execution/Externs/Func.php delete mode 100644 src/Execution/Externs/Global_.php delete mode 100644 src/Execution/Externs/Mem.php delete mode 100644 src/Execution/Externs/Table.php delete mode 100644 src/Execution/Frame.php delete mode 100644 src/Execution/FuncInst.php delete mode 100644 src/Execution/FuncInsts/Host.php delete mode 100644 src/Execution/FuncInsts/Wasm.php delete mode 100644 src/Execution/GlobalInst.php delete mode 100644 src/Execution/Label.php delete mode 100644 src/Execution/MemInst.php delete mode 100644 src/Execution/ModuleInst.php delete mode 100644 src/Execution/NumericOps.php delete mode 100644 src/Execution/Ref.php delete mode 100644 src/Execution/Refs/RefExtern.php delete mode 100644 src/Execution/Refs/RefFunc.php delete mode 100644 src/Execution/Refs/RefNull.php delete mode 100644 src/Execution/Result.php delete mode 100644 src/Execution/Results/Trap.php delete mode 100644 src/Execution/Results/Values.php delete mode 100644 src/Execution/Runtime.php delete mode 100644 src/Execution/Stack.php delete mode 100644 src/Execution/StackOverflowException.php delete mode 100644 src/Execution/Store.php delete mode 100644 src/Execution/TableInst.php delete mode 100644 src/Execution/TrapException.php delete mode 100644 src/Execution/TrapKind.php delete mode 100644 src/Structure/Instructions/Instr.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Block.php delete mode 100644 src/Structure/Instructions/Instrs/Control/BlockType.php delete mode 100644 src/Structure/Instructions/Instrs/Control/BlockTypes/TypeIdx.php delete mode 100644 src/Structure/Instructions/Instrs/Control/BlockTypes/ValType.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Br.php delete mode 100644 src/Structure/Instructions/Instrs/Control/BrIf.php delete mode 100644 src/Structure/Instructions/Instrs/Control/BrTable.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Call.php delete mode 100644 src/Structure/Instructions/Instrs/Control/CallIndirect.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Else_.php delete mode 100644 src/Structure/Instructions/Instrs/Control/End.php delete mode 100644 src/Structure/Instructions/Instrs/Control/If_.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Loop.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Nop.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Return_.php delete mode 100644 src/Structure/Instructions/Instrs/Control/Unreachable.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/DataDrop.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/F32Load.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/F32Store.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/F64Load.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/F64Store.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Load.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Load16S.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Load16U.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Load8S.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Load8U.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Store.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Store16.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I32Store8.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Load.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Load16S.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Load16U.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Load32S.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Load32U.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Load8S.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Load8U.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Store.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Store16.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Store32.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/I64Store8.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/MemoryCopy.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/MemoryFill.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/MemoryGrow.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/MemoryInit.php delete mode 100644 src/Structure/Instructions/Instrs/Memory/MemorySize.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Abs.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Add.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Ceil.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Const.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32ConvertI32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32ConvertI32U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32ConvertI64S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32ConvertI64U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32CopySign.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32DemoteF64.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Div.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Eq.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Floor.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Ge.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Gt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Le.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Lt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Max.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Min.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Mul.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Ne.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Nearest.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Neg.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32ReinterpretI32.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32ReinterpretI64.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Sqrt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Sub.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F32Trunc.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Abs.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Add.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Ceil.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Const.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64ConvertI32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64ConvertI32U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64ConvertI64S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64ConvertI64U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64CopySign.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Div.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Eq.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Floor.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Ge.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Gt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Le.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Lt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Max.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Min.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Mul.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Ne.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Nearest.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Neg.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64PromoteF32.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64ReinterpretI32.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64ReinterpretI64.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Sqrt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Sub.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/F64Trunc.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Add.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32And.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Clz.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Const.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Ctz.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32DivS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32DivU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Eq.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Eqz.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Extend16S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Extend8S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32GeS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32GeU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32GtS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32GtU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32LeS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32LeU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32LtS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32LtU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Mul.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Ne.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Or.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Popcnt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32ReinterpretF32.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32ReinterpretF64.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32RemS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32RemU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32RotL.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32RotR.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Shl.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32ShrS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32ShrU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Sub.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncF32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncF32U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncF64S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncF64U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncSatF32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncSatF32U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncSatF64S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32TruncSatF64U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32WrapI64.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I32Xor.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Add.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64And.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Clz.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Const.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Ctz.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64DivS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64DivU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Eq.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Eqz.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Extend16S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Extend32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Extend8S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64ExtendI32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64ExtendI32U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64GeS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64GeU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64GtS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64GtU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64LeS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64LeU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64LtS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64LtU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Mul.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Ne.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Or.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Popcnt.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64ReinterpretF32.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64ReinterpretF64.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64RemS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64RemU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64RotL.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64RotR.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Shl.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64ShrS.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64ShrU.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Sub.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncF32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncF32U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncF64S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncF64U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncSatF32S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncSatF32U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncSatF64S.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64TruncSatF64U.php delete mode 100644 src/Structure/Instructions/Instrs/Numeric/I64Xor.php delete mode 100644 src/Structure/Instructions/Instrs/Parametric/Drop.php delete mode 100644 src/Structure/Instructions/Instrs/Parametric/Select.php delete mode 100644 src/Structure/Instructions/Instrs/Reference/RefFunc.php delete mode 100644 src/Structure/Instructions/Instrs/Reference/RefIsNull.php delete mode 100644 src/Structure/Instructions/Instrs/Reference/RefNull.php delete mode 100644 src/Structure/Instructions/Instrs/Table/ElemDrop.php delete mode 100644 src/Structure/Instructions/Instrs/Table/TableCopy.php delete mode 100644 src/Structure/Instructions/Instrs/Table/TableFill.php delete mode 100644 src/Structure/Instructions/Instrs/Table/TableGet.php delete mode 100644 src/Structure/Instructions/Instrs/Table/TableGrow.php delete mode 100644 src/Structure/Instructions/Instrs/Table/TableInit.php delete mode 100644 src/Structure/Instructions/Instrs/Table/TableSet.php delete mode 100644 src/Structure/Instructions/Instrs/Table/TableSize.php delete mode 100644 src/Structure/Instructions/Instrs/Variable/GlobalGet.php delete mode 100644 src/Structure/Instructions/Instrs/Variable/GlobalSet.php delete mode 100644 src/Structure/Instructions/Instrs/Variable/LocalGet.php delete mode 100644 src/Structure/Instructions/Instrs/Variable/LocalSet.php delete mode 100644 src/Structure/Instructions/Instrs/Variable/LocalTee.php delete mode 100644 src/Structure/Modules/Data.php delete mode 100644 src/Structure/Modules/DataMode.php delete mode 100644 src/Structure/Modules/DataModes/Active.php delete mode 100644 src/Structure/Modules/DataModes/Passive.php delete mode 100644 src/Structure/Modules/Elem.php delete mode 100644 src/Structure/Modules/ElemMode.php delete mode 100644 src/Structure/Modules/ElemModes/Active.php delete mode 100644 src/Structure/Modules/ElemModes/Declarative.php delete mode 100644 src/Structure/Modules/ElemModes/Passive.php delete mode 100644 src/Structure/Modules/Export.php delete mode 100644 src/Structure/Modules/ExportDesc.php delete mode 100644 src/Structure/Modules/ExportDescs/Func.php delete mode 100644 src/Structure/Modules/ExportDescs/Global_.php delete mode 100644 src/Structure/Modules/ExportDescs/Mem.php delete mode 100644 src/Structure/Modules/ExportDescs/Table.php delete mode 100644 src/Structure/Modules/Func.php delete mode 100644 src/Structure/Modules/Global_.php delete mode 100644 src/Structure/Modules/Import.php delete mode 100644 src/Structure/Modules/ImportDesc.php delete mode 100644 src/Structure/Modules/ImportDescs/Func.php delete mode 100644 src/Structure/Modules/ImportDescs/Global_.php delete mode 100644 src/Structure/Modules/ImportDescs/Mem.php delete mode 100644 src/Structure/Modules/ImportDescs/Table.php delete mode 100644 src/Structure/Modules/Local.php delete mode 100644 src/Structure/Modules/Mem.php delete mode 100644 src/Structure/Modules/Module.php delete mode 100644 src/Structure/Modules/Start.php delete mode 100644 src/Structure/Modules/Table.php delete mode 100644 src/Structure/Types/ExternType.php delete mode 100644 src/Structure/Types/ExternTypes/Func.php delete mode 100644 src/Structure/Types/ExternTypes/Global_.php delete mode 100644 src/Structure/Types/ExternTypes/Mem.php delete mode 100644 src/Structure/Types/ExternTypes/Table.php delete mode 100644 src/Structure/Types/FuncType.php delete mode 100644 src/Structure/Types/GlobalType.php delete mode 100644 src/Structure/Types/Limits.php delete mode 100644 src/Structure/Types/MemType.php delete mode 100644 src/Structure/Types/Mut.php delete mode 100644 src/Structure/Types/NumType.php delete mode 100644 src/Structure/Types/RefType.php delete mode 100644 src/Structure/Types/ResultType.php delete mode 100644 src/Structure/Types/TableType.php delete mode 100644 src/Structure/Types/ValType.php delete mode 100644 src/Structure/Types/ValTypes/NumType.php delete mode 100644 src/Structure/Types/ValTypes/RefType.php delete mode 100644 src/Structure/Types/ValTypes/VecType.php delete mode 100644 src/Structure/Types/VecType.php create mode 100644 src/WebAssembly/BinaryFormat/Decoder.php create mode 100644 src/WebAssembly/BinaryFormat/Internal/Code.php create mode 100644 src/WebAssembly/BinaryFormat/Internal/Locals.php create mode 100644 src/WebAssembly/BinaryFormat/Internal/SectionId.php create mode 100644 src/WebAssembly/BinaryFormat/InvalidBinaryFormatException.php create mode 100644 src/WebAssembly/Debug/Debug.php create mode 100644 src/WebAssembly/Execution/Allocator.php create mode 100644 src/WebAssembly/Execution/DataInst.php create mode 100644 src/WebAssembly/Execution/ElemInst.php create mode 100644 src/WebAssembly/Execution/ExportInst.php create mode 100644 src/WebAssembly/Execution/Extern.php create mode 100644 src/WebAssembly/Execution/ExternVal.php create mode 100644 src/WebAssembly/Execution/ExternVals/Func.php create mode 100644 src/WebAssembly/Execution/ExternVals/Global_.php create mode 100644 src/WebAssembly/Execution/ExternVals/Mem.php create mode 100644 src/WebAssembly/Execution/ExternVals/Table.php create mode 100644 src/WebAssembly/Execution/Externs/Func.php create mode 100644 src/WebAssembly/Execution/Externs/Global_.php create mode 100644 src/WebAssembly/Execution/Externs/Mem.php create mode 100644 src/WebAssembly/Execution/Externs/Table.php create mode 100644 src/WebAssembly/Execution/Frame.php create mode 100644 src/WebAssembly/Execution/FuncInst.php create mode 100644 src/WebAssembly/Execution/FuncInsts/Host.php create mode 100644 src/WebAssembly/Execution/FuncInsts/Wasm.php create mode 100644 src/WebAssembly/Execution/GlobalInst.php create mode 100644 src/WebAssembly/Execution/Label.php create mode 100644 src/WebAssembly/Execution/MemInst.php create mode 100644 src/WebAssembly/Execution/ModuleInst.php create mode 100644 src/WebAssembly/Execution/NumericOps.php create mode 100644 src/WebAssembly/Execution/Ref.php create mode 100644 src/WebAssembly/Execution/Refs/RefExtern.php create mode 100644 src/WebAssembly/Execution/Refs/RefFunc.php create mode 100644 src/WebAssembly/Execution/Refs/RefNull.php create mode 100644 src/WebAssembly/Execution/Result.php create mode 100644 src/WebAssembly/Execution/Results/Trap.php create mode 100644 src/WebAssembly/Execution/Results/Values.php create mode 100644 src/WebAssembly/Execution/Runtime.php create mode 100644 src/WebAssembly/Execution/Stack.php create mode 100644 src/WebAssembly/Execution/StackOverflowException.php create mode 100644 src/WebAssembly/Execution/Store.php create mode 100644 src/WebAssembly/Execution/TableInst.php create mode 100644 src/WebAssembly/Execution/TrapException.php create mode 100644 src/WebAssembly/Execution/TrapKind.php create mode 100644 src/WebAssembly/Structure/Instructions/Instr.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Block.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/BlockType.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/BlockTypes/TypeIdx.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/BlockTypes/ValType.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Br.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/BrIf.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/BrTable.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Call.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/CallIndirect.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Else_.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/End.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/If_.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Loop.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Nop.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Return_.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Control/Unreachable.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/DataDrop.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/F32Load.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/F32Store.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/F64Load.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/F64Store.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Load.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Load16S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Load16U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Load8S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Load8U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Store.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Store16.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I32Store8.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Load.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Load16S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Load16U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Load32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Load32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Load8S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Load8U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Store.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Store16.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Store32.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/I64Store8.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/MemoryCopy.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/MemoryFill.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/MemoryGrow.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/MemoryInit.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Memory/MemorySize.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Abs.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Add.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Ceil.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Const.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32ConvertI32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32ConvertI32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32ConvertI64S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32ConvertI64U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32CopySign.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32DemoteF64.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Div.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Eq.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Floor.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Ge.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Gt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Le.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Lt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Max.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Min.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Mul.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Ne.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Nearest.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Neg.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32ReinterpretI32.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32ReinterpretI64.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Sqrt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Sub.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F32Trunc.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Abs.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Add.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Ceil.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Const.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64ConvertI32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64ConvertI32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64ConvertI64S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64ConvertI64U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64CopySign.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Div.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Eq.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Floor.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Ge.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Gt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Le.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Lt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Max.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Min.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Mul.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Ne.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Nearest.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Neg.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64PromoteF32.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64ReinterpretI32.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64ReinterpretI64.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Sqrt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Sub.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/F64Trunc.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Add.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32And.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Clz.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Const.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Ctz.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32DivS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32DivU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Eq.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Eqz.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Extend16S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Extend8S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32GeS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32GeU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32GtS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32GtU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32LeS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32LeU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32LtS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32LtU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Mul.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Ne.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Or.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Popcnt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32ReinterpretF32.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32ReinterpretF64.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32RemS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32RemU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32RotL.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32RotR.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Shl.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32ShrS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32ShrU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Sub.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncF32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncF32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncF64S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncF64U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncSatF32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncSatF32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncSatF64S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32TruncSatF64U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32WrapI64.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I32Xor.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Add.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64And.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Clz.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Const.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Ctz.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64DivS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64DivU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Eq.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Eqz.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Extend16S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Extend32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Extend8S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64ExtendI32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64ExtendI32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64GeS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64GeU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64GtS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64GtU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64LeS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64LeU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64LtS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64LtU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Mul.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Ne.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Or.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Popcnt.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64ReinterpretF32.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64ReinterpretF64.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64RemS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64RemU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64RotL.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64RotR.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Shl.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64ShrS.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64ShrU.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Sub.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncF32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncF32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncF64S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncF64U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncSatF32S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncSatF32U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncSatF64S.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64TruncSatF64U.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Numeric/I64Xor.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Parametric/Drop.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Parametric/Select.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Reference/RefFunc.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Reference/RefIsNull.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Reference/RefNull.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/ElemDrop.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/TableCopy.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/TableFill.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/TableGet.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/TableGrow.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/TableInit.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/TableSet.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Table/TableSize.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Variable/GlobalGet.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Variable/GlobalSet.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Variable/LocalGet.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Variable/LocalSet.php create mode 100644 src/WebAssembly/Structure/Instructions/Instrs/Variable/LocalTee.php create mode 100644 src/WebAssembly/Structure/Modules/Data.php create mode 100644 src/WebAssembly/Structure/Modules/DataMode.php create mode 100644 src/WebAssembly/Structure/Modules/DataModes/Active.php create mode 100644 src/WebAssembly/Structure/Modules/DataModes/Passive.php create mode 100644 src/WebAssembly/Structure/Modules/Elem.php create mode 100644 src/WebAssembly/Structure/Modules/ElemMode.php create mode 100644 src/WebAssembly/Structure/Modules/ElemModes/Active.php create mode 100644 src/WebAssembly/Structure/Modules/ElemModes/Declarative.php create mode 100644 src/WebAssembly/Structure/Modules/ElemModes/Passive.php create mode 100644 src/WebAssembly/Structure/Modules/Export.php create mode 100644 src/WebAssembly/Structure/Modules/ExportDesc.php create mode 100644 src/WebAssembly/Structure/Modules/ExportDescs/Func.php create mode 100644 src/WebAssembly/Structure/Modules/ExportDescs/Global_.php create mode 100644 src/WebAssembly/Structure/Modules/ExportDescs/Mem.php create mode 100644 src/WebAssembly/Structure/Modules/ExportDescs/Table.php create mode 100644 src/WebAssembly/Structure/Modules/Func.php create mode 100644 src/WebAssembly/Structure/Modules/Global_.php create mode 100644 src/WebAssembly/Structure/Modules/Import.php create mode 100644 src/WebAssembly/Structure/Modules/ImportDesc.php create mode 100644 src/WebAssembly/Structure/Modules/ImportDescs/Func.php create mode 100644 src/WebAssembly/Structure/Modules/ImportDescs/Global_.php create mode 100644 src/WebAssembly/Structure/Modules/ImportDescs/Mem.php create mode 100644 src/WebAssembly/Structure/Modules/ImportDescs/Table.php create mode 100644 src/WebAssembly/Structure/Modules/Local.php create mode 100644 src/WebAssembly/Structure/Modules/Mem.php create mode 100644 src/WebAssembly/Structure/Modules/Module.php create mode 100644 src/WebAssembly/Structure/Modules/Start.php create mode 100644 src/WebAssembly/Structure/Modules/Table.php create mode 100644 src/WebAssembly/Structure/Types/ExternType.php create mode 100644 src/WebAssembly/Structure/Types/ExternTypes/Func.php create mode 100644 src/WebAssembly/Structure/Types/ExternTypes/Global_.php create mode 100644 src/WebAssembly/Structure/Types/ExternTypes/Mem.php create mode 100644 src/WebAssembly/Structure/Types/ExternTypes/Table.php create mode 100644 src/WebAssembly/Structure/Types/FuncType.php create mode 100644 src/WebAssembly/Structure/Types/GlobalType.php create mode 100644 src/WebAssembly/Structure/Types/Limits.php create mode 100644 src/WebAssembly/Structure/Types/MemType.php create mode 100644 src/WebAssembly/Structure/Types/Mut.php create mode 100644 src/WebAssembly/Structure/Types/NumType.php create mode 100644 src/WebAssembly/Structure/Types/RefType.php create mode 100644 src/WebAssembly/Structure/Types/ResultType.php create mode 100644 src/WebAssembly/Structure/Types/TableType.php create mode 100644 src/WebAssembly/Structure/Types/ValType.php create mode 100644 src/WebAssembly/Structure/Types/ValTypes/NumType.php create mode 100644 src/WebAssembly/Structure/Types/ValTypes/RefType.php create mode 100644 src/WebAssembly/Structure/Types/ValTypes/VecType.php create mode 100644 src/WebAssembly/Structure/Types/VecType.php diff --git a/examples/php-on-wasm/php-wasm.php b/examples/php-on-wasm/php-wasm.php index de9dd91..f7aca1f 100644 --- a/examples/php-on-wasm/php-wasm.php +++ b/examples/php-on-wasm/php-wasm.php @@ -4,18 +4,18 @@ declare(strict_types=1); require_once __DIR__ . '/../../vendor/autoload.php'; -use Nsfisis\Waddiwasi\BinaryFormat\Decoder; -use Nsfisis\Waddiwasi\Execution\Extern; -use Nsfisis\Waddiwasi\Execution\Externs; -use Nsfisis\Waddiwasi\Execution\FuncInst; -use Nsfisis\Waddiwasi\Execution\Refs; -use Nsfisis\Waddiwasi\Execution\Runtime; -use Nsfisis\Waddiwasi\Execution\Store; use Nsfisis\Waddiwasi\Stream\FileStream; -use Nsfisis\Waddiwasi\Structure\Types\FuncType; -use Nsfisis\Waddiwasi\Structure\Types\NumType; -use Nsfisis\Waddiwasi\Structure\Types\ResultType; -use Nsfisis\Waddiwasi\Structure\Types\ValType; +use Nsfisis\Waddiwasi\WebAssembly\BinaryFormat\Decoder; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Extern; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Externs; +use Nsfisis\Waddiwasi\WebAssembly\Execution\FuncInst; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Refs; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Runtime; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Store; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\FuncType; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\NumType; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\ResultType; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\ValType; const PHP_HELLO_WORLD = <<<'EOS' echo "Hello, World!\n"; diff --git a/examples/rubyvm-on-php-on-wasm/php-wasm.php b/examples/rubyvm-on-php-on-wasm/php-wasm.php index 59c911e..789e6ac 100644 --- a/examples/rubyvm-on-php-on-wasm/php-wasm.php +++ b/examples/rubyvm-on-php-on-wasm/php-wasm.php @@ -4,18 +4,18 @@ declare(strict_types=1); require_once __DIR__ . '/vendor/autoload.php'; -use Nsfisis\Waddiwasi\BinaryFormat\Decoder; -use Nsfisis\Waddiwasi\Execution\Extern; -use Nsfisis\Waddiwasi\Execution\Externs; -use Nsfisis\Waddiwasi\Execution\FuncInst; -use Nsfisis\Waddiwasi\Execution\Refs; -use Nsfisis\Waddiwasi\Execution\Runtime; -use Nsfisis\Waddiwasi\Execution\Store; use Nsfisis\Waddiwasi\Stream\FileStream; -use Nsfisis\Waddiwasi\Structure\Types\FuncType; -use Nsfisis\Waddiwasi\Structure\Types\NumType; -use Nsfisis\Waddiwasi\Structure\Types\ResultType; -use Nsfisis\Waddiwasi\Structure\Types\ValType; +use Nsfisis\Waddiwasi\WebAssembly\BinaryFormat\Decoder; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Extern; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Externs; +use Nsfisis\Waddiwasi\WebAssembly\Execution\FuncInst; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Refs; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Runtime; +use Nsfisis\Waddiwasi\WebAssembly\Execution\Store; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\FuncType; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\NumType; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\ResultType; +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\ValType; const PHP_HELLO_WORLD = <<<'EOS' require_once '%DIR%/HelloWorld.php'; diff --git a/src/BinaryFormat/Decoder.php b/src/BinaryFormat/Decoder.php deleted file mode 100644 index 14079e1..0000000 --- a/src/BinaryFormat/Decoder.php +++ /dev/null @@ -1,1173 +0,0 @@ -checkMagic(); - $this->checkVersion(); - - $types = $this->decodeSection(SectionId::Type, $this->decodeTypeSecRest(...)) ?? []; - $imports = $this->decodeSection(SectionId::Import, $this->decodeImportSecRest(...)) ?? []; - $typeIndices = $this->decodeSection(SectionId::Func, $this->decodeFuncSecRest(...)) ?? []; - $tables = $this->decodeSection(SectionId::Table, $this->decodeTableSecRest(...)) ?? []; - $mems = $this->decodeSection(SectionId::Mem, $this->decodeMemSecRest(...)) ?? []; - $globals = $this->decodeSection(SectionId::Global, $this->decodeGlobalSecRest(...)) ?? []; - $exports = $this->decodeSection(SectionId::Export, $this->decodeExportSecRest(...)) ?? []; - $start = $this->decodeSection(SectionId::Start, $this->decodeStartSecRest(...)); - $elems = $this->decodeSection(SectionId::Elem, $this->decodeElemSecRest(...)) ?? []; - $dataCount = $this->decodeSection(SectionId::DataCount, $this->decodeDataCountSecRest(...)); - $codes = $this->decodeSection(SectionId::Code, $this->decodeCodeSecRest(...)) ?? []; - $datas = $this->decodeSection(SectionId::Data, $this->decodeDataSecRest(...)) ?? []; - - if (!$this->stream->eof()) { - throw new InvalidBinaryFormatException("eof"); - } - if ($dataCount === null) { - foreach ($codes as $code) { - if ($this->countDataIndicesUsedInCode($code) !== 0) { - throw new InvalidBinaryFormatException("datacount section is required"); - } - } - } else { - if (count($datas) !== $dataCount) { - throw new InvalidBinaryFormatException("datasec"); - } - } - if (count($typeIndices) !== count($codes)) { - throw new InvalidBinaryFormatException("number of funcs and codes does not match"); - } - - $funcs = []; - foreach ($typeIndices as $i => $type) { - $code = $codes[$i]; - $decompressedLocals = array_map( - fn ($l) => array_fill(0, $l->count, new Local($l->type)), - $code->compressedLocals, - ); - $funcs[] = new Func( - $type, - array_merge(...$decompressedLocals), - $code->body, - ); - } - - return new Module( - $types, - $funcs, - $tables, - $mems, - $globals, - $elems, - $datas, - $start, - $imports, - $exports, - ); - } - - private function checkMagic(): void - { - $bs = $this->stream->read(4); - $b1 = ord($bs[0]); - $b2 = ord($bs[1]); - $b3 = ord($bs[2]); - $b4 = ord($bs[3]); - if ([$b1, $b2, $b3, $b4] !== [0x00, 0x61, 0x73, 0x6D]) { - throw new InvalidBinaryFormatException("magic"); - } - } - - private function checkVersion(): void - { - $bs = $this->stream->read(4); - $b1 = ord($bs[0]); - $b2 = ord($bs[1]); - $b3 = ord($bs[2]); - $b4 = ord($bs[3]); - if ([$b1, $b2, $b3, $b4] !== [0x01, 0x00, 0x00, 0x00]) { - throw new InvalidBinaryFormatException(sprintf("version: [%x, %x, %x, %x]", $b1, $b2, $b3, $b4)); - } - } - - /** - * @template T - * @param callable(): T $decoder - * @return ?T - */ - private function decodeSection(SectionId $sectionId, callable $decoder): mixed - { - $this->skipCustomSections(); - if ($this->stream->eof()) { - return null; - } - - $idValue = $this->stream->peekByte(); - $id = SectionId::tryFrom($idValue); - if ($id === null) { - throw new InvalidBinaryFormatException("section id"); - } - if ($id !== $sectionId) { - return null; - } - $this->stream->seek(1); - - $size = $this->decodeU32(); - $prevPos = $this->stream->tell(); - $result = $decoder(); - if ($this->stream->tell() - $prevPos !== $size) { - throw new InvalidBinaryFormatException("type section size"); - } - return $result; - } - - private function skipCustomSections(): void - { - while (!$this->stream->eof()) { - $b = $this->stream->peekByte(); - if ($b !== SectionId::Custom->value) { - break; - } - $this->stream->seek(1); - $size = $this->decodeU32(); - $prevPos = $this->stream->tell(); - $this->decodeName(); - $encodedSizeOfName = $this->stream->tell() - $prevPos; - $offset = $size - $encodedSizeOfName; - if ($offset < 0) { - throw new InvalidBinaryFormatException("custom section size"); - } - if ($offset !== 0) { - $this->stream->seek($offset); - } - } - } - - /** - * @return list - */ - private function decodeTypeSecRest(): array - { - return $this->decodeVec($this->decodeFuncType(...)); - } - - /** - * @return list - */ - private function decodeImportSecRest(): array - { - return $this->decodeVec($this->decodeImport(...)); - } - - /** - * @return list - */ - private function decodeFuncSecRest(): array - { - return $this->decodeVec($this->decodeTypeIdx(...)); - } - - /** - * @return list - */ - private function decodeTableSecRest(): array - { - return $this->decodeVec($this->decodeTable(...)); - } - - /** - * @return list - */ - private function decodeMemSecRest(): array - { - return $this->decodeVec($this->decodeMem(...)); - } - - /** - * @return list - */ - private function decodeGlobalSecRest(): array - { - return $this->decodeVec($this->decodeGlobal(...)); - } - - /** - * @return list - */ - private function decodeExportSecRest(): array - { - return $this->decodeVec($this->decodeExport(...)); - } - - private function decodeStartSecRest(): Start - { - return new Start($this->decodeFuncIdx()); - } - - /** - * @return list - */ - private function decodeElemSecRest(): array - { - return $this->decodeVec($this->decodeElem(...)); - } - - /** - * @return list - */ - private function decodeCodeSecRest(): array - { - return $this->decodeVec($this->decodeCode(...)); - } - - /** - * @return list - */ - private function decodeDataSecRest(): array - { - return $this->decodeVec($this->decodeData(...)); - } - - /** - * @return U32 - */ - private function decodeDataCountSecRest(): int - { - return $this->decodeU32(); - } - - private function decodeResultType(): ResultType - { - return new ResultType($this->decodeVec($this->decodeValType(...))); - } - - private function decodeFuncType(): FuncType - { - $b = $this->decodeByte(); - if ($b !== 0x60) { - throw new InvalidBinaryFormatException("functype"); - } - $params = $this->decodeResultType(); - $results = $this->decodeResultType(); - return new FuncType( - $params, - $results, - ); - } - - private function decodeValType(): ValType - { - $b = $this->stream->peekByte(); - if ($b === 0x7F) { - $this->stream->seek(1); - return ValType::NumType(NumType::I32); - } elseif ($b === 0x7E) { - $this->stream->seek(1); - return ValType::NumType(NumType::I64); - } elseif ($b === 0x7D) { - $this->stream->seek(1); - return ValType::NumType(NumType::F32); - } elseif ($b === 0x7C) { - $this->stream->seek(1); - return ValType::NumType(NumType::F64); - } elseif ($b === 0x7B) { - $this->stream->seek(1); - return ValType::VecType(VecType::V128); - } else { - return ValType::RefType($this->decodeRefType()); - } - } - - private function decodeRefType(): RefType - { - return match ($this->decodeByte()) { - 0x70 => RefType::FuncRef, - 0x6F => RefType::ExternRef, - default => throw new InvalidBinaryFormatException("reftype"), - }; - } - - private function decodeLimits(): Limits - { - $b = $this->decodeByte(); - if ($b === 0x00) { - $min = $this->decodeU32(); - return new Limits($min, null); - } elseif ($b === 0x01) { - $min = $this->decodeU32(); - $max = $this->decodeU32(); - return new Limits($min, $max); - } else { - throw new InvalidBinaryFormatException("limits"); - } - } - - private function decodeMemType(): MemType - { - return new MemType($this->decodeLimits()); - } - - private function decodeTableType(): TableType - { - $refType = $this->decodeRefType(); - $limits = $this->decodeLimits(); - return new TableType( - $limits, - $refType, - ); - } - - private function decodeGlobalType(): GlobalType - { - $valType = $this->decodeValType(); - $mut = $this->decodeMut(); - return new GlobalType( - $mut, - $valType, - ); - } - - private function decodeMut(): Mut - { - return match ($this->decodeByte()) { - 0x00 => Mut::Const, - 0x01 => Mut::Var, - default => throw new InvalidBinaryFormatException("mutability"), - }; - } - - private function decodeImport(): Import - { - $module = $this->decodeName(); - $name = $this->decodeName(); - $desc = $this->decodeImportDesc(); - return new Import( - $module, - $name, - $desc, - ); - } - - private function decodeTable(): Table - { - return new Table($this->decodeTableType()); - } - - private function decodeMem(): Mem - { - return new Mem($this->decodeMemType()); - } - - private function decodeGlobal(): Global_ - { - $type = $this->decodeGlobalType(); - $init = $this->decodeExpr(); - return new Global_( - $type, - $init, - ); - } - - private function decodeExport(): Export - { - $name = $this->decodeName(); - $desc = $this->decodeExportDesc(); - return new Export( - $name, - $desc, - ); - } - - private function decodeElem(): Elem - { - $mode = $this->decodeU32(); - if ($mode === 0) { - $offset = $this->decodeExpr(); - $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); - return new Elem( - RefType::FuncRef, - array_map( - fn ($funcRef) => [Instr::RefFunc($funcRef)], - $initFuncRefs, - ), - ElemMode::Active(0, $offset), - ); - } elseif ($mode === 1) { - $refType = $this->decodeElemKind(); - $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); - return new Elem( - $refType, - array_map( - fn ($funcRef) => [Instr::RefFunc($funcRef)], - $initFuncRefs, - ), - ElemMode::Passive(), - ); - } elseif ($mode === 2) { - $table = $this->decodeTableIdx(); - $offset = $this->decodeExpr(); - $refType = $this->decodeElemKind(); - $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); - return new Elem( - $refType, - array_map( - fn ($funcRef) => [Instr::RefFunc($funcRef)], - $initFuncRefs, - ), - ElemMode::Active($table, $offset), - ); - } elseif ($mode === 3) { - $refType = $this->decodeElemKind(); - $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); - return new Elem( - $refType, - array_map( - fn ($funcRef) => [Instr::RefFunc($funcRef)], - $initFuncRefs, - ), - ElemMode::Declarative(), - ); - } elseif ($mode === 4) { - $offset = $this->decodeExpr(); - $init = $this->decodeVec($this->decodeExpr(...)); - return new Elem( - RefType::FuncRef, - $init, - ElemMode::Active(0, $offset), - ); - } elseif ($mode === 5) { - $refType = $this->decodeRefType(); - $init = $this->decodeVec($this->decodeExpr(...)); - return new Elem( - $refType, - $init, - ElemMode::Passive(), - ); - } elseif ($mode === 6) { - $table = $this->decodeTableIdx(); - $offset = $this->decodeExpr(); - $refType = $this->decodeRefType(); - $init = $this->decodeVec($this->decodeExpr(...)); - return new Elem( - $refType, - $init, - ElemMode::Active($table, $offset), - ); - } elseif ($mode === 7) { - $refType = $this->decodeRefType(); - $init = $this->decodeVec($this->decodeExpr(...)); - return new Elem( - $refType, - $init, - ElemMode::Declarative(), - ); - } else { - throw new InvalidBinaryFormatException("code"); - } - } - - private function decodeCode(): Code - { - $size = $this->decodeU32(); - $prevPos = $this->stream->tell(); - $compressedLocals = $this->decodeVec($this->decodeLocals(...)); - $body = $this->decodeExpr(); - if ($this->stream->tell() - $prevPos !== $size) { - throw new InvalidBinaryFormatException("code size"); - } - return new Code( - $compressedLocals, - $body, - ); - } - - private function decodeData(): Data - { - $mode = $this->decodeU32(); - if ($mode === 0) { - $offset = $this->decodeExpr(); - $init = $this->decodeVec($this->decodeByte(...)); - return new Data( - $init, - DataMode::Active( - 0, - $offset, - ), - ); - } elseif ($mode === 1) { - $init = $this->decodeVec($this->decodeByte(...)); - return new Data( - $init, - DataMode::Passive(), - ); - } elseif ($mode === 2) { - $memory = $this->decodeMemIdx(); - $offset = $this->decodeExpr(); - $init = $this->decodeVec($this->decodeByte(...)); - return new Data( - $init, - DataMode::Active( - $memory, - $offset, - ), - ); - } else { - throw new InvalidBinaryFormatException("data"); - } - } - - private function decodeImportDesc(): ImportDesc - { - return match ($this->decodeByte()) { - 0x00 => ImportDesc::Func($this->decodeTypeIdx()), - 0x01 => ImportDesc::Table($this->decodeTableType()), - 0x02 => ImportDesc::Mem($this->decodeMemType()), - 0x03 => ImportDesc::Global($this->decodeGlobalType()), - default => throw new InvalidBinaryFormatException("importdesc"), - }; - } - - private function decodeExportDesc(): ExportDesc - { - return match ($this->decodeByte()) { - 0x00 => ExportDesc::Func($this->decodeFuncIdx()), - 0x01 => ExportDesc::Table($this->decodeTableIdx()), - 0x02 => ExportDesc::Mem($this->decodeMemIdx()), - 0x03 => ExportDesc::Global($this->decodeGlobalIdx()), - default => throw new InvalidBinaryFormatException("exportdesc"), - }; - } - - private function decodeElemKind(): RefType - { - $b = $this->decodeByte(); - if ($b === 0x00) { - return RefType::FuncRef; - } else { - throw new InvalidBinaryFormatException("elemkind"); - } - } - - private function decodeLocals(): Locals - { - $count = $this->decodeU32(); - // @todo Provide a way to configure the limit. - if (1024 < $count) { - throw new InvalidBinaryFormatException("too many local variables"); - } - $type = $this->decodeValType(); - return new Locals( - $count, - $type, - ); - } - - private function decodeTypeIdx(): int - { - return $this->decodeU32(); - } - - private function decodeMemIdx(): int - { - return $this->decodeU32(); - } - - private function decodeFuncIdx(): int - { - return $this->decodeU32(); - } - - private function decodeTableIdx(): int - { - return $this->decodeU32(); - } - - private function decodeGlobalIdx(): int - { - return $this->decodeU32(); - } - - private function decodeLocalIdx(): int - { - return $this->decodeU32(); - } - - private function decodeLabelIdx(): int - { - return $this->decodeU32(); - } - - private function decodeDataIdx(): int - { - return $this->decodeU32(); - } - - private function decodeElemIdx(): int - { - return $this->decodeU32(); - } - - /** - * @return list - */ - private function decodeExpr(): array - { - return $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; - } - - private function decodeInstr(): Instr - { - switch ($op = $this->decodeByte()) { - case 0x00: return Instr::Unreachable(); - case 0x01: return Instr::Nop(); - case 0x02: - $blockType = $this->decodeBlockType(); - $body = $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; - return Instr::Block($blockType, $body); - case 0x03: - $blockType = $this->decodeBlockType(); - $body = $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; - return Instr::Loop($blockType, $body); - case 0x04: - $blockType = $this->decodeBlockType(); - [$thenBody, $delimiter] = $this->decodeInstrsUntil([Instrs\Control\End::class, Instrs\Control\Else_::class]); - if ($delimiter instanceof Instrs\Control\End) { - return Instr::If_($blockType, $thenBody, []); - } - $elseBody = $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; - return Instr::If_($blockType, $thenBody, $elseBody); - case 0x05: return Instr::Else_(); - case 0x0B: return Instr::End(); - case 0x0C: return Instr::Br($this->decodeLabelIdx()); - case 0x0D: return Instr::BrIf($this->decodeLabelIdx()); - case 0x0E: - $labelTable = $this->decodeVec($this->decodeLabelIdx(...)); - $defaultLabel = $this->decodeLabelIdx(); - return Instr::BrTable($labelTable, $defaultLabel); - case 0x0F: return Instr::Return_(); - case 0x10: return Instr::Call($this->decodeFuncIdx()); - case 0x11: - $type = $this->decodeTypeIdx(); - $funcTable = $this->decodeTableIdx(); - return Instr::CallIndirect($funcTable, $type); - case 0x1A: return Instr::Drop(); - case 0x1B: return Instr::Select([]); - case 0x1C: return Instr::Select($this->decodeVec($this->decodeValType(...))); - case 0x20: return Instr::LocalGet($this->decodeLocalIdx()); - case 0x21: return Instr::LocalSet($this->decodeLocalIdx()); - case 0x22: return Instr::LocalTee($this->decodeLocalIdx()); - case 0x23: return Instr::GlobalGet($this->decodeGlobalIdx()); - case 0x24: return Instr::GlobalSet($this->decodeGlobalIdx()); - case 0x25: return Instr::TableGet($this->decodeTableIdx()); - case 0x26: return Instr::TableSet($this->decodeTableIdx()); - case 0x28: return Instr::I32Load(...$this->decodeMemArg()); - case 0x29: return Instr::I64Load(...$this->decodeMemArg()); - case 0x2A: return Instr::F32Load(...$this->decodeMemArg()); - case 0x2B: return Instr::F64Load(...$this->decodeMemArg()); - case 0x2C: return Instr::I32Load8S(...$this->decodeMemArg()); - case 0x2D: return Instr::I32Load8U(...$this->decodeMemArg()); - case 0x2E: return Instr::I32Load16S(...$this->decodeMemArg()); - case 0x2F: return Instr::I32Load16U(...$this->decodeMemArg()); - case 0x30: return Instr::I64Load8S(...$this->decodeMemArg()); - case 0x31: return Instr::I64Load8U(...$this->decodeMemArg()); - case 0x32: return Instr::I64Load16S(...$this->decodeMemArg()); - case 0x33: return Instr::I64Load16U(...$this->decodeMemArg()); - case 0x34: return Instr::I64Load32S(...$this->decodeMemArg()); - case 0x35: return Instr::I64Load32U(...$this->decodeMemArg()); - case 0x36: return Instr::I32Store(...$this->decodeMemArg()); - case 0x37: return Instr::I64Store(...$this->decodeMemArg()); - case 0x38: return Instr::F32Store(...$this->decodeMemArg()); - case 0x39: return Instr::F64Store(...$this->decodeMemArg()); - case 0x3A: return Instr::I32Store8(...$this->decodeMemArg()); - case 0x3B: return Instr::I32Store16(...$this->decodeMemArg()); - case 0x3C: return Instr::I64Store8(...$this->decodeMemArg()); - case 0x3D: return Instr::I64Store16(...$this->decodeMemArg()); - case 0x3E: return Instr::I64Store32(...$this->decodeMemArg()); - case 0x3F: - $c = $this->decodeByte(); - if ($c !== 0) { - throw new InvalidBinaryFormatException("Unexpected value while decoding an instruction `memory.size`, expected 0, but got $c"); - } - return Instr::MemorySize(); - case 0x40: - $c = $this->decodeByte(); - if ($c !== 0) { - throw new InvalidBinaryFormatException("Unexpected value while decoding an instruction `memory.grow`, expected 0, but got $c"); - } - return Instr::MemoryGrow(); - case 0x41: return Instr::I32Const($this->decodeS32()); - case 0x42: return Instr::I64Const($this->decodeS64()); - case 0x43: return Instr::F32Const($this->decodeF32()); - case 0x44: return Instr::F64Const($this->decodeF64()); - case 0x45: return Instr::I32Eqz(); - case 0x46: return Instr::I32Eq(); - case 0x47: return Instr::I32Ne(); - case 0x48: return Instr::I32LtS(); - case 0x49: return Instr::I32LtU(); - case 0x4A: return Instr::I32GtS(); - case 0x4B: return Instr::I32GtU(); - case 0x4C: return Instr::I32LeS(); - case 0x4D: return Instr::I32LeU(); - case 0x4E: return Instr::I32GeS(); - case 0x4F: return Instr::I32GeU(); - case 0x50: return Instr::I64Eqz(); - case 0x51: return Instr::I64Eq(); - case 0x52: return Instr::I64Ne(); - case 0x53: return Instr::I64LtS(); - case 0x54: return Instr::I64LtU(); - case 0x55: return Instr::I64GtS(); - case 0x56: return Instr::I64GtU(); - case 0x57: return Instr::I64LeS(); - case 0x58: return Instr::I64LeU(); - case 0x59: return Instr::I64GeS(); - case 0x5A: return Instr::I64GeU(); - case 0x5B: return Instr::F32Eq(); - case 0x5C: return Instr::F32Ne(); - case 0x5D: return Instr::F32Lt(); - case 0x5E: return Instr::F32Gt(); - case 0x5F: return Instr::F32Le(); - case 0x60: return Instr::F32Ge(); - case 0x61: return Instr::F64Eq(); - case 0x62: return Instr::F64Ne(); - case 0x63: return Instr::F64Lt(); - case 0x64: return Instr::F64Gt(); - case 0x65: return Instr::F64Le(); - case 0x66: return Instr::F64Ge(); - case 0x67: return Instr::I32Clz(); - case 0x68: return Instr::I32Ctz(); - case 0x69: return Instr::I32Popcnt(); - case 0x6A: return Instr::I32Add(); - case 0x6B: return Instr::I32Sub(); - case 0x6C: return Instr::I32Mul(); - case 0x6D: return Instr::I32DivS(); - case 0x6E: return Instr::I32DivU(); - case 0x6F: return Instr::I32RemS(); - case 0x70: return Instr::I32RemU(); - case 0x71: return Instr::I32And(); - case 0x72: return Instr::I32Or(); - case 0x73: return Instr::I32Xor(); - case 0x74: return Instr::I32Shl(); - case 0x75: return Instr::I32ShrS(); - case 0x76: return Instr::I32ShrU(); - case 0x77: return Instr::I32RotL(); - case 0x78: return Instr::I32RotR(); - case 0x79: return Instr::I64Clz(); - case 0x7A: return Instr::I64Ctz(); - case 0x7B: return Instr::I64Popcnt(); - case 0x7C: return Instr::I64Add(); - case 0x7D: return Instr::I64Sub(); - case 0x7E: return Instr::I64Mul(); - case 0x7F: return Instr::I64DivS(); - case 0x80: return Instr::I64DivU(); - case 0x81: return Instr::I64RemS(); - case 0x82: return Instr::I64RemU(); - case 0x83: return Instr::I64And(); - case 0x84: return Instr::I64Or(); - case 0x85: return Instr::I64Xor(); - case 0x86: return Instr::I64Shl(); - case 0x87: return Instr::I64ShrS(); - case 0x88: return Instr::I64ShrU(); - case 0x89: return Instr::I64RotL(); - case 0x8A: return Instr::I64RotR(); - case 0x8B: return Instr::F32Abs(); - case 0x8C: return Instr::F32Neg(); - case 0x8D: return Instr::F32Ceil(); - case 0x8E: return Instr::F32Floor(); - case 0x8F: return Instr::F32Trunc(); - case 0x90: return Instr::F32Nearest(); - case 0x91: return Instr::F32Sqrt(); - case 0x92: return Instr::F32Add(); - case 0x93: return Instr::F32Sub(); - case 0x94: return Instr::F32Mul(); - case 0x95: return Instr::F32Div(); - case 0x96: return Instr::F32Min(); - case 0x97: return Instr::F32Max(); - case 0x98: return Instr::F32CopySign(); - case 0x99: return Instr::F64Abs(); - case 0x9A: return Instr::F64Neg(); - case 0x9B: return Instr::F64Ceil(); - case 0x9C: return Instr::F64Floor(); - case 0x9D: return Instr::F64Trunc(); - case 0x9E: return Instr::F64Nearest(); - case 0x9F: return Instr::F64Sqrt(); - case 0xA0: return Instr::F64Add(); - case 0xA1: return Instr::F64Sub(); - case 0xA2: return Instr::F64Mul(); - case 0xA3: return Instr::F64Div(); - case 0xA4: return Instr::F64Min(); - case 0xA5: return Instr::F64Max(); - case 0xA6: return Instr::F64CopySign(); - case 0xA7: return Instr::I32WrapI64(); - case 0xA8: return Instr::I32TruncF32S(); - case 0xA9: return Instr::I32TruncF32U(); - case 0xAA: return Instr::I32TruncF64S(); - case 0xAB: return Instr::I32TruncF64U(); - case 0xAC: return Instr::I64ExtendI32S(); - case 0xAD: return Instr::I64ExtendI32U(); - case 0xAE: return Instr::I64TruncF32S(); - case 0xAF: return Instr::I64TruncF32U(); - case 0xB0: return Instr::I64TruncF64S(); - case 0xB1: return Instr::I64TruncF64U(); - case 0xB2: return Instr::F32ConvertI32S(); - case 0xB3: return Instr::F32ConvertI32U(); - case 0xB4: return Instr::F32ConvertI64S(); - case 0xB5: return Instr::F32ConvertI64U(); - case 0xB6: return Instr::F32DemoteF64(); - case 0xB7: return Instr::F64ConvertI32S(); - case 0xB8: return Instr::F64ConvertI32U(); - case 0xB9: return Instr::F64ConvertI64S(); - case 0xBA: return Instr::F64ConvertI64U(); - case 0xBB: return Instr::F64PromoteF32(); - case 0xBC: return Instr::I32ReinterpretF32(); - case 0xBD: return Instr::I64ReinterpretF64(); - case 0xBE: return Instr::F32ReinterpretI32(); - case 0xBF: return Instr::F64ReinterpretI64(); - case 0xC0: return Instr::I32Extend8S(); - case 0xC1: return Instr::I32Extend16S(); - case 0xC2: return Instr::I64Extend8S(); - case 0xC3: return Instr::I64Extend16S(); - case 0xC4: return Instr::I64Extend32S(); - case 0xD0: return Instr::RefNull($this->decodeRefType()); - case 0xD1: return Instr::RefIsNull(); - case 0xD2: return Instr::RefFunc($this->decodeFuncIdx()); - case 0xFC: - switch ($this->decodeU32()) { - case 0: return Instr::I32TruncSatF32S(); - case 1: return Instr::I32TruncSatF32U(); - case 2: return Instr::I32TruncSatF64S(); - case 3: return Instr::I32TruncSatF64U(); - case 4: return Instr::I64TruncSatF32S(); - case 5: return Instr::I64TruncSatF32U(); - case 6: return Instr::I64TruncSatF64S(); - case 7: return Instr::I64TruncSatF64U(); - case 8: - $data = $this->decodeDataIdx(); - if ($this->decodeByte() !== 0) { - throw new InvalidBinaryFormatException("memory init"); - } - return Instr::MemoryInit($data); - case 9: return Instr::DataDrop($this->decodeDataIdx()); - case 10: - if ($this->decodeByte() !== 0) { - throw new InvalidBinaryFormatException("memory copy"); - } - if ($this->decodeByte() !== 0) { - throw new InvalidBinaryFormatException("memory copy"); - } - return Instr::MemoryCopy(); - case 11: - if ($this->decodeByte() !== 0) { - throw new InvalidBinaryFormatException("memory fill"); - } - return Instr::MemoryFill(); - case 12: - $from = $this->decodeElemIdx(); - $to = $this->decodeTableIdx(); - return Instr::TableInit($to, $from); - case 13: return Instr::ElemDrop($this->decodeElemIdx()); - case 14: - $to = $this->decodeTableIdx(); - $from = $this->decodeTableIdx(); - return Instr::TableCopy($to, $from); - case 15: return Instr::TableGrow($this->decodeTableIdx()); - case 16: return Instr::TableSize($this->decodeTableIdx()); - case 17: return Instr::TableFill($this->decodeTableIdx()); - default: - throw new InvalidBinaryFormatException("instr"); - } - // no break - default: - throw new InvalidBinaryFormatException("Unexpected opcode $op while decoding an instruction"); - } - } - - /** - * @return array{0: U32, 1: U32} - */ - private function decodeMemArg(): array - { - $align = $this->decodeU32(); - $offset = $this->decodeU32(); - return [$offset, $align]; - } - - private function decodeBlockType(): BlockType - { - $b = $this->stream->peekByte(); - if ($b === 0x40) { - $this->stream->seek(1); - return BlockType::ValType(null); - } elseif (in_array($b, [0x7F, 0x7E, 0x7D, 0x7C, 0x7B, 0x70, 0x6F], true)) { - return BlockType::ValType($this->decodeValType()); - } else { - $type = $this->decodeS33(); - if ($type < 0) { - throw new InvalidBinaryFormatException("blocktype"); - } - return BlockType::TypeIdx($type); - } - } - - /** - * @param list> $delimiters - * @return array{0: list, 1: Instr} - */ - private function decodeInstrsUntil(array $delimiters): array - { - $result = []; - while (true) { - $instr = $this->decodeInstr(); - if (in_array(get_class($instr), $delimiters, true)) { - return [$result, $instr]; - } - $result[] = $instr; - } - } - - /** - * @template T - * @param callable(): T $decoder - * @return list - */ - private function decodeVec(callable $decoder): array - { - $count = $this->decodeU32(); - $result = []; - for ($i = 0; $i < $count; $i++) { - $result[] = $decoder(); - } - return $result; - } - - /** - * @phpstan-impure - */ - private function decodeByte(): int - { - return $this->stream->readByte(); - } - - private function decodeU32(): int - { - return $this->decodeUnsignedLeb128(32); - } - - private function decodeUnsignedLeb128(int $maxBits): int - { - $result = 0; - for ($shiftBits = 0; $shiftBits < $maxBits; $shiftBits += 7) { - $b = $this->decodeByte(); - $leftBitsCount = $maxBits - $shiftBits; - if ($leftBitsCount < 7) { - if ((($b & 0b01111111) >> $leftBitsCount) !== 0) { - throw new InvalidBinaryFormatException("unsigned leb128 ($maxBits): too large"); - } - } - $result |= ($b & 0b01111111) << $shiftBits; - if (($b & 0b10000000) === 0) { - return $result; - } - } - throw new InvalidBinaryFormatException("unsigned leb128 ($maxBits): too large"); - } - - private function decodeSignedLeb128(int $maxBits): int - { - $result = 0; - for ($shiftBits = 0; $shiftBits < $maxBits; $shiftBits += 7) { - $b = $this->decodeByte(); - $leftBitsCount = $maxBits - $shiftBits; - if ($leftBitsCount < 7) { - if (($b & (1 << ($leftBitsCount - 1))) === 0) { - if ((($b & 0b01111111) >> $leftBitsCount) !== 0) { - throw new InvalidBinaryFormatException("signed leb128 ($maxBits): too large"); - } - } else { - if ((($b & 0b01111111) >> $leftBitsCount) + 1 !== (1 << (7 - $leftBitsCount))) { - throw new InvalidBinaryFormatException("signed leb128 ($maxBits): too large"); - } - } - } - $result |= ($b & 0b01111111) << $shiftBits; - if (($b & 0b10000000) === 0) { - if (($b & 0b01000000) === 0) { - return $result; - } else { - return $result | (~0 << ($shiftBits + 7)); - } - } - } - throw new InvalidBinaryFormatException("signed leb128 ($maxBits): too large"); - } - - /** - * @return S32 - */ - private function decodeS32(): int - { - return $this->decodeSignedLeb128(32); - } - - /** - * @return S33 - */ - private function decodeS33(): int - { - return $this->decodeSignedLeb128(33); - } - - /** - * @return S64 - */ - private function decodeS64(): int - { - return $this->decodeSignedLeb128(64); - } - - /** - * @return F32 - */ - private function decodeF32(): float - { - $buf = $this->stream->read(4); - $result = unpack('g', $buf); - if ($result === false) { - throw new InvalidBinaryFormatException("f32"); - } - assert(isset($result[1]) && is_float($result[1])); - return $result[1]; - } - - /** - * @return F64 - */ - private function decodeF64(): float - { - $buf = $this->stream->read(8); - $result = unpack('e', $buf); - if ($result === false) { - throw new InvalidBinaryFormatException("f64"); - } - assert(isset($result[1]) && is_float($result[1])); - return $result[1]; - } - - /** - * @return Name - */ - private function decodeName(): string - { - $bytes = $this->decodeVec($this->decodeByte(...)); - $name = $this->implodeUtf8BytesToString($bytes); - if ($name === null) { - throw new InvalidBinaryFormatException("name"); - } - return $name; - } - - /** - * @param list $bytes - */ - private function implodeUtf8BytesToString(array $bytes): ?string - { - $s = ''; - $count = count($bytes); - for ($i = 0; $i < $count; $i++) { - if (($bytes[$i] & 0x80) === 0) { - $code = $bytes[$i]; - } elseif (($bytes[$i] & 0xE0) === 0xC0) { - if ($count <= $i + 1) { - return null; - } - $code = (($bytes[$i] & 0x1F) << 6) | ($bytes[$i + 1] & 0x3F); - $i++; - } elseif (($bytes[$i] & 0xF0) === 0xE0) { - if ($count <= $i + 2) { - return null; - } - $code = (($bytes[$i] & 0x0F) << 12) | (($bytes[$i + 1] & 0x3F) << 6) | ($bytes[$i + 2] & 0x3F); - $i += 2; - } elseif (($bytes[$i] & 0xF8) === 0xF0) { - if ($count <= $i + 3) { - return null; - } - $code = (($bytes[$i] & 0x07) << 18) | (($bytes[$i + 1] & 0x3F) << 12) | (($bytes[$i + 2] & 0x3F) << 6) | ($bytes[$i + 3] & 0x3F); - $i += 3; - } else { - return null; - } - $s .= mb_chr($code, 'UTF-8'); - } - return $s; - } - - /** - * @return 0|positive-int - */ - private function countDataIndicesUsedInCode(Code $code): int - { - $result = array_reduce( - $code->body, - fn ($acc, $instr) => $acc + match ($instr::class) { - Instrs\Memory\DataDrop::class => 1, - Instrs\Memory\MemoryInit::class => 1, - default => 0, - }, - 0, - ); - assert(is_int($result), '$result is guaranteed not to exceed the maximum value of an integer because the number of instructions in $code is limited'); - assert(0 <= $result, '$result is guaranteed to be non-negative because it is the sum of non-negative integers'); - return $result; - } -} diff --git a/src/BinaryFormat/Internal/Code.php b/src/BinaryFormat/Internal/Code.php deleted file mode 100644 index bc30c92..0000000 --- a/src/BinaryFormat/Internal/Code.php +++ /dev/null @@ -1,23 +0,0 @@ - $compressedLocals - * @param list $body - */ - public function __construct( - public array $compressedLocals, - public array $body, - ) { - } -} diff --git a/src/BinaryFormat/Internal/Locals.php b/src/BinaryFormat/Internal/Locals.php deleted file mode 100644 index 3938371..0000000 --- a/src/BinaryFormat/Internal/Locals.php +++ /dev/null @@ -1,22 +0,0 @@ -imports as $import) { - $mod = $import->module; - $name = $import->name; - $desc = $import->desc; - $type = match ($desc::class) { - ImportDescs\Func::class => 'func', - ImportDescs\Table::class => 'table', - ImportDescs\Mem::class => 'mem', - ImportDescs\Global_::class => 'global', - default => 'unknown', - }; - $extraInfo = match ($desc::class) { - ImportDescs\Func::class => self::typeIdxToString($desc->func, $module), - ImportDescs\Table::class => self::tableTypeToString($desc->table), - ImportDescs\Mem::class => self::memTypeToString($desc->mem), - ImportDescs\Global_::class => self::globalTypeToString($desc->global), - default => 'unknown', - }; - echo "$mod::$name: $type $extraInfo\n"; - } - } - - private static function typeIdxToString(int $idx, Module $module): string - { - $type = $module->types[$idx]; - return self::funcTypeToString($type); - } - - private static function tableTypeToString(TableType $type): string - { - return self::valTypeToString(ValType::RefType($type->refType)) . ' ' . self::limitsToString($type->limits); - } - - private static function memTypeToString(MemType $type): string - { - return self::limitsToString($type->limits); - } - - private static function globalTypeToString(GlobalType $type): string - { - return self::mutToString($type->mut) . ' ' . self::valTypeToString($type->valType); - } - - private static function funcTypeToString(FuncType $type): string - { - $params = self::resultTypeToString($type->params); - $results = self::resultTypeToString($type->results); - return "($params) -> ($results)"; - } - - private static function valTypeToString(ValType $type): string - { - return match ($type::class) { - ValTypes\NumType::class => match ($type->inner) { - NumType::I32 => 'i32', - NumType::I64 => 'i64', - NumType::F32 => 'f32', - NumType::F64 => 'f64', - }, - ValTypes\VecType::class => 'v128', - ValTypes\RefType::class => match ($type->inner) { - RefType::FuncRef => 'funcref', - RefType::ExternRef => 'externref', - }, - default => 'unknown', - }; - } - - private static function limitsToString(Limits $limit): string - { - $min = $limit->min; - $max = $limit->max; - return $max === null ? "[$min, inf)" : "[$min, $max]"; - } - - private static function mutToString(Mut $mut): string - { - return match ($mut) { - Mut::Const => 'const', - Mut::Var => 'var', - }; - } - - private static function resultTypeToString(ResultType $type): string - { - return implode(', ', array_map(self::valTypeToString(...), $type->types)); - } -} diff --git a/src/Execution/Allocator.php b/src/Execution/Allocator.php deleted file mode 100644 index f808174..0000000 --- a/src/Execution/Allocator.php +++ /dev/null @@ -1,165 +0,0 @@ - $externVals - */ - public function allocPreInitModule( - Module $module, - array $externVals, - ): ModuleInst { - $m = new ModuleInst($module->types, [], [], [], [], [], [], []); - - foreach ($externVals as $externVal) { - match ($externVal::class) { - ExternVals\Func::class => $m->funcAddrs[] = $externVal->addr, - ExternVals\Global_::class => $m->globalAddrs[] = $externVal->addr, - default => null, - }; - } - foreach ($module->funcs as $func) { - $m->funcAddrs[] = $this->allocFunc($func, $m); - } - - return $m; - } - - /** - * @param list $externVals - * @param list $vals - * @param list> $refsList - * @param list $preAllocatedFuncs - */ - public function allocModule( - Module $module, - array $externVals, - array $vals, - array $refsList, - array $preAllocatedFuncs, - ): ModuleInst { - $m = new ModuleInst($module->types, [], [], [], [], [], [], []); - - foreach ($externVals as $externVal) { - match ($externVal::class) { - ExternVals\Func::class => null, // handled below. - ExternVals\Table::class => $m->tableAddrs[] = $externVal->addr, - ExternVals\Mem::class => $m->memAddrs[] = $externVal->addr, - ExternVals\Global_::class => $m->globalAddrs[] = $externVal->addr, - default => throw new RuntimeException("unreachable"), - }; - } - - foreach ($preAllocatedFuncs as $funcAddr) { - $m->funcAddrs[] = $funcAddr; - $funcInst = $this->store->funcs[$funcAddr]; - if ($funcInst instanceof FuncInsts\Wasm) { - // @phpstan-ignore-next-line - $this->store->funcs[$funcAddr] = FuncInst::Wasm( - $funcInst->type, - $m, - $funcInst->code, - ); - } - } - foreach ($module->tables as $table) { - $m->tableAddrs[] = $this->allocTable($table->type, Ref::RefNull($table->type->refType)); - } - foreach ($module->mems as $mem) { - $m->memAddrs[] = $this->allocMem($mem->type); - } - foreach ($module->datas as $data) { - $m->dataAddrs[] = $this->allocData($data->init); - } - - foreach ($module->globals as $i => $global) { - $m->globalAddrs[] = $this->allocGlobal($global->type, $vals[$i]); - } - foreach ($module->elems as $i => $elem) { - $m->elemAddrs[] = $this->allocElem($elem->type, $refsList[$i]); - } - - foreach ($module->exports as $export) { - $value = match ($export->desc::class) { - ExportDescs\Func::class => ExternVal::Func($m->funcAddrs[$export->desc->func]), - ExportDescs\Table::class => ExternVal::Table($m->tableAddrs[$export->desc->table]), - ExportDescs\Mem::class => ExternVal::Mem($m->memAddrs[$export->desc->mem]), - ExportDescs\Global_::class => ExternVal::Global_($m->globalAddrs[$export->desc->global]), - default => throw new RuntimeException("unreachable"), - }; - $m->exports[] = new ExportInst($export->name, $value); - } - - return $m; - } - - private function allocFunc(Func $func, ModuleInst $moduleInst): int - { - $funcType = $moduleInst->types[$func->type]; - $funcInst = FuncInst::Wasm($funcType, $moduleInst, $func); - $this->store->funcs[] = $funcInst; - return count($this->store->funcs) - 1; - } - - private function allocTable(TableType $tableType, Ref $ref): int - { - $minSize = $tableType->limits->min; - $elem = array_fill(0, $minSize, $ref); - $tableInst = new TableInst($tableType, $elem); - $this->store->tables[] = $tableInst; - return count($this->store->tables) - 1; - } - - private function allocMem(MemType $memType): int - { - $memInst = new MemInst($memType); - $this->store->mems[] = $memInst; - return count($this->store->mems) - 1; - } - - private function allocGlobal(GlobalType $globalType, int|float|Ref $val): int - { - $globalInst = new GlobalInst($globalType, $val); - $this->store->globals[] = $globalInst; - return count($this->store->globals) - 1; - } - - /** - * @param list $elem - */ - private function allocElem(RefType $refType, array $elem): int - { - $elemInst = new ElemInst($refType, $elem); - $this->store->elems[] = $elemInst; - return count($this->store->elems) - 1; - } - - /** - * @param list $data - */ - private function allocData(array $data): int - { - $dataInst = new DataInst($data); - $this->store->datas[] = $dataInst; - return count($this->store->datas) - 1; - } -} diff --git a/src/Execution/DataInst.php b/src/Execution/DataInst.php deleted file mode 100644 index 0db1da5..0000000 --- a/src/Execution/DataInst.php +++ /dev/null @@ -1,16 +0,0 @@ - $data - */ - public function __construct( - public array $data, - ) { - } -} diff --git a/src/Execution/ElemInst.php b/src/Execution/ElemInst.php deleted file mode 100644 index 24e655c..0000000 --- a/src/Execution/ElemInst.php +++ /dev/null @@ -1,19 +0,0 @@ - $elem - */ - public function __construct( - public RefType $type, - public array $elem, - ) { - } -} diff --git a/src/Execution/ExportInst.php b/src/Execution/ExportInst.php deleted file mode 100644 index e4521b4..0000000 --- a/src/Execution/ExportInst.php +++ /dev/null @@ -1,17 +0,0 @@ - $arity - * @param list $locals - */ - public function __construct( - public readonly int $arity, - public array $locals, - public readonly ModuleInst $module, - public string $debugName, - ) { - } -} diff --git a/src/Execution/FuncInst.php b/src/Execution/FuncInst.php deleted file mode 100644 index 901fa81..0000000 --- a/src/Execution/FuncInst.php +++ /dev/null @@ -1,21 +0,0 @@ - $arity - */ - public function __construct( - public int $arity, - ) { - } -} diff --git a/src/Execution/MemInst.php b/src/Execution/MemInst.php deleted file mode 100644 index 1be4860..0000000 --- a/src/Execution/MemInst.php +++ /dev/null @@ -1,662 +0,0 @@ -ffi = FFI::cdef(); - $this->initInternalMemory($type->limits->min); - } - - public function size(): int - { - return $this->dataSize; - } - - public function nPages(): int - { - return $this->size() / self::PAGE_SIZE; - } - - /** - * @return int - * Returns the original size of the memory in pages or -1 if failed. - */ - public function grow(int $n): int - { - $sz = $this->nPages(); - $len = $sz + $n; - if (self::MAX_PAGES < $len) { - return -1; - } - - $limits = $this->type->limits; - $limits_ = new Limits($len, $limits->max); - if (!$limits_->isValid()) { - return -1; - } - - $originalSize = $this->size(); - // @phpstan-ignore-next-line - $originalData = $this->ffi->new("uint8_t[$originalSize+8]"); - assert($originalData !== null); - FFI::memcpy($originalData, $this->dataU8, $originalSize); - - $this->initInternalMemory($len); - - FFI::memcpy($this->dataU8, $originalData, $originalSize); - - return $sz; - } - - private function initInternalMemory(int $n): void - { - $this->dataSize = $n * self::PAGE_SIZE; - - // @phpstan-ignore-next-line - $this->dataU8 = $this->ffi->new("uint8_t[$this->dataSize+8]"); - // @phpstan-ignore-next-line - $this->dataS8 = $this->ffi->cast("int8_t[$this->dataSize]", $this->dataU8); - - // @phpstan-ignore-next-line - $castInt = fn ($n, $signed, $offset) => $this->ffi->cast( - sprintf("%sint%d_t[$this->dataSize/%d]", $signed ? "" : "u", $n, $n / 8), - // @phpstan-ignore-next-line - $this->ffi->cast("uint8_t[$this->dataSize+8]", $this->dataU8 + $offset), - ); - - // @phpstan-ignore-next-line - $castFloat = fn ($n, $offset) => $this->ffi->cast( - sprintf("%s[$this->dataSize/%d]", $n === 32 ? "float" : "double", $n / 8), - // @phpstan-ignore-next-line - $this->ffi->cast("uint8_t[$this->dataSize+8]", $this->dataU8 + $offset), - ); - - // @phpstan-ignore-next-line - $this->dataU16_0 = $castInt(16, false, 0); - // @phpstan-ignore-next-line - $this->dataU16_1 = $castInt(16, false, 1); - // @phpstan-ignore-next-line - $this->dataS16_0 = $castInt(16, true, 0); - // @phpstan-ignore-next-line - $this->dataS16_1 = $castInt(16, true, 1); - - // @phpstan-ignore-next-line - $this->dataU32_0 = $castInt(32, false, 0); - // @phpstan-ignore-next-line - $this->dataU32_1 = $castInt(32, false, 1); - // @phpstan-ignore-next-line - $this->dataU32_2 = $castInt(32, false, 2); - // @phpstan-ignore-next-line - $this->dataU32_3 = $castInt(32, false, 3); - // @phpstan-ignore-next-line - $this->dataS32_0 = $castInt(32, true, 0); - // @phpstan-ignore-next-line - $this->dataS32_1 = $castInt(32, true, 1); - // @phpstan-ignore-next-line - $this->dataS32_2 = $castInt(32, true, 2); - // @phpstan-ignore-next-line - $this->dataS32_3 = $castInt(32, true, 3); - - // @phpstan-ignore-next-line - $this->dataS64_0 = $castInt(64, true, 0); - // @phpstan-ignore-next-line - $this->dataS64_1 = $castInt(64, true, 1); - // @phpstan-ignore-next-line - $this->dataS64_2 = $castInt(64, true, 2); - // @phpstan-ignore-next-line - $this->dataS64_3 = $castInt(64, true, 3); - // @phpstan-ignore-next-line - $this->dataS64_4 = $castInt(64, true, 4); - // @phpstan-ignore-next-line - $this->dataS64_5 = $castInt(64, true, 5); - // @phpstan-ignore-next-line - $this->dataS64_6 = $castInt(64, true, 6); - // @phpstan-ignore-next-line - $this->dataS64_7 = $castInt(64, true, 7); - - // @phpstan-ignore-next-line - $this->dataF32_0 = $castFloat(32, 0); - // @phpstan-ignore-next-line - $this->dataF32_1 = $castFloat(32, 1); - // @phpstan-ignore-next-line - $this->dataF32_2 = $castFloat(32, 2); - // @phpstan-ignore-next-line - $this->dataF32_3 = $castFloat(32, 3); - - // @phpstan-ignore-next-line - $this->dataF64_0 = $castFloat(64, 0); - // @phpstan-ignore-next-line - $this->dataF64_1 = $castFloat(64, 1); - // @phpstan-ignore-next-line - $this->dataF64_2 = $castFloat(64, 2); - // @phpstan-ignore-next-line - $this->dataF64_3 = $castFloat(64, 3); - // @phpstan-ignore-next-line - $this->dataF64_4 = $castFloat(64, 4); - // @phpstan-ignore-next-line - $this->dataF64_5 = $castFloat(64, 5); - // @phpstan-ignore-next-line - $this->dataF64_6 = $castFloat(64, 6); - // @phpstan-ignore-next-line - $this->dataF64_7 = $castFloat(64, 7); - - // @phpstan-ignore-next-line - FFI::memset($this->dataU8, 0, $this->dataSize); - } - - /** - * @param list $data - */ - public function copyData(array $data, int $src, int $dst, int $len): void - { - assert(0 <= $len); - assert(0 <= $src); - assert(0 <= $dst); - assert($src + $len <= count($data)); - assert($dst + $len <= $this->size()); - for ($i = 0; $i < $len; $i++) { - $this->storeByte($dst + $i, $data[$src + $i]); - } - } - - public function memcpy(int $dst, int $src, int $len): void - { - assert(0 <= $len); - assert(0 <= $src); - assert(0 <= $dst); - assert($src + $len <= $this->size()); - assert($dst + $len <= $this->size()); - if ($src === $dst || $len === 0) { - return; - } - for ($i = 0; $i < $len; $i++) { - $s = ($dst < $src) ? ($src + $i) : ($src + $len - 1 - $i); - $d = ($dst < $src) ? ($dst + $i) : ($dst + $len - 1 - $i); - $x = $this->loadByte($s); - assert($x !== null); - $this->storeByte($d, $x); - } - } - - public function memset(int $dst, int $c, int $len): void - { - assert(0 <= $len); - assert(0 <= $dst); - assert($dst + $len <= $this->size()); - for ($i = 0; $i < $len; $i++) { - $this->storeI32_s8($dst + $i, $c); - } - } - - /** - * @return ?S32 - */ - public function loadI32_s8(int $ptr): ?int - { - if ($this->size() < $ptr + 1) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataS8[$ptr]; - assert(-0x80 <= $c && $c <= 0x7F, "$c"); - return $c; - } - - /** - * @return ?S32 - */ - public function loadI32_u8(int $ptr): ?int - { - if ($this->size() < $ptr + 1) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataU8[$ptr]; - assert(0 <= $c && $c <= 0xFF, "$c"); - return $c; - } - - /** - * @return ?S32 - */ - public function loadI32_s16(int $ptr): ?int - { - if ($this->size() < $ptr + 2) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataS16($ptr)[$ptr >> 1]; - assert(-0x8000 <= $c && $c <= 0x7FFF, "$c"); - return $c; - } - - /** - * @return ?S32 - */ - public function loadI32_u16(int $ptr): ?int - { - if ($this->size() < $ptr + 2) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataU16($ptr)[$ptr >> 1]; - assert(0 <= $c && $c <= 0xFFFF, "$c"); - return $c; - } - - /** - * @return ?S32 - */ - public function loadI32_s32(int $ptr): ?int - { - if ($this->size() < $ptr + 4) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataS32($ptr)[$ptr >> 2]; - assert(-0x80000000 <= $c && $c <= 0x7FFFFFFF, "$c"); - return $c; - } - - /** - * @return ?S64 - */ - public function loadI64_s8(int $ptr): ?int - { - if ($this->size() < $ptr + 1) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataS8[$ptr]; - assert(-0x80 <= $c && $c <= 0x7F, "$c"); - return $c; - } - - /** - * @return ?S64 - */ - public function loadI64_u8(int $ptr): ?int - { - if ($this->size() < $ptr + 1) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataU8[$ptr]; - assert(0 <= $c && $c <= 0xFF, "$c"); - return $c; - } - - /** - * @return ?S64 - */ - public function loadI64_s16(int $ptr): ?int - { - if ($this->size() < $ptr + 2) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataS16($ptr)[$ptr >> 1]; - assert(-0x8000 <= $c && $c <= 0x7FFF, "$c"); - return $c; - } - - /** - * @return ?S64 - */ - public function loadI64_u16(int $ptr): ?int - { - if ($this->size() < $ptr + 2) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataU16($ptr)[$ptr >> 1]; - assert(0 <= $c && $c <= 0xFFFF, "$c"); - return $c; - } - - /** - * @return ?S64 - */ - public function loadI64_s32(int $ptr): ?int - { - if ($this->size() < $ptr + 4) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataS32($ptr)[$ptr >> 2]; - assert(-0x80000000 <= $c && $c <= 0x7FFFFFFF, "$c"); - return $c; - } - - /** - * @return ?S64 - */ - public function loadI64_u32(int $ptr): ?int - { - if ($this->size() < $ptr + 4) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataU32($ptr)[$ptr >> 2]; - assert(0 <= $c && $c <= 0xFFFFFFFF, "$c"); - return $c; - } - - /** - * @return ?S64 - */ - public function loadI64_s64(int $ptr): ?int - { - if ($this->size() < $ptr + 8) { - return null; - } - // @phpstan-ignore-next-line - $c = $this->dataS64($ptr)[$ptr >> 3]; - assert(-0x8000000000000000 <= $c && $c <= 0x7FFFFFFFFFFFFFFF, "$c"); - return $c; - } - - /** - * @return ?F32 - */ - public function loadF32(int $ptr): ?float - { - if ($this->size() < $ptr + 4) { - return null; - } - // @phpstan-ignore-next-line - return $this->dataF32($ptr)[$ptr >> 2]; - } - - /** - * @return ?F64 - */ - public function loadF64(int $ptr): ?float - { - if ($this->size() < $ptr + 8) { - return null; - } - // @phpstan-ignore-next-line - return $this->dataF64($ptr)[$ptr >> 3]; - } - - /** - * @return ?int - */ - public function loadByte(int $ptr): ?int - { - if ($this->size() < $ptr + 1) { - return null; - } - // @phpstan-ignore-next-line - return $this->dataU8[$ptr]; - } - - /** - * @return bool - */ - public function storeByte(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 1) { - return false; - } - // @phpstan-ignore-next-line - $this->dataU8[$ptr] = $c; - return true; - } - - /** - * @param S32 $c - * @return bool - */ - public function storeI32_s8(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 1) { - return false; - } - // @phpstan-ignore-next-line - $this->dataS8[$ptr] = $c; - return true; - } - - /** - * @param S32 $c - * @return bool - */ - public function storeI32_s16(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 2) { - return false; - } - // @phpstan-ignore-next-line - $this->dataS16($ptr)[$ptr >> 1] = $c; - return true; - } - - /** - * @param S32 $c - * @return bool - */ - public function storeI32_s32(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 4) { - return false; - } - // @phpstan-ignore-next-line - $this->dataS32($ptr)[$ptr >> 2] = $c; - return true; - } - - /** - * @param S64 $c - * @return bool - */ - public function storeI64_s8(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 1) { - return false; - } - // @phpstan-ignore-next-line - $this->dataS8[$ptr] = $c; - return true; - } - - /** - * @param S64 $c - * @return bool - */ - public function storeI64_s16(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 2) { - return false; - } - // @phpstan-ignore-next-line - $this->dataS16($ptr)[$ptr >> 1] = $c; - return true; - } - - /** - * @param S64 $c - * @return bool - */ - public function storeI64_s32(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 4) { - return false; - } - // @phpstan-ignore-next-line - $this->dataS32($ptr)[$ptr >> 2] = $c; - return true; - } - - /** - * @param S64 $c - * @return bool - */ - public function storeI64_s64(int $ptr, int $c): bool - { - if ($this->size() < $ptr + 8) { - return false; - } - // @phpstan-ignore-next-line - $this->dataS64($ptr)[$ptr >> 3] = $c; - return true; - } - - /** - * @param F32 $c - * @return bool - */ - public function storeF32(int $ptr, float $c): bool - { - if ($this->size() < $ptr + 4) { - return false; - } - // @phpstan-ignore-next-line - $this->dataF32($ptr)[$ptr >> 2] = $c; - return true; - } - - /** - * @param F64 $c - * @return bool - */ - public function storeF64(int $ptr, float $c): bool - { - if ($this->size() < $ptr + 8) { - return false; - } - // @phpstan-ignore-next-line - $this->dataF64($ptr)[$ptr >> 3] = $c; - return true; - } - - private function dataU16(int $ptr): CData - { - return ($ptr & 1) !== 0 ? $this->dataU16_1 : $this->dataU16_0; - } - - private function dataS16(int $ptr): CData - { - return ($ptr & 1) !== 0 ? $this->dataS16_1 : $this->dataS16_0; - } - - private function dataU32(int $ptr): CData - { - return match ($ptr & 3) { - 0 => $this->dataU32_0, - 1 => $this->dataU32_1, - 2 => $this->dataU32_2, - 3 => $this->dataU32_3, - }; - } - - private function dataS32(int $ptr): CData - { - return match ($ptr & 3) { - 0 => $this->dataS32_0, - 1 => $this->dataS32_1, - 2 => $this->dataS32_2, - 3 => $this->dataS32_3, - }; - } - - private function dataS64(int $ptr): CData - { - return match ($ptr & 7) { - 0 => $this->dataS64_0, - 1 => $this->dataS64_1, - 2 => $this->dataS64_2, - 3 => $this->dataS64_3, - 4 => $this->dataS64_4, - 5 => $this->dataS64_5, - 6 => $this->dataS64_6, - 7 => $this->dataS64_7, - }; - } - - private function dataF32(int $ptr): CData - { - return match ($ptr & 3) { - 0 => $this->dataF32_0, - 1 => $this->dataF32_1, - 2 => $this->dataF32_2, - 3 => $this->dataF32_3, - }; - } - - private function dataF64(int $ptr): CData - { - return match ($ptr & 7) { - 0 => $this->dataF64_0, - 1 => $this->dataF64_1, - 2 => $this->dataF64_2, - 3 => $this->dataF64_3, - 4 => $this->dataF64_4, - 5 => $this->dataF64_5, - 6 => $this->dataF64_6, - 7 => $this->dataF64_7, - }; - } -} diff --git a/src/Execution/ModuleInst.php b/src/Execution/ModuleInst.php deleted file mode 100644 index e073916..0000000 --- a/src/Execution/ModuleInst.php +++ /dev/null @@ -1,32 +0,0 @@ - $types - * @param list $funcAddrs - * @param list $tableAddrs - * @param list $memAddrs - * @param list $globalAddrs - * @param list $elemAddrs - * @param list $dataAddrs - * @param list $exports - */ - public function __construct( - public array $types, - public array $funcAddrs, - public array $tableAddrs, - public array $memAddrs, - public array $globalAddrs, - public array $elemAddrs, - public array $dataAddrs, - public array $exports, - ) { - } -} diff --git a/src/Execution/NumericOps.php b/src/Execution/NumericOps.php deleted file mode 100644 index 6fd19bf..0000000 --- a/src/Execution/NumericOps.php +++ /dev/null @@ -1,193 +0,0 @@ -> 63) & 1) === 1 ? -1 : 1; - } elseif ($p !== 0.0) { - return $p < 0.0 ? -1 : 1; - } else { - // Comparison with 0 does not work for -0.0. - return fdiv(1, $p) < 0.0 ? -1 : 1; - } - } -} diff --git a/src/Execution/Ref.php b/src/Execution/Ref.php deleted file mode 100644 index a5682d3..0000000 --- a/src/Execution/Ref.php +++ /dev/null @@ -1,25 +0,0 @@ - $values - */ - final public static function Values(array $values): Results\Values - { - return new Results\Values($values); - } - - final public static function Trap(): Results\Trap - { - return new Results\Trap(); - } -} diff --git a/src/Execution/Results/Trap.php b/src/Execution/Results/Trap.php deleted file mode 100644 index eb676b2..0000000 --- a/src/Execution/Results/Trap.php +++ /dev/null @@ -1,11 +0,0 @@ - $values - */ - protected function __construct( - public array $values, - ) { - } -} diff --git a/src/Execution/Runtime.php b/src/Execution/Runtime.php deleted file mode 100644 index 2cc37b2..0000000 --- a/src/Execution/Runtime.php +++ /dev/null @@ -1,2668 +0,0 @@ - - */ - private array $instrMetrics = []; - - private function __construct( - public readonly Store $store, - public readonly Stack $stack, - public readonly ModuleInst $module, - ) { - } - - /** - * @param array> $imports - */ - public static function instantiate( - Store $store, - Module $module, - array $imports, - ): self { - $externVals = []; - foreach ($module->imports as $import) { - $extern = $imports[$import->module][$import->name] ?? null; - if ($extern === null) { - throw new RuntimeException("instantiate: import not found: {$import->module}::{$import->name}"); - } - $externVals[] = $store->register($extern); - } - return self::doInstantiate($store, $module, $externVals); - } - - /** - * @param list $externVals - */ - private static function doInstantiate( - Store $store, - Module $module, - array $externVals, - ): self { - $allocator = new Allocator($store); - - $moduleInstInit = $allocator->allocPreInitModule($module, $externVals); - - $stack = new Stack(); - $frameInit = new Frame(0, [], $moduleInstInit, 'preinit'); - $stack->pushFrame($frameInit); - - $runtimeInit = new self($store, $stack, $moduleInstInit); - - $vals = []; - foreach ($module->globals as $global) { - $vals[] = $runtimeInit->evalInstrsForInit($global->init); - assert($stack->top() === $frameInit); - } - - $refsList = []; - foreach ($module->elems as $elem) { - $refs = []; - foreach ($elem->init as $expr) { - $result = $runtimeInit->evalInstrsForInit($expr); - assert($result instanceof Ref); - $refs[] = $result; - } - $refsList[] = $refs; - } - - assert($stack->top() === $frameInit); - $stack->popFrame(); - - $moduleInst = $allocator->allocModule( - $module, - $externVals, - $vals, - $refsList, - $moduleInstInit->funcAddrs, - ); - - $runtime = new self($store, $stack, $moduleInst); - - $frame = new Frame(0, [], $moduleInst, 'init'); - $stack->pushFrame($frame); - - foreach ($module->elems as $i => $elem) { - if ($elem->mode instanceof ElemModes\Active) { - $n = count($elem->init); - $instrs = $elem->mode->offset; - $instrs[] = Instr::I32Const(0); - $instrs[] = Instr::I32Const($n); - $instrs[] = Instr::TableInit($elem->mode->table, $i); - $instrs[] = Instr::ElemDrop($i); - $runtime->execInstrsForInit($instrs); - } elseif ($elem->mode instanceof ElemModes\Declarative) { - $runtime->execInstrsForInit([Instr::ElemDrop($i)]); - } - } - foreach ($module->datas as $i => $data) { - if ($data->mode instanceof DataModes\Active) { - assert($data->mode->memory === 0); - $n = count($data->init); - $instrs = $data->mode->offset; - $instrs[] = Instr::I32Const(0); - $instrs[] = Instr::I32Const($n); - $instrs[] = Instr::MemoryInit($i); - $instrs[] = Instr::DataDrop($i); - $runtime->execInstrsForInit($instrs); - } - } - - if ($module->start !== null) { - $runtime->execInstrsForInit([Instr::Call($module->start->func)]); - } - - assert($stack->top() === $frame); - $stack->popFrame(); - - return new self($store, $stack, $moduleInst); - } - - public function getExport(string $name): ?ExternVal - { - foreach ($this->module->exports as $export) { - if ($name === $export->name) { - return $export->value; - } - } - return null; - } - - public function getExportedTable(string $name): ?TableInst - { - $export = $this->getExport($name); - if ($export instanceof ExternVals\Table) { - return $this->store->tables[$export->addr]; - } - return null; - } - - public function getExportedMemory(string $name): ?MemInst - { - $export = $this->getExport($name); - if ($export instanceof ExternVals\Mem) { - return $this->store->mems[$export->addr]; - } - return null; - } - - /** - * @return array - */ - public function getInstrMetrics(): array - { - asort($this->instrMetrics); - return $this->instrMetrics; - } - - /** - * @param list $vals - * @return list - */ - public function invoke(string $name, array $vals): array - { - try { - $export = $this->getExport($name); - assert($export instanceof ExternVals\Func); - $funcAddr = $export->addr; - - $funcInst = $this->store->funcs[$funcAddr]; - assert($funcInst instanceof FuncInsts\Wasm); - $paramTypes = $funcInst->type->params->types; - $resultTypes = $funcInst->type->results->types; - if (count($paramTypes) !== count($vals)) { - throw new RuntimeException("invoke($name) invalid function arity: expected " . count($paramTypes) . ", got " . count($vals)); - } - $f = new Frame(0, [], new ModuleInst([], [], [], [], [], [], [], []), "export: $name"); - $this->stack->pushFrame($f); - foreach ($vals as $val) { - $this->stack->pushValue($val); - } - $this->doInvokeFunc($funcAddr); - $results = $this->stack->popNValues(count($resultTypes)); - $this->stack->popFrame(); - return array_reverse($results); - } catch (RuntimeException $e) { - // @todo more reliable way to clear the stack - $this->stack->clear(); - throw $e; - } - } - - public function invokeByFuncAddr(int $funcAddr): void - { - $this->doInvokeFunc($funcAddr); - } - - private function doInvokeFunc(int $funcAddr): void - { - $fn = $this->store->funcs[$funcAddr]; - if ($fn instanceof FuncInsts\Wasm) { - $this->doInvokeWasmFunc($fn, $funcAddr); - } elseif ($fn instanceof FuncInsts\Host) { - $this->doInvokeHostFunc($fn); - } else { - throw new RuntimeException("doInvokeFunc: unreachable"); - } - } - - private function doInvokeWasmFunc(FuncInsts\Wasm $fn, int $funcAddr): void - { - $paramTypes = $fn->type->params->types; - $n = count($paramTypes); - $resultTypes = $fn->type->results->types; - $m = count($resultTypes); - $ts = $fn->code->locals; - $vals = $this->stack->popNValues($n); - $f = new Frame( - $m, - array_merge( - array_reverse($vals), - array_map(fn ($local) => self::defaultValueFromValType($local->type), $ts), - ), - $fn->module, - "wasm: $funcAddr", - ); - $this->activateFrame($f); - $l = new Label($m); - $this->execInstrs($fn->code->body, $l, null); - $this->deactivateFrame($m); - } - - private function activateFrame(Frame $f): void - { - $this->stack->pushFrame($f); - } - - private function deactivateFrame(int $arity): void - { - $vals = $this->stack->popNValues($arity); - $this->stack->popEntriesToCurrentFrame(); - for ($i = $arity - 1; 0 <= $i; $i--) { - $this->stack->pushValue($vals[$i]); - } - } - - private function activateLabel(Label $l): void - { - $this->stack->pushLabel($l); - } - - private function deactivateLabel(?int $arity): void - { - if ($arity === null) { - $vals = $this->stack->popValuesToLabel(); - } else { - $vals = $this->stack->popNValues($arity); - $this->stack->popValuesToLabel(); - } - for ($i = count($vals) - 1; 0 <= $i; $i--) { - $this->stack->pushValue($vals[$i]); - } - } - - private function doInvokeHostFunc(FuncInsts\Host $fn): void - { - $paramTypes = $fn->type->params->types; - $n = count($paramTypes); - $resultTypes = $fn->type->results->types; - $m = count($resultTypes); - - $params = array_reverse($this->stack->popNValues($n)); - $results = ($fn->callback)($this, ...$params); - if ($results === null) { - assert($m === 0); - return; - } - if (!is_array($results)) { - $results = [$results]; - } - assert($m === count($results)); - foreach ($results as $result) { - $this->stack->pushValue($result); - } - } - - /** - * @param list $instrs - * @param ?list $params - */ - private function execInstrs( - array $instrs, - Label $l, - ?array $params, - ): ?int { - $this->activateLabel($l); - if ($params !== null) { - foreach ($params as $param) { - $this->stack->pushValue($param); - } - } - - foreach ($instrs as $i => $instr) { - $result = $this->execInstr($instr); - if ($result !== null) { - return $result; - } - } - - $this->deactivateLabel(null); - return null; - } - - /** - * @param list $instrs - */ - private function execInstrsForInit(array $instrs): void - { - foreach ($instrs as $i) { - $this->execInstr($i); - } - } - - /** - * @param list $instrs - */ - private function evalInstrsForInit(array $instrs): int|float|Ref - { - $this->execInstrsForInit($instrs); - $result = $this->stack->popValue(); - return $result; - } - - private function execInstr(Instr $instr): ?int - { - return match ($instr::class) { - Instrs\Numeric\F32Abs::class => $this->execInstrNumericF32Abs($instr), - Instrs\Numeric\F32Add::class => $this->execInstrNumericF32Add($instr), - Instrs\Numeric\F32Ceil::class => $this->execInstrNumericF32Ceil($instr), - Instrs\Numeric\F32Const::class => $this->execInstrNumericF32Const($instr), - Instrs\Numeric\F32ConvertI32S::class => $this->execInstrNumericF32ConvertI32S($instr), - Instrs\Numeric\F32ConvertI32U::class => $this->execInstrNumericF32ConvertI32U($instr), - Instrs\Numeric\F32ConvertI64S::class => $this->execInstrNumericF32ConvertI64S($instr), - Instrs\Numeric\F32ConvertI64U::class => $this->execInstrNumericF32ConvertI64U($instr), - Instrs\Numeric\F32CopySign::class => $this->execInstrNumericF32CopySign($instr), - Instrs\Numeric\F32DemoteF64::class => $this->execInstrNumericF32DemoteF64($instr), - Instrs\Numeric\F32Div::class => $this->execInstrNumericF32Div($instr), - Instrs\Numeric\F32Eq::class => $this->execInstrNumericF32Eq($instr), - Instrs\Numeric\F32Floor::class => $this->execInstrNumericF32Floor($instr), - Instrs\Numeric\F32Ge::class => $this->execInstrNumericF32Ge($instr), - Instrs\Numeric\F32Gt::class => $this->execInstrNumericF32Gt($instr), - Instrs\Numeric\F32Le::class => $this->execInstrNumericF32Le($instr), - Instrs\Numeric\F32Lt::class => $this->execInstrNumericF32Lt($instr), - Instrs\Numeric\F32Max::class => $this->execInstrNumericF32Max($instr), - Instrs\Numeric\F32Min::class => $this->execInstrNumericF32Min($instr), - Instrs\Numeric\F32Mul::class => $this->execInstrNumericF32Mul($instr), - Instrs\Numeric\F32Ne::class => $this->execInstrNumericF32Ne($instr), - Instrs\Numeric\F32Nearest::class => $this->execInstrNumericF32Nearest($instr), - Instrs\Numeric\F32Neg::class => $this->execInstrNumericF32Neg($instr), - Instrs\Numeric\F32ReinterpretI32::class => $this->execInstrNumericF32ReinterpretI32($instr), - Instrs\Numeric\F32ReinterpretI64::class => $this->execInstrNumericF32ReinterpretI64($instr), - Instrs\Numeric\F32Sqrt::class => $this->execInstrNumericF32Sqrt($instr), - Instrs\Numeric\F32Sub::class => $this->execInstrNumericF32Sub($instr), - Instrs\Numeric\F32Trunc::class => $this->execInstrNumericF32Trunc($instr), - Instrs\Numeric\F64Abs::class => $this->execInstrNumericF64Abs($instr), - Instrs\Numeric\F64Add::class => $this->execInstrNumericF64Add($instr), - Instrs\Numeric\F64Ceil::class => $this->execInstrNumericF64Ceil($instr), - Instrs\Numeric\F64Const::class => $this->execInstrNumericF64Const($instr), - Instrs\Numeric\F64ConvertI32S::class => $this->execInstrNumericF64ConvertI32S($instr), - Instrs\Numeric\F64ConvertI32U::class => $this->execInstrNumericF64ConvertI32U($instr), - Instrs\Numeric\F64ConvertI64S::class => $this->execInstrNumericF64ConvertI64S($instr), - Instrs\Numeric\F64ConvertI64U::class => $this->execInstrNumericF64ConvertI64U($instr), - Instrs\Numeric\F64CopySign::class => $this->execInstrNumericF64CopySign($instr), - Instrs\Numeric\F64Div::class => $this->execInstrNumericF64Div($instr), - Instrs\Numeric\F64Eq::class => $this->execInstrNumericF64Eq($instr), - Instrs\Numeric\F64Floor::class => $this->execInstrNumericF64Floor($instr), - Instrs\Numeric\F64Ge::class => $this->execInstrNumericF64Ge($instr), - Instrs\Numeric\F64Gt::class => $this->execInstrNumericF64Gt($instr), - Instrs\Numeric\F64Le::class => $this->execInstrNumericF64Le($instr), - Instrs\Numeric\F64Lt::class => $this->execInstrNumericF64Lt($instr), - Instrs\Numeric\F64Max::class => $this->execInstrNumericF64Max($instr), - Instrs\Numeric\F64Min::class => $this->execInstrNumericF64Min($instr), - Instrs\Numeric\F64Mul::class => $this->execInstrNumericF64Mul($instr), - Instrs\Numeric\F64Ne::class => $this->execInstrNumericF64Ne($instr), - Instrs\Numeric\F64Nearest::class => $this->execInstrNumericF64Nearest($instr), - Instrs\Numeric\F64Neg::class => $this->execInstrNumericF64Neg($instr), - Instrs\Numeric\F64PromoteF32::class => $this->execInstrNumericF64PromoteF32($instr), - Instrs\Numeric\F64ReinterpretI32::class => $this->execInstrNumericF64ReinterpretI32($instr), - Instrs\Numeric\F64ReinterpretI64::class => $this->execInstrNumericF64ReinterpretI64($instr), - Instrs\Numeric\F64Sqrt::class => $this->execInstrNumericF64Sqrt($instr), - Instrs\Numeric\F64Sub::class => $this->execInstrNumericF64Sub($instr), - Instrs\Numeric\F64Trunc::class => $this->execInstrNumericF64Trunc($instr), - Instrs\Numeric\I32Add::class => $this->execInstrNumericI32Add($instr), - Instrs\Numeric\I32And::class => $this->execInstrNumericI32And($instr), - Instrs\Numeric\I32Clz::class => $this->execInstrNumericI32Clz($instr), - Instrs\Numeric\I32Const::class => $this->execInstrNumericI32Const($instr), - Instrs\Numeric\I32Ctz::class => $this->execInstrNumericI32Ctz($instr), - Instrs\Numeric\I32DivS::class => $this->execInstrNumericI32DivS($instr), - Instrs\Numeric\I32DivU::class => $this->execInstrNumericI32DivU($instr), - Instrs\Numeric\I32Eq::class => $this->execInstrNumericI32Eq($instr), - Instrs\Numeric\I32Eqz::class => $this->execInstrNumericI32Eqz($instr), - Instrs\Numeric\I32Extend16S::class => $this->execInstrNumericI32Extend16S($instr), - Instrs\Numeric\I32Extend8S::class => $this->execInstrNumericI32Extend8S($instr), - Instrs\Numeric\I32GeS::class => $this->execInstrNumericI32GeS($instr), - Instrs\Numeric\I32GeU::class => $this->execInstrNumericI32GeU($instr), - Instrs\Numeric\I32GtS::class => $this->execInstrNumericI32GtS($instr), - Instrs\Numeric\I32GtU::class => $this->execInstrNumericI32GtU($instr), - Instrs\Numeric\I32LeS::class => $this->execInstrNumericI32LeS($instr), - Instrs\Numeric\I32LeU::class => $this->execInstrNumericI32LeU($instr), - Instrs\Numeric\I32LtS::class => $this->execInstrNumericI32LtS($instr), - Instrs\Numeric\I32LtU::class => $this->execInstrNumericI32LtU($instr), - Instrs\Numeric\I32Mul::class => $this->execInstrNumericI32Mul($instr), - Instrs\Numeric\I32Ne::class => $this->execInstrNumericI32Ne($instr), - Instrs\Numeric\I32Or::class => $this->execInstrNumericI32Or($instr), - Instrs\Numeric\I32Popcnt::class => $this->execInstrNumericI32Popcnt($instr), - Instrs\Numeric\I32ReinterpretF32::class => $this->execInstrNumericI32ReinterpretF32($instr), - Instrs\Numeric\I32ReinterpretF64::class => $this->execInstrNumericI32ReinterpretF64($instr), - Instrs\Numeric\I32RemS::class => $this->execInstrNumericI32RemS($instr), - Instrs\Numeric\I32RemU::class => $this->execInstrNumericI32RemU($instr), - Instrs\Numeric\I32RotL::class => $this->execInstrNumericI32RotL($instr), - Instrs\Numeric\I32RotR::class => $this->execInstrNumericI32RotR($instr), - Instrs\Numeric\I32Shl::class => $this->execInstrNumericI32Shl($instr), - Instrs\Numeric\I32ShrS::class => $this->execInstrNumericI32ShrS($instr), - Instrs\Numeric\I32ShrU::class => $this->execInstrNumericI32ShrU($instr), - Instrs\Numeric\I32Sub::class => $this->execInstrNumericI32Sub($instr), - Instrs\Numeric\I32TruncF32S::class => $this->execInstrNumericI32TruncF32S($instr), - Instrs\Numeric\I32TruncF32U::class => $this->execInstrNumericI32TruncF32U($instr), - Instrs\Numeric\I32TruncF64S::class => $this->execInstrNumericI32TruncF64S($instr), - Instrs\Numeric\I32TruncF64U::class => $this->execInstrNumericI32TruncF64U($instr), - Instrs\Numeric\I32TruncSatF32S::class => $this->execInstrNumericI32TruncSatF32S($instr), - Instrs\Numeric\I32TruncSatF32U::class => $this->execInstrNumericI32TruncSatF32U($instr), - Instrs\Numeric\I32TruncSatF64S::class => $this->execInstrNumericI32TruncSatF64S($instr), - Instrs\Numeric\I32TruncSatF64U::class => $this->execInstrNumericI32TruncSatF64U($instr), - Instrs\Numeric\I32WrapI64::class => $this->execInstrNumericI32WrapI64($instr), - Instrs\Numeric\I32Xor::class => $this->execInstrNumericI32Xor($instr), - Instrs\Numeric\I64Add::class => $this->execInstrNumericI64Add($instr), - Instrs\Numeric\I64And::class => $this->execInstrNumericI64And($instr), - Instrs\Numeric\I64Clz::class => $this->execInstrNumericI64Clz($instr), - Instrs\Numeric\I64Const::class => $this->execInstrNumericI64Const($instr), - Instrs\Numeric\I64Ctz::class => $this->execInstrNumericI64Ctz($instr), - Instrs\Numeric\I64DivS::class => $this->execInstrNumericI64DivS($instr), - Instrs\Numeric\I64DivU::class => $this->execInstrNumericI64DivU($instr), - Instrs\Numeric\I64Eq::class => $this->execInstrNumericI64Eq($instr), - Instrs\Numeric\I64Eqz::class => $this->execInstrNumericI64Eqz($instr), - Instrs\Numeric\I64Extend16S::class => $this->execInstrNumericI64Extend16S($instr), - Instrs\Numeric\I64Extend32S::class => $this->execInstrNumericI64Extend32S($instr), - Instrs\Numeric\I64Extend8S::class => $this->execInstrNumericI64Extend8S($instr), - Instrs\Numeric\I64ExtendI32S::class => $this->execInstrNumericI64ExtendI32S($instr), - Instrs\Numeric\I64ExtendI32U::class => $this->execInstrNumericI64ExtendI32U($instr), - Instrs\Numeric\I64GeS::class => $this->execInstrNumericI64GeS($instr), - Instrs\Numeric\I64GeU::class => $this->execInstrNumericI64GeU($instr), - Instrs\Numeric\I64GtS::class => $this->execInstrNumericI64GtS($instr), - Instrs\Numeric\I64GtU::class => $this->execInstrNumericI64GtU($instr), - Instrs\Numeric\I64LeS::class => $this->execInstrNumericI64LeS($instr), - Instrs\Numeric\I64LeU::class => $this->execInstrNumericI64LeU($instr), - Instrs\Numeric\I64LtS::class => $this->execInstrNumericI64LtS($instr), - Instrs\Numeric\I64LtU::class => $this->execInstrNumericI64LtU($instr), - Instrs\Numeric\I64Mul::class => $this->execInstrNumericI64Mul($instr), - Instrs\Numeric\I64Ne::class => $this->execInstrNumericI64Ne($instr), - Instrs\Numeric\I64Or::class => $this->execInstrNumericI64Or($instr), - Instrs\Numeric\I64Popcnt::class => $this->execInstrNumericI64Popcnt($instr), - Instrs\Numeric\I64ReinterpretF32::class => $this->execInstrNumericI64ReinterpretF32($instr), - Instrs\Numeric\I64ReinterpretF64::class => $this->execInstrNumericI64ReinterpretF64($instr), - Instrs\Numeric\I64RemS::class => $this->execInstrNumericI64RemS($instr), - Instrs\Numeric\I64RemU::class => $this->execInstrNumericI64RemU($instr), - Instrs\Numeric\I64RotL::class => $this->execInstrNumericI64RotL($instr), - Instrs\Numeric\I64RotR::class => $this->execInstrNumericI64RotR($instr), - Instrs\Numeric\I64Shl::class => $this->execInstrNumericI64Shl($instr), - Instrs\Numeric\I64ShrS::class => $this->execInstrNumericI64ShrS($instr), - Instrs\Numeric\I64ShrU::class => $this->execInstrNumericI64ShrU($instr), - Instrs\Numeric\I64Sub::class => $this->execInstrNumericI64Sub($instr), - Instrs\Numeric\I64TruncF32S::class => $this->execInstrNumericI64TruncF32S($instr), - Instrs\Numeric\I64TruncF32U::class => $this->execInstrNumericI64TruncF32U($instr), - Instrs\Numeric\I64TruncF64S::class => $this->execInstrNumericI64TruncF64S($instr), - Instrs\Numeric\I64TruncF64U::class => $this->execInstrNumericI64TruncF64U($instr), - Instrs\Numeric\I64TruncSatF32S::class => $this->execInstrNumericI64TruncSatF32S($instr), - Instrs\Numeric\I64TruncSatF32U::class => $this->execInstrNumericI64TruncSatF32U($instr), - Instrs\Numeric\I64TruncSatF64S::class => $this->execInstrNumericI64TruncSatF64S($instr), - Instrs\Numeric\I64TruncSatF64U::class => $this->execInstrNumericI64TruncSatF64U($instr), - Instrs\Numeric\I64Xor::class => $this->execInstrNumericI64Xor($instr), - Instrs\Reference\RefFunc::class => $this->execInstrReferenceRefFunc($instr), - Instrs\Reference\RefIsNull::class => $this->execInstrReferenceRefIsNull($instr), - Instrs\Reference\RefNull::class => $this->execInstrReferenceRefNull($instr), - Instrs\Parametric\Drop::class => $this->execInstrParametricDrop($instr), - Instrs\Parametric\Select::class => $this->execInstrParametricSelect($instr), - Instrs\Variable\GlobalGet::class => $this->execInstrVariableGlobalGet($instr), - Instrs\Variable\GlobalSet::class => $this->execInstrVariableGlobalSet($instr), - Instrs\Variable\LocalGet::class => $this->execInstrVariableLocalGet($instr), - Instrs\Variable\LocalSet::class => $this->execInstrVariableLocalSet($instr), - Instrs\Variable\LocalTee::class => $this->execInstrVariableLocalTee($instr), - Instrs\Table\ElemDrop::class => $this->execInstrTableElemDrop($instr), - Instrs\Table\TableCopy::class => $this->execInstrTableTableCopy($instr), - Instrs\Table\TableFill::class => $this->execInstrTableTableFill($instr), - Instrs\Table\TableGet::class => $this->execInstrTableTableGet($instr), - Instrs\Table\TableGrow::class => $this->execInstrTableTableGrow($instr), - Instrs\Table\TableInit::class => $this->execInstrTableTableInit($instr), - Instrs\Table\TableSet::class => $this->execInstrTableTableSet($instr), - Instrs\Table\TableSize::class => $this->execInstrTableTableSize($instr), - Instrs\Memory\DataDrop::class => $this->execInstrMemoryDataDrop($instr), - Instrs\Memory\F32Load::class => $this->execInstrMemoryF32Load($instr), - Instrs\Memory\F32Store::class => $this->execInstrMemoryF32Store($instr), - Instrs\Memory\F64Load::class => $this->execInstrMemoryF64Load($instr), - Instrs\Memory\F64Store::class => $this->execInstrMemoryF64Store($instr), - Instrs\Memory\I32Load::class => $this->execInstrMemoryI32Load($instr), - Instrs\Memory\I32Load16S::class => $this->execInstrMemoryI32Load16S($instr), - Instrs\Memory\I32Load16U::class => $this->execInstrMemoryI32Load16U($instr), - Instrs\Memory\I32Load8S::class => $this->execInstrMemoryI32Load8S($instr), - Instrs\Memory\I32Load8U::class => $this->execInstrMemoryI32Load8U($instr), - Instrs\Memory\I32Store::class => $this->execInstrMemoryI32Store($instr), - Instrs\Memory\I32Store16::class => $this->execInstrMemoryI32Store16($instr), - Instrs\Memory\I32Store8::class => $this->execInstrMemoryI32Store8($instr), - Instrs\Memory\I64Load::class => $this->execInstrMemoryI64Load($instr), - Instrs\Memory\I64Load16S::class => $this->execInstrMemoryI64Load16S($instr), - Instrs\Memory\I64Load16U::class => $this->execInstrMemoryI64Load16U($instr), - Instrs\Memory\I64Load32S::class => $this->execInstrMemoryI64Load32S($instr), - Instrs\Memory\I64Load32U::class => $this->execInstrMemoryI64Load32U($instr), - Instrs\Memory\I64Load8S::class => $this->execInstrMemoryI64Load8S($instr), - Instrs\Memory\I64Load8U::class => $this->execInstrMemoryI64Load8U($instr), - Instrs\Memory\I64Store::class => $this->execInstrMemoryI64Store($instr), - Instrs\Memory\I64Store16::class => $this->execInstrMemoryI64Store16($instr), - Instrs\Memory\I64Store32::class => $this->execInstrMemoryI64Store32($instr), - Instrs\Memory\I64Store8::class => $this->execInstrMemoryI64Store8($instr), - Instrs\Memory\MemoryCopy::class => $this->execInstrMemoryMemoryCopy($instr), - Instrs\Memory\MemoryFill::class => $this->execInstrMemoryMemoryFill($instr), - Instrs\Memory\MemoryGrow::class => $this->execInstrMemoryMemoryGrow($instr), - Instrs\Memory\MemoryInit::class => $this->execInstrMemoryMemoryInit($instr), - Instrs\Memory\MemorySize::class => $this->execInstrMemoryMemorySize($instr), - Instrs\Control\Block::class => $this->execInstrControlBlock($instr), - Instrs\Control\Br::class => $this->execInstrControlBr($instr), - Instrs\Control\BrIf::class => $this->execInstrControlBrIf($instr), - Instrs\Control\BrTable::class => $this->execInstrControlBrTable($instr), - Instrs\Control\Call::class => $this->execInstrControlCall($instr), - Instrs\Control\CallIndirect::class => $this->execInstrControlCallIndirect($instr), - Instrs\Control\Else_::class => $this->execInstrControlElse_($instr), - Instrs\Control\End::class => $this->execInstrControlEnd($instr), - Instrs\Control\If_::class => $this->execInstrControlIf_($instr), - Instrs\Control\Loop::class => $this->execInstrControlLoop($instr), - Instrs\Control\Nop::class => $this->execInstrControlNop($instr), - Instrs\Control\Return_::class => $this->execInstrControlReturn_($instr), - Instrs\Control\Unreachable::class => $this->execInstrControlUnreachable($instr), - default => throw new RuntimeException("invalid instruction"), - }; - } - - private function execInstrNumericF32Abs(Instrs\Numeric\F32Abs $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(abs($v)); - } - - private function execInstrNumericF32Add(Instrs\Numeric\F32Add $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::truncateF64ToF32($c1 + $c2)); - } - - private function execInstrNumericF32Ceil(Instrs\Numeric\F32Ceil $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(ceil($v)); - } - - private function execInstrNumericF32Const(Instrs\Numeric\F32Const $instr): void - { - $this->stack->pushValue($instr->value); - } - - private function execInstrNumericF32ConvertI32S(Instrs\Numeric\F32ConvertI32S $instr): void - { - $v = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); - } - - private function execInstrNumericF32ConvertI32U(Instrs\Numeric\F32ConvertI32U $instr): void - { - $v = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); - } - - private function execInstrNumericF32ConvertI64S(Instrs\Numeric\F32ConvertI64S $instr): void - { - $v = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); - } - - private function execInstrNumericF32ConvertI64U(Instrs\Numeric\F32ConvertI64U $instr): void - { - $v = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); - } - - private function execInstrNumericF32CopySign(Instrs\Numeric\F32CopySign $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $c1Sign = NumericOps::getFloatSign($c1); - $c2Sign = NumericOps::getFloatSign($c2); - $this->stack->pushValue($c1Sign === $c2Sign ? $c1 : -$c1); - } - - private function execInstrNumericF32DemoteF64(Instrs\Numeric\F32DemoteF64 $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue($v); - } - - private function execInstrNumericF32Div(Instrs\Numeric\F32Div $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::truncateF64ToF32(fdiv($c1, $c2))); - } - - private function execInstrNumericF32Eq(Instrs\Numeric\F32Eq $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 === $c2); - } - - private function execInstrNumericF32Floor(Instrs\Numeric\F32Floor $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(floor($v)); - } - - private function execInstrNumericF32Ge(Instrs\Numeric\F32Ge $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 >= $c2); - } - - private function execInstrNumericF32Gt(Instrs\Numeric\F32Gt $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 > $c2); - } - - private function execInstrNumericF32Le(Instrs\Numeric\F32Le $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 <= $c2); - } - - private function execInstrNumericF32Lt(Instrs\Numeric\F32Lt $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 < $c2); - } - - private function execInstrNumericF32Max(Instrs\Numeric\F32Max $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - if (is_nan($c1) || is_nan($c2)) { - // PHP's standard max() handles NaNs in diffrent way than WebAssembly spec does. - $this->stack->pushValue(NAN); - return; - } - $this->stack->pushValue(max($c1, $c2)); - } - - private function execInstrNumericF32Min(Instrs\Numeric\F32Min $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - if (is_nan($c1) || is_nan($c2)) { - // PHP's standard min() handles NaNs in diffrent way than WebAssembly spec does. - $this->stack->pushValue(NAN); - return; - } - $this->stack->pushValue(min($c1, $c2)); - } - - private function execInstrNumericF32Mul(Instrs\Numeric\F32Mul $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::truncateF64ToF32($c1 * $c2)); - } - - private function execInstrNumericF32Ne(Instrs\Numeric\F32Ne $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 !== $c2); - } - - private function execInstrNumericF32Nearest(Instrs\Numeric\F32Nearest $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(round($v, mode: PHP_ROUND_HALF_EVEN)); - } - - private function execInstrNumericF32Neg(Instrs\Numeric\F32Neg $instr): void - { - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(-$c1); - } - - private function execInstrNumericF32ReinterpretI32(Instrs\Numeric\F32ReinterpretI32 $instr): void - { - $v = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::reinterpretI32AsF32($v)); - } - - private function execInstrNumericF32ReinterpretI64(Instrs\Numeric\F32ReinterpretI64 $instr): void - { - $v = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::reinterpretI64AsF32($v)); - } - - private function execInstrNumericF32Sqrt(Instrs\Numeric\F32Sqrt $instr): void - { - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::truncateF64ToF32(sqrt($c1))); - } - - private function execInstrNumericF32Sub(Instrs\Numeric\F32Sub $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::truncateF64ToF32($c1 - $c2)); - } - - private function execInstrNumericF32Trunc(Instrs\Numeric\F32Trunc $instr): void - { - $v = $this->stack->popFloat(); - if ($v < 0) { - $this->stack->pushValue(NumericOps::truncateF64ToF32(ceil($v))); - } else { - $this->stack->pushValue(NumericOps::truncateF64ToF32(floor($v))); - } - } - - private function execInstrNumericF64Abs(Instrs\Numeric\F64Abs $instr): void - { - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(abs($c1)); - } - - private function execInstrNumericF64Add(Instrs\Numeric\F64Add $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue($c1 + $c2); - } - - private function execInstrNumericF64Ceil(Instrs\Numeric\F64Ceil $instr): void - { - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(ceil($c1)); - } - - private function execInstrNumericF64Const(Instrs\Numeric\F64Const $instr): void - { - $this->stack->pushValue($instr->value); - } - - private function execInstrNumericF64ConvertI32S(Instrs\Numeric\F64ConvertI32S $instr): void - { - $c = $this->stack->popInt(); - $this->stack->pushValue((float) $c); - } - - private function execInstrNumericF64ConvertI32U(Instrs\Numeric\F64ConvertI32U $instr): void - { - $c = $this->stack->popInt(); - $this->stack->pushValue((float) $c); - } - - private function execInstrNumericF64ConvertI64S(Instrs\Numeric\F64ConvertI64S $instr): void - { - $c = $this->stack->popInt(); - $this->stack->pushValue((float) $c); - } - - private function execInstrNumericF64ConvertI64U(Instrs\Numeric\F64ConvertI64U $instr): void - { - $c = $this->stack->popInt(); - $this->stack->pushValue((float) $c); - } - - private function execInstrNumericF64CopySign(Instrs\Numeric\F64CopySign $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $c1Sign = NumericOps::getFloatSign($c1); - $c2Sign = NumericOps::getFloatSign($c2); - $this->stack->pushValue($c1Sign === $c2Sign ? $c1 : -$c1); - } - - private function execInstrNumericF64Div(Instrs\Numeric\F64Div $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(fdiv($c1, $c2)); - } - - private function execInstrNumericF64Eq(Instrs\Numeric\F64Eq $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 === $c2); - } - - private function execInstrNumericF64Floor(Instrs\Numeric\F64Floor $instr): void - { - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(floor($c1)); - } - - private function execInstrNumericF64Ge(Instrs\Numeric\F64Ge $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 >= $c2); - } - - private function execInstrNumericF64Gt(Instrs\Numeric\F64Gt $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 > $c2); - } - - private function execInstrNumericF64Le(Instrs\Numeric\F64Le $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 <= $c2); - } - - private function execInstrNumericF64Lt(Instrs\Numeric\F64Lt $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 < $c2); - } - - private function execInstrNumericF64Max(Instrs\Numeric\F64Max $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - if (is_nan($c1) || is_nan($c2)) { - // PHP's standard max() handles NaNs in diffrent way than WebAssembly spec does. - $this->stack->pushValue(NAN); - return; - } - $this->stack->pushValue(max($c1, $c2)); - } - - private function execInstrNumericF64Min(Instrs\Numeric\F64Min $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - if (is_nan($c1) || is_nan($c2)) { - // PHP's standard min() handles NaNs in diffrent way than WebAssembly spec does. - $this->stack->pushValue(NAN); - return; - } - $this->stack->pushValue(min($c1, $c2)); - } - - private function execInstrNumericF64Mul(Instrs\Numeric\F64Mul $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue($c1 * $c2); - } - - private function execInstrNumericF64Ne(Instrs\Numeric\F64Ne $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushBool($c1 !== $c2); - } - - private function execInstrNumericF64Nearest(Instrs\Numeric\F64Nearest $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(round($v, mode: PHP_ROUND_HALF_EVEN)); - } - - private function execInstrNumericF64Neg(Instrs\Numeric\F64Neg $instr): void - { - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(-$c1); - } - - private function execInstrNumericF64PromoteF32(Instrs\Numeric\F64PromoteF32 $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue($v); - } - - private function execInstrNumericF64ReinterpretI32(Instrs\Numeric\F64ReinterpretI32 $instr): void - { - $v = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::reinterpretI32AsF64($v)); - } - - private function execInstrNumericF64ReinterpretI64(Instrs\Numeric\F64ReinterpretI64 $instr): void - { - $v = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::reinterpretI64AsF64($v)); - } - - private function execInstrNumericF64Sqrt(Instrs\Numeric\F64Sqrt $instr): void - { - $c1 = $this->stack->popFloat(); - $this->stack->pushValue(sqrt($c1)); - } - - private function execInstrNumericF64Sub(Instrs\Numeric\F64Sub $instr): void - { - $c2 = $this->stack->popFloat(); - $c1 = $this->stack->popFloat(); - $this->stack->pushValue($c1 - $c2); - } - - private function execInstrNumericF64Trunc(Instrs\Numeric\F64Trunc $instr): void - { - $v = $this->stack->popFloat(); - if ($v < 0) { - $this->stack->pushValue(ceil($v)); - } else { - $this->stack->pushValue(floor($v)); - } - } - - private function execInstrNumericI32Add(Instrs\Numeric\I32Add $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 + $c2) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32And(Instrs\Numeric\I32And $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 & $c2) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32Clz(Instrs\Numeric\I32Clz $instr): void - { - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $leadingZeros = 0; - for ($j = 31; 0 <= $j; $j--) { - if (($i & (1 << $j)) === 0) { - $leadingZeros++; - } else { - break; - } - } - $this->stack->pushValue($leadingZeros); - } - - private function execInstrNumericI32Const(Instrs\Numeric\I32Const $instr): void - { - $this->stack->pushValue($instr->value); - } - - private function execInstrNumericI32Ctz(Instrs\Numeric\I32Ctz $instr): void - { - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $trailingZeros = 0; - for ($j = 0; $j < 32; $j++) { - if (($i & (1 << $j)) === 0) { - $trailingZeros++; - } else { - break; - } - } - $this->stack->pushValue($trailingZeros); - } - - private function execInstrNumericI32DivS(Instrs\Numeric\I32DivS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - if ($c2 === 0) { - throw new TrapException("i32.div_s: divide by zero", trapKind: TrapKind::DivideByZero); - } - if ($c1 === -2147483648 && $c2 === -1) { - throw new TrapException("i32.div_s: overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue(intdiv($c1, $c2)); - } - - private function execInstrNumericI32DivU(Instrs\Numeric\I32DivU $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - if ($c2 === 0) { - throw new TrapException("i32.div_u: divide by zero", trapKind: TrapKind::DivideByZero); - } - $this->stack->pushValue(NumericOps::convertU32ToS32(intdiv($c1, $c2))); - } - - private function execInstrNumericI32Eq(Instrs\Numeric\I32Eq $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 === $c2); - } - - private function execInstrNumericI32Eqz(Instrs\Numeric\I32Eqz $instr): void - { - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 === 0); - } - - private function execInstrNumericI32Extend16S(Instrs\Numeric\I32Extend16S $instr): void - { - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c2 = $c1 & 0xFFFF; - $result = unpack('s', pack('S', $c2)); - assert($result !== false); - $this->stack->pushValue($result[1]); - } - - private function execInstrNumericI32Extend8S(Instrs\Numeric\I32Extend8S $instr): void - { - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c2 = $c1 & 0xFF; - $result = unpack('c', pack('C', $c2)); - assert($result !== false); - $this->stack->pushValue($result[1]); - } - - private function execInstrNumericI32GeS(Instrs\Numeric\I32GeS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 >= $c2); - } - - private function execInstrNumericI32GeU(Instrs\Numeric\I32GeU $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushBool($c1 >= $c2); - } - - private function execInstrNumericI32GtS(Instrs\Numeric\I32GtS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 > $c2); - } - - private function execInstrNumericI32GtU(Instrs\Numeric\I32GtU $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushBool($c1 > $c2); - } - - private function execInstrNumericI32LeS(Instrs\Numeric\I32LeS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 <= $c2); - } - - private function execInstrNumericI32LeU(Instrs\Numeric\I32LeU $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushBool($c1 <= $c2); - } - - private function execInstrNumericI32LtS(Instrs\Numeric\I32LtS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 < $c2); - } - - private function execInstrNumericI32LtU(Instrs\Numeric\I32LtU $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushBool($c1 < $c2); - } - - private function execInstrNumericI32Mul(Instrs\Numeric\I32Mul $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 * $c2) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32Ne(Instrs\Numeric\I32Ne $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 !== $c2); - } - - private function execInstrNumericI32Or(Instrs\Numeric\I32Or $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 | $c2) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32Popcnt(Instrs\Numeric\I32Popcnt $instr): void - { - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $popcnt = 0; - for ($j = 0; $j < 32; $j++) { - if (($i & (1 << $j)) !== 0) { - $popcnt++; - } - } - $this->stack->pushValue($popcnt); - } - - private function execInstrNumericI32ReinterpretF32(Instrs\Numeric\I32ReinterpretF32 $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::reinterpretF32AsI32($v)); - } - - private function execInstrNumericI32ReinterpretF64(Instrs\Numeric\I32ReinterpretF64 $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::reinterpretF64AsI32($v)); - } - - private function execInstrNumericI32RemS(Instrs\Numeric\I32RemS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - if ($c2 === 0) { - throw new TrapException("i32.rem_s: divide by zero or overflow", trapKind: TrapKind::DivideByZero); - } - $this->stack->pushValue($c1 % $c2); - } - - private function execInstrNumericI32RemU(Instrs\Numeric\I32RemU $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - if ($c2 === 0) { - throw new TrapException("i32.rem_u: divide by zero", trapKind: TrapKind::DivideByZero); - } - $this->stack->pushValue(NumericOps::convertU32ToS32($c1 % $c2)); - } - - private function execInstrNumericI32RotL(Instrs\Numeric\I32RotL $instr): void - { - $i2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $i1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $k = $i2 % 32; - $this->stack->pushValue(NumericOps::convertU32ToS32((($i1 << $k) | ($i1 >> (32 - $k))) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32RotR(Instrs\Numeric\I32RotR $instr): void - { - $i2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $i1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $k = $i2 % 32; - $this->stack->pushValue(NumericOps::convertU32ToS32((($i1 >> $k) | ($i1 << (32 - $k))) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32Shl(Instrs\Numeric\I32Shl $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $k = $c2 % 32; - $c1 = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 << $k) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32ShrS(Instrs\Numeric\I32ShrS $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $k = $c2 % 32; - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $signed = $c1 & 0x80000000; - if ($signed !== 0) { - $result = $c1; - for ($i = 0; $i < $k; $i++) { - $result = ($result >> 1) | 0x80000000; - } - $this->stack->pushValue(NumericOps::convertU32ToS32($result)); - } else { - $this->stack->pushValue($c1 >> $k); - } - } - - private function execInstrNumericI32ShrU(Instrs\Numeric\I32ShrU $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $k = $c2 % 32; - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushValue(NumericOps::convertU32ToS32($c1 >> $k)); - } - - private function execInstrNumericI32Sub(Instrs\Numeric\I32Sub $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c2Neg = ((~$c2 & 0xFFFFFFFF) + 1) & 0xFFFFFFFF; - $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 + $c2Neg) & 0xFFFFFFFF)); - } - - private function execInstrNumericI32TruncF32S(Instrs\Numeric\I32TruncF32S $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -2147483649.0 || 2147483648.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue((int) $v); - } - - private function execInstrNumericI32TruncF32U(Instrs\Numeric\I32TruncF32U $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -1.0 || 4294967296.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue(NumericOps::convertU32ToS32((int) $v)); - } - - private function execInstrNumericI32TruncF64S(Instrs\Numeric\I32TruncF64S $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -2147483649.0 || 2147483648.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue((int) $v); - } - - private function execInstrNumericI32TruncF64U(Instrs\Numeric\I32TruncF64U $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -1.0 || 4294967296.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue(NumericOps::convertU32ToS32((int) $v)); - } - - private function execInstrNumericI32TruncSatF32S(Instrs\Numeric\I32TruncSatF32S $instr): void - { - $v = $this->stack->popFloat(); - if ($v < -2147483648.0) { - $this->stack->pushValue(-2147483648); - } elseif ($v > 2147483647.0) { - $this->stack->pushValue(2147483647); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI32TruncSatF32U(Instrs\Numeric\I32TruncSatF32U $instr): void - { - $v = $this->stack->popFloat(); - if ($v < 0.0) { - $this->stack->pushValue(0); - } elseif ($v > 4294967295.0) { - $this->stack->pushValue(4294967295); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI32TruncSatF64S(Instrs\Numeric\I32TruncSatF64S $instr): void - { - $v = $this->stack->popFloat(); - if ($v < -2147483648.0) { - $this->stack->pushValue(-2147483648); - } elseif ($v > 2147483647.0) { - $this->stack->pushValue(2147483647); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI32TruncSatF64U(Instrs\Numeric\I32TruncSatF64U $instr): void - { - $v = $this->stack->popFloat(); - if ($v < 0.0) { - $this->stack->pushValue(0); - } elseif ($v > 4294967295.0) { - $this->stack->pushValue(4294967295); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI32WrapI64(Instrs\Numeric\I32WrapI64 $instr): void - { - $c1 = $this->stack->popInt(); - $this->stack->pushValue(NumericOps::convertU32ToS32($c1 & 0xFFFFFFFF)); - } - - private function execInstrNumericI32Xor(Instrs\Numeric\I32Xor $instr): void - { - $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 ^ $c2) & 0xFFFFFFFF)); - } - - private function execInstrNumericI64Add(Instrs\Numeric\I64Add $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $result = NumericOps::bigIntToPhpInt(bcadd((string)$c1, (string)$c2)); - $this->stack->pushValue($result); - } - - private function execInstrNumericI64And(Instrs\Numeric\I64And $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushValue($c1 & $c2); - } - - private function execInstrNumericI64Clz(Instrs\Numeric\I64Clz $instr): void - { - $i = $this->stack->popInt(); - $leadingZeros = 0; - for ($j = 63; 0 <= $j; $j--) { - if ($j === 63) { - if ($i < 0) { - break; - } else { - $leadingZeros++; - } - } else { - if (($i & (1 << $j)) === 0) { - $leadingZeros++; - } else { - break; - } - } - } - $this->stack->pushValue($leadingZeros); - } - - private function execInstrNumericI64Const(Instrs\Numeric\I64Const $instr): void - { - $this->stack->pushValue($instr->value); - } - - private function execInstrNumericI64Ctz(Instrs\Numeric\I64Ctz $instr): void - { - $i = $this->stack->popInt(); - $trailingZeros = 0; - for ($j = 0; $j < 64; $j++) { - if ($j === 63) { - if ($i >= 0) { - $trailingZeros++; - } - } else { - if (($i & (1 << $j)) === 0) { - $trailingZeros++; - } else { - break; - } - } - } - $this->stack->pushValue($trailingZeros); - } - - private function execInstrNumericI64DivS(Instrs\Numeric\I64DivS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - if ($c2 === 0) { - throw new TrapException("i64.div_s: divide by zero", trapKind: TrapKind::DivideByZero); - } - if ($c1 === PHP_INT_MIN && $c2 === -1) { - throw new TrapException("i64.div_s: overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue(intdiv($c1, $c2)); - } - - private function execInstrNumericI64DivU(Instrs\Numeric\I64DivU $instr): void - { - $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - $c1 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - if ($c2 === '0') { - throw new TrapException("i64.div_u: divide by zero", trapKind: TrapKind::DivideByZero); - } - $this->stack->pushValue(NumericOps::bigIntToPhpInt(bcdiv($c1, $c2, 0))); - } - - private function execInstrNumericI64Eq(Instrs\Numeric\I64Eq $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 === $c2); - } - - private function execInstrNumericI64Eqz(Instrs\Numeric\I64Eqz $instr): void - { - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 === 0); - } - - private function execInstrNumericI64Extend16S(Instrs\Numeric\I64Extend16S $instr): void - { - $c1 = $this->stack->popInt(); - $c2 = $c1 & 0xFFFF; - $result = unpack('s', pack('S', $c2)); - assert($result !== false); - $this->stack->pushValue($result[1]); - } - - private function execInstrNumericI64Extend32S(Instrs\Numeric\I64Extend32S $instr): void - { - $c1 = $this->stack->popInt(); - $c2 = $c1 & 0xFFFFFFFF; - $result = unpack('l', pack('L', $c2)); - assert($result !== false); - $this->stack->pushValue($result[1]); - } - - private function execInstrNumericI64Extend8S(Instrs\Numeric\I64Extend8S $instr): void - { - $c1 = $this->stack->popInt(); - $c2 = $c1 & 0xFF; - $result = unpack('c', pack('C', $c2)); - assert($result !== false); - $this->stack->pushValue($result[1]); - } - - private function execInstrNumericI64ExtendI32S(Instrs\Numeric\I64ExtendI32S $instr): void - { - $c1 = $this->stack->popInt(); - $this->stack->pushValue($c1); - } - - private function execInstrNumericI64ExtendI32U(Instrs\Numeric\I64ExtendI32U $instr): void - { - $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); - $c2 = $c1 & 0xFFFFFFFF; - $this->stack->pushValue($c2); - } - - private function execInstrNumericI64GeS(Instrs\Numeric\I64GeS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 >= $c2); - } - - private function execInstrNumericI64GeU(Instrs\Numeric\I64GeU $instr): void - { - $c2 = $this->stack->popInt(); - $c2Packed = pack('J', $c2); - $c1 = $this->stack->popInt(); - $c1Packed = pack('J', $c1); - $this->stack->pushBool($c1Packed >= $c2Packed); - } - - private function execInstrNumericI64GtS(Instrs\Numeric\I64GtS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 > $c2); - } - - private function execInstrNumericI64GtU(Instrs\Numeric\I64GtU $instr): void - { - $c2 = $this->stack->popInt(); - $c2Packed = pack('J', $c2); - $c1 = $this->stack->popInt(); - $c1Packed = pack('J', $c1); - $this->stack->pushBool($c1Packed > $c2Packed); - } - - private function execInstrNumericI64LeS(Instrs\Numeric\I64LeS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 <= $c2); - } - - private function execInstrNumericI64LeU(Instrs\Numeric\I64LeU $instr): void - { - $c2 = $this->stack->popInt(); - $c2Packed = pack('J', $c2); - $c1 = $this->stack->popInt(); - $c1Packed = pack('J', $c1); - $this->stack->pushBool($c1Packed <= $c2Packed); - } - - private function execInstrNumericI64LtS(Instrs\Numeric\I64LtS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 < $c2); - } - - private function execInstrNumericI64LtU(Instrs\Numeric\I64LtU $instr): void - { - $c2 = $this->stack->popInt(); - $c2Packed = pack('J', $c2); - $c1 = $this->stack->popInt(); - $c1Packed = pack('J', $c1); - $this->stack->pushBool($c1Packed < $c2Packed); - } - - private function execInstrNumericI64Mul(Instrs\Numeric\I64Mul $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $result = NumericOps::bigIntToPhpInt(bcmul((string)$c1, (string)$c2)); - $this->stack->pushValue($result); - } - - private function execInstrNumericI64Ne(Instrs\Numeric\I64Ne $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushBool($c1 !== $c2); - } - - private function execInstrNumericI64Or(Instrs\Numeric\I64Or $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushValue($c1 | $c2); - } - - private function execInstrNumericI64Popcnt(Instrs\Numeric\I64Popcnt $instr): void - { - $i = $this->stack->popInt(); - $popcnt = 0; - for ($j = 0; $j < 64; $j++) { - if (($i & (1 << $j)) !== 0) { - $popcnt++; - } - } - $this->stack->pushValue($popcnt); - } - - private function execInstrNumericI64ReinterpretF32(Instrs\Numeric\I64ReinterpretF32 $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::reinterpretF32AsI64($v)); - } - - private function execInstrNumericI64ReinterpretF64(Instrs\Numeric\I64ReinterpretF64 $instr): void - { - $v = $this->stack->popFloat(); - $this->stack->pushValue(NumericOps::reinterpretF64AsI64($v)); - } - - private function execInstrNumericI64RemS(Instrs\Numeric\I64RemS $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - if ($c2 === 0) { - throw new TrapException("i64.rem_s: divide by zero", trapKind: TrapKind::DivideByZero); - } - $this->stack->pushValue($c1 % $c2); - } - - private function execInstrNumericI64RemU(Instrs\Numeric\I64RemU $instr): void - { - $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - $c1 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - if ($c2 === '0') { - throw new TrapException("i64.rem_u: divide by zero", trapKind: TrapKind::DivideByZero); - } - $this->stack->pushValue(NumericOps::bigIntToPhpInt(bcmod($c1, $c2, 0))); - } - - private function execInstrNumericI64RotL(Instrs\Numeric\I64RotL $instr): void - { - $i2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - $k = (int)bcmod($i2, '64'); - $i1 = $this->stack->popInt(); - $left = $i1 << $k; - $right = $i1; - for ($i = 0; $i < 64 - $k; $i++) { - $right = ($right >> 1) & 0x7FFFFFFFFFFFFFFF; - } - $this->stack->pushValue($left | $right); - } - - private function execInstrNumericI64RotR(Instrs\Numeric\I64RotR $instr): void - { - $i2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - $k = (int)bcmod($i2, '64'); - $i1 = $this->stack->popInt(); - $left = $i1; - for ($i = 0; $i < $k; $i++) { - $left = ($left >> 1) & 0x7FFFFFFFFFFFFFFF; - } - $right = $i1 << (64 - $k); - $this->stack->pushValue($left | $right); - } - - private function execInstrNumericI64Shl(Instrs\Numeric\I64Shl $instr): void - { - $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - $k = (int)bcmod($c2, '64'); - $c1 = $this->stack->popInt(); - $this->stack->pushValue($c1 << $k); - } - - private function execInstrNumericI64ShrS(Instrs\Numeric\I64ShrS $instr): void - { - $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - $k = (int)bcmod($c2, '64'); - $c1 = $this->stack->popInt(); - $this->stack->pushValue($c1 >> $k); - } - - private function execInstrNumericI64ShrU(Instrs\Numeric\I64ShrU $instr): void - { - $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); - $k = (int)bcmod($c2, '64'); - if ($k === 0) { - return; - } - // Perform shr_u based on string manipulation because PHP does not - // support shr_u operation. - $c1 = $this->stack->popInt(); - $this->stack->pushValue(bindec(substr(decbin($c1), 0, -$k))); - } - - private function execInstrNumericI64Sub(Instrs\Numeric\I64Sub $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $result = NumericOps::bigIntToPhpInt(bcsub((string)$c1, (string)$c2)); - $this->stack->pushValue($result); - } - - private function execInstrNumericI64TruncF32S(Instrs\Numeric\I64TruncF32S $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion ($v)", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow ($v)", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -9223372036854775809.0 || 9223372036854775808.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow ($v)", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue((int) $v); - } - - private function execInstrNumericI64TruncF32U(Instrs\Numeric\I64TruncF32U $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -1.0 || 18446744073709551616.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue((int) $v); - } - - private function execInstrNumericI64TruncF64S(Instrs\Numeric\I64TruncF64S $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -9223372036854775809.0 || 9223372036854775808.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue((int) $v); - } - - private function execInstrNumericI64TruncF64U(Instrs\Numeric\I64TruncF64U $instr): void - { - $v = $this->stack->popFloat(); - if (is_nan($v)) { - throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); - } - if (is_infinite($v)) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - if ($v <= -1.0 || 18446744073709551616.0 <= $v) { - throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); - } - $this->stack->pushValue((int) $v); - } - - private function execInstrNumericI64TruncSatF32S(Instrs\Numeric\I64TruncSatF32S $instr): void - { - $v = $this->stack->popFloat(); - if ($v < -9223372036854775808.0) { - $this->stack->pushValue(-9223372036854775808); - } elseif ($v > 9223372036854775807.0) { - $this->stack->pushValue(9223372036854775807); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI64TruncSatF32U(Instrs\Numeric\I64TruncSatF32U $instr): void - { - $v = $this->stack->popFloat(); - if ($v < 0.0) { - $this->stack->pushValue(0); - } elseif ($v > 18446744073709551615.0) { - $this->stack->pushValue(18446744073709551615); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI64TruncSatF64S(Instrs\Numeric\I64TruncSatF64S $instr): void - { - $v = $this->stack->popFloat(); - if ($v < -9223372036854775808.0) { - $this->stack->pushValue(-9223372036854775808); - } elseif ($v > 9223372036854775807.0) { - $this->stack->pushValue(9223372036854775807); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI64TruncSatF64U(Instrs\Numeric\I64TruncSatF64U $instr): void - { - $v = $this->stack->popFloat(); - if ($v < 0.0) { - $this->stack->pushValue(0); - } elseif ($v > 18446744073709551615.0) { - $this->stack->pushValue(18446744073709551615); - } else { - $this->stack->pushValue((int) $v); - } - } - - private function execInstrNumericI64Xor(Instrs\Numeric\I64Xor $instr): void - { - $c2 = $this->stack->popInt(); - $c1 = $this->stack->popInt(); - $this->stack->pushValue($c1 ^ $c2); - } - - private function execInstrReferenceRefFunc(Instrs\Reference\RefFunc $instr): void - { - $x = $instr->func; - $f = $this->stack->currentFrame(); - $a = $f->module->funcAddrs[$x]; - $this->stack->pushRefFunc($a); - } - - private function execInstrReferenceRefIsNull(Instrs\Reference\RefIsNull $instr): void - { - $val = $this->stack->popRef(); - $this->stack->pushBool($val instanceof Refs\RefNull); - } - - private function execInstrReferenceRefNull(Instrs\Reference\RefNull $instr): void - { - $t = $instr->type; - $this->stack->pushRefNull($t); - } - - private function execInstrParametricDrop(Instrs\Parametric\Drop $instr): void - { - $this->stack->popValue(); - } - - private function execInstrParametricSelect(Instrs\Parametric\Select $instr): void - { - $c = $this->stack->popInt(); - $val2 = $this->stack->popValue(); - $val1 = $this->stack->popValue(); - if ($c !== 0) { - $this->stack->pushValue($val1); - } else { - $this->stack->pushValue($val2); - } - } - - private function execInstrVariableGlobalGet(Instrs\Variable\GlobalGet $instr): void - { - $x = $instr->var; - $f = $this->stack->currentFrame(); - $a = $f->module->globalAddrs[$x]; - $glob = $this->store->globals[$a]; - $val = $glob->value; - $this->stack->pushValue($val); - } - - private function execInstrVariableGlobalSet(Instrs\Variable\GlobalSet $instr): void - { - $x = $instr->var; - $f = $this->stack->currentFrame(); - $a = $f->module->globalAddrs[$x]; - $glob = $this->store->globals[$a]; - $val = $this->stack->popValue(); - $glob->value = $val; - } - - private function execInstrVariableLocalGet(Instrs\Variable\LocalGet $instr): void - { - $x = $instr->var; - $f = $this->stack->currentFrame(); - $val = $f->locals[$x] ?? null; - if ($val === null) { - throw new RuntimeException("local.get: local $x not found in [$f->debugName]"); - } - $this->stack->pushValue($val); - } - - private function execInstrVariableLocalSet(Instrs\Variable\LocalSet $instr): void - { - $x = $instr->var; - $f = $this->stack->currentFrame(); - $val = $this->stack->popValue(); - // @phpstan-ignore-next-line - $f->locals[$x] = $val; - } - - private function execInstrVariableLocalTee(Instrs\Variable\LocalTee $instr): void - { - $x = $instr->var; - $f = $this->stack->currentFrame(); - $val = $this->stack->popValue(); - // @phpstan-ignore-next-line - $f->locals[$x] = $val; - $this->stack->pushValue($val); - } - - private function execInstrTableElemDrop(Instrs\Table\ElemDrop $instr): void - { - $x = $instr->elem; - $f = $this->stack->currentFrame(); - $a = $f->module->elemAddrs[$x]; - $elem = $this->store->elems[$a]; - // @phpstan-ignore-next-line - $this->store->elems[$a] = new ElemInst($elem->type, []); - } - - private function execInstrTableTableCopy(Instrs\Table\TableCopy $instr): void - { - $x = $instr->to; - $y = $instr->from; - $f = $this->stack->currentFrame(); - $taX = $f->module->tableAddrs[$x]; - $tabX = $this->store->tables[$taX]; - $taY = $f->module->tableAddrs[$y]; - $tabY = $this->store->tables[$taY]; - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $s = NumericOps::convertS32ToU32($this->stack->popInt()); - $d = NumericOps::convertS32ToU32($this->stack->popInt()); - if (count($tabX->elem) < $d + $n || count($tabY->elem) < $s + $n) { - throw new TrapException("table.copy: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); - } - if ($n === 0 || ($x === $y && $d === $s)) { - return; - } - for ($i = 0; $i < $n; $i++) { - $s_ = ($d < $s) ? ($s + $i) : ($s + $n - 1 - $i); - $d_ = ($d < $s) ? ($d + $i) : ($d + $n - 1 - $i); - // @phpstan-ignore-next-line - $tabX->elem[$d_] = $tabY->elem[$s_]; - } - } - - private function execInstrTableTableFill(Instrs\Table\TableFill $instr): void - { - $x = $instr->table; - $f = $this->stack->currentFrame(); - $ta = $f->module->tableAddrs[$x]; - $tab = $this->store->tables[$ta]; - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $val = $this->stack->popRef(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - if (count($tab->elem) < $i + $n) { - throw new TrapException("table.fill: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); - } - for ($k = 0; $k < $n; $k++) { - // @phpstan-ignore-next-line - $tab->elem[$i + $k] = $val; - } - } - - private function execInstrTableTableGet(Instrs\Table\TableGet $instr): void - { - $x = $instr->table; - $f = $this->stack->currentFrame(); - $a = $f->module->tableAddrs[$x]; - $tab = $this->store->tables[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - if (count($tab->elem) <= $i) { - throw new TrapException("table.get: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); - } - $val = $tab->elem[$i]; - $this->stack->pushValue($val); - } - - private function execInstrTableTableGrow(Instrs\Table\TableGrow $instr): void - { - $x = $instr->table; - $f = $this->stack->currentFrame(); - $a = $f->module->tableAddrs[$x]; - $tab = $this->store->tables[$a]; - $sz = count($tab->elem); - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $val = $this->stack->popRef(); - - $len = $sz + $n; - if ((1 << 32) <= $len) { - $this->stack->pushValue(-1); - return; - } - - $limits = $tab->type->limits; - $limits_ = new Limits($len, $limits->max); - if (!$limits_->isValid()) { - $this->stack->pushValue(-1); - return; - } - - for ($i = 0; $i < $n; $i++) { - $tab->elem[] = $val; - } - $tab->type = new TableType($limits_, $tab->type->refType); - - $this->stack->pushValue($sz); - } - - private function execInstrTableTableInit(Instrs\Table\TableInit $instr): void - { - $x = $instr->to; - $y = $instr->from; - $f = $this->stack->currentFrame(); - $ta = $f->module->tableAddrs[$x]; - $tab = $this->store->tables[$ta]; - $ea = $f->module->elemAddrs[$y]; - $elem = $this->store->elems[$ea]; - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $s = NumericOps::convertS32ToU32($this->stack->popInt()); - $d = NumericOps::convertS32ToU32($this->stack->popInt()); - if (count($elem->elem) < $s + $n) { - throw new TrapException("table.init: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); - } - if (count($tab->elem) < $d + $n) { - throw new TrapException("table.init: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); - } - for ($i = 0; $i < $n; $i++) { - // @phpstan-ignore-next-line - $tab->elem[$d + $i] = $elem->elem[$s + $i]; - } - } - - private function execInstrTableTableSet(Instrs\Table\TableSet $instr): void - { - $x = $instr->table; - $f = $this->stack->currentFrame(); - $a = $f->module->tableAddrs[$x]; - $tab = $this->store->tables[$a]; - $val = $this->stack->popRef(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - if (count($tab->elem) <= $i) { - throw new TrapException("table.set: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); - } - // @phpstan-ignore-next-line - $tab->elem[$i] = $val; - } - - private function execInstrTableTableSize(Instrs\Table\TableSize $instr): void - { - $x = $instr->table; - $f = $this->stack->currentFrame(); - $a = $f->module->tableAddrs[$x]; - $tab = $this->store->tables[$a]; - $sz = count($tab->elem); - $this->stack->pushValue($sz); - } - - private function execInstrMemoryDataDrop(Instrs\Memory\DataDrop $instr): void - { - $x = $instr->data; - $f = $this->stack->currentFrame(); - $a = $f->module->dataAddrs[$x]; - // @phpstan-ignore-next-line - $this->store->datas[$a] = new DataInst([]); - } - - private function execInstrMemoryF32Load(Instrs\Memory\F32Load $instr): void - { - $this->doLoadF32($instr->offset, $instr::opName()); - } - - private function execInstrMemoryF32Store(Instrs\Memory\F32Store $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popFloat(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeF32($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryF64Load(Instrs\Memory\F64Load $instr): void - { - $this->doLoadF64($instr->offset, $instr::opName()); - } - - private function execInstrMemoryF64Store(Instrs\Memory\F64Store $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popFloat(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeF64($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryI32Load(Instrs\Memory\I32Load $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI32_s32($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI32Load16S(Instrs\Memory\I32Load16S $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI32_s16($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI32Load16U(Instrs\Memory\I32Load16U $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI32_u16($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI32Load8S(Instrs\Memory\I32Load8S $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI32_s8($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI32Load8U(Instrs\Memory\I32Load8U $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI32_u8($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI32Store(Instrs\Memory\I32Store $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popInt(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeI32_s32($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryI32Store16(Instrs\Memory\I32Store16 $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popInt(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeI32_s16($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryI32Store8(Instrs\Memory\I32Store8 $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popInt(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeI32_s8($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryI64Load(Instrs\Memory\I64Load $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI64_s64($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI64Load16S(Instrs\Memory\I64Load16S $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI64_s16($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI64Load16U(Instrs\Memory\I64Load16U $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI64_u16($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI64Load32S(Instrs\Memory\I64Load32S $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI64_s32($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI64Load32U(Instrs\Memory\I64Load32U $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI64_u32($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI64Load8S(Instrs\Memory\I64Load8S $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI64_s8($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI64Load8U(Instrs\Memory\I64Load8U $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadI64_u8($ea); - if ($c === null) { - throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function execInstrMemoryI64Store(Instrs\Memory\I64Store $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popInt(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeI64_s64($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryI64Store16(Instrs\Memory\I64Store16 $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popInt(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeI64_s16($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryI64Store32(Instrs\Memory\I64Store32 $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popInt(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeI64_s32($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryI64Store8(Instrs\Memory\I64Store8 $instr): void - { - $offset = $instr->offset; - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $c = $this->stack->popInt(); - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $ok = $mem->storeI64_s8($ea, $c); - if (!$ok) { - throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - } - - private function execInstrMemoryMemoryCopy(Instrs\Memory\MemoryCopy $instr): void - { - $f = $this->stack->currentFrame(); - $ma = $f->module->memAddrs[0]; - $mem = $this->store->mems[$ma]; - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $s = NumericOps::convertS32ToU32($this->stack->popInt()); - $d = NumericOps::convertS32ToU32($this->stack->popInt()); - if ($mem->size() < $s + $n || $mem->size() < $d + $n) { - throw new TrapException("memory.copy: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $mem->memcpy($d, $s, $n); - } - - private function execInstrMemoryMemoryFill(Instrs\Memory\MemoryFill $instr): void - { - $f = $this->stack->currentFrame(); - $ma = $f->module->memAddrs[0]; - $mem = $this->store->mems[$ma]; - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $val = $this->stack->popInt(); - $d = NumericOps::convertS32ToU32($this->stack->popInt()); - if ($mem->size() < $d + $n) { - throw new TrapException("memory.fill: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $mem->memset($d, $val, $n); - } - - private function execInstrMemoryMemoryGrow(Instrs\Memory\MemoryGrow $instr): void - { - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $result = $mem->grow($n); - $this->stack->pushValue($result); - } - - private function execInstrMemoryMemoryInit(Instrs\Memory\MemoryInit $instr): void - { - $x = $instr->data; - $f = $this->stack->currentFrame(); - $ma = $f->module->memAddrs[0]; - $mem = $this->store->mems[$ma]; - $da = $f->module->dataAddrs[$x]; - $data = $this->store->datas[$da]; - $n = NumericOps::convertS32ToU32($this->stack->popInt()); - $s = NumericOps::convertS32ToU32($this->stack->popInt()); - $d = NumericOps::convertS32ToU32($this->stack->popInt()); - if (count($data->data) < $s + $n) { - throw new TrapException("memory.init: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - if ($mem->size() < $d + $n) { - throw new TrapException("memory.init: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $mem->copyData($data->data, $s, $d, $n); - } - - private function execInstrMemoryMemorySize(Instrs\Memory\MemorySize $instr): void - { - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $szInByte = $mem->size(); - assert(is_int($szInByte / (64 * 1024))); - $sz = $szInByte / (64 * 1024); - $this->stack->pushValue($sz); - } - - private function execInstrControlBlock(Instrs\Control\Block $instr): ?int - { - $blockType = $instr->type; - $instrs = $instr->body; - $f = $this->stack->currentFrame(); - $bt = self::expandBlockType($blockType, $f->module); - $params = array_reverse($this->stack->popNValues(count($bt->params->types))); - $n = count($bt->results->types); - $l = new Label($n); - $result = $this->execInstrs($instrs, $l, $params); - if ($result === null) { - // Do nothing. - } elseif ($result === -1) { - return -1; - } elseif ($result === 0) { - $this->deactivateLabel($n); - } else { - $this->deactivateLabel(null); - return $result - 1; - } - return null; - } - - private function execInstrControlBr(Instrs\Control\Br $instr): int - { - return $instr->label; - } - - private function execInstrControlBrIf(Instrs\Control\BrIf $instr): ?int - { - $l = $instr->label; - $c = $this->stack->popInt(); - if ($c !== 0) { - return $l; - } else { - return null; - } - } - - private function execInstrControlBrTable(Instrs\Control\BrTable $instr): int - { - $ls = $instr->labelTable; - $ln = $instr->defaultLabel; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - if ($i < count($ls)) { - return $ls[$i]; - } else { - return $ln; - } - } - - private function execInstrControlCall(Instrs\Control\Call $instr): void - { - $x = $instr->func; - $f = $this->stack->currentFrame(); - $a = $f->module->funcAddrs[$x]; - $this->doInvokeFunc($a); - } - - private function execInstrControlCallIndirect(Instrs\Control\CallIndirect $instr): void - { - $x = $instr->funcTable; - $y = $instr->type; - $f = $this->stack->currentFrame(); - $ta = $f->module->tableAddrs[$x]; - $tab = $this->store->tables[$ta]; - $ftExpect = $f->module->types[$y]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - if (count($tab->elem) <= $i) { - throw new TrapException("call_indirect: out of bounds", trapKind: TrapKind::UndefinedElement); - } - $r = $tab->elem[$i]; - if ($r instanceof Refs\RefNull) { - throw new TrapException("call_indirect: ref.null", trapKind: TrapKind::UninitializedElement); - } - assert($r instanceof Refs\RefFunc); - $a = $r->addr; - $fn = $this->store->funcs[$a]; - assert($fn instanceof FuncInsts\Wasm || $fn instanceof FuncInsts\Host); - $ftActual = $fn->type; - if (!$ftExpect->equals($ftActual)) { - throw new TrapException("call_indirect: type mismatch", trapKind: TrapKind::IndirectCallTypeMismatch); - } - $this->doInvokeFunc($a); - } - - private function execInstrControlElse_(Instrs\Control\Else_ $instr): void - { - // Do nothing. - } - - private function execInstrControlEnd(Instrs\Control\End $instr): void - { - // Do nothing. - } - - private function execInstrControlIf_(Instrs\Control\If_ $instr): ?int - { - $blockType = $instr->type; - $instrs1 = $instr->thenBody; - $instrs2 = $instr->elseBody; - $c = $this->stack->popInt(); - if ($c !== 0) { - return $this->execInstr(Instr::Block($blockType, $instrs1)); - } else { - return $this->execInstr(Instr::Block($blockType, $instrs2)); - } - } - - private function execInstrControlLoop(Instrs\Control\Loop $instr): ?int - { - $blockType = $instr->type; - $instrs = $instr->body; - $f = $this->stack->currentFrame(); - $bt = self::expandBlockType($blockType, $f->module); - $m = count($bt->params->types); - $l = new Label($m); - while (true) { - $params = array_reverse($this->stack->popNValues($m)); - $result = $this->execInstrs($instrs, $l, $params); - if ($result === null) { - return null; - } elseif ($result === -1) { - return -1; - } elseif ($result === 0) { - $this->deactivateLabel($m); - continue; - } else { - $this->deactivateLabel(null); - return $result - 1; - } - } - } - - private function execInstrControlNop(Instrs\Control\Nop $instr): void - { - // Do nothing. - } - - private function execInstrControlReturn_(Instrs\Control\Return_ $instr): int - { - return -1; - } - - private function execInstrControlUnreachable(Instrs\Control\Unreachable $instr): void - { - throw new TrapException("unreachable", trapKind: TrapKind::Unreachable); - } - - private function doLoadF32(int $offset, string $instrOpName): void - { - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadF32($ea); - if ($c === null) { - throw new TrapException("$instrOpName: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private function doLoadF64(int $offset, string $instrOpName): void - { - $f = $this->stack->currentFrame(); - $a = $f->module->memAddrs[0]; - $mem = $this->store->mems[$a]; - $i = NumericOps::convertS32ToU32($this->stack->popInt()); - $ea = $i + $offset; - $c = $mem->loadF64($ea); - if ($c === null) { - throw new TrapException("$instrOpName: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); - } - $this->stack->pushValue($c); - } - - private static function defaultValueFromValType(ValType $type): int|float|Ref - { - return match ($type::class) { - ValTypes\NumType::class => match ($type->inner) { - NumType::I32 => 0, - NumType::I64 => 0, - NumType::F32 => 0.0, - NumType::F64 => 0.0, - }, - ValTypes\RefType::class => Ref::RefNull($type->inner), - default => throw new RuntimeException("unreachable"), - }; - } - - private static function expandBlockType(BlockType $bt, ModuleInst $module): FuncType - { - if ($bt instanceof BlockTypes\TypeIdx) { - return $module->types[$bt->inner]; - } elseif ($bt instanceof BlockTypes\ValType) { - $t = $bt->inner; - return new FuncType( - new ResultType([]), - new ResultType($t === null ? [] : [$t]), - ); - } else { - throw new RuntimeException("expand(): invalid blocktype"); - } - } -} diff --git a/src/Execution/Stack.php b/src/Execution/Stack.php deleted file mode 100644 index 1e32388..0000000 --- a/src/Execution/Stack.php +++ /dev/null @@ -1,212 +0,0 @@ - - */ - private array $frames = []; - - private ?Frame $currentFrame = null; - - /** - * @var list - */ - private array $entries; - - public function __construct() - { - } - - public function pushFrame(Frame $frame): void - { - if ($this->getCallStackLimit() <= count($this->frames)) { - throw new StackOverflowException(); - } - $this->push($frame); - $this->frames[] = $frame; - $this->currentFrame = $frame; - } - - public function pushLabel(Label $label): void - { - $this->push($label); - } - - public function pushValue(int|float|Ref $val): void - { - $this->push($val); - } - - public function pushBool(bool $value): void - { - $this->pushValue((int)$value); - } - - public function pushRefNull(RefType $type): void - { - $this->pushValue(Ref::RefNull($type)); - } - - public function pushRefFunc(int $addr): void - { - $this->pushValue(Ref::RefFunc($addr)); - } - - public function pushRefExtern(int $addr): void - { - $this->pushValue(Ref::RefExtern($addr)); - } - - public function clear(): void - { - $this->frames = []; - $this->currentFrame = null; - $this->entries = []; - } - - public function popFrame(): Frame - { - $result = $this->pop(); - assert($result instanceof Frame); - array_pop($this->frames); - if (count($this->frames) === 0) { - $this->currentFrame = null; - } else { - $this->currentFrame = end($this->frames); - } - return $result; - } - - public function popValue(): int|float|Ref - { - $result = $this->pop(); - assert( - is_int($result) || is_float($result) || $result instanceof Ref, - 'Expected a value on the stack, but got ' . print_r($result, true), - ); - return $result; - } - - /** - * @return list - */ - public function popNValues(int $n): array - { - $results = []; - for ($i = 0; $i < $n; $i++) { - $results[] = $this->popValue(); - } - return $results; - } - - public function popInt(): int - { - $v = $this->popValue(); - assert(is_int($v), "Expected an int on top of the stack, but got " . self::getValueTypeName($v)); - return $v; - } - - /** - * @return F32 - */ - public function popFloat(): float - { - $v = $this->popValue(); - assert(is_float($v), "Expected a float on top of the stack, but got " . self::getValueTypeName($v)); - return $v; - } - - public function popRef(): Ref - { - $v = $this->popValue(); - assert($v instanceof Ref, "Expected a Ref on top of the stack, but got " . self::getValueTypeName($v)); - return $v; - } - - /** - * @return list - */ - public function popValuesToLabel(): array - { - $results = []; - while (!$this->isEmpty()) { - $top = $this->pop(); - if ($top instanceof Label) { - break; - } else { - assert(is_int($top) || is_float($top) || $top instanceof Ref); - $results[] = $top; - } - } - return $results; - } - - public function popEntriesToCurrentFrame(): void - { - while (!$this->isEmpty() && !$this->top() instanceof Frame) { - $this->pop(); - } - $this->popFrame(); - } - - public function top(): int|float|Ref|Frame|Label|null - { - $n = array_key_last($this->entries); - return $n === null ? null : $this->entries[$n]; - } - - public function count(): int - { - return count($this->entries); - } - - public function isEmpty(): bool - { - return $this->count() === 0; - } - - public function currentFrame(): Frame - { - assert($this->currentFrame !== null); - return $this->currentFrame; - } - - public function getCallStackLimit(): int - { - return 1024; - } - - private function push(int|float|Ref|Frame|Label $entry): void - { - $this->entries[] = $entry; - } - - private function pop(): int|float|Ref|Frame|Label|null - { - return array_pop($this->entries); - } - - private static function getValueTypeName(int|float|Ref|Frame|Label|null $value): string - { - return match (true) { - is_null($value) => 'null', - is_int($value) => 'int', - is_float($value) => 'float', - $value instanceof Ref => 'Ref', - $value instanceof Frame => 'Frame', - $value instanceof Label => 'Label', - }; - } -} diff --git a/src/Execution/StackOverflowException.php b/src/Execution/StackOverflowException.php deleted file mode 100644 index 92991e2..0000000 --- a/src/Execution/StackOverflowException.php +++ /dev/null @@ -1,19 +0,0 @@ - $funcs - * @param list $tables - * @param list $mems - * @param list $globals - * @param list $elems - * @param list $datas - */ - public function __construct( - public array $funcs, - public array $tables, - public array $mems, - public array $globals, - public array $elems, - public array $datas, - ) { - } - - public static function empty(): self - { - return new self([], [], [], [], [], []); - } - - public function register(Extern $extern): ExternVal - { - match ($extern::class) { - Externs\Func::class => $this->funcs[] = $extern->func, - Externs\Table::class => $this->tables[] = $extern->table, - Externs\Mem::class => $this->mems[] = $extern->mem, - Externs\Global_::class => $this->globals[] = $extern->global, - default => throw new RuntimeException("unreachable"), - }; - return match ($extern::class) { - Externs\Func::class => ExternVal::Func(count($this->funcs) - 1), - Externs\Table::class => ExternVal::Table(count($this->tables) - 1), - Externs\Mem::class => ExternVal::Mem(count($this->mems) - 1), - Externs\Global_::class => ExternVal::Global_(count($this->globals) - 1), - default => throw new RuntimeException("unreachable"), - }; - } -} diff --git a/src/Execution/TableInst.php b/src/Execution/TableInst.php deleted file mode 100644 index 002ddec..0000000 --- a/src/Execution/TableInst.php +++ /dev/null @@ -1,19 +0,0 @@ - $elem - */ - public function __construct( - public TableType $type, - public array $elem, - ) { - } -} diff --git a/src/Execution/TrapException.php b/src/Execution/TrapException.php deleted file mode 100644 index 3e6a440..0000000 --- a/src/Execution/TrapException.php +++ /dev/null @@ -1,28 +0,0 @@ -trapKind = $trapKind; - } - - public function getTrapKind(): TrapKind - { - return $this->trapKind; - } -} diff --git a/src/Execution/TrapKind.php b/src/Execution/TrapKind.php deleted file mode 100644 index a92b973..0000000 --- a/src/Execution/TrapKind.php +++ /dev/null @@ -1,19 +0,0 @@ - $types - */ - final public static function Select(array $types): Parametric\Select - { - return new Parametric\Select($types); - } - - // Variable instructions - final public static function GlobalGet(int $var): Variable\GlobalGet - { - return new Variable\GlobalGet($var); - } - final public static function GlobalSet(int $var): Variable\GlobalSet - { - return new Variable\GlobalSet($var); - } - final public static function LocalGet(int $var): Variable\LocalGet - { - return new Variable\LocalGet($var); - } - final public static function LocalSet(int $var): Variable\LocalSet - { - return new Variable\LocalSet($var); - } - final public static function LocalTee(int $var): Variable\LocalTee - { - return new Variable\LocalTee($var); - } - - // Table instructions - final public static function ElemDrop(int $elem): Table\ElemDrop - { - return new Table\ElemDrop($elem); - } - final public static function TableCopy(int $to, int $from): Table\TableCopy - { - return new Table\TableCopy($to, $from); - } - final public static function TableFill(int $table): Table\TableFill - { - return new Table\TableFill($table); - } - final public static function TableGet(int $table): Table\TableGet - { - return new Table\TableGet($table); - } - final public static function TableGrow(int $table): Table\TableGrow - { - return new Table\TableGrow($table); - } - final public static function TableInit(int $to, int $from): Table\TableInit - { - return new Table\TableInit($to, $from); - } - final public static function TableSet(int $table): Table\TableSet - { - return new Table\TableSet($table); - } - final public static function TableSize(int $table): Table\TableSize - { - return new Table\TableSize($table); - } - - // Memory instructions - final public static function DataDrop(int $data): Memory\DataDrop - { - return new Memory\DataDrop($data); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function F32Load(int $offset, int $align): Memory\F32Load - { - return new Memory\F32Load($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function F32Store(int $offset, int $align): Memory\F32Store - { - return new Memory\F32Store($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function F64Load(int $offset, int $align): Memory\F64Load - { - return new Memory\F64Load($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function F64Store(int $offset, int $align): Memory\F64Store - { - return new Memory\F64Store($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Load(int $offset, int $align): Memory\I32Load - { - return new Memory\I32Load($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Load16S(int $offset, int $align): Memory\I32Load16S - { - return new Memory\I32Load16S($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Load16U(int $offset, int $align): Memory\I32Load16U - { - return new Memory\I32Load16U($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Load8S(int $offset, int $align): Memory\I32Load8S - { - return new Memory\I32Load8S($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Load8U(int $offset, int $align): Memory\I32Load8U - { - return new Memory\I32Load8U($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Store(int $offset, int $align): Memory\I32Store - { - return new Memory\I32Store($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Store16(int $offset, int $align): Memory\I32Store16 - { - return new Memory\I32Store16($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I32Store8(int $offset, int $align): Memory\I32Store8 - { - return new Memory\I32Store8($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Load(int $offset, int $align): Memory\I64Load - { - return new Memory\I64Load($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Load16S(int $offset, int $align): Memory\I64Load16S - { - return new Memory\I64Load16S($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Load16U(int $offset, int $align): Memory\I64Load16U - { - return new Memory\I64Load16U($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Load32S(int $offset, int $align): Memory\I64Load32S - { - return new Memory\I64Load32S($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Load32U(int $offset, int $align): Memory\I64Load32U - { - return new Memory\I64Load32U($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Load8S(int $offset, int $align): Memory\I64Load8S - { - return new Memory\I64Load8S($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Load8U(int $offset, int $align): Memory\I64Load8U - { - return new Memory\I64Load8U($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Store(int $offset, int $align): Memory\I64Store - { - return new Memory\I64Store($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Store16(int $offset, int $align): Memory\I64Store16 - { - return new Memory\I64Store16($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Store32(int $offset, int $align): Memory\I64Store32 - { - return new Memory\I64Store32($offset, $align); - } - /** - * @param U32 $offset - * @param U32 $align - */ - final public static function I64Store8(int $offset, int $align): Memory\I64Store8 - { - return new Memory\I64Store8($offset, $align); - } - final public static function MemoryCopy(): Memory\MemoryCopy - { - return new Memory\MemoryCopy(); - } - final public static function MemoryFill(): Memory\MemoryFill - { - return new Memory\MemoryFill(); - } - final public static function MemoryGrow(): Memory\MemoryGrow - { - return new Memory\MemoryGrow(); - } - final public static function MemoryInit(int $data): Memory\MemoryInit - { - return new Memory\MemoryInit($data); - } - final public static function MemorySize(): Memory\MemorySize - { - return new Memory\MemorySize(); - } - - // Control instructions - /** - * @param list $body - */ - final public static function Block(BlockType $type, array $body): Control\Block - { - return new Control\Block($type, $body); - } - final public static function Br(int $label): Control\Br - { - return new Control\Br($label); - } - final public static function BrIf(int $label): Control\BrIf - { - return new Control\BrIf($label); - } - /** - * @param list $labelTable - */ - final public static function BrTable(array $labelTable, int $defaultLabel): Control\BrTable - { - return new Control\BrTable($labelTable, $defaultLabel); - } - final public static function Call(int $func): Control\Call - { - return new Control\Call($func); - } - final public static function CallIndirect(int $funcTable, int $type): Control\CallIndirect - { - return new Control\CallIndirect($funcTable, $type); - } - final public static function Else_(): Control\Else_ - { - return new Control\Else_(); - } - final public static function End(): Control\End - { - return new Control\End(); - } - /** - * @param list $thenBody - * @param list $elseBody - */ - final public static function If_(BlockType $type, array $thenBody, array $elseBody): Control\If_ - { - return new Control\If_($type, $thenBody, $elseBody); - } - /** - * @param list $body - */ - final public static function Loop(BlockType $type, array $body): Control\Loop - { - return new Control\Loop($type, $body); - } - final public static function Nop(): Control\Nop - { - return new Control\Nop(); - } - final public static function Return_(): Control\Return_ - { - return new Control\Return_(); - } - final public static function Unreachable(): Control\Unreachable - { - return new Control\Unreachable(); - } - - abstract public static function opName(): string; -} diff --git a/src/Structure/Instructions/Instrs/Control/Block.php b/src/Structure/Instructions/Instrs/Control/Block.php deleted file mode 100644 index 29e68fd..0000000 --- a/src/Structure/Instructions/Instrs/Control/Block.php +++ /dev/null @@ -1,24 +0,0 @@ - $body - */ - protected function __construct( - public BlockType $type, - public array $body, - ) { - } - - public static function opName(): string - { - return "block"; - } -} diff --git a/src/Structure/Instructions/Instrs/Control/BlockType.php b/src/Structure/Instructions/Instrs/Control/BlockType.php deleted file mode 100644 index a90389b..0000000 --- a/src/Structure/Instructions/Instrs/Control/BlockType.php +++ /dev/null @@ -1,25 +0,0 @@ - $labelTable - */ - protected function __construct( - public array $labelTable, - public int $defaultLabel, - ) { - } - - public static function opName(): string - { - return "br_table"; - } -} diff --git a/src/Structure/Instructions/Instrs/Control/Call.php b/src/Structure/Instructions/Instrs/Control/Call.php deleted file mode 100644 index 9ee14be..0000000 --- a/src/Structure/Instructions/Instrs/Control/Call.php +++ /dev/null @@ -1,20 +0,0 @@ - $thenBody - * @param list $elseBody - */ - protected function __construct( - public BlockType $type, - public array $thenBody, - public array $elseBody, - ) { - } - - public static function opName(): string - { - return "if"; - } -} diff --git a/src/Structure/Instructions/Instrs/Control/Loop.php b/src/Structure/Instructions/Instrs/Control/Loop.php deleted file mode 100644 index 1572e4b..0000000 --- a/src/Structure/Instructions/Instrs/Control/Loop.php +++ /dev/null @@ -1,24 +0,0 @@ - $body - */ - protected function __construct( - public BlockType $type, - public array $body, - ) { - } - - public static function opName(): string - { - return "loop"; - } -} diff --git a/src/Structure/Instructions/Instrs/Control/Nop.php b/src/Structure/Instructions/Instrs/Control/Nop.php deleted file mode 100644 index 3aaeb4d..0000000 --- a/src/Structure/Instructions/Instrs/Control/Nop.php +++ /dev/null @@ -1,15 +0,0 @@ - $types - */ - protected function __construct( - public array $types, - ) { - } - - public static function opName(): string - { - return "select"; - } -} diff --git a/src/Structure/Instructions/Instrs/Reference/RefFunc.php b/src/Structure/Instructions/Instrs/Reference/RefFunc.php deleted file mode 100644 index 97f11b5..0000000 --- a/src/Structure/Instructions/Instrs/Reference/RefFunc.php +++ /dev/null @@ -1,20 +0,0 @@ - $init - */ - public function __construct( - public array $init, - public DataMode $mode, - ) { - } -} diff --git a/src/Structure/Modules/DataMode.php b/src/Structure/Modules/DataMode.php deleted file mode 100644 index e9b5a72..0000000 --- a/src/Structure/Modules/DataMode.php +++ /dev/null @@ -1,25 +0,0 @@ - $offset - */ - final public static function Active( - int $memory, - array $offset, - ): DataModes\Active { - return new DataModes\Active($memory, $offset); - } -} diff --git a/src/Structure/Modules/DataModes/Active.php b/src/Structure/Modules/DataModes/Active.php deleted file mode 100644 index de52861..0000000 --- a/src/Structure/Modules/DataModes/Active.php +++ /dev/null @@ -1,20 +0,0 @@ - $offset - */ - protected function __construct( - public int $memory, - public array $offset, - ) { - } -} diff --git a/src/Structure/Modules/DataModes/Passive.php b/src/Structure/Modules/DataModes/Passive.php deleted file mode 100644 index 3686cdc..0000000 --- a/src/Structure/Modules/DataModes/Passive.php +++ /dev/null @@ -1,14 +0,0 @@ -> $init - */ - public function __construct( - public RefType $type, - public array $init, - public ElemMode $mode, - ) { - } -} diff --git a/src/Structure/Modules/ElemMode.php b/src/Structure/Modules/ElemMode.php deleted file mode 100644 index e3e7693..0000000 --- a/src/Structure/Modules/ElemMode.php +++ /dev/null @@ -1,30 +0,0 @@ - $offset - */ - final public static function Active( - int $table, - array $offset, - ): ElemModes\Active { - return new ElemModes\Active($table, $offset); - } - - final public static function Declarative(): ElemModes\Declarative - { - return new ElemModes\Declarative(); - } -} diff --git a/src/Structure/Modules/ElemModes/Active.php b/src/Structure/Modules/ElemModes/Active.php deleted file mode 100644 index 5487542..0000000 --- a/src/Structure/Modules/ElemModes/Active.php +++ /dev/null @@ -1,20 +0,0 @@ - $offset - */ - protected function __construct( - public int $table, - public array $offset, - ) { - } -} diff --git a/src/Structure/Modules/ElemModes/Declarative.php b/src/Structure/Modules/ElemModes/Declarative.php deleted file mode 100644 index 7c68331..0000000 --- a/src/Structure/Modules/ElemModes/Declarative.php +++ /dev/null @@ -1,14 +0,0 @@ - $locals - * @param list $body - */ - public function __construct( - public int $type, - public array $locals, - public array $body, - ) { - } -} diff --git a/src/Structure/Modules/Global_.php b/src/Structure/Modules/Global_.php deleted file mode 100644 index aae1706..0000000 --- a/src/Structure/Modules/Global_.php +++ /dev/null @@ -1,20 +0,0 @@ - $init - */ - public function __construct( - public GlobalType $type, - public array $init, - ) { - } -} diff --git a/src/Structure/Modules/Import.php b/src/Structure/Modules/Import.php deleted file mode 100644 index 4609a3f..0000000 --- a/src/Structure/Modules/Import.php +++ /dev/null @@ -1,19 +0,0 @@ - $types - * @param list $funcs - * @param list
$tables - * @param list $mems - * @param list $globals - * @param list $elems - * @param list $datas - * @param list $imports - * @param list $exports - */ - public function __construct( - public array $types, - public array $funcs, - public array $tables, - public array $mems, - public array $globals, - public array $elems, - public array $datas, - public ?Start $start, - public array $imports, - public array $exports, - ) { - } -} diff --git a/src/Structure/Modules/Start.php b/src/Structure/Modules/Start.php deleted file mode 100644 index 1ef75df..0000000 --- a/src/Structure/Modules/Start.php +++ /dev/null @@ -1,13 +0,0 @@ -params->equals($other->params) - && $this->results->equals($other->results); - } -} diff --git a/src/Structure/Types/GlobalType.php b/src/Structure/Types/GlobalType.php deleted file mode 100644 index 182b761..0000000 --- a/src/Structure/Types/GlobalType.php +++ /dev/null @@ -1,14 +0,0 @@ -min && - ($this->max === null || $this->min <= $this->max); - } -} diff --git a/src/Structure/Types/MemType.php b/src/Structure/Types/MemType.php deleted file mode 100644 index d4c9009..0000000 --- a/src/Structure/Types/MemType.php +++ /dev/null @@ -1,13 +0,0 @@ - $types - */ - public function __construct( - public array $types, - ) { - } - - public function equals(ResultType $other): bool - { - if (count($this->types) !== count($other->types)) { - return false; - } - foreach ($this->types as $i => $type) { - if (!$type->equals($other->types[$i])) { - return false; - } - } - return true; - } -} diff --git a/src/Structure/Types/TableType.php b/src/Structure/Types/TableType.php deleted file mode 100644 index 619ad25..0000000 --- a/src/Structure/Types/TableType.php +++ /dev/null @@ -1,14 +0,0 @@ -inner === $other->inner; - } -} diff --git a/src/Structure/Types/ValTypes/RefType.php b/src/Structure/Types/ValTypes/RefType.php deleted file mode 100644 index fc48dfb..0000000 --- a/src/Structure/Types/ValTypes/RefType.php +++ /dev/null @@ -1,21 +0,0 @@ -inner === $other->inner; - } -} diff --git a/src/Structure/Types/ValTypes/VecType.php b/src/Structure/Types/ValTypes/VecType.php deleted file mode 100644 index a28081f..0000000 --- a/src/Structure/Types/ValTypes/VecType.php +++ /dev/null @@ -1,21 +0,0 @@ -inner === $other->inner; // @phpstan-ignore-line - } -} diff --git a/src/Structure/Types/VecType.php b/src/Structure/Types/VecType.php deleted file mode 100644 index ae8a0f6..0000000 --- a/src/Structure/Types/VecType.php +++ /dev/null @@ -1,10 +0,0 @@ -checkMagic(); + $this->checkVersion(); + + $types = $this->decodeSection(SectionId::Type, $this->decodeTypeSecRest(...)) ?? []; + $imports = $this->decodeSection(SectionId::Import, $this->decodeImportSecRest(...)) ?? []; + $typeIndices = $this->decodeSection(SectionId::Func, $this->decodeFuncSecRest(...)) ?? []; + $tables = $this->decodeSection(SectionId::Table, $this->decodeTableSecRest(...)) ?? []; + $mems = $this->decodeSection(SectionId::Mem, $this->decodeMemSecRest(...)) ?? []; + $globals = $this->decodeSection(SectionId::Global, $this->decodeGlobalSecRest(...)) ?? []; + $exports = $this->decodeSection(SectionId::Export, $this->decodeExportSecRest(...)) ?? []; + $start = $this->decodeSection(SectionId::Start, $this->decodeStartSecRest(...)); + $elems = $this->decodeSection(SectionId::Elem, $this->decodeElemSecRest(...)) ?? []; + $dataCount = $this->decodeSection(SectionId::DataCount, $this->decodeDataCountSecRest(...)); + $codes = $this->decodeSection(SectionId::Code, $this->decodeCodeSecRest(...)) ?? []; + $datas = $this->decodeSection(SectionId::Data, $this->decodeDataSecRest(...)) ?? []; + + if (!$this->stream->eof()) { + throw new InvalidBinaryFormatException("eof"); + } + if ($dataCount === null) { + foreach ($codes as $code) { + if ($this->countDataIndicesUsedInCode($code) !== 0) { + throw new InvalidBinaryFormatException("datacount section is required"); + } + } + } else { + if (count($datas) !== $dataCount) { + throw new InvalidBinaryFormatException("datasec"); + } + } + if (count($typeIndices) !== count($codes)) { + throw new InvalidBinaryFormatException("number of funcs and codes does not match"); + } + + $funcs = []; + foreach ($typeIndices as $i => $type) { + $code = $codes[$i]; + $decompressedLocals = array_map( + fn ($l) => array_fill(0, $l->count, new Local($l->type)), + $code->compressedLocals, + ); + $funcs[] = new Func( + $type, + array_merge(...$decompressedLocals), + $code->body, + ); + } + + return new Module( + $types, + $funcs, + $tables, + $mems, + $globals, + $elems, + $datas, + $start, + $imports, + $exports, + ); + } + + private function checkMagic(): void + { + $bs = $this->stream->read(4); + $b1 = ord($bs[0]); + $b2 = ord($bs[1]); + $b3 = ord($bs[2]); + $b4 = ord($bs[3]); + if ([$b1, $b2, $b3, $b4] !== [0x00, 0x61, 0x73, 0x6D]) { + throw new InvalidBinaryFormatException("magic"); + } + } + + private function checkVersion(): void + { + $bs = $this->stream->read(4); + $b1 = ord($bs[0]); + $b2 = ord($bs[1]); + $b3 = ord($bs[2]); + $b4 = ord($bs[3]); + if ([$b1, $b2, $b3, $b4] !== [0x01, 0x00, 0x00, 0x00]) { + throw new InvalidBinaryFormatException(sprintf("version: [%x, %x, %x, %x]", $b1, $b2, $b3, $b4)); + } + } + + /** + * @template T + * @param callable(): T $decoder + * @return ?T + */ + private function decodeSection(SectionId $sectionId, callable $decoder): mixed + { + $this->skipCustomSections(); + if ($this->stream->eof()) { + return null; + } + + $idValue = $this->stream->peekByte(); + $id = SectionId::tryFrom($idValue); + if ($id === null) { + throw new InvalidBinaryFormatException("section id"); + } + if ($id !== $sectionId) { + return null; + } + $this->stream->seek(1); + + $size = $this->decodeU32(); + $prevPos = $this->stream->tell(); + $result = $decoder(); + if ($this->stream->tell() - $prevPos !== $size) { + throw new InvalidBinaryFormatException("type section size"); + } + return $result; + } + + private function skipCustomSections(): void + { + while (!$this->stream->eof()) { + $b = $this->stream->peekByte(); + if ($b !== SectionId::Custom->value) { + break; + } + $this->stream->seek(1); + $size = $this->decodeU32(); + $prevPos = $this->stream->tell(); + $this->decodeName(); + $encodedSizeOfName = $this->stream->tell() - $prevPos; + $offset = $size - $encodedSizeOfName; + if ($offset < 0) { + throw new InvalidBinaryFormatException("custom section size"); + } + if ($offset !== 0) { + $this->stream->seek($offset); + } + } + } + + /** + * @return list + */ + private function decodeTypeSecRest(): array + { + return $this->decodeVec($this->decodeFuncType(...)); + } + + /** + * @return list + */ + private function decodeImportSecRest(): array + { + return $this->decodeVec($this->decodeImport(...)); + } + + /** + * @return list + */ + private function decodeFuncSecRest(): array + { + return $this->decodeVec($this->decodeTypeIdx(...)); + } + + /** + * @return list
+ */ + private function decodeTableSecRest(): array + { + return $this->decodeVec($this->decodeTable(...)); + } + + /** + * @return list + */ + private function decodeMemSecRest(): array + { + return $this->decodeVec($this->decodeMem(...)); + } + + /** + * @return list + */ + private function decodeGlobalSecRest(): array + { + return $this->decodeVec($this->decodeGlobal(...)); + } + + /** + * @return list + */ + private function decodeExportSecRest(): array + { + return $this->decodeVec($this->decodeExport(...)); + } + + private function decodeStartSecRest(): Start + { + return new Start($this->decodeFuncIdx()); + } + + /** + * @return list + */ + private function decodeElemSecRest(): array + { + return $this->decodeVec($this->decodeElem(...)); + } + + /** + * @return list + */ + private function decodeCodeSecRest(): array + { + return $this->decodeVec($this->decodeCode(...)); + } + + /** + * @return list + */ + private function decodeDataSecRest(): array + { + return $this->decodeVec($this->decodeData(...)); + } + + /** + * @return U32 + */ + private function decodeDataCountSecRest(): int + { + return $this->decodeU32(); + } + + private function decodeResultType(): ResultType + { + return new ResultType($this->decodeVec($this->decodeValType(...))); + } + + private function decodeFuncType(): FuncType + { + $b = $this->decodeByte(); + if ($b !== 0x60) { + throw new InvalidBinaryFormatException("functype"); + } + $params = $this->decodeResultType(); + $results = $this->decodeResultType(); + return new FuncType( + $params, + $results, + ); + } + + private function decodeValType(): ValType + { + $b = $this->stream->peekByte(); + if ($b === 0x7F) { + $this->stream->seek(1); + return ValType::NumType(NumType::I32); + } elseif ($b === 0x7E) { + $this->stream->seek(1); + return ValType::NumType(NumType::I64); + } elseif ($b === 0x7D) { + $this->stream->seek(1); + return ValType::NumType(NumType::F32); + } elseif ($b === 0x7C) { + $this->stream->seek(1); + return ValType::NumType(NumType::F64); + } elseif ($b === 0x7B) { + $this->stream->seek(1); + return ValType::VecType(VecType::V128); + } else { + return ValType::RefType($this->decodeRefType()); + } + } + + private function decodeRefType(): RefType + { + return match ($this->decodeByte()) { + 0x70 => RefType::FuncRef, + 0x6F => RefType::ExternRef, + default => throw new InvalidBinaryFormatException("reftype"), + }; + } + + private function decodeLimits(): Limits + { + $b = $this->decodeByte(); + if ($b === 0x00) { + $min = $this->decodeU32(); + return new Limits($min, null); + } elseif ($b === 0x01) { + $min = $this->decodeU32(); + $max = $this->decodeU32(); + return new Limits($min, $max); + } else { + throw new InvalidBinaryFormatException("limits"); + } + } + + private function decodeMemType(): MemType + { + return new MemType($this->decodeLimits()); + } + + private function decodeTableType(): TableType + { + $refType = $this->decodeRefType(); + $limits = $this->decodeLimits(); + return new TableType( + $limits, + $refType, + ); + } + + private function decodeGlobalType(): GlobalType + { + $valType = $this->decodeValType(); + $mut = $this->decodeMut(); + return new GlobalType( + $mut, + $valType, + ); + } + + private function decodeMut(): Mut + { + return match ($this->decodeByte()) { + 0x00 => Mut::Const, + 0x01 => Mut::Var, + default => throw new InvalidBinaryFormatException("mutability"), + }; + } + + private function decodeImport(): Import + { + $module = $this->decodeName(); + $name = $this->decodeName(); + $desc = $this->decodeImportDesc(); + return new Import( + $module, + $name, + $desc, + ); + } + + private function decodeTable(): Table + { + return new Table($this->decodeTableType()); + } + + private function decodeMem(): Mem + { + return new Mem($this->decodeMemType()); + } + + private function decodeGlobal(): Global_ + { + $type = $this->decodeGlobalType(); + $init = $this->decodeExpr(); + return new Global_( + $type, + $init, + ); + } + + private function decodeExport(): Export + { + $name = $this->decodeName(); + $desc = $this->decodeExportDesc(); + return new Export( + $name, + $desc, + ); + } + + private function decodeElem(): Elem + { + $mode = $this->decodeU32(); + if ($mode === 0) { + $offset = $this->decodeExpr(); + $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); + return new Elem( + RefType::FuncRef, + array_map( + fn ($funcRef) => [Instr::RefFunc($funcRef)], + $initFuncRefs, + ), + ElemMode::Active(0, $offset), + ); + } elseif ($mode === 1) { + $refType = $this->decodeElemKind(); + $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); + return new Elem( + $refType, + array_map( + fn ($funcRef) => [Instr::RefFunc($funcRef)], + $initFuncRefs, + ), + ElemMode::Passive(), + ); + } elseif ($mode === 2) { + $table = $this->decodeTableIdx(); + $offset = $this->decodeExpr(); + $refType = $this->decodeElemKind(); + $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); + return new Elem( + $refType, + array_map( + fn ($funcRef) => [Instr::RefFunc($funcRef)], + $initFuncRefs, + ), + ElemMode::Active($table, $offset), + ); + } elseif ($mode === 3) { + $refType = $this->decodeElemKind(); + $initFuncRefs = $this->decodeVec($this->decodeFuncIdx(...)); + return new Elem( + $refType, + array_map( + fn ($funcRef) => [Instr::RefFunc($funcRef)], + $initFuncRefs, + ), + ElemMode::Declarative(), + ); + } elseif ($mode === 4) { + $offset = $this->decodeExpr(); + $init = $this->decodeVec($this->decodeExpr(...)); + return new Elem( + RefType::FuncRef, + $init, + ElemMode::Active(0, $offset), + ); + } elseif ($mode === 5) { + $refType = $this->decodeRefType(); + $init = $this->decodeVec($this->decodeExpr(...)); + return new Elem( + $refType, + $init, + ElemMode::Passive(), + ); + } elseif ($mode === 6) { + $table = $this->decodeTableIdx(); + $offset = $this->decodeExpr(); + $refType = $this->decodeRefType(); + $init = $this->decodeVec($this->decodeExpr(...)); + return new Elem( + $refType, + $init, + ElemMode::Active($table, $offset), + ); + } elseif ($mode === 7) { + $refType = $this->decodeRefType(); + $init = $this->decodeVec($this->decodeExpr(...)); + return new Elem( + $refType, + $init, + ElemMode::Declarative(), + ); + } else { + throw new InvalidBinaryFormatException("code"); + } + } + + private function decodeCode(): Code + { + $size = $this->decodeU32(); + $prevPos = $this->stream->tell(); + $compressedLocals = $this->decodeVec($this->decodeLocals(...)); + $body = $this->decodeExpr(); + if ($this->stream->tell() - $prevPos !== $size) { + throw new InvalidBinaryFormatException("code size"); + } + return new Code( + $compressedLocals, + $body, + ); + } + + private function decodeData(): Data + { + $mode = $this->decodeU32(); + if ($mode === 0) { + $offset = $this->decodeExpr(); + $init = $this->decodeVec($this->decodeByte(...)); + return new Data( + $init, + DataMode::Active( + 0, + $offset, + ), + ); + } elseif ($mode === 1) { + $init = $this->decodeVec($this->decodeByte(...)); + return new Data( + $init, + DataMode::Passive(), + ); + } elseif ($mode === 2) { + $memory = $this->decodeMemIdx(); + $offset = $this->decodeExpr(); + $init = $this->decodeVec($this->decodeByte(...)); + return new Data( + $init, + DataMode::Active( + $memory, + $offset, + ), + ); + } else { + throw new InvalidBinaryFormatException("data"); + } + } + + private function decodeImportDesc(): ImportDesc + { + return match ($this->decodeByte()) { + 0x00 => ImportDesc::Func($this->decodeTypeIdx()), + 0x01 => ImportDesc::Table($this->decodeTableType()), + 0x02 => ImportDesc::Mem($this->decodeMemType()), + 0x03 => ImportDesc::Global($this->decodeGlobalType()), + default => throw new InvalidBinaryFormatException("importdesc"), + }; + } + + private function decodeExportDesc(): ExportDesc + { + return match ($this->decodeByte()) { + 0x00 => ExportDesc::Func($this->decodeFuncIdx()), + 0x01 => ExportDesc::Table($this->decodeTableIdx()), + 0x02 => ExportDesc::Mem($this->decodeMemIdx()), + 0x03 => ExportDesc::Global($this->decodeGlobalIdx()), + default => throw new InvalidBinaryFormatException("exportdesc"), + }; + } + + private function decodeElemKind(): RefType + { + $b = $this->decodeByte(); + if ($b === 0x00) { + return RefType::FuncRef; + } else { + throw new InvalidBinaryFormatException("elemkind"); + } + } + + private function decodeLocals(): Locals + { + $count = $this->decodeU32(); + // @todo Provide a way to configure the limit. + if (1024 < $count) { + throw new InvalidBinaryFormatException("too many local variables"); + } + $type = $this->decodeValType(); + return new Locals( + $count, + $type, + ); + } + + private function decodeTypeIdx(): int + { + return $this->decodeU32(); + } + + private function decodeMemIdx(): int + { + return $this->decodeU32(); + } + + private function decodeFuncIdx(): int + { + return $this->decodeU32(); + } + + private function decodeTableIdx(): int + { + return $this->decodeU32(); + } + + private function decodeGlobalIdx(): int + { + return $this->decodeU32(); + } + + private function decodeLocalIdx(): int + { + return $this->decodeU32(); + } + + private function decodeLabelIdx(): int + { + return $this->decodeU32(); + } + + private function decodeDataIdx(): int + { + return $this->decodeU32(); + } + + private function decodeElemIdx(): int + { + return $this->decodeU32(); + } + + /** + * @return list + */ + private function decodeExpr(): array + { + return $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; + } + + private function decodeInstr(): Instr + { + switch ($op = $this->decodeByte()) { + case 0x00: return Instr::Unreachable(); + case 0x01: return Instr::Nop(); + case 0x02: + $blockType = $this->decodeBlockType(); + $body = $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; + return Instr::Block($blockType, $body); + case 0x03: + $blockType = $this->decodeBlockType(); + $body = $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; + return Instr::Loop($blockType, $body); + case 0x04: + $blockType = $this->decodeBlockType(); + [$thenBody, $delimiter] = $this->decodeInstrsUntil([Instrs\Control\End::class, Instrs\Control\Else_::class]); + if ($delimiter instanceof Instrs\Control\End) { + return Instr::If_($blockType, $thenBody, []); + } + $elseBody = $this->decodeInstrsUntil([Instrs\Control\End::class])[0]; + return Instr::If_($blockType, $thenBody, $elseBody); + case 0x05: return Instr::Else_(); + case 0x0B: return Instr::End(); + case 0x0C: return Instr::Br($this->decodeLabelIdx()); + case 0x0D: return Instr::BrIf($this->decodeLabelIdx()); + case 0x0E: + $labelTable = $this->decodeVec($this->decodeLabelIdx(...)); + $defaultLabel = $this->decodeLabelIdx(); + return Instr::BrTable($labelTable, $defaultLabel); + case 0x0F: return Instr::Return_(); + case 0x10: return Instr::Call($this->decodeFuncIdx()); + case 0x11: + $type = $this->decodeTypeIdx(); + $funcTable = $this->decodeTableIdx(); + return Instr::CallIndirect($funcTable, $type); + case 0x1A: return Instr::Drop(); + case 0x1B: return Instr::Select([]); + case 0x1C: return Instr::Select($this->decodeVec($this->decodeValType(...))); + case 0x20: return Instr::LocalGet($this->decodeLocalIdx()); + case 0x21: return Instr::LocalSet($this->decodeLocalIdx()); + case 0x22: return Instr::LocalTee($this->decodeLocalIdx()); + case 0x23: return Instr::GlobalGet($this->decodeGlobalIdx()); + case 0x24: return Instr::GlobalSet($this->decodeGlobalIdx()); + case 0x25: return Instr::TableGet($this->decodeTableIdx()); + case 0x26: return Instr::TableSet($this->decodeTableIdx()); + case 0x28: return Instr::I32Load(...$this->decodeMemArg()); + case 0x29: return Instr::I64Load(...$this->decodeMemArg()); + case 0x2A: return Instr::F32Load(...$this->decodeMemArg()); + case 0x2B: return Instr::F64Load(...$this->decodeMemArg()); + case 0x2C: return Instr::I32Load8S(...$this->decodeMemArg()); + case 0x2D: return Instr::I32Load8U(...$this->decodeMemArg()); + case 0x2E: return Instr::I32Load16S(...$this->decodeMemArg()); + case 0x2F: return Instr::I32Load16U(...$this->decodeMemArg()); + case 0x30: return Instr::I64Load8S(...$this->decodeMemArg()); + case 0x31: return Instr::I64Load8U(...$this->decodeMemArg()); + case 0x32: return Instr::I64Load16S(...$this->decodeMemArg()); + case 0x33: return Instr::I64Load16U(...$this->decodeMemArg()); + case 0x34: return Instr::I64Load32S(...$this->decodeMemArg()); + case 0x35: return Instr::I64Load32U(...$this->decodeMemArg()); + case 0x36: return Instr::I32Store(...$this->decodeMemArg()); + case 0x37: return Instr::I64Store(...$this->decodeMemArg()); + case 0x38: return Instr::F32Store(...$this->decodeMemArg()); + case 0x39: return Instr::F64Store(...$this->decodeMemArg()); + case 0x3A: return Instr::I32Store8(...$this->decodeMemArg()); + case 0x3B: return Instr::I32Store16(...$this->decodeMemArg()); + case 0x3C: return Instr::I64Store8(...$this->decodeMemArg()); + case 0x3D: return Instr::I64Store16(...$this->decodeMemArg()); + case 0x3E: return Instr::I64Store32(...$this->decodeMemArg()); + case 0x3F: + $c = $this->decodeByte(); + if ($c !== 0) { + throw new InvalidBinaryFormatException("Unexpected value while decoding an instruction `memory.size`, expected 0, but got $c"); + } + return Instr::MemorySize(); + case 0x40: + $c = $this->decodeByte(); + if ($c !== 0) { + throw new InvalidBinaryFormatException("Unexpected value while decoding an instruction `memory.grow`, expected 0, but got $c"); + } + return Instr::MemoryGrow(); + case 0x41: return Instr::I32Const($this->decodeS32()); + case 0x42: return Instr::I64Const($this->decodeS64()); + case 0x43: return Instr::F32Const($this->decodeF32()); + case 0x44: return Instr::F64Const($this->decodeF64()); + case 0x45: return Instr::I32Eqz(); + case 0x46: return Instr::I32Eq(); + case 0x47: return Instr::I32Ne(); + case 0x48: return Instr::I32LtS(); + case 0x49: return Instr::I32LtU(); + case 0x4A: return Instr::I32GtS(); + case 0x4B: return Instr::I32GtU(); + case 0x4C: return Instr::I32LeS(); + case 0x4D: return Instr::I32LeU(); + case 0x4E: return Instr::I32GeS(); + case 0x4F: return Instr::I32GeU(); + case 0x50: return Instr::I64Eqz(); + case 0x51: return Instr::I64Eq(); + case 0x52: return Instr::I64Ne(); + case 0x53: return Instr::I64LtS(); + case 0x54: return Instr::I64LtU(); + case 0x55: return Instr::I64GtS(); + case 0x56: return Instr::I64GtU(); + case 0x57: return Instr::I64LeS(); + case 0x58: return Instr::I64LeU(); + case 0x59: return Instr::I64GeS(); + case 0x5A: return Instr::I64GeU(); + case 0x5B: return Instr::F32Eq(); + case 0x5C: return Instr::F32Ne(); + case 0x5D: return Instr::F32Lt(); + case 0x5E: return Instr::F32Gt(); + case 0x5F: return Instr::F32Le(); + case 0x60: return Instr::F32Ge(); + case 0x61: return Instr::F64Eq(); + case 0x62: return Instr::F64Ne(); + case 0x63: return Instr::F64Lt(); + case 0x64: return Instr::F64Gt(); + case 0x65: return Instr::F64Le(); + case 0x66: return Instr::F64Ge(); + case 0x67: return Instr::I32Clz(); + case 0x68: return Instr::I32Ctz(); + case 0x69: return Instr::I32Popcnt(); + case 0x6A: return Instr::I32Add(); + case 0x6B: return Instr::I32Sub(); + case 0x6C: return Instr::I32Mul(); + case 0x6D: return Instr::I32DivS(); + case 0x6E: return Instr::I32DivU(); + case 0x6F: return Instr::I32RemS(); + case 0x70: return Instr::I32RemU(); + case 0x71: return Instr::I32And(); + case 0x72: return Instr::I32Or(); + case 0x73: return Instr::I32Xor(); + case 0x74: return Instr::I32Shl(); + case 0x75: return Instr::I32ShrS(); + case 0x76: return Instr::I32ShrU(); + case 0x77: return Instr::I32RotL(); + case 0x78: return Instr::I32RotR(); + case 0x79: return Instr::I64Clz(); + case 0x7A: return Instr::I64Ctz(); + case 0x7B: return Instr::I64Popcnt(); + case 0x7C: return Instr::I64Add(); + case 0x7D: return Instr::I64Sub(); + case 0x7E: return Instr::I64Mul(); + case 0x7F: return Instr::I64DivS(); + case 0x80: return Instr::I64DivU(); + case 0x81: return Instr::I64RemS(); + case 0x82: return Instr::I64RemU(); + case 0x83: return Instr::I64And(); + case 0x84: return Instr::I64Or(); + case 0x85: return Instr::I64Xor(); + case 0x86: return Instr::I64Shl(); + case 0x87: return Instr::I64ShrS(); + case 0x88: return Instr::I64ShrU(); + case 0x89: return Instr::I64RotL(); + case 0x8A: return Instr::I64RotR(); + case 0x8B: return Instr::F32Abs(); + case 0x8C: return Instr::F32Neg(); + case 0x8D: return Instr::F32Ceil(); + case 0x8E: return Instr::F32Floor(); + case 0x8F: return Instr::F32Trunc(); + case 0x90: return Instr::F32Nearest(); + case 0x91: return Instr::F32Sqrt(); + case 0x92: return Instr::F32Add(); + case 0x93: return Instr::F32Sub(); + case 0x94: return Instr::F32Mul(); + case 0x95: return Instr::F32Div(); + case 0x96: return Instr::F32Min(); + case 0x97: return Instr::F32Max(); + case 0x98: return Instr::F32CopySign(); + case 0x99: return Instr::F64Abs(); + case 0x9A: return Instr::F64Neg(); + case 0x9B: return Instr::F64Ceil(); + case 0x9C: return Instr::F64Floor(); + case 0x9D: return Instr::F64Trunc(); + case 0x9E: return Instr::F64Nearest(); + case 0x9F: return Instr::F64Sqrt(); + case 0xA0: return Instr::F64Add(); + case 0xA1: return Instr::F64Sub(); + case 0xA2: return Instr::F64Mul(); + case 0xA3: return Instr::F64Div(); + case 0xA4: return Instr::F64Min(); + case 0xA5: return Instr::F64Max(); + case 0xA6: return Instr::F64CopySign(); + case 0xA7: return Instr::I32WrapI64(); + case 0xA8: return Instr::I32TruncF32S(); + case 0xA9: return Instr::I32TruncF32U(); + case 0xAA: return Instr::I32TruncF64S(); + case 0xAB: return Instr::I32TruncF64U(); + case 0xAC: return Instr::I64ExtendI32S(); + case 0xAD: return Instr::I64ExtendI32U(); + case 0xAE: return Instr::I64TruncF32S(); + case 0xAF: return Instr::I64TruncF32U(); + case 0xB0: return Instr::I64TruncF64S(); + case 0xB1: return Instr::I64TruncF64U(); + case 0xB2: return Instr::F32ConvertI32S(); + case 0xB3: return Instr::F32ConvertI32U(); + case 0xB4: return Instr::F32ConvertI64S(); + case 0xB5: return Instr::F32ConvertI64U(); + case 0xB6: return Instr::F32DemoteF64(); + case 0xB7: return Instr::F64ConvertI32S(); + case 0xB8: return Instr::F64ConvertI32U(); + case 0xB9: return Instr::F64ConvertI64S(); + case 0xBA: return Instr::F64ConvertI64U(); + case 0xBB: return Instr::F64PromoteF32(); + case 0xBC: return Instr::I32ReinterpretF32(); + case 0xBD: return Instr::I64ReinterpretF64(); + case 0xBE: return Instr::F32ReinterpretI32(); + case 0xBF: return Instr::F64ReinterpretI64(); + case 0xC0: return Instr::I32Extend8S(); + case 0xC1: return Instr::I32Extend16S(); + case 0xC2: return Instr::I64Extend8S(); + case 0xC3: return Instr::I64Extend16S(); + case 0xC4: return Instr::I64Extend32S(); + case 0xD0: return Instr::RefNull($this->decodeRefType()); + case 0xD1: return Instr::RefIsNull(); + case 0xD2: return Instr::RefFunc($this->decodeFuncIdx()); + case 0xFC: + switch ($this->decodeU32()) { + case 0: return Instr::I32TruncSatF32S(); + case 1: return Instr::I32TruncSatF32U(); + case 2: return Instr::I32TruncSatF64S(); + case 3: return Instr::I32TruncSatF64U(); + case 4: return Instr::I64TruncSatF32S(); + case 5: return Instr::I64TruncSatF32U(); + case 6: return Instr::I64TruncSatF64S(); + case 7: return Instr::I64TruncSatF64U(); + case 8: + $data = $this->decodeDataIdx(); + if ($this->decodeByte() !== 0) { + throw new InvalidBinaryFormatException("memory init"); + } + return Instr::MemoryInit($data); + case 9: return Instr::DataDrop($this->decodeDataIdx()); + case 10: + if ($this->decodeByte() !== 0) { + throw new InvalidBinaryFormatException("memory copy"); + } + if ($this->decodeByte() !== 0) { + throw new InvalidBinaryFormatException("memory copy"); + } + return Instr::MemoryCopy(); + case 11: + if ($this->decodeByte() !== 0) { + throw new InvalidBinaryFormatException("memory fill"); + } + return Instr::MemoryFill(); + case 12: + $from = $this->decodeElemIdx(); + $to = $this->decodeTableIdx(); + return Instr::TableInit($to, $from); + case 13: return Instr::ElemDrop($this->decodeElemIdx()); + case 14: + $to = $this->decodeTableIdx(); + $from = $this->decodeTableIdx(); + return Instr::TableCopy($to, $from); + case 15: return Instr::TableGrow($this->decodeTableIdx()); + case 16: return Instr::TableSize($this->decodeTableIdx()); + case 17: return Instr::TableFill($this->decodeTableIdx()); + default: + throw new InvalidBinaryFormatException("instr"); + } + // no break + default: + throw new InvalidBinaryFormatException("Unexpected opcode $op while decoding an instruction"); + } + } + + /** + * @return array{0: U32, 1: U32} + */ + private function decodeMemArg(): array + { + $align = $this->decodeU32(); + $offset = $this->decodeU32(); + return [$offset, $align]; + } + + private function decodeBlockType(): BlockType + { + $b = $this->stream->peekByte(); + if ($b === 0x40) { + $this->stream->seek(1); + return BlockType::ValType(null); + } elseif (in_array($b, [0x7F, 0x7E, 0x7D, 0x7C, 0x7B, 0x70, 0x6F], true)) { + return BlockType::ValType($this->decodeValType()); + } else { + $type = $this->decodeS33(); + if ($type < 0) { + throw new InvalidBinaryFormatException("blocktype"); + } + return BlockType::TypeIdx($type); + } + } + + /** + * @param list> $delimiters + * @return array{0: list, 1: Instr} + */ + private function decodeInstrsUntil(array $delimiters): array + { + $result = []; + while (true) { + $instr = $this->decodeInstr(); + if (in_array(get_class($instr), $delimiters, true)) { + return [$result, $instr]; + } + $result[] = $instr; + } + } + + /** + * @template T + * @param callable(): T $decoder + * @return list + */ + private function decodeVec(callable $decoder): array + { + $count = $this->decodeU32(); + $result = []; + for ($i = 0; $i < $count; $i++) { + $result[] = $decoder(); + } + return $result; + } + + /** + * @phpstan-impure + */ + private function decodeByte(): int + { + return $this->stream->readByte(); + } + + private function decodeU32(): int + { + return $this->decodeUnsignedLeb128(32); + } + + private function decodeUnsignedLeb128(int $maxBits): int + { + $result = 0; + for ($shiftBits = 0; $shiftBits < $maxBits; $shiftBits += 7) { + $b = $this->decodeByte(); + $leftBitsCount = $maxBits - $shiftBits; + if ($leftBitsCount < 7) { + if ((($b & 0b01111111) >> $leftBitsCount) !== 0) { + throw new InvalidBinaryFormatException("unsigned leb128 ($maxBits): too large"); + } + } + $result |= ($b & 0b01111111) << $shiftBits; + if (($b & 0b10000000) === 0) { + return $result; + } + } + throw new InvalidBinaryFormatException("unsigned leb128 ($maxBits): too large"); + } + + private function decodeSignedLeb128(int $maxBits): int + { + $result = 0; + for ($shiftBits = 0; $shiftBits < $maxBits; $shiftBits += 7) { + $b = $this->decodeByte(); + $leftBitsCount = $maxBits - $shiftBits; + if ($leftBitsCount < 7) { + if (($b & (1 << ($leftBitsCount - 1))) === 0) { + if ((($b & 0b01111111) >> $leftBitsCount) !== 0) { + throw new InvalidBinaryFormatException("signed leb128 ($maxBits): too large"); + } + } else { + if ((($b & 0b01111111) >> $leftBitsCount) + 1 !== (1 << (7 - $leftBitsCount))) { + throw new InvalidBinaryFormatException("signed leb128 ($maxBits): too large"); + } + } + } + $result |= ($b & 0b01111111) << $shiftBits; + if (($b & 0b10000000) === 0) { + if (($b & 0b01000000) === 0) { + return $result; + } else { + return $result | (~0 << ($shiftBits + 7)); + } + } + } + throw new InvalidBinaryFormatException("signed leb128 ($maxBits): too large"); + } + + /** + * @return S32 + */ + private function decodeS32(): int + { + return $this->decodeSignedLeb128(32); + } + + /** + * @return S33 + */ + private function decodeS33(): int + { + return $this->decodeSignedLeb128(33); + } + + /** + * @return S64 + */ + private function decodeS64(): int + { + return $this->decodeSignedLeb128(64); + } + + /** + * @return F32 + */ + private function decodeF32(): float + { + $buf = $this->stream->read(4); + $result = unpack('g', $buf); + if ($result === false) { + throw new InvalidBinaryFormatException("f32"); + } + assert(isset($result[1]) && is_float($result[1])); + return $result[1]; + } + + /** + * @return F64 + */ + private function decodeF64(): float + { + $buf = $this->stream->read(8); + $result = unpack('e', $buf); + if ($result === false) { + throw new InvalidBinaryFormatException("f64"); + } + assert(isset($result[1]) && is_float($result[1])); + return $result[1]; + } + + /** + * @return Name + */ + private function decodeName(): string + { + $bytes = $this->decodeVec($this->decodeByte(...)); + $name = $this->implodeUtf8BytesToString($bytes); + if ($name === null) { + throw new InvalidBinaryFormatException("name"); + } + return $name; + } + + /** + * @param list $bytes + */ + private function implodeUtf8BytesToString(array $bytes): ?string + { + $s = ''; + $count = count($bytes); + for ($i = 0; $i < $count; $i++) { + if (($bytes[$i] & 0x80) === 0) { + $code = $bytes[$i]; + } elseif (($bytes[$i] & 0xE0) === 0xC0) { + if ($count <= $i + 1) { + return null; + } + $code = (($bytes[$i] & 0x1F) << 6) | ($bytes[$i + 1] & 0x3F); + $i++; + } elseif (($bytes[$i] & 0xF0) === 0xE0) { + if ($count <= $i + 2) { + return null; + } + $code = (($bytes[$i] & 0x0F) << 12) | (($bytes[$i + 1] & 0x3F) << 6) | ($bytes[$i + 2] & 0x3F); + $i += 2; + } elseif (($bytes[$i] & 0xF8) === 0xF0) { + if ($count <= $i + 3) { + return null; + } + $code = (($bytes[$i] & 0x07) << 18) | (($bytes[$i + 1] & 0x3F) << 12) | (($bytes[$i + 2] & 0x3F) << 6) | ($bytes[$i + 3] & 0x3F); + $i += 3; + } else { + return null; + } + $s .= mb_chr($code, 'UTF-8'); + } + return $s; + } + + /** + * @return 0|positive-int + */ + private function countDataIndicesUsedInCode(Code $code): int + { + $result = array_reduce( + $code->body, + fn ($acc, $instr) => $acc + match ($instr::class) { + Instrs\Memory\DataDrop::class => 1, + Instrs\Memory\MemoryInit::class => 1, + default => 0, + }, + 0, + ); + assert(is_int($result), '$result is guaranteed not to exceed the maximum value of an integer because the number of instructions in $code is limited'); + assert(0 <= $result, '$result is guaranteed to be non-negative because it is the sum of non-negative integers'); + return $result; + } +} diff --git a/src/WebAssembly/BinaryFormat/Internal/Code.php b/src/WebAssembly/BinaryFormat/Internal/Code.php new file mode 100644 index 0000000..80010e2 --- /dev/null +++ b/src/WebAssembly/BinaryFormat/Internal/Code.php @@ -0,0 +1,23 @@ + $compressedLocals + * @param list $body + */ + public function __construct( + public array $compressedLocals, + public array $body, + ) { + } +} diff --git a/src/WebAssembly/BinaryFormat/Internal/Locals.php b/src/WebAssembly/BinaryFormat/Internal/Locals.php new file mode 100644 index 0000000..7cc0029 --- /dev/null +++ b/src/WebAssembly/BinaryFormat/Internal/Locals.php @@ -0,0 +1,22 @@ +imports as $import) { + $mod = $import->module; + $name = $import->name; + $desc = $import->desc; + $type = match ($desc::class) { + ImportDescs\Func::class => 'func', + ImportDescs\Table::class => 'table', + ImportDescs\Mem::class => 'mem', + ImportDescs\Global_::class => 'global', + default => 'unknown', + }; + $extraInfo = match ($desc::class) { + ImportDescs\Func::class => self::typeIdxToString($desc->func, $module), + ImportDescs\Table::class => self::tableTypeToString($desc->table), + ImportDescs\Mem::class => self::memTypeToString($desc->mem), + ImportDescs\Global_::class => self::globalTypeToString($desc->global), + default => 'unknown', + }; + echo "$mod::$name: $type $extraInfo\n"; + } + } + + private static function typeIdxToString(int $idx, Module $module): string + { + $type = $module->types[$idx]; + return self::funcTypeToString($type); + } + + private static function tableTypeToString(TableType $type): string + { + return self::valTypeToString(ValType::RefType($type->refType)) . ' ' . self::limitsToString($type->limits); + } + + private static function memTypeToString(MemType $type): string + { + return self::limitsToString($type->limits); + } + + private static function globalTypeToString(GlobalType $type): string + { + return self::mutToString($type->mut) . ' ' . self::valTypeToString($type->valType); + } + + private static function funcTypeToString(FuncType $type): string + { + $params = self::resultTypeToString($type->params); + $results = self::resultTypeToString($type->results); + return "($params) -> ($results)"; + } + + private static function valTypeToString(ValType $type): string + { + return match ($type::class) { + ValTypes\NumType::class => match ($type->inner) { + NumType::I32 => 'i32', + NumType::I64 => 'i64', + NumType::F32 => 'f32', + NumType::F64 => 'f64', + }, + ValTypes\VecType::class => 'v128', + ValTypes\RefType::class => match ($type->inner) { + RefType::FuncRef => 'funcref', + RefType::ExternRef => 'externref', + }, + default => 'unknown', + }; + } + + private static function limitsToString(Limits $limit): string + { + $min = $limit->min; + $max = $limit->max; + return $max === null ? "[$min, inf)" : "[$min, $max]"; + } + + private static function mutToString(Mut $mut): string + { + return match ($mut) { + Mut::Const => 'const', + Mut::Var => 'var', + }; + } + + private static function resultTypeToString(ResultType $type): string + { + return implode(', ', array_map(self::valTypeToString(...), $type->types)); + } +} diff --git a/src/WebAssembly/Execution/Allocator.php b/src/WebAssembly/Execution/Allocator.php new file mode 100644 index 0000000..67eb467 --- /dev/null +++ b/src/WebAssembly/Execution/Allocator.php @@ -0,0 +1,165 @@ + $externVals + */ + public function allocPreInitModule( + Module $module, + array $externVals, + ): ModuleInst { + $m = new ModuleInst($module->types, [], [], [], [], [], [], []); + + foreach ($externVals as $externVal) { + match ($externVal::class) { + ExternVals\Func::class => $m->funcAddrs[] = $externVal->addr, + ExternVals\Global_::class => $m->globalAddrs[] = $externVal->addr, + default => null, + }; + } + foreach ($module->funcs as $func) { + $m->funcAddrs[] = $this->allocFunc($func, $m); + } + + return $m; + } + + /** + * @param list $externVals + * @param list $vals + * @param list> $refsList + * @param list $preAllocatedFuncs + */ + public function allocModule( + Module $module, + array $externVals, + array $vals, + array $refsList, + array $preAllocatedFuncs, + ): ModuleInst { + $m = new ModuleInst($module->types, [], [], [], [], [], [], []); + + foreach ($externVals as $externVal) { + match ($externVal::class) { + ExternVals\Func::class => null, // handled below. + ExternVals\Table::class => $m->tableAddrs[] = $externVal->addr, + ExternVals\Mem::class => $m->memAddrs[] = $externVal->addr, + ExternVals\Global_::class => $m->globalAddrs[] = $externVal->addr, + default => throw new RuntimeException("unreachable"), + }; + } + + foreach ($preAllocatedFuncs as $funcAddr) { + $m->funcAddrs[] = $funcAddr; + $funcInst = $this->store->funcs[$funcAddr]; + if ($funcInst instanceof FuncInsts\Wasm) { + // @phpstan-ignore-next-line + $this->store->funcs[$funcAddr] = FuncInst::Wasm( + $funcInst->type, + $m, + $funcInst->code, + ); + } + } + foreach ($module->tables as $table) { + $m->tableAddrs[] = $this->allocTable($table->type, Ref::RefNull($table->type->refType)); + } + foreach ($module->mems as $mem) { + $m->memAddrs[] = $this->allocMem($mem->type); + } + foreach ($module->datas as $data) { + $m->dataAddrs[] = $this->allocData($data->init); + } + + foreach ($module->globals as $i => $global) { + $m->globalAddrs[] = $this->allocGlobal($global->type, $vals[$i]); + } + foreach ($module->elems as $i => $elem) { + $m->elemAddrs[] = $this->allocElem($elem->type, $refsList[$i]); + } + + foreach ($module->exports as $export) { + $value = match ($export->desc::class) { + ExportDescs\Func::class => ExternVal::Func($m->funcAddrs[$export->desc->func]), + ExportDescs\Table::class => ExternVal::Table($m->tableAddrs[$export->desc->table]), + ExportDescs\Mem::class => ExternVal::Mem($m->memAddrs[$export->desc->mem]), + ExportDescs\Global_::class => ExternVal::Global_($m->globalAddrs[$export->desc->global]), + default => throw new RuntimeException("unreachable"), + }; + $m->exports[] = new ExportInst($export->name, $value); + } + + return $m; + } + + private function allocFunc(Func $func, ModuleInst $moduleInst): int + { + $funcType = $moduleInst->types[$func->type]; + $funcInst = FuncInst::Wasm($funcType, $moduleInst, $func); + $this->store->funcs[] = $funcInst; + return count($this->store->funcs) - 1; + } + + private function allocTable(TableType $tableType, Ref $ref): int + { + $minSize = $tableType->limits->min; + $elem = array_fill(0, $minSize, $ref); + $tableInst = new TableInst($tableType, $elem); + $this->store->tables[] = $tableInst; + return count($this->store->tables) - 1; + } + + private function allocMem(MemType $memType): int + { + $memInst = new MemInst($memType); + $this->store->mems[] = $memInst; + return count($this->store->mems) - 1; + } + + private function allocGlobal(GlobalType $globalType, int|float|Ref $val): int + { + $globalInst = new GlobalInst($globalType, $val); + $this->store->globals[] = $globalInst; + return count($this->store->globals) - 1; + } + + /** + * @param list $elem + */ + private function allocElem(RefType $refType, array $elem): int + { + $elemInst = new ElemInst($refType, $elem); + $this->store->elems[] = $elemInst; + return count($this->store->elems) - 1; + } + + /** + * @param list $data + */ + private function allocData(array $data): int + { + $dataInst = new DataInst($data); + $this->store->datas[] = $dataInst; + return count($this->store->datas) - 1; + } +} diff --git a/src/WebAssembly/Execution/DataInst.php b/src/WebAssembly/Execution/DataInst.php new file mode 100644 index 0000000..f6918c6 --- /dev/null +++ b/src/WebAssembly/Execution/DataInst.php @@ -0,0 +1,16 @@ + $data + */ + public function __construct( + public array $data, + ) { + } +} diff --git a/src/WebAssembly/Execution/ElemInst.php b/src/WebAssembly/Execution/ElemInst.php new file mode 100644 index 0000000..422cd62 --- /dev/null +++ b/src/WebAssembly/Execution/ElemInst.php @@ -0,0 +1,19 @@ + $elem + */ + public function __construct( + public RefType $type, + public array $elem, + ) { + } +} diff --git a/src/WebAssembly/Execution/ExportInst.php b/src/WebAssembly/Execution/ExportInst.php new file mode 100644 index 0000000..7fe04cd --- /dev/null +++ b/src/WebAssembly/Execution/ExportInst.php @@ -0,0 +1,17 @@ + $arity + * @param list $locals + */ + public function __construct( + public readonly int $arity, + public array $locals, + public readonly ModuleInst $module, + public string $debugName, + ) { + } +} diff --git a/src/WebAssembly/Execution/FuncInst.php b/src/WebAssembly/Execution/FuncInst.php new file mode 100644 index 0000000..dda32cf --- /dev/null +++ b/src/WebAssembly/Execution/FuncInst.php @@ -0,0 +1,21 @@ + $arity + */ + public function __construct( + public int $arity, + ) { + } +} diff --git a/src/WebAssembly/Execution/MemInst.php b/src/WebAssembly/Execution/MemInst.php new file mode 100644 index 0000000..802f334 --- /dev/null +++ b/src/WebAssembly/Execution/MemInst.php @@ -0,0 +1,662 @@ +ffi = FFI::cdef(); + $this->initInternalMemory($type->limits->min); + } + + public function size(): int + { + return $this->dataSize; + } + + public function nPages(): int + { + return $this->size() / self::PAGE_SIZE; + } + + /** + * @return int + * Returns the original size of the memory in pages or -1 if failed. + */ + public function grow(int $n): int + { + $sz = $this->nPages(); + $len = $sz + $n; + if (self::MAX_PAGES < $len) { + return -1; + } + + $limits = $this->type->limits; + $limits_ = new Limits($len, $limits->max); + if (!$limits_->isValid()) { + return -1; + } + + $originalSize = $this->size(); + // @phpstan-ignore-next-line + $originalData = $this->ffi->new("uint8_t[$originalSize+8]"); + assert($originalData !== null); + FFI::memcpy($originalData, $this->dataU8, $originalSize); + + $this->initInternalMemory($len); + + FFI::memcpy($this->dataU8, $originalData, $originalSize); + + return $sz; + } + + private function initInternalMemory(int $n): void + { + $this->dataSize = $n * self::PAGE_SIZE; + + // @phpstan-ignore-next-line + $this->dataU8 = $this->ffi->new("uint8_t[$this->dataSize+8]"); + // @phpstan-ignore-next-line + $this->dataS8 = $this->ffi->cast("int8_t[$this->dataSize]", $this->dataU8); + + // @phpstan-ignore-next-line + $castInt = fn ($n, $signed, $offset) => $this->ffi->cast( + sprintf("%sint%d_t[$this->dataSize/%d]", $signed ? "" : "u", $n, $n / 8), + // @phpstan-ignore-next-line + $this->ffi->cast("uint8_t[$this->dataSize+8]", $this->dataU8 + $offset), + ); + + // @phpstan-ignore-next-line + $castFloat = fn ($n, $offset) => $this->ffi->cast( + sprintf("%s[$this->dataSize/%d]", $n === 32 ? "float" : "double", $n / 8), + // @phpstan-ignore-next-line + $this->ffi->cast("uint8_t[$this->dataSize+8]", $this->dataU8 + $offset), + ); + + // @phpstan-ignore-next-line + $this->dataU16_0 = $castInt(16, false, 0); + // @phpstan-ignore-next-line + $this->dataU16_1 = $castInt(16, false, 1); + // @phpstan-ignore-next-line + $this->dataS16_0 = $castInt(16, true, 0); + // @phpstan-ignore-next-line + $this->dataS16_1 = $castInt(16, true, 1); + + // @phpstan-ignore-next-line + $this->dataU32_0 = $castInt(32, false, 0); + // @phpstan-ignore-next-line + $this->dataU32_1 = $castInt(32, false, 1); + // @phpstan-ignore-next-line + $this->dataU32_2 = $castInt(32, false, 2); + // @phpstan-ignore-next-line + $this->dataU32_3 = $castInt(32, false, 3); + // @phpstan-ignore-next-line + $this->dataS32_0 = $castInt(32, true, 0); + // @phpstan-ignore-next-line + $this->dataS32_1 = $castInt(32, true, 1); + // @phpstan-ignore-next-line + $this->dataS32_2 = $castInt(32, true, 2); + // @phpstan-ignore-next-line + $this->dataS32_3 = $castInt(32, true, 3); + + // @phpstan-ignore-next-line + $this->dataS64_0 = $castInt(64, true, 0); + // @phpstan-ignore-next-line + $this->dataS64_1 = $castInt(64, true, 1); + // @phpstan-ignore-next-line + $this->dataS64_2 = $castInt(64, true, 2); + // @phpstan-ignore-next-line + $this->dataS64_3 = $castInt(64, true, 3); + // @phpstan-ignore-next-line + $this->dataS64_4 = $castInt(64, true, 4); + // @phpstan-ignore-next-line + $this->dataS64_5 = $castInt(64, true, 5); + // @phpstan-ignore-next-line + $this->dataS64_6 = $castInt(64, true, 6); + // @phpstan-ignore-next-line + $this->dataS64_7 = $castInt(64, true, 7); + + // @phpstan-ignore-next-line + $this->dataF32_0 = $castFloat(32, 0); + // @phpstan-ignore-next-line + $this->dataF32_1 = $castFloat(32, 1); + // @phpstan-ignore-next-line + $this->dataF32_2 = $castFloat(32, 2); + // @phpstan-ignore-next-line + $this->dataF32_3 = $castFloat(32, 3); + + // @phpstan-ignore-next-line + $this->dataF64_0 = $castFloat(64, 0); + // @phpstan-ignore-next-line + $this->dataF64_1 = $castFloat(64, 1); + // @phpstan-ignore-next-line + $this->dataF64_2 = $castFloat(64, 2); + // @phpstan-ignore-next-line + $this->dataF64_3 = $castFloat(64, 3); + // @phpstan-ignore-next-line + $this->dataF64_4 = $castFloat(64, 4); + // @phpstan-ignore-next-line + $this->dataF64_5 = $castFloat(64, 5); + // @phpstan-ignore-next-line + $this->dataF64_6 = $castFloat(64, 6); + // @phpstan-ignore-next-line + $this->dataF64_7 = $castFloat(64, 7); + + // @phpstan-ignore-next-line + FFI::memset($this->dataU8, 0, $this->dataSize); + } + + /** + * @param list $data + */ + public function copyData(array $data, int $src, int $dst, int $len): void + { + assert(0 <= $len); + assert(0 <= $src); + assert(0 <= $dst); + assert($src + $len <= count($data)); + assert($dst + $len <= $this->size()); + for ($i = 0; $i < $len; $i++) { + $this->storeByte($dst + $i, $data[$src + $i]); + } + } + + public function memcpy(int $dst, int $src, int $len): void + { + assert(0 <= $len); + assert(0 <= $src); + assert(0 <= $dst); + assert($src + $len <= $this->size()); + assert($dst + $len <= $this->size()); + if ($src === $dst || $len === 0) { + return; + } + for ($i = 0; $i < $len; $i++) { + $s = ($dst < $src) ? ($src + $i) : ($src + $len - 1 - $i); + $d = ($dst < $src) ? ($dst + $i) : ($dst + $len - 1 - $i); + $x = $this->loadByte($s); + assert($x !== null); + $this->storeByte($d, $x); + } + } + + public function memset(int $dst, int $c, int $len): void + { + assert(0 <= $len); + assert(0 <= $dst); + assert($dst + $len <= $this->size()); + for ($i = 0; $i < $len; $i++) { + $this->storeI32_s8($dst + $i, $c); + } + } + + /** + * @return ?S32 + */ + public function loadI32_s8(int $ptr): ?int + { + if ($this->size() < $ptr + 1) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataS8[$ptr]; + assert(-0x80 <= $c && $c <= 0x7F, "$c"); + return $c; + } + + /** + * @return ?S32 + */ + public function loadI32_u8(int $ptr): ?int + { + if ($this->size() < $ptr + 1) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataU8[$ptr]; + assert(0 <= $c && $c <= 0xFF, "$c"); + return $c; + } + + /** + * @return ?S32 + */ + public function loadI32_s16(int $ptr): ?int + { + if ($this->size() < $ptr + 2) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataS16($ptr)[$ptr >> 1]; + assert(-0x8000 <= $c && $c <= 0x7FFF, "$c"); + return $c; + } + + /** + * @return ?S32 + */ + public function loadI32_u16(int $ptr): ?int + { + if ($this->size() < $ptr + 2) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataU16($ptr)[$ptr >> 1]; + assert(0 <= $c && $c <= 0xFFFF, "$c"); + return $c; + } + + /** + * @return ?S32 + */ + public function loadI32_s32(int $ptr): ?int + { + if ($this->size() < $ptr + 4) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataS32($ptr)[$ptr >> 2]; + assert(-0x80000000 <= $c && $c <= 0x7FFFFFFF, "$c"); + return $c; + } + + /** + * @return ?S64 + */ + public function loadI64_s8(int $ptr): ?int + { + if ($this->size() < $ptr + 1) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataS8[$ptr]; + assert(-0x80 <= $c && $c <= 0x7F, "$c"); + return $c; + } + + /** + * @return ?S64 + */ + public function loadI64_u8(int $ptr): ?int + { + if ($this->size() < $ptr + 1) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataU8[$ptr]; + assert(0 <= $c && $c <= 0xFF, "$c"); + return $c; + } + + /** + * @return ?S64 + */ + public function loadI64_s16(int $ptr): ?int + { + if ($this->size() < $ptr + 2) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataS16($ptr)[$ptr >> 1]; + assert(-0x8000 <= $c && $c <= 0x7FFF, "$c"); + return $c; + } + + /** + * @return ?S64 + */ + public function loadI64_u16(int $ptr): ?int + { + if ($this->size() < $ptr + 2) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataU16($ptr)[$ptr >> 1]; + assert(0 <= $c && $c <= 0xFFFF, "$c"); + return $c; + } + + /** + * @return ?S64 + */ + public function loadI64_s32(int $ptr): ?int + { + if ($this->size() < $ptr + 4) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataS32($ptr)[$ptr >> 2]; + assert(-0x80000000 <= $c && $c <= 0x7FFFFFFF, "$c"); + return $c; + } + + /** + * @return ?S64 + */ + public function loadI64_u32(int $ptr): ?int + { + if ($this->size() < $ptr + 4) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataU32($ptr)[$ptr >> 2]; + assert(0 <= $c && $c <= 0xFFFFFFFF, "$c"); + return $c; + } + + /** + * @return ?S64 + */ + public function loadI64_s64(int $ptr): ?int + { + if ($this->size() < $ptr + 8) { + return null; + } + // @phpstan-ignore-next-line + $c = $this->dataS64($ptr)[$ptr >> 3]; + assert(-0x8000000000000000 <= $c && $c <= 0x7FFFFFFFFFFFFFFF, "$c"); + return $c; + } + + /** + * @return ?F32 + */ + public function loadF32(int $ptr): ?float + { + if ($this->size() < $ptr + 4) { + return null; + } + // @phpstan-ignore-next-line + return $this->dataF32($ptr)[$ptr >> 2]; + } + + /** + * @return ?F64 + */ + public function loadF64(int $ptr): ?float + { + if ($this->size() < $ptr + 8) { + return null; + } + // @phpstan-ignore-next-line + return $this->dataF64($ptr)[$ptr >> 3]; + } + + /** + * @return ?int + */ + public function loadByte(int $ptr): ?int + { + if ($this->size() < $ptr + 1) { + return null; + } + // @phpstan-ignore-next-line + return $this->dataU8[$ptr]; + } + + /** + * @return bool + */ + public function storeByte(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 1) { + return false; + } + // @phpstan-ignore-next-line + $this->dataU8[$ptr] = $c; + return true; + } + + /** + * @param S32 $c + * @return bool + */ + public function storeI32_s8(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 1) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS8[$ptr] = $c; + return true; + } + + /** + * @param S32 $c + * @return bool + */ + public function storeI32_s16(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 2) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS16($ptr)[$ptr >> 1] = $c; + return true; + } + + /** + * @param S32 $c + * @return bool + */ + public function storeI32_s32(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 4) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS32($ptr)[$ptr >> 2] = $c; + return true; + } + + /** + * @param S64 $c + * @return bool + */ + public function storeI64_s8(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 1) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS8[$ptr] = $c; + return true; + } + + /** + * @param S64 $c + * @return bool + */ + public function storeI64_s16(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 2) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS16($ptr)[$ptr >> 1] = $c; + return true; + } + + /** + * @param S64 $c + * @return bool + */ + public function storeI64_s32(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 4) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS32($ptr)[$ptr >> 2] = $c; + return true; + } + + /** + * @param S64 $c + * @return bool + */ + public function storeI64_s64(int $ptr, int $c): bool + { + if ($this->size() < $ptr + 8) { + return false; + } + // @phpstan-ignore-next-line + $this->dataS64($ptr)[$ptr >> 3] = $c; + return true; + } + + /** + * @param F32 $c + * @return bool + */ + public function storeF32(int $ptr, float $c): bool + { + if ($this->size() < $ptr + 4) { + return false; + } + // @phpstan-ignore-next-line + $this->dataF32($ptr)[$ptr >> 2] = $c; + return true; + } + + /** + * @param F64 $c + * @return bool + */ + public function storeF64(int $ptr, float $c): bool + { + if ($this->size() < $ptr + 8) { + return false; + } + // @phpstan-ignore-next-line + $this->dataF64($ptr)[$ptr >> 3] = $c; + return true; + } + + private function dataU16(int $ptr): CData + { + return ($ptr & 1) !== 0 ? $this->dataU16_1 : $this->dataU16_0; + } + + private function dataS16(int $ptr): CData + { + return ($ptr & 1) !== 0 ? $this->dataS16_1 : $this->dataS16_0; + } + + private function dataU32(int $ptr): CData + { + return match ($ptr & 3) { + 0 => $this->dataU32_0, + 1 => $this->dataU32_1, + 2 => $this->dataU32_2, + 3 => $this->dataU32_3, + }; + } + + private function dataS32(int $ptr): CData + { + return match ($ptr & 3) { + 0 => $this->dataS32_0, + 1 => $this->dataS32_1, + 2 => $this->dataS32_2, + 3 => $this->dataS32_3, + }; + } + + private function dataS64(int $ptr): CData + { + return match ($ptr & 7) { + 0 => $this->dataS64_0, + 1 => $this->dataS64_1, + 2 => $this->dataS64_2, + 3 => $this->dataS64_3, + 4 => $this->dataS64_4, + 5 => $this->dataS64_5, + 6 => $this->dataS64_6, + 7 => $this->dataS64_7, + }; + } + + private function dataF32(int $ptr): CData + { + return match ($ptr & 3) { + 0 => $this->dataF32_0, + 1 => $this->dataF32_1, + 2 => $this->dataF32_2, + 3 => $this->dataF32_3, + }; + } + + private function dataF64(int $ptr): CData + { + return match ($ptr & 7) { + 0 => $this->dataF64_0, + 1 => $this->dataF64_1, + 2 => $this->dataF64_2, + 3 => $this->dataF64_3, + 4 => $this->dataF64_4, + 5 => $this->dataF64_5, + 6 => $this->dataF64_6, + 7 => $this->dataF64_7, + }; + } +} diff --git a/src/WebAssembly/Execution/ModuleInst.php b/src/WebAssembly/Execution/ModuleInst.php new file mode 100644 index 0000000..37bbcfd --- /dev/null +++ b/src/WebAssembly/Execution/ModuleInst.php @@ -0,0 +1,32 @@ + $types + * @param list $funcAddrs + * @param list $tableAddrs + * @param list $memAddrs + * @param list $globalAddrs + * @param list $elemAddrs + * @param list $dataAddrs + * @param list $exports + */ + public function __construct( + public array $types, + public array $funcAddrs, + public array $tableAddrs, + public array $memAddrs, + public array $globalAddrs, + public array $elemAddrs, + public array $dataAddrs, + public array $exports, + ) { + } +} diff --git a/src/WebAssembly/Execution/NumericOps.php b/src/WebAssembly/Execution/NumericOps.php new file mode 100644 index 0000000..895b8c2 --- /dev/null +++ b/src/WebAssembly/Execution/NumericOps.php @@ -0,0 +1,193 @@ +> 63) & 1) === 1 ? -1 : 1; + } elseif ($p !== 0.0) { + return $p < 0.0 ? -1 : 1; + } else { + // Comparison with 0 does not work for -0.0. + return fdiv(1, $p) < 0.0 ? -1 : 1; + } + } +} diff --git a/src/WebAssembly/Execution/Ref.php b/src/WebAssembly/Execution/Ref.php new file mode 100644 index 0000000..f7b6760 --- /dev/null +++ b/src/WebAssembly/Execution/Ref.php @@ -0,0 +1,25 @@ + $values + */ + final public static function Values(array $values): Results\Values + { + return new Results\Values($values); + } + + final public static function Trap(): Results\Trap + { + return new Results\Trap(); + } +} diff --git a/src/WebAssembly/Execution/Results/Trap.php b/src/WebAssembly/Execution/Results/Trap.php new file mode 100644 index 0000000..e0a9cfd --- /dev/null +++ b/src/WebAssembly/Execution/Results/Trap.php @@ -0,0 +1,11 @@ + $values + */ + protected function __construct( + public array $values, + ) { + } +} diff --git a/src/WebAssembly/Execution/Runtime.php b/src/WebAssembly/Execution/Runtime.php new file mode 100644 index 0000000..dbefea2 --- /dev/null +++ b/src/WebAssembly/Execution/Runtime.php @@ -0,0 +1,2668 @@ + + */ + private array $instrMetrics = []; + + private function __construct( + public readonly Store $store, + public readonly Stack $stack, + public readonly ModuleInst $module, + ) { + } + + /** + * @param array> $imports + */ + public static function instantiate( + Store $store, + Module $module, + array $imports, + ): self { + $externVals = []; + foreach ($module->imports as $import) { + $extern = $imports[$import->module][$import->name] ?? null; + if ($extern === null) { + throw new RuntimeException("instantiate: import not found: {$import->module}::{$import->name}"); + } + $externVals[] = $store->register($extern); + } + return self::doInstantiate($store, $module, $externVals); + } + + /** + * @param list $externVals + */ + private static function doInstantiate( + Store $store, + Module $module, + array $externVals, + ): self { + $allocator = new Allocator($store); + + $moduleInstInit = $allocator->allocPreInitModule($module, $externVals); + + $stack = new Stack(); + $frameInit = new Frame(0, [], $moduleInstInit, 'preinit'); + $stack->pushFrame($frameInit); + + $runtimeInit = new self($store, $stack, $moduleInstInit); + + $vals = []; + foreach ($module->globals as $global) { + $vals[] = $runtimeInit->evalInstrsForInit($global->init); + assert($stack->top() === $frameInit); + } + + $refsList = []; + foreach ($module->elems as $elem) { + $refs = []; + foreach ($elem->init as $expr) { + $result = $runtimeInit->evalInstrsForInit($expr); + assert($result instanceof Ref); + $refs[] = $result; + } + $refsList[] = $refs; + } + + assert($stack->top() === $frameInit); + $stack->popFrame(); + + $moduleInst = $allocator->allocModule( + $module, + $externVals, + $vals, + $refsList, + $moduleInstInit->funcAddrs, + ); + + $runtime = new self($store, $stack, $moduleInst); + + $frame = new Frame(0, [], $moduleInst, 'init'); + $stack->pushFrame($frame); + + foreach ($module->elems as $i => $elem) { + if ($elem->mode instanceof ElemModes\Active) { + $n = count($elem->init); + $instrs = $elem->mode->offset; + $instrs[] = Instr::I32Const(0); + $instrs[] = Instr::I32Const($n); + $instrs[] = Instr::TableInit($elem->mode->table, $i); + $instrs[] = Instr::ElemDrop($i); + $runtime->execInstrsForInit($instrs); + } elseif ($elem->mode instanceof ElemModes\Declarative) { + $runtime->execInstrsForInit([Instr::ElemDrop($i)]); + } + } + foreach ($module->datas as $i => $data) { + if ($data->mode instanceof DataModes\Active) { + assert($data->mode->memory === 0); + $n = count($data->init); + $instrs = $data->mode->offset; + $instrs[] = Instr::I32Const(0); + $instrs[] = Instr::I32Const($n); + $instrs[] = Instr::MemoryInit($i); + $instrs[] = Instr::DataDrop($i); + $runtime->execInstrsForInit($instrs); + } + } + + if ($module->start !== null) { + $runtime->execInstrsForInit([Instr::Call($module->start->func)]); + } + + assert($stack->top() === $frame); + $stack->popFrame(); + + return new self($store, $stack, $moduleInst); + } + + public function getExport(string $name): ?ExternVal + { + foreach ($this->module->exports as $export) { + if ($name === $export->name) { + return $export->value; + } + } + return null; + } + + public function getExportedTable(string $name): ?TableInst + { + $export = $this->getExport($name); + if ($export instanceof ExternVals\Table) { + return $this->store->tables[$export->addr]; + } + return null; + } + + public function getExportedMemory(string $name): ?MemInst + { + $export = $this->getExport($name); + if ($export instanceof ExternVals\Mem) { + return $this->store->mems[$export->addr]; + } + return null; + } + + /** + * @return array + */ + public function getInstrMetrics(): array + { + asort($this->instrMetrics); + return $this->instrMetrics; + } + + /** + * @param list $vals + * @return list + */ + public function invoke(string $name, array $vals): array + { + try { + $export = $this->getExport($name); + assert($export instanceof ExternVals\Func); + $funcAddr = $export->addr; + + $funcInst = $this->store->funcs[$funcAddr]; + assert($funcInst instanceof FuncInsts\Wasm); + $paramTypes = $funcInst->type->params->types; + $resultTypes = $funcInst->type->results->types; + if (count($paramTypes) !== count($vals)) { + throw new RuntimeException("invoke($name) invalid function arity: expected " . count($paramTypes) . ", got " . count($vals)); + } + $f = new Frame(0, [], new ModuleInst([], [], [], [], [], [], [], []), "export: $name"); + $this->stack->pushFrame($f); + foreach ($vals as $val) { + $this->stack->pushValue($val); + } + $this->doInvokeFunc($funcAddr); + $results = $this->stack->popNValues(count($resultTypes)); + $this->stack->popFrame(); + return array_reverse($results); + } catch (RuntimeException $e) { + // @todo more reliable way to clear the stack + $this->stack->clear(); + throw $e; + } + } + + public function invokeByFuncAddr(int $funcAddr): void + { + $this->doInvokeFunc($funcAddr); + } + + private function doInvokeFunc(int $funcAddr): void + { + $fn = $this->store->funcs[$funcAddr]; + if ($fn instanceof FuncInsts\Wasm) { + $this->doInvokeWasmFunc($fn, $funcAddr); + } elseif ($fn instanceof FuncInsts\Host) { + $this->doInvokeHostFunc($fn); + } else { + throw new RuntimeException("doInvokeFunc: unreachable"); + } + } + + private function doInvokeWasmFunc(FuncInsts\Wasm $fn, int $funcAddr): void + { + $paramTypes = $fn->type->params->types; + $n = count($paramTypes); + $resultTypes = $fn->type->results->types; + $m = count($resultTypes); + $ts = $fn->code->locals; + $vals = $this->stack->popNValues($n); + $f = new Frame( + $m, + array_merge( + array_reverse($vals), + array_map(fn ($local) => self::defaultValueFromValType($local->type), $ts), + ), + $fn->module, + "wasm: $funcAddr", + ); + $this->activateFrame($f); + $l = new Label($m); + $this->execInstrs($fn->code->body, $l, null); + $this->deactivateFrame($m); + } + + private function activateFrame(Frame $f): void + { + $this->stack->pushFrame($f); + } + + private function deactivateFrame(int $arity): void + { + $vals = $this->stack->popNValues($arity); + $this->stack->popEntriesToCurrentFrame(); + for ($i = $arity - 1; 0 <= $i; $i--) { + $this->stack->pushValue($vals[$i]); + } + } + + private function activateLabel(Label $l): void + { + $this->stack->pushLabel($l); + } + + private function deactivateLabel(?int $arity): void + { + if ($arity === null) { + $vals = $this->stack->popValuesToLabel(); + } else { + $vals = $this->stack->popNValues($arity); + $this->stack->popValuesToLabel(); + } + for ($i = count($vals) - 1; 0 <= $i; $i--) { + $this->stack->pushValue($vals[$i]); + } + } + + private function doInvokeHostFunc(FuncInsts\Host $fn): void + { + $paramTypes = $fn->type->params->types; + $n = count($paramTypes); + $resultTypes = $fn->type->results->types; + $m = count($resultTypes); + + $params = array_reverse($this->stack->popNValues($n)); + $results = ($fn->callback)($this, ...$params); + if ($results === null) { + assert($m === 0); + return; + } + if (!is_array($results)) { + $results = [$results]; + } + assert($m === count($results)); + foreach ($results as $result) { + $this->stack->pushValue($result); + } + } + + /** + * @param list $instrs + * @param ?list $params + */ + private function execInstrs( + array $instrs, + Label $l, + ?array $params, + ): ?int { + $this->activateLabel($l); + if ($params !== null) { + foreach ($params as $param) { + $this->stack->pushValue($param); + } + } + + foreach ($instrs as $i => $instr) { + $result = $this->execInstr($instr); + if ($result !== null) { + return $result; + } + } + + $this->deactivateLabel(null); + return null; + } + + /** + * @param list $instrs + */ + private function execInstrsForInit(array $instrs): void + { + foreach ($instrs as $i) { + $this->execInstr($i); + } + } + + /** + * @param list $instrs + */ + private function evalInstrsForInit(array $instrs): int|float|Ref + { + $this->execInstrsForInit($instrs); + $result = $this->stack->popValue(); + return $result; + } + + private function execInstr(Instr $instr): ?int + { + return match ($instr::class) { + Instrs\Numeric\F32Abs::class => $this->execInstrNumericF32Abs($instr), + Instrs\Numeric\F32Add::class => $this->execInstrNumericF32Add($instr), + Instrs\Numeric\F32Ceil::class => $this->execInstrNumericF32Ceil($instr), + Instrs\Numeric\F32Const::class => $this->execInstrNumericF32Const($instr), + Instrs\Numeric\F32ConvertI32S::class => $this->execInstrNumericF32ConvertI32S($instr), + Instrs\Numeric\F32ConvertI32U::class => $this->execInstrNumericF32ConvertI32U($instr), + Instrs\Numeric\F32ConvertI64S::class => $this->execInstrNumericF32ConvertI64S($instr), + Instrs\Numeric\F32ConvertI64U::class => $this->execInstrNumericF32ConvertI64U($instr), + Instrs\Numeric\F32CopySign::class => $this->execInstrNumericF32CopySign($instr), + Instrs\Numeric\F32DemoteF64::class => $this->execInstrNumericF32DemoteF64($instr), + Instrs\Numeric\F32Div::class => $this->execInstrNumericF32Div($instr), + Instrs\Numeric\F32Eq::class => $this->execInstrNumericF32Eq($instr), + Instrs\Numeric\F32Floor::class => $this->execInstrNumericF32Floor($instr), + Instrs\Numeric\F32Ge::class => $this->execInstrNumericF32Ge($instr), + Instrs\Numeric\F32Gt::class => $this->execInstrNumericF32Gt($instr), + Instrs\Numeric\F32Le::class => $this->execInstrNumericF32Le($instr), + Instrs\Numeric\F32Lt::class => $this->execInstrNumericF32Lt($instr), + Instrs\Numeric\F32Max::class => $this->execInstrNumericF32Max($instr), + Instrs\Numeric\F32Min::class => $this->execInstrNumericF32Min($instr), + Instrs\Numeric\F32Mul::class => $this->execInstrNumericF32Mul($instr), + Instrs\Numeric\F32Ne::class => $this->execInstrNumericF32Ne($instr), + Instrs\Numeric\F32Nearest::class => $this->execInstrNumericF32Nearest($instr), + Instrs\Numeric\F32Neg::class => $this->execInstrNumericF32Neg($instr), + Instrs\Numeric\F32ReinterpretI32::class => $this->execInstrNumericF32ReinterpretI32($instr), + Instrs\Numeric\F32ReinterpretI64::class => $this->execInstrNumericF32ReinterpretI64($instr), + Instrs\Numeric\F32Sqrt::class => $this->execInstrNumericF32Sqrt($instr), + Instrs\Numeric\F32Sub::class => $this->execInstrNumericF32Sub($instr), + Instrs\Numeric\F32Trunc::class => $this->execInstrNumericF32Trunc($instr), + Instrs\Numeric\F64Abs::class => $this->execInstrNumericF64Abs($instr), + Instrs\Numeric\F64Add::class => $this->execInstrNumericF64Add($instr), + Instrs\Numeric\F64Ceil::class => $this->execInstrNumericF64Ceil($instr), + Instrs\Numeric\F64Const::class => $this->execInstrNumericF64Const($instr), + Instrs\Numeric\F64ConvertI32S::class => $this->execInstrNumericF64ConvertI32S($instr), + Instrs\Numeric\F64ConvertI32U::class => $this->execInstrNumericF64ConvertI32U($instr), + Instrs\Numeric\F64ConvertI64S::class => $this->execInstrNumericF64ConvertI64S($instr), + Instrs\Numeric\F64ConvertI64U::class => $this->execInstrNumericF64ConvertI64U($instr), + Instrs\Numeric\F64CopySign::class => $this->execInstrNumericF64CopySign($instr), + Instrs\Numeric\F64Div::class => $this->execInstrNumericF64Div($instr), + Instrs\Numeric\F64Eq::class => $this->execInstrNumericF64Eq($instr), + Instrs\Numeric\F64Floor::class => $this->execInstrNumericF64Floor($instr), + Instrs\Numeric\F64Ge::class => $this->execInstrNumericF64Ge($instr), + Instrs\Numeric\F64Gt::class => $this->execInstrNumericF64Gt($instr), + Instrs\Numeric\F64Le::class => $this->execInstrNumericF64Le($instr), + Instrs\Numeric\F64Lt::class => $this->execInstrNumericF64Lt($instr), + Instrs\Numeric\F64Max::class => $this->execInstrNumericF64Max($instr), + Instrs\Numeric\F64Min::class => $this->execInstrNumericF64Min($instr), + Instrs\Numeric\F64Mul::class => $this->execInstrNumericF64Mul($instr), + Instrs\Numeric\F64Ne::class => $this->execInstrNumericF64Ne($instr), + Instrs\Numeric\F64Nearest::class => $this->execInstrNumericF64Nearest($instr), + Instrs\Numeric\F64Neg::class => $this->execInstrNumericF64Neg($instr), + Instrs\Numeric\F64PromoteF32::class => $this->execInstrNumericF64PromoteF32($instr), + Instrs\Numeric\F64ReinterpretI32::class => $this->execInstrNumericF64ReinterpretI32($instr), + Instrs\Numeric\F64ReinterpretI64::class => $this->execInstrNumericF64ReinterpretI64($instr), + Instrs\Numeric\F64Sqrt::class => $this->execInstrNumericF64Sqrt($instr), + Instrs\Numeric\F64Sub::class => $this->execInstrNumericF64Sub($instr), + Instrs\Numeric\F64Trunc::class => $this->execInstrNumericF64Trunc($instr), + Instrs\Numeric\I32Add::class => $this->execInstrNumericI32Add($instr), + Instrs\Numeric\I32And::class => $this->execInstrNumericI32And($instr), + Instrs\Numeric\I32Clz::class => $this->execInstrNumericI32Clz($instr), + Instrs\Numeric\I32Const::class => $this->execInstrNumericI32Const($instr), + Instrs\Numeric\I32Ctz::class => $this->execInstrNumericI32Ctz($instr), + Instrs\Numeric\I32DivS::class => $this->execInstrNumericI32DivS($instr), + Instrs\Numeric\I32DivU::class => $this->execInstrNumericI32DivU($instr), + Instrs\Numeric\I32Eq::class => $this->execInstrNumericI32Eq($instr), + Instrs\Numeric\I32Eqz::class => $this->execInstrNumericI32Eqz($instr), + Instrs\Numeric\I32Extend16S::class => $this->execInstrNumericI32Extend16S($instr), + Instrs\Numeric\I32Extend8S::class => $this->execInstrNumericI32Extend8S($instr), + Instrs\Numeric\I32GeS::class => $this->execInstrNumericI32GeS($instr), + Instrs\Numeric\I32GeU::class => $this->execInstrNumericI32GeU($instr), + Instrs\Numeric\I32GtS::class => $this->execInstrNumericI32GtS($instr), + Instrs\Numeric\I32GtU::class => $this->execInstrNumericI32GtU($instr), + Instrs\Numeric\I32LeS::class => $this->execInstrNumericI32LeS($instr), + Instrs\Numeric\I32LeU::class => $this->execInstrNumericI32LeU($instr), + Instrs\Numeric\I32LtS::class => $this->execInstrNumericI32LtS($instr), + Instrs\Numeric\I32LtU::class => $this->execInstrNumericI32LtU($instr), + Instrs\Numeric\I32Mul::class => $this->execInstrNumericI32Mul($instr), + Instrs\Numeric\I32Ne::class => $this->execInstrNumericI32Ne($instr), + Instrs\Numeric\I32Or::class => $this->execInstrNumericI32Or($instr), + Instrs\Numeric\I32Popcnt::class => $this->execInstrNumericI32Popcnt($instr), + Instrs\Numeric\I32ReinterpretF32::class => $this->execInstrNumericI32ReinterpretF32($instr), + Instrs\Numeric\I32ReinterpretF64::class => $this->execInstrNumericI32ReinterpretF64($instr), + Instrs\Numeric\I32RemS::class => $this->execInstrNumericI32RemS($instr), + Instrs\Numeric\I32RemU::class => $this->execInstrNumericI32RemU($instr), + Instrs\Numeric\I32RotL::class => $this->execInstrNumericI32RotL($instr), + Instrs\Numeric\I32RotR::class => $this->execInstrNumericI32RotR($instr), + Instrs\Numeric\I32Shl::class => $this->execInstrNumericI32Shl($instr), + Instrs\Numeric\I32ShrS::class => $this->execInstrNumericI32ShrS($instr), + Instrs\Numeric\I32ShrU::class => $this->execInstrNumericI32ShrU($instr), + Instrs\Numeric\I32Sub::class => $this->execInstrNumericI32Sub($instr), + Instrs\Numeric\I32TruncF32S::class => $this->execInstrNumericI32TruncF32S($instr), + Instrs\Numeric\I32TruncF32U::class => $this->execInstrNumericI32TruncF32U($instr), + Instrs\Numeric\I32TruncF64S::class => $this->execInstrNumericI32TruncF64S($instr), + Instrs\Numeric\I32TruncF64U::class => $this->execInstrNumericI32TruncF64U($instr), + Instrs\Numeric\I32TruncSatF32S::class => $this->execInstrNumericI32TruncSatF32S($instr), + Instrs\Numeric\I32TruncSatF32U::class => $this->execInstrNumericI32TruncSatF32U($instr), + Instrs\Numeric\I32TruncSatF64S::class => $this->execInstrNumericI32TruncSatF64S($instr), + Instrs\Numeric\I32TruncSatF64U::class => $this->execInstrNumericI32TruncSatF64U($instr), + Instrs\Numeric\I32WrapI64::class => $this->execInstrNumericI32WrapI64($instr), + Instrs\Numeric\I32Xor::class => $this->execInstrNumericI32Xor($instr), + Instrs\Numeric\I64Add::class => $this->execInstrNumericI64Add($instr), + Instrs\Numeric\I64And::class => $this->execInstrNumericI64And($instr), + Instrs\Numeric\I64Clz::class => $this->execInstrNumericI64Clz($instr), + Instrs\Numeric\I64Const::class => $this->execInstrNumericI64Const($instr), + Instrs\Numeric\I64Ctz::class => $this->execInstrNumericI64Ctz($instr), + Instrs\Numeric\I64DivS::class => $this->execInstrNumericI64DivS($instr), + Instrs\Numeric\I64DivU::class => $this->execInstrNumericI64DivU($instr), + Instrs\Numeric\I64Eq::class => $this->execInstrNumericI64Eq($instr), + Instrs\Numeric\I64Eqz::class => $this->execInstrNumericI64Eqz($instr), + Instrs\Numeric\I64Extend16S::class => $this->execInstrNumericI64Extend16S($instr), + Instrs\Numeric\I64Extend32S::class => $this->execInstrNumericI64Extend32S($instr), + Instrs\Numeric\I64Extend8S::class => $this->execInstrNumericI64Extend8S($instr), + Instrs\Numeric\I64ExtendI32S::class => $this->execInstrNumericI64ExtendI32S($instr), + Instrs\Numeric\I64ExtendI32U::class => $this->execInstrNumericI64ExtendI32U($instr), + Instrs\Numeric\I64GeS::class => $this->execInstrNumericI64GeS($instr), + Instrs\Numeric\I64GeU::class => $this->execInstrNumericI64GeU($instr), + Instrs\Numeric\I64GtS::class => $this->execInstrNumericI64GtS($instr), + Instrs\Numeric\I64GtU::class => $this->execInstrNumericI64GtU($instr), + Instrs\Numeric\I64LeS::class => $this->execInstrNumericI64LeS($instr), + Instrs\Numeric\I64LeU::class => $this->execInstrNumericI64LeU($instr), + Instrs\Numeric\I64LtS::class => $this->execInstrNumericI64LtS($instr), + Instrs\Numeric\I64LtU::class => $this->execInstrNumericI64LtU($instr), + Instrs\Numeric\I64Mul::class => $this->execInstrNumericI64Mul($instr), + Instrs\Numeric\I64Ne::class => $this->execInstrNumericI64Ne($instr), + Instrs\Numeric\I64Or::class => $this->execInstrNumericI64Or($instr), + Instrs\Numeric\I64Popcnt::class => $this->execInstrNumericI64Popcnt($instr), + Instrs\Numeric\I64ReinterpretF32::class => $this->execInstrNumericI64ReinterpretF32($instr), + Instrs\Numeric\I64ReinterpretF64::class => $this->execInstrNumericI64ReinterpretF64($instr), + Instrs\Numeric\I64RemS::class => $this->execInstrNumericI64RemS($instr), + Instrs\Numeric\I64RemU::class => $this->execInstrNumericI64RemU($instr), + Instrs\Numeric\I64RotL::class => $this->execInstrNumericI64RotL($instr), + Instrs\Numeric\I64RotR::class => $this->execInstrNumericI64RotR($instr), + Instrs\Numeric\I64Shl::class => $this->execInstrNumericI64Shl($instr), + Instrs\Numeric\I64ShrS::class => $this->execInstrNumericI64ShrS($instr), + Instrs\Numeric\I64ShrU::class => $this->execInstrNumericI64ShrU($instr), + Instrs\Numeric\I64Sub::class => $this->execInstrNumericI64Sub($instr), + Instrs\Numeric\I64TruncF32S::class => $this->execInstrNumericI64TruncF32S($instr), + Instrs\Numeric\I64TruncF32U::class => $this->execInstrNumericI64TruncF32U($instr), + Instrs\Numeric\I64TruncF64S::class => $this->execInstrNumericI64TruncF64S($instr), + Instrs\Numeric\I64TruncF64U::class => $this->execInstrNumericI64TruncF64U($instr), + Instrs\Numeric\I64TruncSatF32S::class => $this->execInstrNumericI64TruncSatF32S($instr), + Instrs\Numeric\I64TruncSatF32U::class => $this->execInstrNumericI64TruncSatF32U($instr), + Instrs\Numeric\I64TruncSatF64S::class => $this->execInstrNumericI64TruncSatF64S($instr), + Instrs\Numeric\I64TruncSatF64U::class => $this->execInstrNumericI64TruncSatF64U($instr), + Instrs\Numeric\I64Xor::class => $this->execInstrNumericI64Xor($instr), + Instrs\Reference\RefFunc::class => $this->execInstrReferenceRefFunc($instr), + Instrs\Reference\RefIsNull::class => $this->execInstrReferenceRefIsNull($instr), + Instrs\Reference\RefNull::class => $this->execInstrReferenceRefNull($instr), + Instrs\Parametric\Drop::class => $this->execInstrParametricDrop($instr), + Instrs\Parametric\Select::class => $this->execInstrParametricSelect($instr), + Instrs\Variable\GlobalGet::class => $this->execInstrVariableGlobalGet($instr), + Instrs\Variable\GlobalSet::class => $this->execInstrVariableGlobalSet($instr), + Instrs\Variable\LocalGet::class => $this->execInstrVariableLocalGet($instr), + Instrs\Variable\LocalSet::class => $this->execInstrVariableLocalSet($instr), + Instrs\Variable\LocalTee::class => $this->execInstrVariableLocalTee($instr), + Instrs\Table\ElemDrop::class => $this->execInstrTableElemDrop($instr), + Instrs\Table\TableCopy::class => $this->execInstrTableTableCopy($instr), + Instrs\Table\TableFill::class => $this->execInstrTableTableFill($instr), + Instrs\Table\TableGet::class => $this->execInstrTableTableGet($instr), + Instrs\Table\TableGrow::class => $this->execInstrTableTableGrow($instr), + Instrs\Table\TableInit::class => $this->execInstrTableTableInit($instr), + Instrs\Table\TableSet::class => $this->execInstrTableTableSet($instr), + Instrs\Table\TableSize::class => $this->execInstrTableTableSize($instr), + Instrs\Memory\DataDrop::class => $this->execInstrMemoryDataDrop($instr), + Instrs\Memory\F32Load::class => $this->execInstrMemoryF32Load($instr), + Instrs\Memory\F32Store::class => $this->execInstrMemoryF32Store($instr), + Instrs\Memory\F64Load::class => $this->execInstrMemoryF64Load($instr), + Instrs\Memory\F64Store::class => $this->execInstrMemoryF64Store($instr), + Instrs\Memory\I32Load::class => $this->execInstrMemoryI32Load($instr), + Instrs\Memory\I32Load16S::class => $this->execInstrMemoryI32Load16S($instr), + Instrs\Memory\I32Load16U::class => $this->execInstrMemoryI32Load16U($instr), + Instrs\Memory\I32Load8S::class => $this->execInstrMemoryI32Load8S($instr), + Instrs\Memory\I32Load8U::class => $this->execInstrMemoryI32Load8U($instr), + Instrs\Memory\I32Store::class => $this->execInstrMemoryI32Store($instr), + Instrs\Memory\I32Store16::class => $this->execInstrMemoryI32Store16($instr), + Instrs\Memory\I32Store8::class => $this->execInstrMemoryI32Store8($instr), + Instrs\Memory\I64Load::class => $this->execInstrMemoryI64Load($instr), + Instrs\Memory\I64Load16S::class => $this->execInstrMemoryI64Load16S($instr), + Instrs\Memory\I64Load16U::class => $this->execInstrMemoryI64Load16U($instr), + Instrs\Memory\I64Load32S::class => $this->execInstrMemoryI64Load32S($instr), + Instrs\Memory\I64Load32U::class => $this->execInstrMemoryI64Load32U($instr), + Instrs\Memory\I64Load8S::class => $this->execInstrMemoryI64Load8S($instr), + Instrs\Memory\I64Load8U::class => $this->execInstrMemoryI64Load8U($instr), + Instrs\Memory\I64Store::class => $this->execInstrMemoryI64Store($instr), + Instrs\Memory\I64Store16::class => $this->execInstrMemoryI64Store16($instr), + Instrs\Memory\I64Store32::class => $this->execInstrMemoryI64Store32($instr), + Instrs\Memory\I64Store8::class => $this->execInstrMemoryI64Store8($instr), + Instrs\Memory\MemoryCopy::class => $this->execInstrMemoryMemoryCopy($instr), + Instrs\Memory\MemoryFill::class => $this->execInstrMemoryMemoryFill($instr), + Instrs\Memory\MemoryGrow::class => $this->execInstrMemoryMemoryGrow($instr), + Instrs\Memory\MemoryInit::class => $this->execInstrMemoryMemoryInit($instr), + Instrs\Memory\MemorySize::class => $this->execInstrMemoryMemorySize($instr), + Instrs\Control\Block::class => $this->execInstrControlBlock($instr), + Instrs\Control\Br::class => $this->execInstrControlBr($instr), + Instrs\Control\BrIf::class => $this->execInstrControlBrIf($instr), + Instrs\Control\BrTable::class => $this->execInstrControlBrTable($instr), + Instrs\Control\Call::class => $this->execInstrControlCall($instr), + Instrs\Control\CallIndirect::class => $this->execInstrControlCallIndirect($instr), + Instrs\Control\Else_::class => $this->execInstrControlElse_($instr), + Instrs\Control\End::class => $this->execInstrControlEnd($instr), + Instrs\Control\If_::class => $this->execInstrControlIf_($instr), + Instrs\Control\Loop::class => $this->execInstrControlLoop($instr), + Instrs\Control\Nop::class => $this->execInstrControlNop($instr), + Instrs\Control\Return_::class => $this->execInstrControlReturn_($instr), + Instrs\Control\Unreachable::class => $this->execInstrControlUnreachable($instr), + default => throw new RuntimeException("invalid instruction"), + }; + } + + private function execInstrNumericF32Abs(Instrs\Numeric\F32Abs $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(abs($v)); + } + + private function execInstrNumericF32Add(Instrs\Numeric\F32Add $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::truncateF64ToF32($c1 + $c2)); + } + + private function execInstrNumericF32Ceil(Instrs\Numeric\F32Ceil $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(ceil($v)); + } + + private function execInstrNumericF32Const(Instrs\Numeric\F32Const $instr): void + { + $this->stack->pushValue($instr->value); + } + + private function execInstrNumericF32ConvertI32S(Instrs\Numeric\F32ConvertI32S $instr): void + { + $v = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); + } + + private function execInstrNumericF32ConvertI32U(Instrs\Numeric\F32ConvertI32U $instr): void + { + $v = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); + } + + private function execInstrNumericF32ConvertI64S(Instrs\Numeric\F32ConvertI64S $instr): void + { + $v = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); + } + + private function execInstrNumericF32ConvertI64U(Instrs\Numeric\F32ConvertI64U $instr): void + { + $v = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::truncateF64ToF32((float) $v)); + } + + private function execInstrNumericF32CopySign(Instrs\Numeric\F32CopySign $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $c1Sign = NumericOps::getFloatSign($c1); + $c2Sign = NumericOps::getFloatSign($c2); + $this->stack->pushValue($c1Sign === $c2Sign ? $c1 : -$c1); + } + + private function execInstrNumericF32DemoteF64(Instrs\Numeric\F32DemoteF64 $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue($v); + } + + private function execInstrNumericF32Div(Instrs\Numeric\F32Div $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::truncateF64ToF32(fdiv($c1, $c2))); + } + + private function execInstrNumericF32Eq(Instrs\Numeric\F32Eq $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 === $c2); + } + + private function execInstrNumericF32Floor(Instrs\Numeric\F32Floor $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(floor($v)); + } + + private function execInstrNumericF32Ge(Instrs\Numeric\F32Ge $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 >= $c2); + } + + private function execInstrNumericF32Gt(Instrs\Numeric\F32Gt $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 > $c2); + } + + private function execInstrNumericF32Le(Instrs\Numeric\F32Le $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 <= $c2); + } + + private function execInstrNumericF32Lt(Instrs\Numeric\F32Lt $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 < $c2); + } + + private function execInstrNumericF32Max(Instrs\Numeric\F32Max $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + if (is_nan($c1) || is_nan($c2)) { + // PHP's standard max() handles NaNs in diffrent way than WebAssembly spec does. + $this->stack->pushValue(NAN); + return; + } + $this->stack->pushValue(max($c1, $c2)); + } + + private function execInstrNumericF32Min(Instrs\Numeric\F32Min $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + if (is_nan($c1) || is_nan($c2)) { + // PHP's standard min() handles NaNs in diffrent way than WebAssembly spec does. + $this->stack->pushValue(NAN); + return; + } + $this->stack->pushValue(min($c1, $c2)); + } + + private function execInstrNumericF32Mul(Instrs\Numeric\F32Mul $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::truncateF64ToF32($c1 * $c2)); + } + + private function execInstrNumericF32Ne(Instrs\Numeric\F32Ne $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 !== $c2); + } + + private function execInstrNumericF32Nearest(Instrs\Numeric\F32Nearest $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(round($v, mode: PHP_ROUND_HALF_EVEN)); + } + + private function execInstrNumericF32Neg(Instrs\Numeric\F32Neg $instr): void + { + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(-$c1); + } + + private function execInstrNumericF32ReinterpretI32(Instrs\Numeric\F32ReinterpretI32 $instr): void + { + $v = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::reinterpretI32AsF32($v)); + } + + private function execInstrNumericF32ReinterpretI64(Instrs\Numeric\F32ReinterpretI64 $instr): void + { + $v = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::reinterpretI64AsF32($v)); + } + + private function execInstrNumericF32Sqrt(Instrs\Numeric\F32Sqrt $instr): void + { + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::truncateF64ToF32(sqrt($c1))); + } + + private function execInstrNumericF32Sub(Instrs\Numeric\F32Sub $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::truncateF64ToF32($c1 - $c2)); + } + + private function execInstrNumericF32Trunc(Instrs\Numeric\F32Trunc $instr): void + { + $v = $this->stack->popFloat(); + if ($v < 0) { + $this->stack->pushValue(NumericOps::truncateF64ToF32(ceil($v))); + } else { + $this->stack->pushValue(NumericOps::truncateF64ToF32(floor($v))); + } + } + + private function execInstrNumericF64Abs(Instrs\Numeric\F64Abs $instr): void + { + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(abs($c1)); + } + + private function execInstrNumericF64Add(Instrs\Numeric\F64Add $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue($c1 + $c2); + } + + private function execInstrNumericF64Ceil(Instrs\Numeric\F64Ceil $instr): void + { + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(ceil($c1)); + } + + private function execInstrNumericF64Const(Instrs\Numeric\F64Const $instr): void + { + $this->stack->pushValue($instr->value); + } + + private function execInstrNumericF64ConvertI32S(Instrs\Numeric\F64ConvertI32S $instr): void + { + $c = $this->stack->popInt(); + $this->stack->pushValue((float) $c); + } + + private function execInstrNumericF64ConvertI32U(Instrs\Numeric\F64ConvertI32U $instr): void + { + $c = $this->stack->popInt(); + $this->stack->pushValue((float) $c); + } + + private function execInstrNumericF64ConvertI64S(Instrs\Numeric\F64ConvertI64S $instr): void + { + $c = $this->stack->popInt(); + $this->stack->pushValue((float) $c); + } + + private function execInstrNumericF64ConvertI64U(Instrs\Numeric\F64ConvertI64U $instr): void + { + $c = $this->stack->popInt(); + $this->stack->pushValue((float) $c); + } + + private function execInstrNumericF64CopySign(Instrs\Numeric\F64CopySign $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $c1Sign = NumericOps::getFloatSign($c1); + $c2Sign = NumericOps::getFloatSign($c2); + $this->stack->pushValue($c1Sign === $c2Sign ? $c1 : -$c1); + } + + private function execInstrNumericF64Div(Instrs\Numeric\F64Div $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(fdiv($c1, $c2)); + } + + private function execInstrNumericF64Eq(Instrs\Numeric\F64Eq $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 === $c2); + } + + private function execInstrNumericF64Floor(Instrs\Numeric\F64Floor $instr): void + { + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(floor($c1)); + } + + private function execInstrNumericF64Ge(Instrs\Numeric\F64Ge $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 >= $c2); + } + + private function execInstrNumericF64Gt(Instrs\Numeric\F64Gt $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 > $c2); + } + + private function execInstrNumericF64Le(Instrs\Numeric\F64Le $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 <= $c2); + } + + private function execInstrNumericF64Lt(Instrs\Numeric\F64Lt $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 < $c2); + } + + private function execInstrNumericF64Max(Instrs\Numeric\F64Max $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + if (is_nan($c1) || is_nan($c2)) { + // PHP's standard max() handles NaNs in diffrent way than WebAssembly spec does. + $this->stack->pushValue(NAN); + return; + } + $this->stack->pushValue(max($c1, $c2)); + } + + private function execInstrNumericF64Min(Instrs\Numeric\F64Min $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + if (is_nan($c1) || is_nan($c2)) { + // PHP's standard min() handles NaNs in diffrent way than WebAssembly spec does. + $this->stack->pushValue(NAN); + return; + } + $this->stack->pushValue(min($c1, $c2)); + } + + private function execInstrNumericF64Mul(Instrs\Numeric\F64Mul $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue($c1 * $c2); + } + + private function execInstrNumericF64Ne(Instrs\Numeric\F64Ne $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushBool($c1 !== $c2); + } + + private function execInstrNumericF64Nearest(Instrs\Numeric\F64Nearest $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(round($v, mode: PHP_ROUND_HALF_EVEN)); + } + + private function execInstrNumericF64Neg(Instrs\Numeric\F64Neg $instr): void + { + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(-$c1); + } + + private function execInstrNumericF64PromoteF32(Instrs\Numeric\F64PromoteF32 $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue($v); + } + + private function execInstrNumericF64ReinterpretI32(Instrs\Numeric\F64ReinterpretI32 $instr): void + { + $v = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::reinterpretI32AsF64($v)); + } + + private function execInstrNumericF64ReinterpretI64(Instrs\Numeric\F64ReinterpretI64 $instr): void + { + $v = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::reinterpretI64AsF64($v)); + } + + private function execInstrNumericF64Sqrt(Instrs\Numeric\F64Sqrt $instr): void + { + $c1 = $this->stack->popFloat(); + $this->stack->pushValue(sqrt($c1)); + } + + private function execInstrNumericF64Sub(Instrs\Numeric\F64Sub $instr): void + { + $c2 = $this->stack->popFloat(); + $c1 = $this->stack->popFloat(); + $this->stack->pushValue($c1 - $c2); + } + + private function execInstrNumericF64Trunc(Instrs\Numeric\F64Trunc $instr): void + { + $v = $this->stack->popFloat(); + if ($v < 0) { + $this->stack->pushValue(ceil($v)); + } else { + $this->stack->pushValue(floor($v)); + } + } + + private function execInstrNumericI32Add(Instrs\Numeric\I32Add $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 + $c2) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32And(Instrs\Numeric\I32And $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 & $c2) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32Clz(Instrs\Numeric\I32Clz $instr): void + { + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $leadingZeros = 0; + for ($j = 31; 0 <= $j; $j--) { + if (($i & (1 << $j)) === 0) { + $leadingZeros++; + } else { + break; + } + } + $this->stack->pushValue($leadingZeros); + } + + private function execInstrNumericI32Const(Instrs\Numeric\I32Const $instr): void + { + $this->stack->pushValue($instr->value); + } + + private function execInstrNumericI32Ctz(Instrs\Numeric\I32Ctz $instr): void + { + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $trailingZeros = 0; + for ($j = 0; $j < 32; $j++) { + if (($i & (1 << $j)) === 0) { + $trailingZeros++; + } else { + break; + } + } + $this->stack->pushValue($trailingZeros); + } + + private function execInstrNumericI32DivS(Instrs\Numeric\I32DivS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + if ($c2 === 0) { + throw new TrapException("i32.div_s: divide by zero", trapKind: TrapKind::DivideByZero); + } + if ($c1 === -2147483648 && $c2 === -1) { + throw new TrapException("i32.div_s: overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue(intdiv($c1, $c2)); + } + + private function execInstrNumericI32DivU(Instrs\Numeric\I32DivU $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + if ($c2 === 0) { + throw new TrapException("i32.div_u: divide by zero", trapKind: TrapKind::DivideByZero); + } + $this->stack->pushValue(NumericOps::convertU32ToS32(intdiv($c1, $c2))); + } + + private function execInstrNumericI32Eq(Instrs\Numeric\I32Eq $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 === $c2); + } + + private function execInstrNumericI32Eqz(Instrs\Numeric\I32Eqz $instr): void + { + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 === 0); + } + + private function execInstrNumericI32Extend16S(Instrs\Numeric\I32Extend16S $instr): void + { + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c2 = $c1 & 0xFFFF; + $result = unpack('s', pack('S', $c2)); + assert($result !== false); + $this->stack->pushValue($result[1]); + } + + private function execInstrNumericI32Extend8S(Instrs\Numeric\I32Extend8S $instr): void + { + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c2 = $c1 & 0xFF; + $result = unpack('c', pack('C', $c2)); + assert($result !== false); + $this->stack->pushValue($result[1]); + } + + private function execInstrNumericI32GeS(Instrs\Numeric\I32GeS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 >= $c2); + } + + private function execInstrNumericI32GeU(Instrs\Numeric\I32GeU $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushBool($c1 >= $c2); + } + + private function execInstrNumericI32GtS(Instrs\Numeric\I32GtS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 > $c2); + } + + private function execInstrNumericI32GtU(Instrs\Numeric\I32GtU $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushBool($c1 > $c2); + } + + private function execInstrNumericI32LeS(Instrs\Numeric\I32LeS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 <= $c2); + } + + private function execInstrNumericI32LeU(Instrs\Numeric\I32LeU $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushBool($c1 <= $c2); + } + + private function execInstrNumericI32LtS(Instrs\Numeric\I32LtS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 < $c2); + } + + private function execInstrNumericI32LtU(Instrs\Numeric\I32LtU $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushBool($c1 < $c2); + } + + private function execInstrNumericI32Mul(Instrs\Numeric\I32Mul $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 * $c2) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32Ne(Instrs\Numeric\I32Ne $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 !== $c2); + } + + private function execInstrNumericI32Or(Instrs\Numeric\I32Or $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 | $c2) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32Popcnt(Instrs\Numeric\I32Popcnt $instr): void + { + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $popcnt = 0; + for ($j = 0; $j < 32; $j++) { + if (($i & (1 << $j)) !== 0) { + $popcnt++; + } + } + $this->stack->pushValue($popcnt); + } + + private function execInstrNumericI32ReinterpretF32(Instrs\Numeric\I32ReinterpretF32 $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::reinterpretF32AsI32($v)); + } + + private function execInstrNumericI32ReinterpretF64(Instrs\Numeric\I32ReinterpretF64 $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::reinterpretF64AsI32($v)); + } + + private function execInstrNumericI32RemS(Instrs\Numeric\I32RemS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + if ($c2 === 0) { + throw new TrapException("i32.rem_s: divide by zero or overflow", trapKind: TrapKind::DivideByZero); + } + $this->stack->pushValue($c1 % $c2); + } + + private function execInstrNumericI32RemU(Instrs\Numeric\I32RemU $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + if ($c2 === 0) { + throw new TrapException("i32.rem_u: divide by zero", trapKind: TrapKind::DivideByZero); + } + $this->stack->pushValue(NumericOps::convertU32ToS32($c1 % $c2)); + } + + private function execInstrNumericI32RotL(Instrs\Numeric\I32RotL $instr): void + { + $i2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $i1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $k = $i2 % 32; + $this->stack->pushValue(NumericOps::convertU32ToS32((($i1 << $k) | ($i1 >> (32 - $k))) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32RotR(Instrs\Numeric\I32RotR $instr): void + { + $i2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $i1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $k = $i2 % 32; + $this->stack->pushValue(NumericOps::convertU32ToS32((($i1 >> $k) | ($i1 << (32 - $k))) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32Shl(Instrs\Numeric\I32Shl $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $k = $c2 % 32; + $c1 = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 << $k) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32ShrS(Instrs\Numeric\I32ShrS $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $k = $c2 % 32; + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $signed = $c1 & 0x80000000; + if ($signed !== 0) { + $result = $c1; + for ($i = 0; $i < $k; $i++) { + $result = ($result >> 1) | 0x80000000; + } + $this->stack->pushValue(NumericOps::convertU32ToS32($result)); + } else { + $this->stack->pushValue($c1 >> $k); + } + } + + private function execInstrNumericI32ShrU(Instrs\Numeric\I32ShrU $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $k = $c2 % 32; + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushValue(NumericOps::convertU32ToS32($c1 >> $k)); + } + + private function execInstrNumericI32Sub(Instrs\Numeric\I32Sub $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c2Neg = ((~$c2 & 0xFFFFFFFF) + 1) & 0xFFFFFFFF; + $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 + $c2Neg) & 0xFFFFFFFF)); + } + + private function execInstrNumericI32TruncF32S(Instrs\Numeric\I32TruncF32S $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -2147483649.0 || 2147483648.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue((int) $v); + } + + private function execInstrNumericI32TruncF32U(Instrs\Numeric\I32TruncF32U $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -1.0 || 4294967296.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue(NumericOps::convertU32ToS32((int) $v)); + } + + private function execInstrNumericI32TruncF64S(Instrs\Numeric\I32TruncF64S $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -2147483649.0 || 2147483648.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue((int) $v); + } + + private function execInstrNumericI32TruncF64U(Instrs\Numeric\I32TruncF64U $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -1.0 || 4294967296.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue(NumericOps::convertU32ToS32((int) $v)); + } + + private function execInstrNumericI32TruncSatF32S(Instrs\Numeric\I32TruncSatF32S $instr): void + { + $v = $this->stack->popFloat(); + if ($v < -2147483648.0) { + $this->stack->pushValue(-2147483648); + } elseif ($v > 2147483647.0) { + $this->stack->pushValue(2147483647); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI32TruncSatF32U(Instrs\Numeric\I32TruncSatF32U $instr): void + { + $v = $this->stack->popFloat(); + if ($v < 0.0) { + $this->stack->pushValue(0); + } elseif ($v > 4294967295.0) { + $this->stack->pushValue(4294967295); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI32TruncSatF64S(Instrs\Numeric\I32TruncSatF64S $instr): void + { + $v = $this->stack->popFloat(); + if ($v < -2147483648.0) { + $this->stack->pushValue(-2147483648); + } elseif ($v > 2147483647.0) { + $this->stack->pushValue(2147483647); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI32TruncSatF64U(Instrs\Numeric\I32TruncSatF64U $instr): void + { + $v = $this->stack->popFloat(); + if ($v < 0.0) { + $this->stack->pushValue(0); + } elseif ($v > 4294967295.0) { + $this->stack->pushValue(4294967295); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI32WrapI64(Instrs\Numeric\I32WrapI64 $instr): void + { + $c1 = $this->stack->popInt(); + $this->stack->pushValue(NumericOps::convertU32ToS32($c1 & 0xFFFFFFFF)); + } + + private function execInstrNumericI32Xor(Instrs\Numeric\I32Xor $instr): void + { + $c2 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $this->stack->pushValue(NumericOps::convertU32ToS32(($c1 ^ $c2) & 0xFFFFFFFF)); + } + + private function execInstrNumericI64Add(Instrs\Numeric\I64Add $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $result = NumericOps::bigIntToPhpInt(bcadd((string)$c1, (string)$c2)); + $this->stack->pushValue($result); + } + + private function execInstrNumericI64And(Instrs\Numeric\I64And $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushValue($c1 & $c2); + } + + private function execInstrNumericI64Clz(Instrs\Numeric\I64Clz $instr): void + { + $i = $this->stack->popInt(); + $leadingZeros = 0; + for ($j = 63; 0 <= $j; $j--) { + if ($j === 63) { + if ($i < 0) { + break; + } else { + $leadingZeros++; + } + } else { + if (($i & (1 << $j)) === 0) { + $leadingZeros++; + } else { + break; + } + } + } + $this->stack->pushValue($leadingZeros); + } + + private function execInstrNumericI64Const(Instrs\Numeric\I64Const $instr): void + { + $this->stack->pushValue($instr->value); + } + + private function execInstrNumericI64Ctz(Instrs\Numeric\I64Ctz $instr): void + { + $i = $this->stack->popInt(); + $trailingZeros = 0; + for ($j = 0; $j < 64; $j++) { + if ($j === 63) { + if ($i >= 0) { + $trailingZeros++; + } + } else { + if (($i & (1 << $j)) === 0) { + $trailingZeros++; + } else { + break; + } + } + } + $this->stack->pushValue($trailingZeros); + } + + private function execInstrNumericI64DivS(Instrs\Numeric\I64DivS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + if ($c2 === 0) { + throw new TrapException("i64.div_s: divide by zero", trapKind: TrapKind::DivideByZero); + } + if ($c1 === PHP_INT_MIN && $c2 === -1) { + throw new TrapException("i64.div_s: overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue(intdiv($c1, $c2)); + } + + private function execInstrNumericI64DivU(Instrs\Numeric\I64DivU $instr): void + { + $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + $c1 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + if ($c2 === '0') { + throw new TrapException("i64.div_u: divide by zero", trapKind: TrapKind::DivideByZero); + } + $this->stack->pushValue(NumericOps::bigIntToPhpInt(bcdiv($c1, $c2, 0))); + } + + private function execInstrNumericI64Eq(Instrs\Numeric\I64Eq $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 === $c2); + } + + private function execInstrNumericI64Eqz(Instrs\Numeric\I64Eqz $instr): void + { + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 === 0); + } + + private function execInstrNumericI64Extend16S(Instrs\Numeric\I64Extend16S $instr): void + { + $c1 = $this->stack->popInt(); + $c2 = $c1 & 0xFFFF; + $result = unpack('s', pack('S', $c2)); + assert($result !== false); + $this->stack->pushValue($result[1]); + } + + private function execInstrNumericI64Extend32S(Instrs\Numeric\I64Extend32S $instr): void + { + $c1 = $this->stack->popInt(); + $c2 = $c1 & 0xFFFFFFFF; + $result = unpack('l', pack('L', $c2)); + assert($result !== false); + $this->stack->pushValue($result[1]); + } + + private function execInstrNumericI64Extend8S(Instrs\Numeric\I64Extend8S $instr): void + { + $c1 = $this->stack->popInt(); + $c2 = $c1 & 0xFF; + $result = unpack('c', pack('C', $c2)); + assert($result !== false); + $this->stack->pushValue($result[1]); + } + + private function execInstrNumericI64ExtendI32S(Instrs\Numeric\I64ExtendI32S $instr): void + { + $c1 = $this->stack->popInt(); + $this->stack->pushValue($c1); + } + + private function execInstrNumericI64ExtendI32U(Instrs\Numeric\I64ExtendI32U $instr): void + { + $c1 = NumericOps::convertS32ToU32($this->stack->popInt()); + $c2 = $c1 & 0xFFFFFFFF; + $this->stack->pushValue($c2); + } + + private function execInstrNumericI64GeS(Instrs\Numeric\I64GeS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 >= $c2); + } + + private function execInstrNumericI64GeU(Instrs\Numeric\I64GeU $instr): void + { + $c2 = $this->stack->popInt(); + $c2Packed = pack('J', $c2); + $c1 = $this->stack->popInt(); + $c1Packed = pack('J', $c1); + $this->stack->pushBool($c1Packed >= $c2Packed); + } + + private function execInstrNumericI64GtS(Instrs\Numeric\I64GtS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 > $c2); + } + + private function execInstrNumericI64GtU(Instrs\Numeric\I64GtU $instr): void + { + $c2 = $this->stack->popInt(); + $c2Packed = pack('J', $c2); + $c1 = $this->stack->popInt(); + $c1Packed = pack('J', $c1); + $this->stack->pushBool($c1Packed > $c2Packed); + } + + private function execInstrNumericI64LeS(Instrs\Numeric\I64LeS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 <= $c2); + } + + private function execInstrNumericI64LeU(Instrs\Numeric\I64LeU $instr): void + { + $c2 = $this->stack->popInt(); + $c2Packed = pack('J', $c2); + $c1 = $this->stack->popInt(); + $c1Packed = pack('J', $c1); + $this->stack->pushBool($c1Packed <= $c2Packed); + } + + private function execInstrNumericI64LtS(Instrs\Numeric\I64LtS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 < $c2); + } + + private function execInstrNumericI64LtU(Instrs\Numeric\I64LtU $instr): void + { + $c2 = $this->stack->popInt(); + $c2Packed = pack('J', $c2); + $c1 = $this->stack->popInt(); + $c1Packed = pack('J', $c1); + $this->stack->pushBool($c1Packed < $c2Packed); + } + + private function execInstrNumericI64Mul(Instrs\Numeric\I64Mul $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $result = NumericOps::bigIntToPhpInt(bcmul((string)$c1, (string)$c2)); + $this->stack->pushValue($result); + } + + private function execInstrNumericI64Ne(Instrs\Numeric\I64Ne $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushBool($c1 !== $c2); + } + + private function execInstrNumericI64Or(Instrs\Numeric\I64Or $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushValue($c1 | $c2); + } + + private function execInstrNumericI64Popcnt(Instrs\Numeric\I64Popcnt $instr): void + { + $i = $this->stack->popInt(); + $popcnt = 0; + for ($j = 0; $j < 64; $j++) { + if (($i & (1 << $j)) !== 0) { + $popcnt++; + } + } + $this->stack->pushValue($popcnt); + } + + private function execInstrNumericI64ReinterpretF32(Instrs\Numeric\I64ReinterpretF32 $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::reinterpretF32AsI64($v)); + } + + private function execInstrNumericI64ReinterpretF64(Instrs\Numeric\I64ReinterpretF64 $instr): void + { + $v = $this->stack->popFloat(); + $this->stack->pushValue(NumericOps::reinterpretF64AsI64($v)); + } + + private function execInstrNumericI64RemS(Instrs\Numeric\I64RemS $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + if ($c2 === 0) { + throw new TrapException("i64.rem_s: divide by zero", trapKind: TrapKind::DivideByZero); + } + $this->stack->pushValue($c1 % $c2); + } + + private function execInstrNumericI64RemU(Instrs\Numeric\I64RemU $instr): void + { + $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + $c1 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + if ($c2 === '0') { + throw new TrapException("i64.rem_u: divide by zero", trapKind: TrapKind::DivideByZero); + } + $this->stack->pushValue(NumericOps::bigIntToPhpInt(bcmod($c1, $c2, 0))); + } + + private function execInstrNumericI64RotL(Instrs\Numeric\I64RotL $instr): void + { + $i2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + $k = (int)bcmod($i2, '64'); + $i1 = $this->stack->popInt(); + $left = $i1 << $k; + $right = $i1; + for ($i = 0; $i < 64 - $k; $i++) { + $right = ($right >> 1) & 0x7FFFFFFFFFFFFFFF; + } + $this->stack->pushValue($left | $right); + } + + private function execInstrNumericI64RotR(Instrs\Numeric\I64RotR $instr): void + { + $i2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + $k = (int)bcmod($i2, '64'); + $i1 = $this->stack->popInt(); + $left = $i1; + for ($i = 0; $i < $k; $i++) { + $left = ($left >> 1) & 0x7FFFFFFFFFFFFFFF; + } + $right = $i1 << (64 - $k); + $this->stack->pushValue($left | $right); + } + + private function execInstrNumericI64Shl(Instrs\Numeric\I64Shl $instr): void + { + $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + $k = (int)bcmod($c2, '64'); + $c1 = $this->stack->popInt(); + $this->stack->pushValue($c1 << $k); + } + + private function execInstrNumericI64ShrS(Instrs\Numeric\I64ShrS $instr): void + { + $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + $k = (int)bcmod($c2, '64'); + $c1 = $this->stack->popInt(); + $this->stack->pushValue($c1 >> $k); + } + + private function execInstrNumericI64ShrU(Instrs\Numeric\I64ShrU $instr): void + { + $c2 = NumericOps::convertS64ToBigUInt($this->stack->popInt()); + $k = (int)bcmod($c2, '64'); + if ($k === 0) { + return; + } + // Perform shr_u based on string manipulation because PHP does not + // support shr_u operation. + $c1 = $this->stack->popInt(); + $this->stack->pushValue(bindec(substr(decbin($c1), 0, -$k))); + } + + private function execInstrNumericI64Sub(Instrs\Numeric\I64Sub $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $result = NumericOps::bigIntToPhpInt(bcsub((string)$c1, (string)$c2)); + $this->stack->pushValue($result); + } + + private function execInstrNumericI64TruncF32S(Instrs\Numeric\I64TruncF32S $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion ($v)", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow ($v)", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -9223372036854775809.0 || 9223372036854775808.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow ($v)", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue((int) $v); + } + + private function execInstrNumericI64TruncF32U(Instrs\Numeric\I64TruncF32U $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -1.0 || 18446744073709551616.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue((int) $v); + } + + private function execInstrNumericI64TruncF64S(Instrs\Numeric\I64TruncF64S $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -9223372036854775809.0 || 9223372036854775808.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue((int) $v); + } + + private function execInstrNumericI64TruncF64U(Instrs\Numeric\I64TruncF64U $instr): void + { + $v = $this->stack->popFloat(); + if (is_nan($v)) { + throw new TrapException($instr::opName() . ": invalid conversion", trapKind: TrapKind::InvalidConversionToInteger); + } + if (is_infinite($v)) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + if ($v <= -1.0 || 18446744073709551616.0 <= $v) { + throw new TrapException($instr::opName() . ": overflow", trapKind: TrapKind::IntegerOverflow); + } + $this->stack->pushValue((int) $v); + } + + private function execInstrNumericI64TruncSatF32S(Instrs\Numeric\I64TruncSatF32S $instr): void + { + $v = $this->stack->popFloat(); + if ($v < -9223372036854775808.0) { + $this->stack->pushValue(-9223372036854775808); + } elseif ($v > 9223372036854775807.0) { + $this->stack->pushValue(9223372036854775807); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI64TruncSatF32U(Instrs\Numeric\I64TruncSatF32U $instr): void + { + $v = $this->stack->popFloat(); + if ($v < 0.0) { + $this->stack->pushValue(0); + } elseif ($v > 18446744073709551615.0) { + $this->stack->pushValue(18446744073709551615); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI64TruncSatF64S(Instrs\Numeric\I64TruncSatF64S $instr): void + { + $v = $this->stack->popFloat(); + if ($v < -9223372036854775808.0) { + $this->stack->pushValue(-9223372036854775808); + } elseif ($v > 9223372036854775807.0) { + $this->stack->pushValue(9223372036854775807); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI64TruncSatF64U(Instrs\Numeric\I64TruncSatF64U $instr): void + { + $v = $this->stack->popFloat(); + if ($v < 0.0) { + $this->stack->pushValue(0); + } elseif ($v > 18446744073709551615.0) { + $this->stack->pushValue(18446744073709551615); + } else { + $this->stack->pushValue((int) $v); + } + } + + private function execInstrNumericI64Xor(Instrs\Numeric\I64Xor $instr): void + { + $c2 = $this->stack->popInt(); + $c1 = $this->stack->popInt(); + $this->stack->pushValue($c1 ^ $c2); + } + + private function execInstrReferenceRefFunc(Instrs\Reference\RefFunc $instr): void + { + $x = $instr->func; + $f = $this->stack->currentFrame(); + $a = $f->module->funcAddrs[$x]; + $this->stack->pushRefFunc($a); + } + + private function execInstrReferenceRefIsNull(Instrs\Reference\RefIsNull $instr): void + { + $val = $this->stack->popRef(); + $this->stack->pushBool($val instanceof Refs\RefNull); + } + + private function execInstrReferenceRefNull(Instrs\Reference\RefNull $instr): void + { + $t = $instr->type; + $this->stack->pushRefNull($t); + } + + private function execInstrParametricDrop(Instrs\Parametric\Drop $instr): void + { + $this->stack->popValue(); + } + + private function execInstrParametricSelect(Instrs\Parametric\Select $instr): void + { + $c = $this->stack->popInt(); + $val2 = $this->stack->popValue(); + $val1 = $this->stack->popValue(); + if ($c !== 0) { + $this->stack->pushValue($val1); + } else { + $this->stack->pushValue($val2); + } + } + + private function execInstrVariableGlobalGet(Instrs\Variable\GlobalGet $instr): void + { + $x = $instr->var; + $f = $this->stack->currentFrame(); + $a = $f->module->globalAddrs[$x]; + $glob = $this->store->globals[$a]; + $val = $glob->value; + $this->stack->pushValue($val); + } + + private function execInstrVariableGlobalSet(Instrs\Variable\GlobalSet $instr): void + { + $x = $instr->var; + $f = $this->stack->currentFrame(); + $a = $f->module->globalAddrs[$x]; + $glob = $this->store->globals[$a]; + $val = $this->stack->popValue(); + $glob->value = $val; + } + + private function execInstrVariableLocalGet(Instrs\Variable\LocalGet $instr): void + { + $x = $instr->var; + $f = $this->stack->currentFrame(); + $val = $f->locals[$x] ?? null; + if ($val === null) { + throw new RuntimeException("local.get: local $x not found in [$f->debugName]"); + } + $this->stack->pushValue($val); + } + + private function execInstrVariableLocalSet(Instrs\Variable\LocalSet $instr): void + { + $x = $instr->var; + $f = $this->stack->currentFrame(); + $val = $this->stack->popValue(); + // @phpstan-ignore-next-line + $f->locals[$x] = $val; + } + + private function execInstrVariableLocalTee(Instrs\Variable\LocalTee $instr): void + { + $x = $instr->var; + $f = $this->stack->currentFrame(); + $val = $this->stack->popValue(); + // @phpstan-ignore-next-line + $f->locals[$x] = $val; + $this->stack->pushValue($val); + } + + private function execInstrTableElemDrop(Instrs\Table\ElemDrop $instr): void + { + $x = $instr->elem; + $f = $this->stack->currentFrame(); + $a = $f->module->elemAddrs[$x]; + $elem = $this->store->elems[$a]; + // @phpstan-ignore-next-line + $this->store->elems[$a] = new ElemInst($elem->type, []); + } + + private function execInstrTableTableCopy(Instrs\Table\TableCopy $instr): void + { + $x = $instr->to; + $y = $instr->from; + $f = $this->stack->currentFrame(); + $taX = $f->module->tableAddrs[$x]; + $tabX = $this->store->tables[$taX]; + $taY = $f->module->tableAddrs[$y]; + $tabY = $this->store->tables[$taY]; + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $s = NumericOps::convertS32ToU32($this->stack->popInt()); + $d = NumericOps::convertS32ToU32($this->stack->popInt()); + if (count($tabX->elem) < $d + $n || count($tabY->elem) < $s + $n) { + throw new TrapException("table.copy: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); + } + if ($n === 0 || ($x === $y && $d === $s)) { + return; + } + for ($i = 0; $i < $n; $i++) { + $s_ = ($d < $s) ? ($s + $i) : ($s + $n - 1 - $i); + $d_ = ($d < $s) ? ($d + $i) : ($d + $n - 1 - $i); + // @phpstan-ignore-next-line + $tabX->elem[$d_] = $tabY->elem[$s_]; + } + } + + private function execInstrTableTableFill(Instrs\Table\TableFill $instr): void + { + $x = $instr->table; + $f = $this->stack->currentFrame(); + $ta = $f->module->tableAddrs[$x]; + $tab = $this->store->tables[$ta]; + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $val = $this->stack->popRef(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + if (count($tab->elem) < $i + $n) { + throw new TrapException("table.fill: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); + } + for ($k = 0; $k < $n; $k++) { + // @phpstan-ignore-next-line + $tab->elem[$i + $k] = $val; + } + } + + private function execInstrTableTableGet(Instrs\Table\TableGet $instr): void + { + $x = $instr->table; + $f = $this->stack->currentFrame(); + $a = $f->module->tableAddrs[$x]; + $tab = $this->store->tables[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + if (count($tab->elem) <= $i) { + throw new TrapException("table.get: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); + } + $val = $tab->elem[$i]; + $this->stack->pushValue($val); + } + + private function execInstrTableTableGrow(Instrs\Table\TableGrow $instr): void + { + $x = $instr->table; + $f = $this->stack->currentFrame(); + $a = $f->module->tableAddrs[$x]; + $tab = $this->store->tables[$a]; + $sz = count($tab->elem); + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $val = $this->stack->popRef(); + + $len = $sz + $n; + if ((1 << 32) <= $len) { + $this->stack->pushValue(-1); + return; + } + + $limits = $tab->type->limits; + $limits_ = new Limits($len, $limits->max); + if (!$limits_->isValid()) { + $this->stack->pushValue(-1); + return; + } + + for ($i = 0; $i < $n; $i++) { + $tab->elem[] = $val; + } + $tab->type = new TableType($limits_, $tab->type->refType); + + $this->stack->pushValue($sz); + } + + private function execInstrTableTableInit(Instrs\Table\TableInit $instr): void + { + $x = $instr->to; + $y = $instr->from; + $f = $this->stack->currentFrame(); + $ta = $f->module->tableAddrs[$x]; + $tab = $this->store->tables[$ta]; + $ea = $f->module->elemAddrs[$y]; + $elem = $this->store->elems[$ea]; + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $s = NumericOps::convertS32ToU32($this->stack->popInt()); + $d = NumericOps::convertS32ToU32($this->stack->popInt()); + if (count($elem->elem) < $s + $n) { + throw new TrapException("table.init: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); + } + if (count($tab->elem) < $d + $n) { + throw new TrapException("table.init: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); + } + for ($i = 0; $i < $n; $i++) { + // @phpstan-ignore-next-line + $tab->elem[$d + $i] = $elem->elem[$s + $i]; + } + } + + private function execInstrTableTableSet(Instrs\Table\TableSet $instr): void + { + $x = $instr->table; + $f = $this->stack->currentFrame(); + $a = $f->module->tableAddrs[$x]; + $tab = $this->store->tables[$a]; + $val = $this->stack->popRef(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + if (count($tab->elem) <= $i) { + throw new TrapException("table.set: out of bounds", trapKind: TrapKind::OutOfBoundsTableAccess); + } + // @phpstan-ignore-next-line + $tab->elem[$i] = $val; + } + + private function execInstrTableTableSize(Instrs\Table\TableSize $instr): void + { + $x = $instr->table; + $f = $this->stack->currentFrame(); + $a = $f->module->tableAddrs[$x]; + $tab = $this->store->tables[$a]; + $sz = count($tab->elem); + $this->stack->pushValue($sz); + } + + private function execInstrMemoryDataDrop(Instrs\Memory\DataDrop $instr): void + { + $x = $instr->data; + $f = $this->stack->currentFrame(); + $a = $f->module->dataAddrs[$x]; + // @phpstan-ignore-next-line + $this->store->datas[$a] = new DataInst([]); + } + + private function execInstrMemoryF32Load(Instrs\Memory\F32Load $instr): void + { + $this->doLoadF32($instr->offset, $instr::opName()); + } + + private function execInstrMemoryF32Store(Instrs\Memory\F32Store $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popFloat(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeF32($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryF64Load(Instrs\Memory\F64Load $instr): void + { + $this->doLoadF64($instr->offset, $instr::opName()); + } + + private function execInstrMemoryF64Store(Instrs\Memory\F64Store $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popFloat(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeF64($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryI32Load(Instrs\Memory\I32Load $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI32_s32($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI32Load16S(Instrs\Memory\I32Load16S $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI32_s16($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI32Load16U(Instrs\Memory\I32Load16U $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI32_u16($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI32Load8S(Instrs\Memory\I32Load8S $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI32_s8($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI32Load8U(Instrs\Memory\I32Load8U $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI32_u8($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI32Store(Instrs\Memory\I32Store $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popInt(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeI32_s32($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryI32Store16(Instrs\Memory\I32Store16 $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popInt(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeI32_s16($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryI32Store8(Instrs\Memory\I32Store8 $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popInt(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeI32_s8($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryI64Load(Instrs\Memory\I64Load $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI64_s64($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI64Load16S(Instrs\Memory\I64Load16S $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI64_s16($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI64Load16U(Instrs\Memory\I64Load16U $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI64_u16($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI64Load32S(Instrs\Memory\I64Load32S $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI64_s32($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI64Load32U(Instrs\Memory\I64Load32U $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI64_u32($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI64Load8S(Instrs\Memory\I64Load8S $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI64_s8($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI64Load8U(Instrs\Memory\I64Load8U $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadI64_u8($ea); + if ($c === null) { + throw new TrapException($instr::opName() . ": out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function execInstrMemoryI64Store(Instrs\Memory\I64Store $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popInt(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeI64_s64($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryI64Store16(Instrs\Memory\I64Store16 $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popInt(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeI64_s16($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryI64Store32(Instrs\Memory\I64Store32 $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popInt(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeI64_s32($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryI64Store8(Instrs\Memory\I64Store8 $instr): void + { + $offset = $instr->offset; + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $c = $this->stack->popInt(); + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $ok = $mem->storeI64_s8($ea, $c); + if (!$ok) { + throw new TrapException($instr::opName() . ": out of bounds: $ea >= " . $mem->size(), trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + } + + private function execInstrMemoryMemoryCopy(Instrs\Memory\MemoryCopy $instr): void + { + $f = $this->stack->currentFrame(); + $ma = $f->module->memAddrs[0]; + $mem = $this->store->mems[$ma]; + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $s = NumericOps::convertS32ToU32($this->stack->popInt()); + $d = NumericOps::convertS32ToU32($this->stack->popInt()); + if ($mem->size() < $s + $n || $mem->size() < $d + $n) { + throw new TrapException("memory.copy: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $mem->memcpy($d, $s, $n); + } + + private function execInstrMemoryMemoryFill(Instrs\Memory\MemoryFill $instr): void + { + $f = $this->stack->currentFrame(); + $ma = $f->module->memAddrs[0]; + $mem = $this->store->mems[$ma]; + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $val = $this->stack->popInt(); + $d = NumericOps::convertS32ToU32($this->stack->popInt()); + if ($mem->size() < $d + $n) { + throw new TrapException("memory.fill: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $mem->memset($d, $val, $n); + } + + private function execInstrMemoryMemoryGrow(Instrs\Memory\MemoryGrow $instr): void + { + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $result = $mem->grow($n); + $this->stack->pushValue($result); + } + + private function execInstrMemoryMemoryInit(Instrs\Memory\MemoryInit $instr): void + { + $x = $instr->data; + $f = $this->stack->currentFrame(); + $ma = $f->module->memAddrs[0]; + $mem = $this->store->mems[$ma]; + $da = $f->module->dataAddrs[$x]; + $data = $this->store->datas[$da]; + $n = NumericOps::convertS32ToU32($this->stack->popInt()); + $s = NumericOps::convertS32ToU32($this->stack->popInt()); + $d = NumericOps::convertS32ToU32($this->stack->popInt()); + if (count($data->data) < $s + $n) { + throw new TrapException("memory.init: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + if ($mem->size() < $d + $n) { + throw new TrapException("memory.init: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $mem->copyData($data->data, $s, $d, $n); + } + + private function execInstrMemoryMemorySize(Instrs\Memory\MemorySize $instr): void + { + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $szInByte = $mem->size(); + assert(is_int($szInByte / (64 * 1024))); + $sz = $szInByte / (64 * 1024); + $this->stack->pushValue($sz); + } + + private function execInstrControlBlock(Instrs\Control\Block $instr): ?int + { + $blockType = $instr->type; + $instrs = $instr->body; + $f = $this->stack->currentFrame(); + $bt = self::expandBlockType($blockType, $f->module); + $params = array_reverse($this->stack->popNValues(count($bt->params->types))); + $n = count($bt->results->types); + $l = new Label($n); + $result = $this->execInstrs($instrs, $l, $params); + if ($result === null) { + // Do nothing. + } elseif ($result === -1) { + return -1; + } elseif ($result === 0) { + $this->deactivateLabel($n); + } else { + $this->deactivateLabel(null); + return $result - 1; + } + return null; + } + + private function execInstrControlBr(Instrs\Control\Br $instr): int + { + return $instr->label; + } + + private function execInstrControlBrIf(Instrs\Control\BrIf $instr): ?int + { + $l = $instr->label; + $c = $this->stack->popInt(); + if ($c !== 0) { + return $l; + } else { + return null; + } + } + + private function execInstrControlBrTable(Instrs\Control\BrTable $instr): int + { + $ls = $instr->labelTable; + $ln = $instr->defaultLabel; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + if ($i < count($ls)) { + return $ls[$i]; + } else { + return $ln; + } + } + + private function execInstrControlCall(Instrs\Control\Call $instr): void + { + $x = $instr->func; + $f = $this->stack->currentFrame(); + $a = $f->module->funcAddrs[$x]; + $this->doInvokeFunc($a); + } + + private function execInstrControlCallIndirect(Instrs\Control\CallIndirect $instr): void + { + $x = $instr->funcTable; + $y = $instr->type; + $f = $this->stack->currentFrame(); + $ta = $f->module->tableAddrs[$x]; + $tab = $this->store->tables[$ta]; + $ftExpect = $f->module->types[$y]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + if (count($tab->elem) <= $i) { + throw new TrapException("call_indirect: out of bounds", trapKind: TrapKind::UndefinedElement); + } + $r = $tab->elem[$i]; + if ($r instanceof Refs\RefNull) { + throw new TrapException("call_indirect: ref.null", trapKind: TrapKind::UninitializedElement); + } + assert($r instanceof Refs\RefFunc); + $a = $r->addr; + $fn = $this->store->funcs[$a]; + assert($fn instanceof FuncInsts\Wasm || $fn instanceof FuncInsts\Host); + $ftActual = $fn->type; + if (!$ftExpect->equals($ftActual)) { + throw new TrapException("call_indirect: type mismatch", trapKind: TrapKind::IndirectCallTypeMismatch); + } + $this->doInvokeFunc($a); + } + + private function execInstrControlElse_(Instrs\Control\Else_ $instr): void + { + // Do nothing. + } + + private function execInstrControlEnd(Instrs\Control\End $instr): void + { + // Do nothing. + } + + private function execInstrControlIf_(Instrs\Control\If_ $instr): ?int + { + $blockType = $instr->type; + $instrs1 = $instr->thenBody; + $instrs2 = $instr->elseBody; + $c = $this->stack->popInt(); + if ($c !== 0) { + return $this->execInstr(Instr::Block($blockType, $instrs1)); + } else { + return $this->execInstr(Instr::Block($blockType, $instrs2)); + } + } + + private function execInstrControlLoop(Instrs\Control\Loop $instr): ?int + { + $blockType = $instr->type; + $instrs = $instr->body; + $f = $this->stack->currentFrame(); + $bt = self::expandBlockType($blockType, $f->module); + $m = count($bt->params->types); + $l = new Label($m); + while (true) { + $params = array_reverse($this->stack->popNValues($m)); + $result = $this->execInstrs($instrs, $l, $params); + if ($result === null) { + return null; + } elseif ($result === -1) { + return -1; + } elseif ($result === 0) { + $this->deactivateLabel($m); + continue; + } else { + $this->deactivateLabel(null); + return $result - 1; + } + } + } + + private function execInstrControlNop(Instrs\Control\Nop $instr): void + { + // Do nothing. + } + + private function execInstrControlReturn_(Instrs\Control\Return_ $instr): int + { + return -1; + } + + private function execInstrControlUnreachable(Instrs\Control\Unreachable $instr): void + { + throw new TrapException("unreachable", trapKind: TrapKind::Unreachable); + } + + private function doLoadF32(int $offset, string $instrOpName): void + { + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadF32($ea); + if ($c === null) { + throw new TrapException("$instrOpName: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private function doLoadF64(int $offset, string $instrOpName): void + { + $f = $this->stack->currentFrame(); + $a = $f->module->memAddrs[0]; + $mem = $this->store->mems[$a]; + $i = NumericOps::convertS32ToU32($this->stack->popInt()); + $ea = $i + $offset; + $c = $mem->loadF64($ea); + if ($c === null) { + throw new TrapException("$instrOpName: out of bounds", trapKind: TrapKind::OutOfBoundsMemoryAccess); + } + $this->stack->pushValue($c); + } + + private static function defaultValueFromValType(ValType $type): int|float|Ref + { + return match ($type::class) { + ValTypes\NumType::class => match ($type->inner) { + NumType::I32 => 0, + NumType::I64 => 0, + NumType::F32 => 0.0, + NumType::F64 => 0.0, + }, + ValTypes\RefType::class => Ref::RefNull($type->inner), + default => throw new RuntimeException("unreachable"), + }; + } + + private static function expandBlockType(BlockType $bt, ModuleInst $module): FuncType + { + if ($bt instanceof BlockTypes\TypeIdx) { + return $module->types[$bt->inner]; + } elseif ($bt instanceof BlockTypes\ValType) { + $t = $bt->inner; + return new FuncType( + new ResultType([]), + new ResultType($t === null ? [] : [$t]), + ); + } else { + throw new RuntimeException("expand(): invalid blocktype"); + } + } +} diff --git a/src/WebAssembly/Execution/Stack.php b/src/WebAssembly/Execution/Stack.php new file mode 100644 index 0000000..4181dcd --- /dev/null +++ b/src/WebAssembly/Execution/Stack.php @@ -0,0 +1,212 @@ + + */ + private array $frames = []; + + private ?Frame $currentFrame = null; + + /** + * @var list + */ + private array $entries; + + public function __construct() + { + } + + public function pushFrame(Frame $frame): void + { + if ($this->getCallStackLimit() <= count($this->frames)) { + throw new StackOverflowException(); + } + $this->push($frame); + $this->frames[] = $frame; + $this->currentFrame = $frame; + } + + public function pushLabel(Label $label): void + { + $this->push($label); + } + + public function pushValue(int|float|Ref $val): void + { + $this->push($val); + } + + public function pushBool(bool $value): void + { + $this->pushValue((int)$value); + } + + public function pushRefNull(RefType $type): void + { + $this->pushValue(Ref::RefNull($type)); + } + + public function pushRefFunc(int $addr): void + { + $this->pushValue(Ref::RefFunc($addr)); + } + + public function pushRefExtern(int $addr): void + { + $this->pushValue(Ref::RefExtern($addr)); + } + + public function clear(): void + { + $this->frames = []; + $this->currentFrame = null; + $this->entries = []; + } + + public function popFrame(): Frame + { + $result = $this->pop(); + assert($result instanceof Frame); + array_pop($this->frames); + if (count($this->frames) === 0) { + $this->currentFrame = null; + } else { + $this->currentFrame = end($this->frames); + } + return $result; + } + + public function popValue(): int|float|Ref + { + $result = $this->pop(); + assert( + is_int($result) || is_float($result) || $result instanceof Ref, + 'Expected a value on the stack, but got ' . print_r($result, true), + ); + return $result; + } + + /** + * @return list + */ + public function popNValues(int $n): array + { + $results = []; + for ($i = 0; $i < $n; $i++) { + $results[] = $this->popValue(); + } + return $results; + } + + public function popInt(): int + { + $v = $this->popValue(); + assert(is_int($v), "Expected an int on top of the stack, but got " . self::getValueTypeName($v)); + return $v; + } + + /** + * @return F32 + */ + public function popFloat(): float + { + $v = $this->popValue(); + assert(is_float($v), "Expected a float on top of the stack, but got " . self::getValueTypeName($v)); + return $v; + } + + public function popRef(): Ref + { + $v = $this->popValue(); + assert($v instanceof Ref, "Expected a Ref on top of the stack, but got " . self::getValueTypeName($v)); + return $v; + } + + /** + * @return list + */ + public function popValuesToLabel(): array + { + $results = []; + while (!$this->isEmpty()) { + $top = $this->pop(); + if ($top instanceof Label) { + break; + } else { + assert(is_int($top) || is_float($top) || $top instanceof Ref); + $results[] = $top; + } + } + return $results; + } + + public function popEntriesToCurrentFrame(): void + { + while (!$this->isEmpty() && !$this->top() instanceof Frame) { + $this->pop(); + } + $this->popFrame(); + } + + public function top(): int|float|Ref|Frame|Label|null + { + $n = array_key_last($this->entries); + return $n === null ? null : $this->entries[$n]; + } + + public function count(): int + { + return count($this->entries); + } + + public function isEmpty(): bool + { + return $this->count() === 0; + } + + public function currentFrame(): Frame + { + assert($this->currentFrame !== null); + return $this->currentFrame; + } + + public function getCallStackLimit(): int + { + return 1024; + } + + private function push(int|float|Ref|Frame|Label $entry): void + { + $this->entries[] = $entry; + } + + private function pop(): int|float|Ref|Frame|Label|null + { + return array_pop($this->entries); + } + + private static function getValueTypeName(int|float|Ref|Frame|Label|null $value): string + { + return match (true) { + is_null($value) => 'null', + is_int($value) => 'int', + is_float($value) => 'float', + $value instanceof Ref => 'Ref', + $value instanceof Frame => 'Frame', + $value instanceof Label => 'Label', + }; + } +} diff --git a/src/WebAssembly/Execution/StackOverflowException.php b/src/WebAssembly/Execution/StackOverflowException.php new file mode 100644 index 0000000..c5ff3a4 --- /dev/null +++ b/src/WebAssembly/Execution/StackOverflowException.php @@ -0,0 +1,19 @@ + $funcs + * @param list $tables + * @param list $mems + * @param list $globals + * @param list $elems + * @param list $datas + */ + public function __construct( + public array $funcs, + public array $tables, + public array $mems, + public array $globals, + public array $elems, + public array $datas, + ) { + } + + public static function empty(): self + { + return new self([], [], [], [], [], []); + } + + public function register(Extern $extern): ExternVal + { + match ($extern::class) { + Externs\Func::class => $this->funcs[] = $extern->func, + Externs\Table::class => $this->tables[] = $extern->table, + Externs\Mem::class => $this->mems[] = $extern->mem, + Externs\Global_::class => $this->globals[] = $extern->global, + default => throw new RuntimeException("unreachable"), + }; + return match ($extern::class) { + Externs\Func::class => ExternVal::Func(count($this->funcs) - 1), + Externs\Table::class => ExternVal::Table(count($this->tables) - 1), + Externs\Mem::class => ExternVal::Mem(count($this->mems) - 1), + Externs\Global_::class => ExternVal::Global_(count($this->globals) - 1), + default => throw new RuntimeException("unreachable"), + }; + } +} diff --git a/src/WebAssembly/Execution/TableInst.php b/src/WebAssembly/Execution/TableInst.php new file mode 100644 index 0000000..9f4cbe7 --- /dev/null +++ b/src/WebAssembly/Execution/TableInst.php @@ -0,0 +1,19 @@ + $elem + */ + public function __construct( + public TableType $type, + public array $elem, + ) { + } +} diff --git a/src/WebAssembly/Execution/TrapException.php b/src/WebAssembly/Execution/TrapException.php new file mode 100644 index 0000000..449f9ca --- /dev/null +++ b/src/WebAssembly/Execution/TrapException.php @@ -0,0 +1,28 @@ +trapKind = $trapKind; + } + + public function getTrapKind(): TrapKind + { + return $this->trapKind; + } +} diff --git a/src/WebAssembly/Execution/TrapKind.php b/src/WebAssembly/Execution/TrapKind.php new file mode 100644 index 0000000..35a4372 --- /dev/null +++ b/src/WebAssembly/Execution/TrapKind.php @@ -0,0 +1,19 @@ + $types + */ + final public static function Select(array $types): Parametric\Select + { + return new Parametric\Select($types); + } + + // Variable instructions + final public static function GlobalGet(int $var): Variable\GlobalGet + { + return new Variable\GlobalGet($var); + } + final public static function GlobalSet(int $var): Variable\GlobalSet + { + return new Variable\GlobalSet($var); + } + final public static function LocalGet(int $var): Variable\LocalGet + { + return new Variable\LocalGet($var); + } + final public static function LocalSet(int $var): Variable\LocalSet + { + return new Variable\LocalSet($var); + } + final public static function LocalTee(int $var): Variable\LocalTee + { + return new Variable\LocalTee($var); + } + + // Table instructions + final public static function ElemDrop(int $elem): Table\ElemDrop + { + return new Table\ElemDrop($elem); + } + final public static function TableCopy(int $to, int $from): Table\TableCopy + { + return new Table\TableCopy($to, $from); + } + final public static function TableFill(int $table): Table\TableFill + { + return new Table\TableFill($table); + } + final public static function TableGet(int $table): Table\TableGet + { + return new Table\TableGet($table); + } + final public static function TableGrow(int $table): Table\TableGrow + { + return new Table\TableGrow($table); + } + final public static function TableInit(int $to, int $from): Table\TableInit + { + return new Table\TableInit($to, $from); + } + final public static function TableSet(int $table): Table\TableSet + { + return new Table\TableSet($table); + } + final public static function TableSize(int $table): Table\TableSize + { + return new Table\TableSize($table); + } + + // Memory instructions + final public static function DataDrop(int $data): Memory\DataDrop + { + return new Memory\DataDrop($data); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function F32Load(int $offset, int $align): Memory\F32Load + { + return new Memory\F32Load($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function F32Store(int $offset, int $align): Memory\F32Store + { + return new Memory\F32Store($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function F64Load(int $offset, int $align): Memory\F64Load + { + return new Memory\F64Load($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function F64Store(int $offset, int $align): Memory\F64Store + { + return new Memory\F64Store($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Load(int $offset, int $align): Memory\I32Load + { + return new Memory\I32Load($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Load16S(int $offset, int $align): Memory\I32Load16S + { + return new Memory\I32Load16S($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Load16U(int $offset, int $align): Memory\I32Load16U + { + return new Memory\I32Load16U($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Load8S(int $offset, int $align): Memory\I32Load8S + { + return new Memory\I32Load8S($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Load8U(int $offset, int $align): Memory\I32Load8U + { + return new Memory\I32Load8U($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Store(int $offset, int $align): Memory\I32Store + { + return new Memory\I32Store($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Store16(int $offset, int $align): Memory\I32Store16 + { + return new Memory\I32Store16($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I32Store8(int $offset, int $align): Memory\I32Store8 + { + return new Memory\I32Store8($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Load(int $offset, int $align): Memory\I64Load + { + return new Memory\I64Load($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Load16S(int $offset, int $align): Memory\I64Load16S + { + return new Memory\I64Load16S($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Load16U(int $offset, int $align): Memory\I64Load16U + { + return new Memory\I64Load16U($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Load32S(int $offset, int $align): Memory\I64Load32S + { + return new Memory\I64Load32S($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Load32U(int $offset, int $align): Memory\I64Load32U + { + return new Memory\I64Load32U($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Load8S(int $offset, int $align): Memory\I64Load8S + { + return new Memory\I64Load8S($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Load8U(int $offset, int $align): Memory\I64Load8U + { + return new Memory\I64Load8U($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Store(int $offset, int $align): Memory\I64Store + { + return new Memory\I64Store($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Store16(int $offset, int $align): Memory\I64Store16 + { + return new Memory\I64Store16($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Store32(int $offset, int $align): Memory\I64Store32 + { + return new Memory\I64Store32($offset, $align); + } + /** + * @param U32 $offset + * @param U32 $align + */ + final public static function I64Store8(int $offset, int $align): Memory\I64Store8 + { + return new Memory\I64Store8($offset, $align); + } + final public static function MemoryCopy(): Memory\MemoryCopy + { + return new Memory\MemoryCopy(); + } + final public static function MemoryFill(): Memory\MemoryFill + { + return new Memory\MemoryFill(); + } + final public static function MemoryGrow(): Memory\MemoryGrow + { + return new Memory\MemoryGrow(); + } + final public static function MemoryInit(int $data): Memory\MemoryInit + { + return new Memory\MemoryInit($data); + } + final public static function MemorySize(): Memory\MemorySize + { + return new Memory\MemorySize(); + } + + // Control instructions + /** + * @param list $body + */ + final public static function Block(BlockType $type, array $body): Control\Block + { + return new Control\Block($type, $body); + } + final public static function Br(int $label): Control\Br + { + return new Control\Br($label); + } + final public static function BrIf(int $label): Control\BrIf + { + return new Control\BrIf($label); + } + /** + * @param list $labelTable + */ + final public static function BrTable(array $labelTable, int $defaultLabel): Control\BrTable + { + return new Control\BrTable($labelTable, $defaultLabel); + } + final public static function Call(int $func): Control\Call + { + return new Control\Call($func); + } + final public static function CallIndirect(int $funcTable, int $type): Control\CallIndirect + { + return new Control\CallIndirect($funcTable, $type); + } + final public static function Else_(): Control\Else_ + { + return new Control\Else_(); + } + final public static function End(): Control\End + { + return new Control\End(); + } + /** + * @param list $thenBody + * @param list $elseBody + */ + final public static function If_(BlockType $type, array $thenBody, array $elseBody): Control\If_ + { + return new Control\If_($type, $thenBody, $elseBody); + } + /** + * @param list $body + */ + final public static function Loop(BlockType $type, array $body): Control\Loop + { + return new Control\Loop($type, $body); + } + final public static function Nop(): Control\Nop + { + return new Control\Nop(); + } + final public static function Return_(): Control\Return_ + { + return new Control\Return_(); + } + final public static function Unreachable(): Control\Unreachable + { + return new Control\Unreachable(); + } + + abstract public static function opName(): string; +} diff --git a/src/WebAssembly/Structure/Instructions/Instrs/Control/Block.php b/src/WebAssembly/Structure/Instructions/Instrs/Control/Block.php new file mode 100644 index 0000000..af2c97f --- /dev/null +++ b/src/WebAssembly/Structure/Instructions/Instrs/Control/Block.php @@ -0,0 +1,24 @@ + $body + */ + protected function __construct( + public BlockType $type, + public array $body, + ) { + } + + public static function opName(): string + { + return "block"; + } +} diff --git a/src/WebAssembly/Structure/Instructions/Instrs/Control/BlockType.php b/src/WebAssembly/Structure/Instructions/Instrs/Control/BlockType.php new file mode 100644 index 0000000..1188625 --- /dev/null +++ b/src/WebAssembly/Structure/Instructions/Instrs/Control/BlockType.php @@ -0,0 +1,25 @@ + $labelTable + */ + protected function __construct( + public array $labelTable, + public int $defaultLabel, + ) { + } + + public static function opName(): string + { + return "br_table"; + } +} diff --git a/src/WebAssembly/Structure/Instructions/Instrs/Control/Call.php b/src/WebAssembly/Structure/Instructions/Instrs/Control/Call.php new file mode 100644 index 0000000..461f561 --- /dev/null +++ b/src/WebAssembly/Structure/Instructions/Instrs/Control/Call.php @@ -0,0 +1,20 @@ + $thenBody + * @param list $elseBody + */ + protected function __construct( + public BlockType $type, + public array $thenBody, + public array $elseBody, + ) { + } + + public static function opName(): string + { + return "if"; + } +} diff --git a/src/WebAssembly/Structure/Instructions/Instrs/Control/Loop.php b/src/WebAssembly/Structure/Instructions/Instrs/Control/Loop.php new file mode 100644 index 0000000..ca48ca1 --- /dev/null +++ b/src/WebAssembly/Structure/Instructions/Instrs/Control/Loop.php @@ -0,0 +1,24 @@ + $body + */ + protected function __construct( + public BlockType $type, + public array $body, + ) { + } + + public static function opName(): string + { + return "loop"; + } +} diff --git a/src/WebAssembly/Structure/Instructions/Instrs/Control/Nop.php b/src/WebAssembly/Structure/Instructions/Instrs/Control/Nop.php new file mode 100644 index 0000000..50a3773 --- /dev/null +++ b/src/WebAssembly/Structure/Instructions/Instrs/Control/Nop.php @@ -0,0 +1,15 @@ + $types + */ + protected function __construct( + public array $types, + ) { + } + + public static function opName(): string + { + return "select"; + } +} diff --git a/src/WebAssembly/Structure/Instructions/Instrs/Reference/RefFunc.php b/src/WebAssembly/Structure/Instructions/Instrs/Reference/RefFunc.php new file mode 100644 index 0000000..3539ede --- /dev/null +++ b/src/WebAssembly/Structure/Instructions/Instrs/Reference/RefFunc.php @@ -0,0 +1,20 @@ + $init + */ + public function __construct( + public array $init, + public DataMode $mode, + ) { + } +} diff --git a/src/WebAssembly/Structure/Modules/DataMode.php b/src/WebAssembly/Structure/Modules/DataMode.php new file mode 100644 index 0000000..37205bc --- /dev/null +++ b/src/WebAssembly/Structure/Modules/DataMode.php @@ -0,0 +1,25 @@ + $offset + */ + final public static function Active( + int $memory, + array $offset, + ): DataModes\Active { + return new DataModes\Active($memory, $offset); + } +} diff --git a/src/WebAssembly/Structure/Modules/DataModes/Active.php b/src/WebAssembly/Structure/Modules/DataModes/Active.php new file mode 100644 index 0000000..41cc9c7 --- /dev/null +++ b/src/WebAssembly/Structure/Modules/DataModes/Active.php @@ -0,0 +1,20 @@ + $offset + */ + protected function __construct( + public int $memory, + public array $offset, + ) { + } +} diff --git a/src/WebAssembly/Structure/Modules/DataModes/Passive.php b/src/WebAssembly/Structure/Modules/DataModes/Passive.php new file mode 100644 index 0000000..6ef6ba9 --- /dev/null +++ b/src/WebAssembly/Structure/Modules/DataModes/Passive.php @@ -0,0 +1,14 @@ +> $init + */ + public function __construct( + public RefType $type, + public array $init, + public ElemMode $mode, + ) { + } +} diff --git a/src/WebAssembly/Structure/Modules/ElemMode.php b/src/WebAssembly/Structure/Modules/ElemMode.php new file mode 100644 index 0000000..0777bd4 --- /dev/null +++ b/src/WebAssembly/Structure/Modules/ElemMode.php @@ -0,0 +1,30 @@ + $offset + */ + final public static function Active( + int $table, + array $offset, + ): ElemModes\Active { + return new ElemModes\Active($table, $offset); + } + + final public static function Declarative(): ElemModes\Declarative + { + return new ElemModes\Declarative(); + } +} diff --git a/src/WebAssembly/Structure/Modules/ElemModes/Active.php b/src/WebAssembly/Structure/Modules/ElemModes/Active.php new file mode 100644 index 0000000..e66e080 --- /dev/null +++ b/src/WebAssembly/Structure/Modules/ElemModes/Active.php @@ -0,0 +1,20 @@ + $offset + */ + protected function __construct( + public int $table, + public array $offset, + ) { + } +} diff --git a/src/WebAssembly/Structure/Modules/ElemModes/Declarative.php b/src/WebAssembly/Structure/Modules/ElemModes/Declarative.php new file mode 100644 index 0000000..28f917a --- /dev/null +++ b/src/WebAssembly/Structure/Modules/ElemModes/Declarative.php @@ -0,0 +1,14 @@ + $locals + * @param list $body + */ + public function __construct( + public int $type, + public array $locals, + public array $body, + ) { + } +} diff --git a/src/WebAssembly/Structure/Modules/Global_.php b/src/WebAssembly/Structure/Modules/Global_.php new file mode 100644 index 0000000..04ffbc6 --- /dev/null +++ b/src/WebAssembly/Structure/Modules/Global_.php @@ -0,0 +1,20 @@ + $init + */ + public function __construct( + public GlobalType $type, + public array $init, + ) { + } +} diff --git a/src/WebAssembly/Structure/Modules/Import.php b/src/WebAssembly/Structure/Modules/Import.php new file mode 100644 index 0000000..e764fcb --- /dev/null +++ b/src/WebAssembly/Structure/Modules/Import.php @@ -0,0 +1,19 @@ + $types + * @param list $funcs + * @param list
$tables + * @param list $mems + * @param list $globals + * @param list $elems + * @param list $datas + * @param list $imports + * @param list $exports + */ + public function __construct( + public array $types, + public array $funcs, + public array $tables, + public array $mems, + public array $globals, + public array $elems, + public array $datas, + public ?Start $start, + public array $imports, + public array $exports, + ) { + } +} diff --git a/src/WebAssembly/Structure/Modules/Start.php b/src/WebAssembly/Structure/Modules/Start.php new file mode 100644 index 0000000..6659929 --- /dev/null +++ b/src/WebAssembly/Structure/Modules/Start.php @@ -0,0 +1,13 @@ +params->equals($other->params) + && $this->results->equals($other->results); + } +} diff --git a/src/WebAssembly/Structure/Types/GlobalType.php b/src/WebAssembly/Structure/Types/GlobalType.php new file mode 100644 index 0000000..3e8ab6c --- /dev/null +++ b/src/WebAssembly/Structure/Types/GlobalType.php @@ -0,0 +1,14 @@ +min && + ($this->max === null || $this->min <= $this->max); + } +} diff --git a/src/WebAssembly/Structure/Types/MemType.php b/src/WebAssembly/Structure/Types/MemType.php new file mode 100644 index 0000000..8d01150 --- /dev/null +++ b/src/WebAssembly/Structure/Types/MemType.php @@ -0,0 +1,13 @@ + $types + */ + public function __construct( + public array $types, + ) { + } + + public function equals(ResultType $other): bool + { + if (count($this->types) !== count($other->types)) { + return false; + } + foreach ($this->types as $i => $type) { + if (!$type->equals($other->types[$i])) { + return false; + } + } + return true; + } +} diff --git a/src/WebAssembly/Structure/Types/TableType.php b/src/WebAssembly/Structure/Types/TableType.php new file mode 100644 index 0000000..7e4b957 --- /dev/null +++ b/src/WebAssembly/Structure/Types/TableType.php @@ -0,0 +1,14 @@ +inner === $other->inner; + } +} diff --git a/src/WebAssembly/Structure/Types/ValTypes/RefType.php b/src/WebAssembly/Structure/Types/ValTypes/RefType.php new file mode 100644 index 0000000..a31b7c3 --- /dev/null +++ b/src/WebAssembly/Structure/Types/ValTypes/RefType.php @@ -0,0 +1,21 @@ +inner === $other->inner; + } +} diff --git a/src/WebAssembly/Structure/Types/ValTypes/VecType.php b/src/WebAssembly/Structure/Types/ValTypes/VecType.php new file mode 100644 index 0000000..03cc95e --- /dev/null +++ b/src/WebAssembly/Structure/Types/ValTypes/VecType.php @@ -0,0 +1,21 @@ +inner === $other->inner; // @phpstan-ignore-line + } +} diff --git a/src/WebAssembly/Structure/Types/VecType.php b/src/WebAssembly/Structure/Types/VecType.php new file mode 100644 index 0000000..dcf7d68 --- /dev/null +++ b/src/WebAssembly/Structure/Types/VecType.php @@ -0,0 +1,10 @@ +