Remove the copy of dukpy. Make it an external dependency.

This commit is contained in:
Kovid Goyal 2017-06-07 15:07:19 +05:30
parent f005731212
commit 9564c94ad1
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
13 changed files with 8 additions and 94805 deletions

View File

@ -115,12 +115,6 @@
"sources": "calibre/ebooks/djvu/bzzdecoder.c",
"windows_inc_dirs": "calibre/utils/chm"
},
{
"name": "dukpy",
"sources": "duktape/errors.c duktape/context.c duktape/conversions.c duktape/proxy.c duktape/module.c duktape/duktape/duktape.c",
"headers": "duktape/dukpy.h duktape/duktape/duk_config.h duktape/duktape/duktape.h",
"optimize_level": 2
},
{
"name": "podofo",
"sources": "calibre/utils/podofo/utils.cpp calibre/utils/podofo/output.cpp calibre/utils/podofo/doc.cpp calibre/utils/podofo/outline.cpp calibre/utils/podofo/podofo.cpp",

View File

@ -167,7 +167,6 @@ class Plugins(collections.Mapping):
'matcher',
'tokenizer',
'certgen',
'dukpy',
'lzma_binding',
]
if iswindows:

View File

@ -12,13 +12,11 @@ __all__ = ['dukpy', 'Context', 'undefined', 'JSError', 'to_python']
import errno, os, sys, numbers, hashlib, json
from functools import partial
from calibre.constants import plugins, iswindows
import dukpy
from calibre.constants import iswindows
from calibre.utils.filenames import atomic_rename
dukpy, err = plugins['dukpy']
if err:
raise RuntimeError('Failed to load dukpy with error: %s' % err)
del err
Context_, undefined = dukpy.Context, dukpy.undefined
fs = '''

View File

@ -1,248 +0,0 @@
#include "dukpy.h"
#include <structmember.h>
static void DukContext_init_internal(DukContext *self)
{
/* heap_stash[(void *)self->ctx] = (void *)self */
duk_push_heap_stash(self->ctx);
duk_push_pointer(self->ctx, self->ctx);
duk_push_pointer(self->ctx, self);
duk_put_prop(self->ctx, -3);
duk_pop(self->ctx);
}
static int DukContext_init(DukContext *self, PyObject *args, PyObject *kw)
{
(void)args;
(void)kw;
self->heap_manager = NULL; /* We manage the heap */
self->py_thread_state = NULL;
self->ctx = duk_create_heap_default();
if (!self->ctx) {
PyErr_SetString(PyExc_MemoryError, "Failed to create duktape heap");
return -1;
}
/* heap_stash.heap = (void *)self */
duk_push_heap_stash(self->ctx);
duk_push_pointer(self->ctx, self);
duk_put_prop_string(self->ctx, -2, "heap");
duk_pop(self->ctx);
DukContext_init_internal(self);
return 0;
}
static PyObject *DukContext_new_global_env(DukContext *self, PyObject *args)
{
DukContext *new_context;
(void)args;
new_context = PyObject_New(DukContext, &DukContext_Type);
if (new_context == NULL)
return NULL;
new_context->heap_manager = self->heap_manager ? self->heap_manager : self;
Py_INCREF(self);
/* heap_stash[(void *)new_context] = new_context->ctx (thread object) */
duk_push_heap_stash(self->ctx);
duk_push_pointer(self->ctx, new_context);
duk_push_thread_new_globalenv(self->ctx);
new_context->ctx = duk_get_context(self->ctx, -1);
duk_put_prop(self->ctx, -3);
duk_pop(self->ctx);
DukContext_init_internal(new_context);
return (PyObject *)new_context;
}
static void DukContext_dealloc(DukContext *self)
{
if (!self->heap_manager) {
duk_destroy_heap(self->ctx);
} else {
/* Use heap manager's ctx because self->ctx is destroyed */
duk_context *ctx = self->heap_manager->ctx;
duk_push_heap_stash(ctx);
/* delete heap_stash[(void *)self->ctx] */
duk_push_pointer(ctx, self->ctx);
duk_del_prop(ctx, -2);
/* delete heap_stash[(void *)self] */
duk_push_pointer(ctx, self);
duk_del_prop(ctx, -2);
duk_pop(ctx);
Py_DECREF(self->heap_manager);
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *DukContext_eval(DukContext *self, PyObject *args, PyObject *kw)
{
const char *code, *fname = "<eval>";
int noresult = 0, ret = 0;
PyObject *result = NULL, *temp = NULL;
static char *keywords[] = {"code", "noreturn", "fname", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|Os:eval", keywords,
&code, &temp, &fname)) {
return NULL;
}
if (temp && PyObject_IsTrue(temp)) noresult = 1;
self->py_thread_state = PyEval_SaveThread(); // Release GIL
ret = DUK_COMPILE_EVAL | DUK_COMPILE_SAFE | DUK_COMPILE_NOSOURCE | DUK_COMPILE_STRLEN | ((noresult) ? DUK_COMPILE_NORESULT : 0);
duk_push_string(self->ctx, fname);
ret = duk_eval_raw(self->ctx, code, 0, ret);
PyEval_RestoreThread(self->py_thread_state); // Acquire GIL
self->py_thread_state = NULL;
if (ret != 0) {
temp = duk_to_python(self->ctx, -1);
duk_pop(self->ctx);
if (temp) {
set_dukpy_error(temp);
Py_DECREF(temp);
} else PyErr_SetString(PyExc_RuntimeError, "The was an error during eval(), but the error could not be read of the stack");
return NULL;
}
if (noresult) {
Py_RETURN_NONE;
}
result = duk_to_python(self->ctx, -1);
duk_pop(self->ctx);
return result;
}
static PyObject *DukContext_eval_file(DukContext *self, PyObject *args, PyObject *kw)
{
const char *path;
int noresult = 0, ret = 0;
PyObject *result = NULL, *temp = NULL;
static char *keywords[] = {"path", "noreturn", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|O:eval_file", keywords,
&path, &temp)) {
return NULL;
}
if (temp && PyObject_IsTrue(temp)) noresult = 1;
self->py_thread_state = PyEval_SaveThread(); // Release GIL
ret = (noresult) ? duk_peval_file_noresult(self->ctx, path) : duk_peval_file(self->ctx, path);
PyEval_RestoreThread(self->py_thread_state); // Acquire GIL
self->py_thread_state = NULL;
if (ret != 0) {
temp = duk_to_python(self->ctx, -1);
duk_pop(self->ctx);
if (temp) {
set_dukpy_error(temp);
Py_DECREF(temp);
} else PyErr_SetString(PyExc_RuntimeError, "The was an error during eval_file(), but the error could not be read of the stack");
return NULL;
}
if (noresult) {
Py_RETURN_NONE;
}
result = duk_to_python(self->ctx, -1);
duk_pop(self->ctx);
return result;
}
DukContext *DukContext_get(duk_context *ctx)
{
DukContext *context;
/* Read DukContext from heap_stash[(void *)ctx] */
duk_push_heap_stash(ctx);
duk_push_pointer(ctx, ctx);
duk_get_prop(ctx, -2);
context = duk_get_pointer(ctx, -1);
duk_pop_n(ctx, 2);
/* context is NULL for uncached contexts */
return context;
}
static PyMethodDef DukContext_methods[] = {
{"eval", (PyCFunction)DukContext_eval,
METH_VARARGS | METH_KEYWORDS, "Evaluate code"},
{"eval_file", (PyCFunction)DukContext_eval_file,
METH_VARARGS | METH_KEYWORDS, "Evaluate a file"},
{"new_global_env", (PyCFunction)DukContext_new_global_env,
METH_NOARGS, "Return a new context with a fresh global object"},
{NULL}
};
PyObject *DukContext_get_global(DukContext *self, void *closure)
{
DukObject *global;
(void)closure;
duk_push_global_object(self->ctx);
global = DukObject_from_DukContext(self, -1);
duk_pop(self->ctx);
return (PyObject *)global;
}
static PyGetSetDef DukContext_getset[] = {
{"g", (getter)DukContext_get_global, NULL, "The global object", NULL},
{NULL}
};
PyTypeObject DukContext_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"dukpy.Context", /* tp_name */
sizeof(DukContext), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DukContext_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"Duktape context", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
DukContext_methods, /* tp_methods */
0, /* tp_members */
DukContext_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)DukContext_init /* tp_init */
};

View File

@ -1,331 +0,0 @@
#include "dukpy.h"
static int get_repr(PyObject *value, char *buf, int bufsz) {
PyObject *temp = NULL, *repr = NULL;
memset(buf, 0, bufsz);
if (!value) return 0;
repr = PyObject_Repr(value);
if (repr && !PyBytes_Check(repr)) {
temp = PyUnicode_AsUTF8String(repr);
Py_DECREF(repr); repr = temp;
}
if (!repr) return 0;
strncpy(buf, PyBytes_AS_STRING(repr), bufsz - 1);
Py_DECREF(repr);
return 1;
}
static duk_ret_t python_function_caller(duk_context *ctx)
{
PyObject *func, *args, *result;
PyObject *ptype = NULL, *pval = NULL, *tb = NULL;
DukContext *dctx;
duk_idx_t nargs, i;
static char buf1[200], buf2[1024];
int gil_acquired = 0, ret = 1, err_occured;
dctx = DukContext_get(ctx);
nargs = duk_get_top(ctx);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "\xff" "py_object");
func = duk_get_pointer(ctx, -1);
if (dctx->py_thread_state) {
gil_acquired = 1;
PyEval_RestoreThread(dctx->py_thread_state);
dctx->py_thread_state = NULL;
}
args = PyTuple_New(nargs);
if (!args) {
ret = DUK_RET_ALLOC_ERROR;
goto error;
}
for (i = 0; i < nargs; i++) {
PyObject *arg = duk_to_python(ctx, i);
if (arg == NULL) {
Py_DECREF(args);
ret = DUK_RET_TYPE_ERROR;
goto error;
}
PyTuple_SET_ITEM(args, i, arg);
}
result = PyObject_Call(func, args, NULL);
Py_DECREF(args);
if (!result) {
err_occured = PyErr_Occurred() != NULL;
get_repr(func, buf1, 200);
if (!err_occured) {
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
get_repr(func, buf1, 200);
duk_error(ctx, DUK_ERR_ERROR, "Function (%s) failed", buf1);
}
PyErr_Fetch(&ptype, &pval, &tb);
if (!get_repr(pval, buf2, 1024)) get_repr(ptype, buf2, 1024);
Py_XDECREF(ptype); Py_XDECREF(pval); Py_XDECREF(tb);
PyErr_Clear(); /* In case there was an error in get_repr() */
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
get_repr(func, buf1, 200);
duk_error(ctx, DUK_ERR_ERROR, "Function (%s) failed with error: %s", buf1, buf2);
}
python_to_duk(ctx, result);
Py_DECREF(result);
error:
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
return ret;
}
static duk_ret_t python_object_decref(duk_context *ctx) {
int deleted = 0, gil_acquired = 0;
DukContext *dctx = DukContext_get(ctx);
duk_get_prop_string(ctx, 0, "\xff""deleted");
deleted = duk_to_boolean(ctx, -1);
duk_pop(ctx);
if (!deleted) {
duk_get_prop_string(ctx, 0, "\xff""py_object");
if (dctx->py_thread_state) {
gil_acquired = 1;
PyEval_RestoreThread(dctx->py_thread_state);
dctx->py_thread_state = NULL;
}
Py_XDECREF(duk_get_pointer(ctx, -1));
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
duk_pop(ctx);
// Mark as deleted
duk_push_boolean(ctx, 1);
duk_put_prop_string(ctx, 0, "\xff""deleted");
}
return 0;
}
int python_to_duk(duk_context *ctx, PyObject *value)
{
/* Python to duktape conversion. If successful, leaves the
converted value on the top of the stack and returns 0.
Otherwise, raises a Python exception and returns -1.
*/
#if PY_MAJOR_VERSION < 3
int ret;
#endif
static char buf[200];
if (value == Duk_undefined) {
duk_push_undefined(ctx);
}
else if (value == Py_None) {
/* Map None to null */
duk_push_null(ctx);
}
else if (value == Py_True) {
duk_push_true(ctx);
}
else if (value == Py_False) {
duk_push_false(ctx);
}
else if (Py_TYPE(value) == &DukObject_Type || Py_TYPE(value) == &DukFunction_Type || Py_TYPE(value) == &DukArray_Type) {
DukObject_push((DukObject *)value, ctx);
}
else if (PyUnicode_Check(value)) {
/* Unicode string */
#ifdef PyUnicode_AsUTF8AndSize
char *str;
Py_ssize_t len;
str = PyUnicode_AsUTF8AndSize(value, &len);
if (str == NULL)
return -1;
duk_push_lstring(ctx, str, len);
#else
PyObject *utf8str = PyUnicode_AsUTF8String(value);
if (utf8str == NULL)
return -1;
duk_push_lstring(ctx, PyBytes_AS_STRING(utf8str), PyBytes_GET_SIZE(utf8str));
Py_DECREF(utf8str);
#endif
}
#if PY_MAJOR_VERSION < 3
else if (PyBytes_Check(value)) {
/* Happens in python 2 for attribute access, for example*/
PyObject *urepr = PyUnicode_FromObject(value);
if (urepr == NULL)
return -1;
ret = python_to_duk(ctx, urepr);
Py_DECREF(urepr);
return ret;
}
#endif
else if (PyLong_Check(value)) {
double val = PyLong_AsDouble(value);
if (PyErr_Occurred())
return -1;
duk_push_number(ctx, val);
}
#if PY_MAJOR_VERSION < 3
else if (PyInt_Check(value)) {
double val = (double)PyInt_AsLong(value);
duk_push_number(ctx, val);
}
#endif
else if (PyFloat_Check(value)) {
double val = PyFloat_AsDouble(value);
if (PyErr_Occurred())
return -1;
duk_push_number(ctx, val);
}
else if (PyDict_Check(value)) {
PyObject *key, *val;
Py_ssize_t pos = 0;
duk_push_object(ctx);
while (PyDict_Next(value, &pos, &key, &val)) {
if (python_to_duk(ctx, key) == -1) {
/* Pop the object */
duk_pop(ctx);
return -1;
}
if (python_to_duk(ctx, val) == -1) {
/* Pop the key and the object */
duk_pop_n(ctx, 2);
return -1;
}
duk_put_prop(ctx, -3);
}
}
else if (PyList_Check(value)) {
PyObject *val;
Py_ssize_t i, len;
duk_push_array(ctx);
len = PyList_Size(value);
for (i = 0; i < len; i++) {
val = PyList_GetItem(value, i);
if (python_to_duk(ctx, val) == -1) {
/* Pop the array */
duk_pop(ctx);
return -1;
}
duk_put_prop_index(ctx, -2, (duk_uarridx_t)i);
}
}
else if (PyCallable_Check(value)) {
// Store the callable
duk_push_c_function(ctx, python_function_caller, DUK_VARARGS);
duk_push_pointer(ctx, value);
Py_INCREF(value);
duk_put_prop_string(ctx, -2, "\xff" "py_object");
// Store a boolean flag to mark the object as deleted because the destructor may be called several times
duk_push_boolean(ctx, 0);
duk_put_prop_string(ctx, -2, "\xff""deleted");
// Store the function destructor
duk_push_c_function(ctx, python_object_decref, 1);
duk_set_finalizer(ctx, -2);
}
else {
if(get_repr(value, buf, 200))
PyErr_Format(PyExc_TypeError, "%s is not coercible", buf);
return -1;
}
return 0;
}
PyObject *duk_to_python(duk_context *ctx, duk_idx_t index)
{
/* Duktape to Python conversion. If successful, returns a pointer
to a new PyObject reference. If not, raises a Python exception
and returns NULL.
*/
duk_idx_t index_n = duk_normalize_index(ctx, index);
PyObject *result;
if (duk_is_undefined(ctx, index_n)) {
Py_INCREF(Duk_undefined);
return Duk_undefined;
}
else if (duk_is_null(ctx, index_n)) {
Py_RETURN_NONE;
}
else if (duk_is_boolean(ctx, index_n)) {
if (duk_get_boolean(ctx, index_n))
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
else if (duk_is_number(ctx, index_n)) {
double number, temp;
number = duk_get_number(ctx, index_n);
if (modf(number, &temp) == 0) {
/* No fractional part */
return PyLong_FromDouble(number);
} else {
/* Has fractional part */
return PyFloat_FromDouble(number);
}
}
else if (duk_is_string(ctx, index_n)) {
const char *str;
duk_size_t len;
/* Duplicate the string because it's replaced by duk_to_lstring() */
duk_dup(ctx, index_n);
str = duk_to_lstring(ctx, -1, &len);
result = PyUnicode_DecodeUTF8(str, len, NULL);
/* Pop the duplicate */
duk_pop(ctx);
return result;
}
else if (duk_is_array(ctx, index_n)) {
return (PyObject *)DukArray_from_ctx(ctx, index_n);
}
else if (duk_is_function(ctx, index_n)) {
return (PyObject *)DukFunction_from_ctx(ctx, index_n);
}
else if (duk_is_object(ctx, index_n)) {
/* Other objects than arrays or functions */
return (PyObject *)DukObject_from_ctx(ctx, index_n);
}
else if (duk_check_type(ctx, index_n, DUK_TYPE_BUFFER)) {
PyErr_SetString(PyExc_TypeError, "'buffer' is not coercible");
return NULL;
}
else if (duk_check_type(ctx, index_n, DUK_TYPE_POINTER)) {
PyErr_SetString(PyExc_TypeError, "'pointer' is not coercible");
return NULL;
}
/* Not reached */
return NULL;
}

View File

@ -1,75 +0,0 @@
#ifndef DUKPY_H
#define DUKPY_H
#include <Python.h>
#include "duktape/duktape.h"
typedef struct DukContext_ DukContext;
typedef struct DukObject_ DukObject;
typedef struct DukEnum_ DukEnum;
/* module.c */
extern PyObject DukUndefined;
#define Duk_undefined (&DukUndefined)
extern PyObject *JSError;
/* context.c */
struct DukContext_ {
PyObject_HEAD
duk_context *ctx;
DukContext *heap_manager;
PyThreadState *py_thread_state;
};
extern PyTypeObject DukContext_Type;
DukContext *DukContext_get(duk_context *ctx);
/* proxy.c */
struct DukObject_ {
PyObject_HEAD
DukContext *context;
DukObject *parent;
};
extern PyTypeObject DukObject_Type;
extern PyTypeObject DukArray_Type;
extern PyTypeObject DukFunction_Type;
extern PyTypeObject DukEnum_Type;
DukObject *DukObject_from_DukContext(DukContext *context, duk_idx_t index);
DukObject *DukObject_from_ctx(duk_context *ctx, duk_idx_t index);
int DukObject_push(DukObject *self, duk_context *ctx);
DukObject *DukArray_from_ctx(duk_context *ctx, duk_idx_t index);
DukObject *DukFunction_from_ctx(duk_context *ctx, duk_idx_t index);
typedef enum {
DUKENUM_KEYS,
DUKENUM_VALUES,
DUKENUM_PAIRS
} dukenum_mode_t;
struct DukEnum_ {
PyObject_HEAD
DukObject base;
dukenum_mode_t mode;
};
DukEnum *DukEnum_from_DukContext(DukContext *context, dukenum_mode_t mode);
/* conversions.c */
int python_to_duk(duk_context *ctx, PyObject *value);
PyObject *duk_to_python(duk_context *ctx, duk_idx_t index);
void set_dukpy_error(PyObject *obj);
#endif /* DUKPY_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +0,0 @@
/*
* errors.c
* Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "dukpy.h"
static int copy_error_attr(PyObject *obj, const char* name, PyObject *dest) {
PyObject *value = NULL;
if (!PyObject_HasAttrString(obj, name)) return 0;
value = PyObject_GetAttrString(obj, name);
if (value == NULL) return 0;
if (PyDict_SetItemString(dest, name, value) != 0) {Py_DECREF(value); return 0;}
Py_DECREF(value);
return 1;
}
void set_dukpy_error(PyObject *obj) {
PyObject *err = NULL, *iterator = NULL, *item = NULL;
if (Py_TYPE(obj) == &DukObject_Type) {
err = PyDict_New();
if (err == NULL) { PyErr_NoMemory(); return; }
// Look for the common error object properties that may be up the prototype chain
if (!copy_error_attr(obj, "name", err)) { Py_DECREF(err); return; }
if (!copy_error_attr(obj, "message", err)) { Py_DECREF(err); return; }
if (!copy_error_attr(obj, "fileName", err)) { Py_DECREF(err); return; }
if (!copy_error_attr(obj, "lineNumber", err)) { Py_DECREF(err); return; }
if (!copy_error_attr(obj, "stack", err)) { Py_DECREF(err); return; }
// Now copy over own properties
iterator = PyObject_CallMethod(obj, "items", NULL);
if (iterator == NULL) { Py_DECREF(err); return; }
while ((item = PyIter_Next(iterator))) {
PyDict_SetItem(err, PyTuple_GET_ITEM(item, 0), PyTuple_GET_ITEM(item, 1));
Py_DECREF(item);
}
PyErr_SetObject(JSError, err);
Py_DECREF(err); Py_DECREF(iterator);
} else PyErr_SetObject(JSError, obj);
}

View File

@ -1,147 +0,0 @@
#include "dukpy.h"
PyObject *JSError = NULL;
/* ARGSUSED */
static PyObject *
undefined_repr(PyObject *op)
{
#if PY_MAJOR_VERSION < 3
return PyBytes_FromString("undefined");
#else
return PyUnicode_FromString("undefined");
#endif
}
/* ARGUSED */
static void
undefined_dealloc(PyObject* ignore)
{
/* This should never get called, but we also don't want to SEGV if
* we accidentally decref undef out of existence.
*/
Py_FatalError("deallocating undefined");
}
static PyTypeObject DukUndefined_Type = {
#if PY_MAJOR_VERSION < 3
PyVarObject_HEAD_INIT(NULL, 0)
#else
PyVarObject_HEAD_INIT(&PyType_Type, 0)
#endif
"UndefinedType",
0,
0,
undefined_dealloc,
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
undefined_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
};
PyObject DukUndefined = {
_PyObject_EXTRA_INIT
1, &DukUndefined_Type
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"dukpy", /* m_name */
NULL, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL /* m_free */
};
#endif
#ifndef _MSC_VER
__attribute__ ((visibility ("default")))
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_dukpy(void)
#else
initdukpy(void)
#endif
{
PyObject *mod;
#if PY_MAJOR_VERSION < 3
DukUndefined_Type.ob_type = &PyType_Type;
#endif
if (PyType_Ready(&DukUndefined_Type) < 0)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
DukContext_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&DukContext_Type) < 0)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
DukObject_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&DukObject_Type) < 0)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
DukArray_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&DukArray_Type) < 0)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
DukFunction_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&DukFunction_Type) < 0)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
DukEnum_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&DukEnum_Type) < 0)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
mod = PyModule_Create(&moduledef);
#else
mod = Py_InitModule3("dukpy", NULL, "Python bindings for duktape");
#endif
if (mod != NULL) {
Py_INCREF(&DukContext_Type);
PyModule_AddObject(mod, "Context", (PyObject *)&DukContext_Type);
Py_INCREF(Duk_undefined);
PyModule_AddObject(mod, "undefined", (PyObject *)Duk_undefined);
JSError = PyErr_NewException("dukpy.JSError", NULL, NULL);
if (JSError) PyModule_AddObject(mod, "JSError", JSError);
}
#if PY_MAJOR_VERSION >= 3
return mod;
#endif
}

View File

@ -1,586 +0,0 @@
#include "dukpy.h"
/* DukObject {{{ */
static void DukObject_INIT(DukObject *self, DukContext *context,
duk_idx_t index)
{
duk_context *ctx = context->ctx;
duk_idx_t index_n = duk_normalize_index(ctx, index);
Py_INCREF(context);
self->context = context;
self->parent = NULL;
/* heap_stash[(void *)self] = proxied_value */
duk_push_heap_stash(ctx);
duk_push_pointer(ctx, self);
duk_dup(ctx, index_n);
duk_put_prop(ctx, -3);
duk_pop(ctx);
}
static void DukObject_DESTRUCT(DukObject *self)
{
duk_context *ctx = self->context->ctx;
/* delete heap_stash[(void *)self] */
duk_push_heap_stash(ctx);
duk_push_pointer(ctx, self);
duk_del_prop(ctx, -2);
duk_pop(ctx);
Py_XDECREF(self->parent);
Py_DECREF(self->context);
}
DukObject *DukObject_from_DukContext(DukContext *context, duk_idx_t index)
{
DukObject *self;
self = PyObject_New(DukObject, &DukObject_Type);
if (self == NULL)
return NULL;
DukObject_INIT(self, context, index);
return self;
}
DukObject *DukObject_from_ctx(duk_context *ctx, duk_idx_t index)
{
DukContext *context = DukContext_get(ctx);
if (!context) {
PyErr_Format(PyExc_RuntimeError, "Unknown context %p", ctx);
return NULL;
}
return DukObject_from_DukContext(context, index);
}
static void DukObject_dealloc(DukObject *self)
{
DukObject_DESTRUCT(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
int DukObject_push(DukObject *self, duk_context *ctx)
{
/* Push the proxied value to given context's stack */
duk_push_heap_stash(ctx);
duk_push_pointer(ctx, self);
duk_get_prop(ctx, -2);
duk_replace(ctx, -2);
return 0;
}
#define PUSH(p) DukObject_push((p), (p)->context->ctx)
static PyObject *DukObject_getattr(DukObject *self, PyObject *name)
{
duk_context *ctx = self->context->ctx;
PyObject *value;
/* Look up normal attributes first */
if (!(value = PyObject_GenericGetAttr((PyObject *)self, name))) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
return NULL;
PyErr_Clear();
} else {
return value;
}
/* Not found, query the duktape object */
PUSH(self);
if (python_to_duk(ctx, name) != 0) {
duk_pop(ctx);
return NULL;
}
duk_get_prop(ctx, -2);
value = duk_to_python(ctx, -1);
duk_pop_n(ctx, 2);
if (Py_TYPE(value) == &DukFunction_Type) {
/* Set parent link for method calls */
Py_INCREF(self);
((DukObject *)value)->parent = self;
}
return value;
}
static int DukObject_setattr(DukObject *self, PyObject *name, PyObject *value)
{
duk_context *ctx = self->context->ctx;
PUSH(self);
if (python_to_duk(ctx, name) != 0) {
duk_pop(ctx);
return -1;
}
if (python_to_duk(ctx, value) != 0) {
duk_pop_n(ctx, 2);
return -1;
}
duk_put_prop(ctx, -3);
duk_pop(ctx);
return 0;
}
static PyObject *DukObject_make_enum(DukObject *self, dukenum_mode_t mode)
{
duk_context *ctx = self->context->ctx;
PyObject *result;
PUSH(self);
duk_enum(ctx, -1, 0);
result = (PyObject *)DukEnum_from_DukContext(self->context, mode);
duk_pop(ctx);
return result;
}
static PyObject *DukObject_iter(DukObject *self)
{
return DukObject_make_enum(self, DUKENUM_KEYS);
}
static PyObject *DukObject_keys(DukObject *self, PyObject *args)
{
(void)args;
return DukObject_make_enum(self, DUKENUM_KEYS);
}
static PyObject *DukObject_values(DukObject *self, PyObject *args)
{
(void)args;
return DukObject_make_enum(self, DUKENUM_VALUES);
}
static PyObject *DukObject_items(DukObject *self, PyObject *args)
{
(void)args;
return DukObject_make_enum(self, DUKENUM_PAIRS);
}
static PyMappingMethods DukObject_as_mapping = {
NULL,
(binaryfunc)DukObject_getattr,
(objobjargproc)DukObject_setattr
};
static PyMethodDef DukObject_methods[] = {
{"keys", (PyCFunction)DukObject_keys, METH_NOARGS,
"Iterate over object keys"},
{"values", (PyCFunction)DukObject_values, METH_NOARGS,
"Iterate over object values"},
{"items", (PyCFunction)DukObject_items, METH_NOARGS,
"Iterate over key-value pairs"},
{NULL}
};
PyTypeObject DukObject_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"Object proxy", /* tp_name */
sizeof(DukObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DukObject_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
&DukObject_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
(getattrofunc)DukObject_getattr, /* tp_getattro */
(setattrofunc)DukObject_setattr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"Duktape object proxy", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)DukObject_iter, /* tp_iter */
0, /* tp_iternext */
DukObject_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0 /* tp_new */
};
// }}}
/* DukArray {{{ */
DukObject *DukArray_from_ctx(duk_context *ctx, duk_idx_t index)
{
DukObject *self;
DukContext *context = DukContext_get(ctx);
if (!context) {
PyErr_Format(PyExc_RuntimeError, "Unknown context %p", ctx);
return NULL;
}
self = PyObject_New(DukObject, &DukArray_Type);
if (self == NULL)
return NULL;
DukObject_INIT(self, context, index);
return self;
}
Py_ssize_t DukArray_length(DukObject *self)
{
duk_context *ctx = self->context->ctx;
duk_size_t len;
PUSH(self);
len = duk_get_length(ctx, -1);
duk_pop(ctx);
return (Py_ssize_t)len;
}
static PyObject *DukArray_getitem(DukObject *self, Py_ssize_t i)
{
duk_context *ctx = self->context->ctx;
PyObject *result;
PUSH(self);
duk_get_prop_index(ctx, -1, (duk_uarridx_t)i);
result = duk_to_python(ctx, -1);
if (!result)
duk_pop(ctx);
else
duk_pop_n(ctx, 2);
#if 0
if (result == Duk_undefined) {
Py_DECREF(result);
PyErr_Format(PyExc_IndexError, "%R has no index %li", self, i);
return NULL;
}
#endif
return result;
}
static int DukArray_setitem(DukObject *self, Py_ssize_t i, PyObject *value)
{
duk_context *ctx = self->context->ctx;
PUSH(self);
if (value) {
/* self[i] = value */
if (python_to_duk(ctx, value) == -1) {
duk_pop(ctx);
return -1;
}
duk_put_prop_index(ctx, -2, (duk_uarridx_t)i);
} else {
/* del self[i]
Note that this always succeeds, even if the index doesn't
exist.
*/
duk_del_prop_index(ctx, -1, (duk_uarridx_t)i);
duk_pop(ctx);
}
return 0;
}
static PyObject *DukArray_iter(DukObject *self)
{
duk_context *ctx = self->context->ctx;
PyObject *result;
PUSH(self);
duk_enum(ctx, -1, DUK_ENUM_ARRAY_INDICES_ONLY);
result = (PyObject *)DukEnum_from_DukContext(self->context, DUKENUM_VALUES);
duk_pop(ctx);
return result;
}
static PySequenceMethods DukArray_as_sequence = {
(lenfunc)DukArray_length, /* sq_length */
NULL, /* sq_concat */
NULL, /* sq_repeat */
(ssizeargfunc)DukArray_getitem, /* sq_item */
NULL, /* unused */
(ssizeobjargproc)DukArray_setitem, /* sq_ass_item */
NULL, /* sq_contains */
NULL, /* sq_inplace_concat */
NULL, /* sq_inplace_repeat */
};
PyTypeObject DukArray_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"Array proxy", /* tp_name */
sizeof(DukObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DukObject_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
&DukArray_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
(getattrofunc)DukObject_getattr, /* tp_getattro */
(setattrofunc)DukObject_setattr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"Duktape array proxy" , /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)DukArray_iter /* tp_iter */
};
/// }}}
/* DukFunction {{{ */
DukObject *DukFunction_from_ctx(duk_context *ctx, duk_idx_t index)
{
DukObject *self;
DukContext *context = DukContext_get(ctx);
if (!context) {
PyErr_Format(PyExc_RuntimeError, "Unknown context %p", ctx);
return NULL;
}
self = PyObject_New(DukObject, &DukFunction_Type);
if (self == NULL)
return NULL;
DukObject_INIT(self, context, index);
return self;
}
PyObject* DukFunction_repr(DukObject *self) {
PyObject *ans = NULL;
PyObject *name = PyObject_GetAttrString((PyObject*)self, "name"), *fname = PyObject_GetAttrString((PyObject*)self, "fileName");
ans = PyUnicode_FromFormat("[Function proxy: %S() in filename: %S]", name, fname);
Py_XDECREF(name); Py_XDECREF(fname);
return ans;
}
PyObject* DukFunction_call(DukObject *self, PyObject *args, PyObject *kw)
{
duk_context *ctx = self->context->ctx;
Py_ssize_t nargs, i;
int return_none = 0, ret = 0;
PyObject *result, *temp;
/* NULL if no parent */
PyObject *this = (PyObject *)self->parent;
if (kw) {
temp = PyDict_GetItemString(kw, "this");
if (temp)
this = temp;
temp = PyDict_GetItemString(kw, "return_none");
if (temp)
return_none = PyObject_IsTrue(temp);
}
nargs = PyTuple_Size(args);
/* Push the function */
PUSH(self);
if (this) {
/* Push the "this" binding */
if (python_to_duk(ctx, this) == -1) {
duk_pop(ctx);
return NULL;
}
}
/* Push args */
for (i = 0; i < nargs; i++) {
PyObject *arg = PyTuple_GetItem(args, i);
if (python_to_duk(ctx, arg) == -1) {
duk_pop_n(ctx, 1 + (this ? 1 : 0) + (duk_idx_t)i);
return NULL;
}
}
if (this)
ret = duk_pcall_method(ctx, (duk_idx_t)nargs);
else
ret = duk_pcall(ctx, (duk_idx_t)nargs);
if (ret != DUK_EXEC_SUCCESS) {
temp = duk_to_python(ctx, -1);
duk_pop(ctx);
if (temp) {
set_dukpy_error(temp);
Py_DECREF(temp);
} else PyErr_SetString(PyExc_RuntimeError, "The was an error during call(), but the error could not be read of the stack");
return NULL;
}
if (return_none) {
/* Always return None. This saves converting the function's
return value. */
duk_pop(ctx);
Py_RETURN_NONE;
} else {
result = duk_to_python(ctx, -1);
duk_pop(ctx);
return result;
}
}
PyTypeObject DukFunction_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"Function proxy", /* tp_name */
sizeof(DukObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DukObject_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)DukFunction_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(ternaryfunc)DukFunction_call, /* tp_call */
0, /* tp_str */
(getattrofunc)DukObject_getattr, /* tp_getattro */
(setattrofunc)DukObject_setattr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"Duktape function proxy" /* tp_doc */
};
// }}}
/* DukEnum {{{ */
DukEnum *DukEnum_from_DukContext(DukContext *context, dukenum_mode_t mode)
{
DukEnum *self;
self = PyObject_New(DukEnum, &DukEnum_Type);
if (self == NULL)
return NULL;
DukObject_INIT(&self->base, context, -1);
self->mode = mode;
return self;
}
static void DukEnum_dealloc(DukEnum *self)
{
DukObject_DESTRUCT(&self->base);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *DukEnum_iter(DukEnum *self)
{
Py_INCREF(self);
return (PyObject *)self;
}
static PyObject *DukEnum_iternext(DukEnum *self)
{
duk_context *ctx = self->base.context->ctx;
PyObject *result = NULL;
int mode = self->mode;
int get_value = mode == DUKENUM_VALUES || mode == DUKENUM_PAIRS;
int pop = 1;
PUSH(&self->base);
if (duk_next(ctx, -1, get_value)) {
switch (mode) {
case DUKENUM_KEYS:
result = duk_to_python(ctx, -1);
pop = 2;
break;
case DUKENUM_VALUES:
result = duk_to_python(ctx, -1);
pop = 3;
break;
case DUKENUM_PAIRS:
result = Py_BuildValue("(NN)",
duk_to_python(ctx, -2),
duk_to_python(ctx, -1));
pop = 3;
break;
}
}
duk_pop_n(ctx, pop);
return result;
}
PyTypeObject DukEnum_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"Enumerator", /* tp_name */
sizeof(DukEnum), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)DukEnum_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"Duktape enumerator", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)DukEnum_iter, /* tp_iter */
(iternextfunc)DukEnum_iternext, /* tp_iternext */
};
// }}}

View File

@ -2,10 +2,13 @@ import os
import sys
import tempfile
import unittest
from threading import Thread, Event
from duktape import dukpy
from threading import Event, Thread
import dukpy
undefined, JSError, Context = dukpy.undefined, dukpy.JSError, dukpy.Context
class ContextTests(unittest.TestCase):
def setUp(self):