提示词:补齐 PythonPlugin C API WASM shim 符号
结论:按 LinuxCNC src/emc/pythonplugin/python_plugin.cc 的初始化、导入、配置和 callable 调用路径,把 PyConfig/PyStatus、PyImport_*、PyDict_SetItemString、Py_InitializeFromConfig、PyRun_SimpleString、Py_GetVersion、PyObject_Call 等符号纳入 inert WASM Python C API shim;真实 Python 执行仍保持禁用。 检查:./test-linuxcnc-wasm-python-c-api-symbols.sh 通过;./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
This commit is contained in:
@@ -85,6 +85,18 @@ struct _inittab {
|
|||||||
PyObject *(*initfunc)(void);
|
PyObject *(*initfunc)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PyStatus {
|
||||||
|
int _type;
|
||||||
|
const char *func;
|
||||||
|
const char *err_msg;
|
||||||
|
int exitcode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PyConfig {
|
||||||
|
wchar_t *program_name;
|
||||||
|
int buffered_stdio;
|
||||||
|
};
|
||||||
|
|
||||||
using PyCFunction = PyObject *(*)(PyObject *, PyObject *);
|
using PyCFunction = PyObject *(*)(PyObject *, PyObject *);
|
||||||
using getter = PyObject *(*)(PyObject *, void *);
|
using getter = PyObject *(*)(PyObject *, void *);
|
||||||
using setter = int (*)(PyObject *, PyObject *, void *);
|
using setter = int (*)(PyObject *, PyObject *, void *);
|
||||||
@@ -146,6 +158,7 @@ void Py_DecRef(PyObject *);
|
|||||||
int PyArg_ParseTuple(PyObject *, const char *, ...);
|
int PyArg_ParseTuple(PyObject *, const char *, ...);
|
||||||
int PyArg_VaParse(PyObject *, const char *, va_list);
|
int PyArg_VaParse(PyObject *, const char *, va_list);
|
||||||
PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...);
|
PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...);
|
||||||
|
PyObject *PyObject_Call(PyObject *, PyObject *, PyObject *);
|
||||||
PyObject *PyObject_GetAttrString(PyObject *, const char *);
|
PyObject *PyObject_GetAttrString(PyObject *, const char *);
|
||||||
int PyObject_SetAttrString(PyObject *, const char *, PyObject *);
|
int PyObject_SetAttrString(PyObject *, const char *, PyObject *);
|
||||||
int PyObject_IsTrue(PyObject *);
|
int PyObject_IsTrue(PyObject *);
|
||||||
@@ -165,6 +178,17 @@ Py_ssize_t PySequence_Length(PyObject *);
|
|||||||
PyObject *Py_BuildValue(const char *, ...);
|
PyObject *Py_BuildValue(const char *, ...);
|
||||||
PyObject *PyModule_Create(PyModuleDef *);
|
PyObject *PyModule_Create(PyModuleDef *);
|
||||||
int PyModule_AddObject(PyObject *, const char *, PyObject *);
|
int PyModule_AddObject(PyObject *, const char *, PyObject *);
|
||||||
|
int PyImport_ExtendInittab(_inittab *);
|
||||||
|
PyObject *PyImport_GetModuleDict(void);
|
||||||
|
PyObject *PyImport_AddModule(const char *);
|
||||||
|
int PyDict_SetItemString(PyObject *, const char *, PyObject *);
|
||||||
|
void PyConfig_InitPythonConfig(PyConfig *);
|
||||||
|
PyStatus PyConfig_SetString(PyConfig *, wchar_t **, const wchar_t *);
|
||||||
|
void PyConfig_Clear(PyConfig *);
|
||||||
|
PyStatus Py_InitializeFromConfig(PyConfig *);
|
||||||
|
wchar_t *Py_DecodeLocale(const char *, std::size_t *);
|
||||||
|
int PyRun_SimpleString(const char *);
|
||||||
|
const char *Py_GetVersion(void);
|
||||||
int PyType_Ready(PyTypeObject *);
|
int PyType_Ready(PyTypeObject *);
|
||||||
PyObject *PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *);
|
PyObject *PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *);
|
||||||
void *PyObject_NewShim(std::size_t, PyTypeObject *);
|
void *PyObject_NewShim(std::size_t, PyTypeObject *);
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ int main() {
|
|||||||
return 11;
|
return 11;
|
||||||
}
|
}
|
||||||
if (PyObject_CallMethod(Py_None, const_cast<char *>("noop"), const_cast<char *>("")) != Py_None ||
|
if (PyObject_CallMethod(Py_None, const_cast<char *>("noop"), const_cast<char *>("")) != Py_None ||
|
||||||
|
PyObject_Call(Py_None, Py_None, Py_None) != Py_None ||
|
||||||
PyObject_GetAttrString(Py_None, "noop") != Py_None ||
|
PyObject_GetAttrString(Py_None, "noop") != Py_None ||
|
||||||
PyObject_SetAttrString(Py_None, "noop", Py_None) != 0) {
|
PyObject_SetAttrString(Py_None, "noop", Py_None) != 0) {
|
||||||
return 19;
|
return 19;
|
||||||
@@ -82,6 +83,40 @@ int main() {
|
|||||||
if (PyModule_Create(nullptr) != Py_None || PyModule_AddObject(Py_None, "value", Py_None) != 0) {
|
if (PyModule_Create(nullptr) != Py_None || PyModule_AddObject(Py_None, "value", Py_None) != 0) {
|
||||||
return 13;
|
return 13;
|
||||||
}
|
}
|
||||||
|
_inittab inittab[] = {
|
||||||
|
{"interpreter", nullptr},
|
||||||
|
{nullptr, nullptr},
|
||||||
|
};
|
||||||
|
if (PyImport_ExtendInittab(inittab) != 0 || PyImport_GetModuleDict() == Py_None ||
|
||||||
|
PyImport_AddModule("interpreter") == Py_None ||
|
||||||
|
PyDict_SetItemString(PyImport_GetModuleDict(), "interpreter", PyImport_AddModule("interpreter")) != 0) {
|
||||||
|
return 21;
|
||||||
|
}
|
||||||
|
PyConfig config{};
|
||||||
|
PyConfig_InitPythonConfig(&config);
|
||||||
|
if (config.program_name != nullptr || config.buffered_stdio != 1) {
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
const wchar_t program_name[] = L"linuxcnc";
|
||||||
|
PyStatus config_status = PyConfig_SetString(&config, &config.program_name, program_name);
|
||||||
|
if (config_status._type != 0 || config.program_name != program_name) {
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
PyStatus init_status = Py_InitializeFromConfig(&config);
|
||||||
|
if (init_status._type != 0) {
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
|
std::size_t decoded_size = 99;
|
||||||
|
if (Py_DecodeLocale("linuxcnc", &decoded_size) != nullptr || decoded_size != 8) {
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
if (PyRun_SimpleString("pass") != 0 || std::strcmp(Py_GetVersion(), "wasm-python-disabled") != 0) {
|
||||||
|
return 26;
|
||||||
|
}
|
||||||
|
PyConfig_Clear(&config);
|
||||||
|
if (config.program_name != nullptr || config.buffered_stdio != 0) {
|
||||||
|
return 27;
|
||||||
|
}
|
||||||
if (PyType_Ready(&PyList_Type) != 0 || PyType_GenericNew(&PyList_Type, Py_None, Py_None) != Py_None) {
|
if (PyType_Ready(&PyList_Type) != 0 || PyType_GenericNew(&PyList_Type, Py_None, Py_None) != Py_None) {
|
||||||
return 14;
|
return 14;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "boost/python/object.hpp"
|
#include "boost/python/object.hpp"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
// LinuxCNC source basis: src/emc/rs274ngc/interp_python.cc,
|
// LinuxCNC source basis: src/emc/rs274ngc/interp_python.cc,
|
||||||
// interp_namedparams.cc, gcodemodule.cc, and src/emc/pythonplugin/python_plugin.cc
|
// interp_namedparams.cc, gcodemodule.cc, and src/emc/pythonplugin/python_plugin.cc
|
||||||
@@ -8,7 +9,11 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
static PyTypeObject none_type = {"NoneType"};
|
static PyTypeObject none_type = {"NoneType"};
|
||||||
|
static PyTypeObject module_type = {"module"};
|
||||||
|
static PyTypeObject dict_type = {"dict"};
|
||||||
static PyObject none_object = {&none_type};
|
static PyObject none_object = {&none_type};
|
||||||
|
static PyObject module_object = {&module_type};
|
||||||
|
static PyObject dict_object = {&dict_type};
|
||||||
|
|
||||||
PyObject *PyExc_KeyError = reinterpret_cast<PyObject *>(1);
|
PyObject *PyExc_KeyError = reinterpret_cast<PyObject *>(1);
|
||||||
PyObject *PyExc_KeyboardInterrupt = reinterpret_cast<PyObject *>(2);
|
PyObject *PyExc_KeyboardInterrupt = reinterpret_cast<PyObject *>(2);
|
||||||
@@ -111,6 +116,10 @@ PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...) {
|
|||||||
return &none_object;
|
return &none_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *PyObject_Call(PyObject *, PyObject *, PyObject *) {
|
||||||
|
return &none_object;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *PyObject_GetAttrString(PyObject *, const char *) {
|
PyObject *PyObject_GetAttrString(PyObject *, const char *) {
|
||||||
return &none_object;
|
return &none_object;
|
||||||
}
|
}
|
||||||
@@ -187,6 +196,64 @@ int PyModule_AddObject(PyObject *, const char *, PyObject *) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PyImport_ExtendInittab(_inittab *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *PyImport_GetModuleDict(void) {
|
||||||
|
return &dict_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *PyImport_AddModule(const char *) {
|
||||||
|
return &module_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PyDict_SetItemString(PyObject *, const char *, PyObject *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyConfig_InitPythonConfig(PyConfig *config) {
|
||||||
|
if (!config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config->program_name = nullptr;
|
||||||
|
config->buffered_stdio = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyStatus PyConfig_SetString(PyConfig *, wchar_t **config_str, const wchar_t *value) {
|
||||||
|
if (config_str) {
|
||||||
|
*config_str = const_cast<wchar_t *>(value);
|
||||||
|
}
|
||||||
|
return PyStatus{0, nullptr, nullptr, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyConfig_Clear(PyConfig *config) {
|
||||||
|
if (!config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config->program_name = nullptr;
|
||||||
|
config->buffered_stdio = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyStatus Py_InitializeFromConfig(PyConfig *) {
|
||||||
|
return PyStatus{0, nullptr, nullptr, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t *Py_DecodeLocale(const char *arg, std::size_t *size) {
|
||||||
|
if (size) {
|
||||||
|
*size = arg ? std::strlen(arg) : 0;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PyRun_SimpleString(const char *) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Py_GetVersion(void) {
|
||||||
|
return "wasm-python-disabled";
|
||||||
|
}
|
||||||
|
|
||||||
int PyType_Ready(PyTypeObject *) {
|
int PyType_Ready(PyTypeObject *) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,9 +63,10 @@ only; it must not add CNC behavior and must not expand the temporary smoke parse
|
|||||||
`PythonPlugin` status edges for `PLUGIN_NO_SECTION`, `PLUGIN_BAD_INIFILE`,
|
`PythonPlugin` status edges for `PLUGIN_NO_SECTION`, `PLUGIN_BAD_INIFILE`,
|
||||||
`PLUGIN_PYTHON_NOT_INITIALIZED`, `PLUGIN_NO_CALLABLE`, and
|
`PLUGIN_PYTHON_NOT_INITIALIZED`, `PLUGIN_NO_CALLABLE`, and
|
||||||
`PLUGIN_EXCEPTION` while keeping Python execution disabled.
|
`PLUGIN_EXCEPTION` while keeping Python execution disabled.
|
||||||
- The PythonPlugin shim must not execute `bp::exec`, `bp::exec_file`, or
|
- The PythonPlugin shim must not execute `bp::exec`, `bp::exec_file`, or real
|
||||||
`PyObject_Call`; those remain browser blockers until implemented as a
|
Python callable dispatch; `PyObject_Call` is an inert C API symbol only.
|
||||||
source-backed browser-safe LinuxCNC adaptation.
|
Browser Python execution remains blocked until implemented as a source-backed
|
||||||
|
browser-safe LinuxCNC adaptation.
|
||||||
|
|
||||||
## Boundaries
|
## Boundaries
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ set -euo pipefail
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
# LinuxCNC source basis: this probe derives required Python C API exports from
|
# LinuxCNC source basis: this probe derives required Python C API exports from
|
||||||
# RS274 Python/remap sources in src/emc/rs274ngc, including
|
# RS274 Python/remap sources in src/emc/rs274ngc and from
|
||||||
# interp_namedparams.cc, interp_o_word.cc, interp_python.cc, interp_remap.cc,
|
# src/emc/pythonplugin/python_plugin.cc initialization/configuration paths,
|
||||||
# rs274ngc_pre.cc, and the py*/module binding sources.
|
# while compiling only the RS274 binding objects that need wasm-safe symbols.
|
||||||
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
|
||||||
cxx=${CXX:-g++}
|
cxx=${CXX:-g++}
|
||||||
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
|
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
|
||||||
@@ -58,6 +58,10 @@ linuxcnc_source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-f
|
|||||||
src/emc/rs274ngc/gcodemodule.cc)
|
src/emc/rs274ngc/gcodemodule.cc)
|
||||||
mapfile -t linuxcnc_sources <<<"$linuxcnc_source_output"
|
mapfile -t linuxcnc_sources <<<"$linuxcnc_source_output"
|
||||||
python_binding_sources=("${linuxcnc_sources[@]:6}")
|
python_binding_sources=("${linuxcnc_sources[@]:6}")
|
||||||
|
python_plugin_source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \
|
||||||
|
src/emc/pythonplugin/python_plugin.cc)
|
||||||
|
mapfile -t python_plugin_sources <<<"$python_plugin_source_output"
|
||||||
|
python_token_sources=("${linuxcnc_sources[@]}" "${python_plugin_sources[@]}")
|
||||||
|
|
||||||
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
|
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
|
||||||
mapfile -t common_flags <<<"$common_flag_output"
|
mapfile -t common_flags <<<"$common_flag_output"
|
||||||
@@ -143,14 +147,14 @@ source_symbols_signature="$build_dir/python-c-api.source-symbols.signature"
|
|||||||
source_symbols_next_signature="$build_dir/python-c-api.source-symbols.signature.next"
|
source_symbols_next_signature="$build_dir/python-c-api.source-symbols.signature.next"
|
||||||
{
|
{
|
||||||
printf 'SOURCE_SYMBOL_RULE_VERSION=1\n'
|
printf 'SOURCE_SYMBOL_RULE_VERSION=1\n'
|
||||||
printf 'LINUXCNC_SOURCES:\n'
|
printf 'LINUXCNC_TOKEN_SOURCES:\n'
|
||||||
printf '%s\n' "${linuxcnc_sources[@]}"
|
printf '%s\n' "${python_token_sources[@]}"
|
||||||
printf 'SOURCE_STATS:\n'
|
printf 'SOURCE_STATS:\n'
|
||||||
stat -c 'SOURCE=%n:%s:%y' "${linuxcnc_sources[@]}"
|
stat -c 'SOURCE=%n:%s:%y' "${python_token_sources[@]}"
|
||||||
} > "$source_symbols_next_signature"
|
} > "$source_symbols_next_signature"
|
||||||
if [[ ! -f "$source_symbols_signature" || ! -f "$build_dir/linuxcnc.py-tokens" ]] ||
|
if [[ ! -f "$source_symbols_signature" || ! -f "$build_dir/linuxcnc.py-tokens" ]] ||
|
||||||
! cmp -s "$source_symbols_signature" "$source_symbols_next_signature"; then
|
! cmp -s "$source_symbols_signature" "$source_symbols_next_signature"; then
|
||||||
rg -o 'Py[A-Za-z0-9_]+' "${linuxcnc_sources[@]}" \
|
rg -o 'Py[A-Za-z0-9_]+' "${python_token_sources[@]}" \
|
||||||
| sed 's/^.*://' \
|
| sed 's/^.*://' \
|
||||||
| LC_ALL=C sort -u \
|
| LC_ALL=C sort -u \
|
||||||
> "$build_dir/linuxcnc.py-tokens"
|
> "$build_dir/linuxcnc.py-tokens"
|
||||||
@@ -204,12 +208,14 @@ awk '
|
|||||||
$0 == "PyModuleDef_HEAD_INIT" { next }
|
$0 == "PyModuleDef_HEAD_INIT" { next }
|
||||||
$0 == "Py_TPFLAGS_DEFAULT" { next }
|
$0 == "Py_TPFLAGS_DEFAULT" { next }
|
||||||
$0 == "PyCFunction" { next }
|
$0 == "PyCFunction" { next }
|
||||||
|
$0 == "PyConfig" { next }
|
||||||
$0 == "PyGetSetDef" { next }
|
$0 == "PyGetSetDef" { next }
|
||||||
$0 == "PyMemberDef" { next }
|
$0 == "PyMemberDef" { next }
|
||||||
$0 == "PyMethodDef" { next }
|
$0 == "PyMethodDef" { next }
|
||||||
$0 == "PyModuleDef" { next }
|
$0 == "PyModuleDef" { next }
|
||||||
$0 == "PyMODINIT_FUNC" { next }
|
$0 == "PyMODINIT_FUNC" { next }
|
||||||
$0 == "PyObject_New" { next }
|
$0 == "PyObject_New" { next }
|
||||||
|
$0 == "PyStatus" { next }
|
||||||
$0 == "PyInit_emccanon" { next }
|
$0 == "PyInit_emccanon" { next }
|
||||||
$0 == "PyInit_gcode" { next }
|
$0 == "PyInit_gcode" { next }
|
||||||
$0 == "PyInit_interpreter" { next }
|
$0 == "PyInit_interpreter" { next }
|
||||||
|
|||||||
Reference in New Issue
Block a user