diff --git a/core/wasm_shims/python_c_api_probe_main.cc b/core/wasm_shims/python_c_api_probe_main.cc new file mode 100644 index 0000000..508fa49 --- /dev/null +++ b/core/wasm_shims/python_c_api_probe_main.cc @@ -0,0 +1,73 @@ +#include "boost/python/object.hpp" + +#include +#include + +// LinuxCNC source basis: src/emc/rs274ngc/gcodemodule.cc, +// interpmodule.cc, and py*.cc use these Python C API entry points while +// defining RS274 Python binding objects. The WASM shim keeps the API inert +// and link-safe while browser Python execution remains disabled. +namespace { + +struct ProbeObject { + PyObject_HEAD + int value; +}; + +} // namespace + +int main() { + if (Py_IsInitialized() != 1) { + return 1; + } + if (PyErr_Occurred() != nullptr) { + return 2; + } + PyObject *exc = reinterpret_cast(1); + PyObject *val = reinterpret_cast(1); + PyObject *tb = reinterpret_cast(1); + PyErr_Fetch(&exc, &val, &tb); + if (exc || val || tb) { + return 3; + } + if (!Py_None || !Py_TYPE(Py_None) || std::strcmp(Py_TYPE(Py_None)->tp_name, "NoneType") != 0) { + return 4; + } + if (PyUnicode_FromString("linuxcnc") != Py_None) { + return 5; + } + if (std::strcmp(PyUnicode_AsUTF8(Py_None), "") != 0) { + return 6; + } + if (PyLong_FromLong(7) != Py_None || PyLong_AsLong(Py_None) != 0) { + return 7; + } + if (PyFloat_AsDouble(Py_None) != 0.0) { + return 8; + } + if (PyTuple_New(2) != Py_None || PyTuple_SetItem(Py_None, 0, Py_None) != 0 || PyTuple_Size(Py_None) != 0) { + return 9; + } + if (PyList_New(2) != Py_None || PyList_SetItem(Py_None, 0, Py_None) != 0 || PyList_Size(Py_None) != 0) { + return 10; + } + if (PyObject_IsTrue(Py_None) != 0 || PyCallable_Check(Py_None) != 0 || PyObject_IsInstance(Py_None, Py_None) != 0) { + return 11; + } + ProbeObject *probe = PyObject_New(ProbeObject, &PyList_Type); + if (!probe || Py_TYPE(reinterpret_cast(probe)) != &PyList_Type) { + std::free(probe); + return 12; + } + std::free(probe); + if (PyModule_Create(nullptr) != Py_None || PyModule_AddObject(Py_None, "value", Py_None) != 0) { + return 13; + } + if (PyType_Ready(&PyList_Type) != 0 || PyType_GenericNew(&PyList_Type, Py_None, Py_None) != Py_None) { + return 14; + } + if (PyErr_Format(PyExc_TypeError, "linuxcnc") != nullptr) { + return 15; + } + return 0; +} diff --git a/test-linuxcnc-wasm-python-c-api-symbols.sh b/test-linuxcnc-wasm-python-c-api-symbols.sh index f7a8b92..e7cab4d 100755 --- a/test-linuxcnc-wasm-python-c-api-symbols.sh +++ b/test-linuxcnc-wasm-python-c-api-symbols.sh @@ -70,6 +70,12 @@ object_sources=( "$python_c_api_shim" "$runtime_shim" ) +probe_objects=( + "$build_dir/python_c_api_probe_main.o" +) +probe_sources=( + core/wasm_shims/python_c_api_probe_main.cc +) for source in "${python_binding_sources[@]}"; do object_name=$(basename "$source" .cc) objects+=("$build_dir/$object_name.o") @@ -88,9 +94,11 @@ next_signature="$build_dir/python-c-api.signature.next" printf '\n' printf 'SOURCES:\n' printf '%s\n' "${object_sources[@]}" + printf 'PROBE_SOURCES:\n' + printf '%s\n' "${probe_sources[@]}" } > "$next_signature" if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then - rm -f "$build_dir"/*.o "$build_dir"/*.d + rm -f "$build_dir"/*.o "$build_dir"/*.d "$build_dir/python_c_api_probe" fi mv "$next_signature" "$signature" @@ -107,8 +115,11 @@ makefile="$build_dir/python-c-api.mk" for object in "${objects[@]}"; do printf 'DEP_FILES += %s.d\n' "$object" done + for object in "${probe_objects[@]}"; do + printf 'DEP_FILES += %s.d\n' "$object" + done printf '\n.PHONY: all\n' - printf 'all: $(OBJECTS)\n\n' + printf 'all: $(OBJECTS) %s\n\n' "$build_dir/python_c_api_probe" object_index=0 for source in "${object_sources[@]}"; do @@ -116,6 +127,14 @@ makefile="$build_dir/python-c-api.mk" printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}" object_index=$((object_index + 1)) done + probe_index=0 + for source in "${probe_sources[@]}"; do + printf '%s: %s\n' "${probe_objects[$probe_index]}" "$source" + printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "${probe_objects[$probe_index]}" "$source" "${probe_objects[$probe_index]}" + probe_index=$((probe_index + 1)) + done + printf '%s: %s %s\n' "$build_dir/python_c_api_probe" "$build_dir/python_c_api_shim.o" "${probe_objects[*]}" + printf '\t@$(CXX) %s %s -o %s\n\n' "$build_dir/python_c_api_shim.o" "${probe_objects[*]}" "$build_dir/python_c_api_probe" printf '\n-include $(DEP_FILES)\n' } > "$makefile" make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null @@ -229,4 +248,6 @@ if [[ -s "$build_dir/object.missing-python" ]]; then exit 1 fi +"$build_dir/python_c_api_probe" + echo "linuxcnc wasm Python C API symbol probe passed"