#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" # LinuxCNC source basis: this guard ties the documented wasm-safe shim map to # the linuxcnc_wasm_safe_probe_shims CMake set, the RS274/WASM source manifest, # and the LinuxCNC sources each shim cites. It validates source coverage only; # it does not add CNC behavior, expand smoke parsing, or interpret G-code. linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest" linuxcnc_root=$(cd "$linuxcnc_root" && pwd) grep -F 'LinuxCNC source basis: wasm-safe shim probe sources are limited to the' \ list-linuxcnc-wasm-safe-shims.sh >/dev/null grep -F 'set(linuxcnc_wasm_safe_probe_shims' core/CMakeLists.txt >/dev/null grep -F 'LinuxCNC source basis: this guard ties the documented WASM blocker map' \ check-linuxcnc-wasm-blockers-source-map.sh >/dev/null grep -F 'dlopen(NULL, RTLD_GLOBAL);' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null grep -F 'interp_lib = dlopen(interp_path, RTLD_NOW);' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null grep -F 'Constructor constructor = (Constructor)dlsym(interp_lib, "makeInterp");' \ "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null grep -F 'dlerror()' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null grep -F '#include "libintl.h"' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null grep -F '#define _(s) gettext(s)' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null grep -F 'CHKS((!_readers[(int)' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.cc" >/dev/null grep -F 'Cannot use polar coordinate on a machine lacking X or Y axes' \ "$linuxcnc_root/src/emc/rs274ngc/interp_internal.cc" >/dev/null grep -F 'int _task = 0;' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'int _task = 1;' "$linuxcnc_root/src/emc/task/emctaskmain.cc" >/dev/null grep -F '{ "interpreter", PyInit_interpreter },' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F '{ "emccanon", PyInit_emccanon },' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'struct _inittab builtin_modules[]' "$linuxcnc_root/src/emc/task/taskclass.cc" >/dev/null grep -F 'int Interp::fetch_hal_param(' "$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null grep -F 'hal_get_pin_value_by_name(hal_name, &type, &ptr, &conn)' \ "$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null grep -F 'hal_get_signal_value_by_name(hal_name, &type, &ptr, &conn)' \ "$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null grep -F 'hal_get_param_value_by_name(hal_name, &type, &ptr)' \ "$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null grep -F 'HAL_TYPE_UNINITIALIZED = 0,' "$linuxcnc_root/include/hal.h" >/dev/null grep -F 'extern int hal_get_pin_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null grep -F 'extern int hal_get_signal_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null grep -F 'extern int hal_get_param_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null grep -F 'int hal_get_pin_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null grep -F 'int hal_get_signal_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null grep -F 'int hal_get_param_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null grep -F 'wordexp(basename, &exp_result, 0);' "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'wordexp(settings->program_prefix, &exp_result, 0);' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'wordexp(settings->subroutines[dct], &exp_result, 0);' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'wordfree(&exp_result);' "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F '#include "nml_intf/emc_nml.hh"' "$linuxcnc_root/src/emc/sai/dummyemcstat.cc" >/dev/null grep -F 'EMC_STAT *emcStatus = new EMC_STAT;' "$linuxcnc_root/src/emc/sai/dummyemcstat.cc" >/dev/null grep -F 'int NMLmsg::automatically_clear = 1;' \ "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null grep -F 'NMLmsg::NMLmsg' "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null grep -F 'void NMLmsg::clear()' "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null grep -F 'memset((void *) this, 0, size);' \ "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null grep -F 'RCS_STAT_MSG::RCS_STAT_MSG' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null grep -F 'command_type = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null grep -F 'echo_serial_number = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null grep -F 'status = RCS_STATUS::UNINITIALIZED;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null grep -F '_state = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null grep -F 'extern int set_print_rcs_error_info(const char *file, int line);' \ "$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null grep -F 'extern int print_rcs_error_new(const char *_fmt, ...)' \ "$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null grep -F '#define rcs_print_error set_print_rcs_error_info( __FILE__, __LINE__); print_rcs_error_new' \ "$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null grep -F 'int set_print_rcs_error_info(const char *file, int line)' \ "$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null grep -F 'print_rcs_error_new' "$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null grep -F 'retval = rcs_vprint(_fmt, args, 1);' \ "$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null grep -F 'extern int rtapi_snprintf(char *buf, unsigned long int size,' \ "$linuxcnc_root/src/rtapi/rtapi.h" >/dev/null grep -F 'extern int rtapi_vsnprintf(char *buf, unsigned long size,' \ "$linuxcnc_root/src/rtapi/rtapi.h" >/dev/null grep -F 'int rtapi_snprintf(char *buffer, unsigned long int size, const char *msg, ...) {' \ "$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null grep -F 'result = vsnprintf(buffer, size, msg, args);' \ "$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null grep -F 'int rtapi_vsnprintf(char *buffer, unsigned long int size, const char *fmt,' \ "$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null grep -F 'return vsnprintf(buffer, size, fmt, args);' \ "$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null grep -F 'class PythonPlugin' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PLUGIN_PYTHON_NOT_INITIALIZED = -12,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PLUGIN_BAD_PATH = -5,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PLUGIN_INITTAB_FAILED = -7 ,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PLUGIN_EXCEPTION_DURING_PATH_PREPEND = -9,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PLUGIN_EXCEPTION_DURING_PATH_APPEND = -10,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PLUGIN_NO_CALLABLE = 1,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PLUGIN_EXCEPTION = 2' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null grep -F 'PythonPlugin::instantiate' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'for (int i = 0; inittab[i].name != NULL; i++) {' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'if (module == NULL) {' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'status = PLUGIN_INITTAB_FAILED;' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'if (status < PLUGIN_OK)' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'if (auto inistring = inifile.findString("TOPLEVEL", section)) {' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'if (realpath(toplevel, real_path) == NULL) {' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'status = PLUGIN_BAD_PATH;' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'while (auto inistring = inifile.findString(n, "PATH_PREPEND", "PYTHON")) {' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'while (auto inistring = inifile.findString(n, "PATH_APPEND", "PYTHON")) {' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'status = PLUGIN_EXCEPTION_DURING_PATH_PREPEND;' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'status = PLUGIN_EXCEPTION_DURING_PATH_APPEND;' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'int IniFile::tildeExpand(const std::string &path, std::string &result)' \ "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null grep -F "if(!home)" "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null grep -F 'IniFile::trim(sect);' "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null grep -F 'Section header has trailing content, ignored' "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null grep -F 'retval = bp::exec_file(cmd, main_namespace, main_namespace);' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'retval = bp::exec(cmd, main_namespace, main_namespace);' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'PyObject *rv = PyObject_Call(function.ptr(), tupleargs.ptr(), kwargs.ptr());' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F 'PyErr_Fetch(&exc,&val,&tb);' \ "$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null grep -F 'PyErr_NormalizeException(&exc,&val,&tb);' \ "$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null grep -F 'if (Py_IsInitialized()) {' \ "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null grep -F '#define callmethod(o, m, f, ...) PyObject_CallMethod((o), (char*)(m), (char*)(f), ## __VA_ARGS__)' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'PyObject *result = PyObject_GetAttrString(callback, "parameter_file");' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'PyErr_Format(PyExc_TypeError,' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'return Py_BuildValue("[ddd][ddd][ddd][ddd]",' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'PyObject *m = PyModule_Create(&gcode_moduledef);' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'PyType_Ready(&LineCodeType);' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'PyModule_AddObject(m, "linecode", (PyObject*)&LineCodeType);' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F 'PyObject_SetAttrString(m, "MAX_ERROR", PyLong_FromLong(maxerror));' \ "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null grep -F '#define TOOL_MMAP_FILENAME ".tool.mmap"' "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null grep -F 'int tool_nml_register(CANON_TOOL_TABLE *tblptr)' "$linuxcnc_root/src/emc/tooldata/tooldata_nml.cc" >/dev/null grep -F 'int tooldata_db_init(char progname_plus_args[]' "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'if (!db_live) {return -1;}' "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null grep -F 'if (!db_live) return 0;' "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null python3 - "$manifest" <<'PY' from pathlib import Path import re import subprocess import sys manifest = Path(sys.argv[1]) source_map = Path("docs/linuxcnc-wasm-shims-source-map.md").read_text(encoding="utf-8") manifest_text = manifest.read_text(encoding="utf-8") shim_paths = [ line for line in subprocess.check_output(["./list-linuxcnc-wasm-safe-shims.sh"], text=True).splitlines() if line ] if len(shim_paths) != 11: raise SystemExit(f"expected 11 wasm-safe CMake shim sources, found {len(shim_paths)}") if f"| CMake `linuxcnc_wasm_safe_probe_shims` sources | {len(shim_paths)} |" not in source_map: raise SystemExit("docs/linuxcnc-wasm-shims-source-map.md shim count mismatch") expected_shims = { "core/wasm_shims/dlfcn.cc": ["src/emc/rs274ngc/interp_base.cc"], "core/wasm_shims/emc_status_shim.cc": ["src/emc/sai/dummyemcstat.cc"], "core/wasm_shims/gettext_shim.cc": ["src/emc/rs274ngc/interp_internal.hh"], "core/wasm_shims/linuxcnc_runtime_shim.cc": [ "src/emc/rs274ngc/gcodemodule.cc", "src/emc/task/emctaskmain.cc", "src/emc/task/taskclass.cc", "src/emc/rs274ngc/interp_namedparams.cc", "include/hal.h", "src/hal/hal_lib.c", "src/emc/rs274ngc/rs274ngc_pre.cc", ], "core/wasm_shims/nml_status_shim.cc": [ "src/libnml/nml/nmlmsg.cc", "src/libnml/nml/stat_msg.cc", ], "core/wasm_shims/python_c_api_shim.cc": [ "src/emc/rs274ngc/interp_python.cc", "src/emc/rs274ngc/interp_namedparams.cc", "src/emc/rs274ngc/gcodemodule.cc", "src/emc/pythonplugin/python_plugin.cc", ], "core/wasm_shims/rcs_print_shim.cc": [ "src/libnml/rcs/rcs_print.cc", "src/libnml/rcs/rcs_print.hh", ], "core/wasm_shims/rtapi_compat.cc": [ "src/rtapi/uspace_common.h", "src/rtapi/rtapi.h", ], "core/wasm_shims/tooldata/tooldata_mmap_backend.cc": [ "src/emc/tooldata/tooldata_mmap.cc", ], "core/wasm_shims/tooldata/tooldata_runtime_stubs.cc": [ "src/emc/tooldata/tooldata_nml.cc", "src/emc/tooldata/tooldata_db.cc", ], "core/wasm_shims/pythonplugin/python_plugin.cc": [ "src/emc/pythonplugin/python_plugin.cc", "src/emc/pythonplugin/python_plugin.hh", "src/emc/ini/inifile.cc", ], } if shim_paths != list(expected_shims): raise SystemExit("wasm-safe shim CMake order no longer matches docs/linuxcnc-wasm-shims-source-map.md") for shim_path, linuxcnc_sources in expected_shims.items(): shim_text = Path(shim_path).read_text(encoding="utf-8") if "LinuxCNC source basis:" not in shim_text: raise SystemExit(f"{shim_path} is missing a LinuxCNC source basis comment") if f"| `{shim_path}` |" not in source_map: raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing shim row: {shim_path}") for source in linuxcnc_sources: if source not in source_map: raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing source {source} for {shim_path}") if source not in manifest_text: raise SystemExit(f"{source} is not tracked in {manifest}") runtime_shim = Path("core/wasm_shims/linuxcnc_runtime_shim.cc").read_text(encoding="utf-8") runtime_probe = Path("core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc").read_text(encoding="utf-8") dlfcn_shim = Path("core/wasm_shims/dlfcn.cc").read_text(encoding="utf-8") dlfcn_header = Path("core/wasm_shims/dlfcn.h").read_text(encoding="utf-8") emc_status_shim = Path("core/wasm_shims/emc_status_shim.cc").read_text(encoding="utf-8") gettext_shim = Path("core/wasm_shims/gettext_shim.cc").read_text(encoding="utf-8") for needle in [ "#define RTLD_GLOBAL 0x00100", "#define RTLD_NOW 0x00002", "void *dlopen(const char *filename, int flags);", "void *dlsym(void *handle, const char *symbol);", "char *dlerror(void);", "int dlclose(void *handle);", ]: if needle not in dlfcn_header: raise SystemExit(f"dlfcn shim header missing LinuxCNC loader boundary: {needle}") for needle in [ "dynamic loading is unavailable in wasm-safe probe", "void *dlopen(const char *, int)", "return nullptr;", "void *dlsym(void *, const char *)", "char *dlerror(void)", "return last_error;", "int dlclose(void *)", "return 0;", ]: if needle not in dlfcn_shim: raise SystemExit(f"dlfcn shim missing unavailable-loader boundary: {needle}") for needle in [ '#include "nml_intf/emc_nml.hh"', "EMC_STAT *emcStatus = new EMC_STAT;", ]: if needle not in emc_status_shim: raise SystemExit(f"EMC status shim missing detached LinuxCNC status singleton: {needle}") for needle in [ "extern \"C\" char *gettext(const char *msgid)", "return const_cast(msgid ? msgid : \"\");", ]: if needle not in gettext_shim: raise SystemExit(f"gettext shim missing diagnostic passthrough boundary: {needle}") nml_status_shim = Path("core/wasm_shims/nml_status_shim.cc").read_text(encoding="utf-8") for needle in [ "int NMLmsg::automatically_clear = 1;", "void NMLmsg::clear()", "std::memset(static_cast(this), 0, static_cast(size));", "command_type = -1;", "echo_serial_number = -1;", "status = RCS_STATUS::UNINITIALIZED;", "_state = -1;", ]: if needle not in nml_status_shim: raise SystemExit(f"NML status shim missing LinuxCNC constructor boundary: {needle}") for needle in [ "int _task = 0;", 'extern "C" PyObject *PyInit_interpreter(void)', 'extern "C" PyObject *PyInit_emccanon(void)', 'extern "C" PyObject *PyInit_gcode(void)', "return nullptr;", 'struct _inittab builtin_modules[] = {', '{"interpreter", PyInit_interpreter}', '{"emccanon", PyInit_emccanon}', ]: if needle not in runtime_shim: raise SystemExit(f"linuxcnc runtime shim missing preview/builtin module boundary: {needle}") for needle in [ "std::string expand_home_directory(const char *words)", "std::string expand_environment_variables(const std::string &words)", "extern \"C\" int wordexp(const char *words, wordexp_t *pwordexp, int)", "return WRDE_BADVAL;", "return WRDE_NOSPACE;", "pwordexp->we_wordc = 1;", "pwordexp->we_offs = 0;", "extern \"C\" void wordfree(wordexp_t *pwordexp)", "pwordexp->we_wordc = 0;", "pwordexp->we_wordv = nullptr;", ]: if needle not in runtime_shim: raise SystemExit(f"linuxcnc runtime shim missing limited wordexp boundary: {needle}") for needle in [ 'extern "C" int hal_get_pin_value_by_name', 'extern "C" int hal_get_signal_value_by_name', 'extern "C" int hal_get_param_value_by_name', "*type = HAL_TYPE_UNINITIALIZED;", "*data = nullptr;", "return -1;", ]: if needle not in runtime_shim: raise SystemExit(f"linuxcnc runtime shim missing HAL unavailable boundary: {needle}") for needle in [ "bool hal_lookup_is_unavailable()", 'hal_get_pin_value_by_name("cnc-sim.missing-pin", &type, &data, &connected) != -1', 'hal_get_signal_value_by_name("cnc-sim.missing-signal", &type, &data, &has_writers) != -1', 'hal_get_param_value_by_name("cnc-sim.missing-param", &type, &data) != -1', "type != HAL_TYPE_UNINITIALIZED", "data != nullptr", ]: if needle not in runtime_probe: raise SystemExit(f"runtime probe missing HAL unavailable assertion: {needle}") python_plugin_shim = Path("core/wasm_shims/pythonplugin/python_plugin.cc").read_text(encoding="utf-8") python_plugin_probe = Path("core/wasm_shims/pythonplugin/python_plugin_probe_main.cc").read_text(encoding="utf-8") python_plugin_inittab_probe = Path("core/wasm_shims/pythonplugin/python_plugin_inittab_probe_main.cc").read_text(encoding="utf-8") python_c_api_shim = Path("core/wasm_shims/python_c_api_shim.cc").read_text(encoding="utf-8") for forbidden in [ "bp::exec(", "bp::exec_file(", "PyObject_Call(", ]: if forbidden in python_plugin_shim: raise SystemExit(f"PythonPlugin wasm shim must not execute Python: {forbidden}") for needle in [ "python disabled in wasm-safe probe", 'find_ini_value(iniFilename, section, "TOPLEVEL", toplevel)', "realpath(toplevel.c_str(), resolved_path) == nullptr", "status = PLUGIN_BAD_PATH;", "status = PLUGIN_INITTAB_FAILED;", "failed to initialize built-in module", "read_ini_section(text, section_name)", "section_name = trim_ini_value(text.substr(1, close - 1));", 'check_python_path_entries(iniFilename,', "PLUGIN_EXCEPTION_DURING_PATH_PREPEND", "PLUGIN_EXCEPTION_DURING_PATH_APPEND", "tilde_expand_path(entry, expanded)", "status = PLUGIN_PYTHON_NOT_INITIALIZED;", "return PLUGIN_NO_CALLABLE;", "status = PLUGIN_EXCEPTION;", "if (status < PLUGIN_OK)", ]: if needle not in python_plugin_shim: raise SystemExit(f"PythonPlugin wasm shim missing disabled-python boundary: {needle}") for needle in [ "PythonPlugin::PythonPlugin(struct _inittab*) reports PLUGIN_INITTAB_FAILED", "PythonPlugin *plugin = PythonPlugin::instantiate(bad_modules);", "plugin != nullptr", "python_plugin->plugin_status() != PLUGIN_INITTAB_FAILED", "python_plugin->usable()", 'python_plugin->last_errmsg() != "failed to initialize built-in module"', ]: if needle not in python_plugin_inittab_probe: raise SystemExit(f"PythonPlugin inittab probe missing first-caller status assertion: {needle}") for needle in [ "[ PYTHON ] ; section comment", "PLUGIN_NO_SECTION", "PLUGIN_BAD_INIFILE", "PLUGIN_BAD_PATH", "PLUGIN_EXCEPTION_DURING_PATH_PREPEND", "PLUGIN_EXCEPTION_DURING_PATH_APPEND", "PLUGIN_PYTHON_NOT_INITIALIZED", "PLUGIN_NO_CALLABLE", "PLUGIN_EXCEPTION", "handle_pyerror().empty()", ]: if needle not in python_plugin_probe: raise SystemExit(f"PythonPlugin probe missing disabled-python status assertion: {needle}") for needle in [ "static PyTypeObject none_type = {\"NoneType\"};", "static PyObject none_object = {&none_type};", "PyObject *_Py_NoneStructPtr = &none_object;", "int Py_IsInitialized(void)", "return 1;", "PyObject *PyErr_Occurred(void)", "return nullptr;", "void PyErr_Fetch(PyObject **exc, PyObject **val, PyObject **tb)", "*exc = nullptr;", "*val = nullptr;", "*tb = nullptr;", "int PyCallable_Check(PyObject *)", "return 0;", "PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...)", "PyObject *PyObject_GetAttrString(PyObject *, const char *)", "return &none_object;", "PyObject *Py_BuildValue(const char *, ...)", "PyObject *PyModule_Create(PyModuleDef *)", "int PyModule_AddObject(PyObject *, const char *, PyObject *)", "int PyType_Ready(PyTypeObject *)", "void *PyObject_NewShim(std::size_t size, PyTypeObject *type)", "std::calloc(1, size)", ]: if needle not in python_c_api_shim: raise SystemExit(f"Python C API shim missing symbol boundary: {needle}") rcs_print_shim = Path("core/wasm_shims/rcs_print_shim.cc").read_text(encoding="utf-8") for needle in [ "set_print_rcs_error_info", "print_rcs_error_new", "std::vfprintf(stderr, format, args)", ]: if needle not in rcs_print_shim: raise SystemExit(f"RCS print shim missing split print-error boundary: {needle}") rtapi_compat_shim = Path("core/wasm_shims/rtapi_compat.cc").read_text(encoding="utf-8") for needle in [ "rtapi_snprintf", "std::vsnprintf(buf, static_cast(size), fmt, ap)", ]: if needle not in rtapi_compat_shim: raise SystemExit(f"RTAPI compatibility shim missing userspace snprintf boundary: {needle}") for phrase in [ "must not add CNC behavior", "must not expand the temporary smoke parser", "CMake `linuxcnc_wasm_safe_probe_shims` set", "RS274/WASM manifest", "browser-safe LinuxCNC adaptations", "LinuxCNC `interp_base.cc` dynamic loader calls", "unavailable-loader returns", "detached LinuxCNC `EMC_STAT` singleton", "LinuxCNC `_()` diagnostic call sites", "browser gettext catalogs unavailable", "LinuxCNC NML status constructors", "`RCS_STAT_MSG` default status fields", "dynamic loading unavailable", "unavailable HAL lookup", "must leave HAL values unavailable", "preview `_task=0`", "builtin module stubs", "must not switch browser-safe probes into task mode", "single-word `wordexp()` path expansion", "`wordfree()` cleanup", "must not become a shell expansion feature", "must not become Python execution", "Python C API shim must return inert objects", "must not call into CPython runtime", "keeping Python execution disabled", "must not execute `bp::exec`, `bp::exec_file`, or", "inactive-DB return values", "negative-status short-circuit behavior", "first-caller `_inittab` `PLUGIN_INITTAB_FAILED` edge", "preserve the `TOPLEVEL` missing-path `PLUGIN_BAD_PATH` edge", "preserve `PATH_PREPEND` and `PATH_APPEND` tilde-expansion failure statuses", "preserve LinuxCNC INI section-name trimming and trailing section-comment handling", "LinuxCNC `rcs_print_error` split entry points", "without native print routing", "LinuxCNC userspace `rtapi_snprintf()` formatting", "`uspace_common.h` `vsnprintf()` implementation path", "./check-linuxcnc-wasm-shims-source-map.sh", "does not interpret G-code", ]: if phrase not in source_map: raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing boundary phrase: {phrase}") for entry in [ "test-native.sh", "test-linuxcnc-source-link.sh", "build-wasm.sh", ]: text = Path(entry).read_text(encoding="utf-8") if "./check-linuxcnc-wasm-shims-source-map.sh" not in text: raise SystemExit(f"{entry} must run ./check-linuxcnc-wasm-shims-source-map.sh") cmake_text = Path("core/CMakeLists.txt").read_text(encoding="utf-8") for shim_path in shim_paths: cmake_suffix = shim_path.removeprefix("core/") if cmake_suffix not in cmake_text: raise SystemExit(f"{shim_path} is not listed in linuxcnc_wasm_safe_probe_shims") blocker_map = Path("docs/linuxcnc-wasm-blockers-source-map.md").read_text(encoding="utf-8") for shim_path in [ "core/wasm_shims/linuxcnc_runtime_shim.cc", "core/wasm_shims/python_c_api_shim.cc", "core/wasm_shims/tooldata/tooldata_mmap_backend.cc", ]: if shim_path not in blocker_map: raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md no longer references {shim_path}") PY