Skip to content

Commit 3c00d43

Browse files
committed
Convert JS exceptions to Python.
1 parent 546ba55 commit 3c00d43

File tree

3 files changed

+62
-30
lines changed

3 files changed

+62
-30
lines changed

module.c

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,47 @@
22

33
#include "third-party/quickjs.h"
44

5+
static PyObject* JSException = NULL;
6+
7+
static PyObject* quickjs_to_python(JSContext* context, JSValue value) {
8+
int tag = JS_VALUE_GET_TAG(value);
9+
PyObject *return_value = NULL;
10+
11+
if (tag == JS_TAG_INT) {
12+
return_value = Py_BuildValue("i", JS_VALUE_GET_INT(value));
13+
} else if (tag == JS_TAG_BOOL) {
14+
return_value = Py_BuildValue("O", JS_VALUE_GET_BOOL(value) ? Py_True : Py_False);
15+
} else if (tag == JS_TAG_NULL) {
16+
return_value = Py_None;
17+
} else if (tag == JS_TAG_UNDEFINED) {
18+
return_value = Py_None;
19+
} else if (tag == JS_TAG_UNINITIALIZED) {
20+
return_value = Py_None;
21+
} else if (tag == JS_TAG_EXCEPTION) {
22+
JSValue exception = JS_GetException(context);
23+
JSValue error_string = JS_ToString(context, exception);
24+
const char *cstring = JS_ToCString(context, error_string);
25+
PyErr_Format(JSException, "%s", cstring);
26+
JS_FreeCString(context, cstring);
27+
JS_FreeValue(context, error_string);
28+
JS_FreeValue(context, exception);
29+
} else if (tag == JS_TAG_FLOAT64) {
30+
return_value = Py_BuildValue("d", JS_VALUE_GET_FLOAT64(value));
31+
} else if (tag == JS_TAG_STRING) {
32+
const char *cstring = JS_ToCString(context, value);
33+
return_value = Py_BuildValue("s", cstring);
34+
JS_FreeCString(context, cstring);
35+
} else {
36+
// TODO: Raise exception.
37+
}
38+
39+
JS_FreeValue(context, value);
40+
if (return_value == Py_None) {
41+
Py_RETURN_NONE;
42+
}
43+
return return_value;
44+
}
45+
546
static PyObject *test(PyObject *self, PyObject *args) {
647
return Py_BuildValue("i", 42);
748
}
@@ -35,36 +76,7 @@ static PyObject *context_eval(ContextData *self, PyObject *args) {
3576
return NULL;
3677
}
3778
JSValue value = JS_Eval(self->context, code, strlen(code), "<input>", JS_EVAL_TYPE_GLOBAL);
38-
int tag = JS_VALUE_GET_TAG(value);
39-
PyObject *return_value = NULL;
40-
41-
if (tag == JS_TAG_INT) {
42-
return_value = Py_BuildValue("i", JS_VALUE_GET_INT(value));
43-
} else if (tag == JS_TAG_BOOL) {
44-
return_value = Py_BuildValue("O", JS_VALUE_GET_BOOL(value) ? Py_True : Py_False);
45-
} else if (tag == JS_TAG_NULL) {
46-
// None
47-
} else if (tag == JS_TAG_UNDEFINED) {
48-
// None
49-
} else if (tag == JS_TAG_UNINITIALIZED) {
50-
// None
51-
} else if (tag == JS_TAG_EXCEPTION) {
52-
// TODO: Raise exception.
53-
} else if (tag == JS_TAG_FLOAT64) {
54-
return_value = Py_BuildValue("d", JS_VALUE_GET_FLOAT64(value));
55-
} else if (tag == JS_TAG_STRING) {
56-
const char *cstring = JS_ToCString(self->context, value);
57-
return_value = Py_BuildValue("s", cstring);
58-
JS_FreeCString(self->context, cstring);
59-
} else {
60-
// TODO: Raise exception.
61-
}
62-
63-
JS_FreeValue(self->context, value);
64-
if (return_value == NULL) {
65-
Py_RETURN_NONE;
66-
}
67-
return return_value;
79+
return quickjs_to_python(self->context, value);
6880
}
6981

7082
static PyMethodDef context_methods[] = {
@@ -104,7 +116,13 @@ PyMODINIT_FUNC PyInit__quickjs(void) {
104116
return NULL;
105117
}
106118

119+
JSException = PyErr_NewException("_quickjs.JSException", NULL, NULL);
120+
if (JSException == NULL) {
121+
return NULL;
122+
}
123+
107124
Py_INCREF(&Context);
108125
PyModule_AddObject(module, "Context", (PyObject *)&Context);
126+
PyModule_AddObject(module, "JSException", JSException);
109127
return module;
110128
}

quickjs/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ def test():
66

77

88
Context = _quickjs.Context
9+
JSException = _quickjs.JSException

test_quickjs.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,16 @@ def test_wrong_type(self):
3737
def test_context_between_calls(self):
3838
self.context.eval("x = 40; y = 2;")
3939
self.assertEqual(self.context.eval("x + y"), 42)
40+
41+
def test_function(self):
42+
self.context.eval("""
43+
function special(x) {
44+
return 40 + x;
45+
}
46+
""")
47+
self.assertEqual(self.context.eval("special(2)"), 42)
48+
49+
50+
def test_error(self):
51+
with self.assertRaisesRegex(quickjs.JSException, "ReferenceError: missing is not defined"):
52+
self.context.eval("missing + missing")

0 commit comments

Comments
 (0)