1+ #include <time.h>
2+
13#include <Python.h>
24
35#include "third-party/quickjs.h"
68typedef struct {
79PyObject_HEAD JSRuntime * runtime ;
810JSContext * context ;
11+ int has_time_limit ;
12+ clock_t time_limit ;
913} ContextData ;
1014
1115// The data of the type _quickjs.Object.
@@ -22,6 +26,38 @@ static PyObject *JSException = NULL;
2226// Takes ownership of the JSValue and will deallocate it (refcount reduced by 1).
2327static PyObject * quickjs_to_python (ContextData * context_obj , JSValue value );
2428
29+ // Keeps track of the time if we are using a time limit.
30+ typedef struct {
31+ clock_t start ;
32+ clock_t limit ;
33+ } InterruptData ;
34+
35+ // Returns nonzero if we should stop due to a time limit.
36+ static int js_interrupt_handler (JSRuntime * rt , void * opaque ) {
37+ InterruptData * data = opaque ;
38+ if (clock () - data -> start >= data -> limit ) {
39+ return 1 ;
40+ } else {
41+ return 0 ;
42+ }
43+ }
44+
45+ // Sets up a context and an InterruptData struct if the context has a time limit.
46+ static void setup_time_limit (ContextData * context , InterruptData * interrupt_data ) {
47+ if (context -> has_time_limit ) {
48+ JS_SetInterruptHandler (context -> runtime , js_interrupt_handler , interrupt_data );
49+ interrupt_data -> limit = context -> time_limit ;
50+ interrupt_data -> start = clock ();
51+ }
52+ }
53+
54+ // Restores the context if the context has a time limit.
55+ static void teardown_time_limit (ContextData * context ) {
56+ if (context -> has_time_limit ) {
57+ JS_SetInterruptHandler (context -> runtime , NULL , NULL );
58+ }
59+ }
60+
2561// Creates an instance of the Object class.
2662static PyObject * object_new (PyTypeObject * type , PyObject * args , PyObject * kwds ) {
2763ObjectData * self ;
@@ -134,7 +170,10 @@ static PyObject *object_call(ObjectData *self, PyObject *args, PyObject *kwds) {
134170// function from JS, this needs to be reversed or improved.
135171JSValue value ;
136172Py_BEGIN_ALLOW_THREADS ;
173+ InterruptData interrupt_data ;
174+ setup_time_limit (self -> context , & interrupt_data );
137175value = JS_Call (self -> context -> context , self -> object , JS_NULL , nargs , jsargs );
176+ teardown_time_limit (self -> context );
138177Py_END_ALLOW_THREADS ;
139178
140179for (int i = 0 ; i < nargs ; ++ i ) {
@@ -213,6 +252,8 @@ static PyObject *context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
213252// _quickjs.Context can be used concurrently.
214253self -> runtime = JS_NewRuntime ();
215254self -> context = JS_NewContext (self -> runtime );
255+ self -> has_time_limit = 0 ;
256+ self -> time_limit = 0 ;
216257}
217258return (PyObject * )self ;
218259}
@@ -239,7 +280,10 @@ static PyObject *context_eval(ContextData *self, PyObject *args) {
239280// function from JS, this needs to be reversed or improved.
240281JSValue value ;
241282Py_BEGIN_ALLOW_THREADS ;
283+ InterruptData interrupt_data ;
284+ setup_time_limit (self , & interrupt_data );
242285value = JS_Eval (self -> context , code , strlen (code ), "<input>" , JS_EVAL_TYPE_GLOBAL );
286+ teardown_time_limit (self );
243287Py_END_ALLOW_THREADS ;
244288return quickjs_to_python (self , value );
245289}
@@ -270,11 +314,35 @@ static PyObject *context_set_memory_limit(ContextData *self, PyObject *args) {
270314Py_RETURN_NONE ;
271315}
272316
317+ // _quickjs.Context.set_time_limit
318+ //
319+ // Retrieves a global variable from the JS context.
320+ static PyObject * context_set_time_limit (ContextData * self , PyObject * args ) {
321+ double limit ;
322+ if (!PyArg_ParseTuple (args , "d" , & limit )) {
323+ return NULL ;
324+ }
325+ if (limit < 0 ) {
326+ self -> has_time_limit = 0 ;
327+ } else {
328+ self -> has_time_limit = 1 ;
329+ self -> time_limit = (clock_t )(limit * CLOCKS_PER_SEC );
330+ }
331+ Py_RETURN_NONE ;
332+ }
333+
273334// All methods of the _quickjs.Context class.
274335static PyMethodDef context_methods [] = {
275336 {"eval" , (PyCFunction )context_eval , METH_VARARGS , "Evaluates a Javascript string." },
276337 {"get" , (PyCFunction )context_get , METH_VARARGS , "Gets a Javascript global variable." },
277- {"set_memory_limit" , (PyCFunction )context_set_memory_limit , METH_VARARGS , "Sets the memory limit in bytes." },
338+ {"set_memory_limit" ,
339+ (PyCFunction )context_set_memory_limit ,
340+ METH_VARARGS ,
341+ "Sets the memory limit in bytes." },
342+ {"set_time_limit" ,
343+ (PyCFunction )context_set_time_limit ,
344+ METH_VARARGS ,
345+ "Sets the CPU time limit in seconds (C function clock() is used)." },
278346 {NULL } /* Sentinel */
279347};
280348
0 commit comments