call_indirect picks the function to call at run time, by index into the table. Today you add it, including the type check that makes indirect dispatch safe.
Execute call_indirect, looking up a function in the table by a popped index and calling it after checking its type.
call_indirect (0x11) is the run-time call: instead of naming a function, it names an expected type and pops an index into the table, then calls whatever function is sitting in that slot. Its immediates are a typeidx (the signature the call site expects) and a tableidx (always 0x00 in the MVP, since there is one table). So a caller pushes the real argument, pushes the slot index, and call_indirect fetches table[index], arrives at a function index, and calls it exactly like a direct call - returning 42 when slot 0 holds an add-one function fed 41.
The reason for the typeidx immediate is safety. The engine cannot trust that the slot holds a function of the right shape - the table is just data, and the index came off the stack - so before calling it verifies that the target function’s actual type matches the expected type. This run-time type check is what makes indirect dispatch sound: it guarantees the callee reads exactly the arguments the caller pushed. Today you implement the happy path where the types match; the next lesson turns the mismatch, the out-of-range index, and the empty slot into clean traps.
// call_indirect (0x11): immediates are a typeidx and a tableidx (0x00 in MVP).// Pop an i32 slot index, fetch the funcidx from the table, check its type// equals Types[typeidx], then call it like a normal call.case 0x11:typeidx := readVarU32(body, &pc); _ = readVarU32(body, &pc) // tableidxslot := stack.PopI32()fidx := table.Funcs[slot]// require m.Types[m.FuncType[fidx]] == m.Types[typeidx]; then call fidx