Skip to content

Commit

Permalink
An embedded javascript interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
kovidgoyal committed Jun 18, 2015
1 parent b48d401 commit 655c949
Show file tree
Hide file tree
Showing 13 changed files with 78,661 additions and 0 deletions.
4 changes: 4 additions & 0 deletions COPYRIGHT
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ License: GPL-3
The full text of the GPL is distributed as in
/usr/share/common-licenses/GPL-3 on Debian systems.

Files: src/duktape/*
Copyright: Various
License: MIT

Files: src/unrar/*
Copyright: Various
License: Non-free
Expand Down
6 changes: 6 additions & 0 deletions setup/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ def preflight(self, obj_dir, compiler, linker, builder, cflags, ldflags):

extensions = [

Extension('dukpy',
['duktape/%s.c' % x for x in 'context conversions proxy module duktape/duktape'.split()],
headers=['duktape/dukpy.h', 'duktape/duktape/duktape.h'],
optimize_level=2,
),

Extension('hunspell',
['hunspell/'+x for x in
'affentry.cxx affixmgr.cxx csutil.cxx dictmgr.cxx filemgr.cxx hashmgr.cxx hunspell.cxx phonet.cxx replist.cxx suggestmgr.cxx'.split()
Expand Down
1 change: 1 addition & 0 deletions src/calibre/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def __init__(self):
'matcher',
'tokenizer',
'certgen',
'dukpy',
]
if iswindows:
plugins.extend(['winutil', 'wpd', 'winfonts'])
Expand Down
7 changes: 7 additions & 0 deletions src/calibre/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ def test_icu():
test_build()
print ('ICU OK!')

def test_dukpy():
print ('Testing dukpy')
from duktape.tests import test_build
test_build()
print ('dukpy OK!')

def test_wpd():
wpd = plugins['wpd'][0]
try:
Expand Down Expand Up @@ -226,6 +232,7 @@ def test():
if iswindows:
test_dlls()
test_plugins()
test_dukpy()
test_lxml()
test_ssl()
test_sqlite()
Expand Down
42 changes: 42 additions & 0 deletions src/duktape/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python2
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)

__copyright__ = '2011, Kovid Goyal <[email protected]>'
__docformat__ = 'restructuredtext en'


__all__ = ['dukpy', 'Context', 'undefined']

import errno, os
from functools import partial

from calibre.constants import plugins
dukpy, err = plugins['dukpy']
if err:
raise RuntimeError('Failed to load dukpy with error: %s' % err)
del err
Context_, undefined = dukpy.Context, dukpy.undefined

def load_file(base_dirs, name):
for b in base_dirs:
try:
return open(os.path.join(b, name), 'rb').read().decode('utf-8')
except EnvironmentError as e:
if e.errno != errno.ENOENT:
raise
raise EnvironmentError('No module named: %s found in the base directories: %s' % (name, os.pathsep.join(base_dirs)))

def Context(base_dirs=()):
ans = Context_()
if not base_dirs:
base_dirs = (os.getcwdu(),)
ans.g.Duktape.load_file = partial(load_file, base_dirs or (os.getcwdu(),))
ans.eval('''
console = { log: function() { print(Array.prototype.join.call(arguments, ' ')); } };
Duktape.modSearch = function (id, require, exports, module) {
return Duktape.load_file(id);
}
''')
return ans
231 changes: 231 additions & 0 deletions src/duktape/context.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
#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->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;
int noresult = 0;
PyObject *result = NULL, *temp = NULL;

static char *keywords[] = {"code", "noreturn", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|O:eval", keywords,
&code, &temp)) {
return NULL;
}
if (temp && PyObject_IsTrue(temp)) noresult = 1;

if (duk_peval_string(self->ctx, code) != 0) {
PyErr_Format(PyExc_SyntaxError, "%s",
duk_safe_to_string(self->ctx, -1));
return NULL;
}

if (noresult) {
duk_pop(self->ctx);
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;
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;

if (duk_peval_file(self->ctx, path) != 0) {
PyErr_Format(PyExc_SyntaxError,
"%s:%s", path, duk_safe_to_string(self->ctx, -1));
return NULL;
}

if (noresult) {
duk_pop(self->ctx);
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 */
};
Loading

0 comments on commit 655c949

Please sign in to comment.