Skip to content

Commit 2aa1790

Browse files
tbluemelakrieger
andcommitted
Add C Closures
This allows C objects to be attached to functions, and it also allows C code to be notified when these functions are finalized. Co-Authored-By: Andrew Krieger <akrieger@users.noreply.github.com>
1 parent 7f82c12 commit 2aa1790

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

quickjs.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ enum {
138138
JS_CLASS_BYTECODE_FUNCTION, /* u.func */
139139
JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
140140
JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
141+
JS_CLASS_C_CLOSURE, /* u.c_closure_record */
141142
JS_CLASS_GENERATOR_FUNCTION, /* u.func */
142143
JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
143144
JS_CLASS_REGEXP, /* u.regexp */
@@ -980,6 +981,7 @@ struct JSObject {
980981
void *opaque;
981982
struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
982983
struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
984+
struct JSCClosureRecord *c_closure_record; /* JS_CLASS_C_CLOSURE */
983985
struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
984986
struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
985987
struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
@@ -1343,6 +1345,10 @@ static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
13431345
static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj,
13441346
JSValueConst this_val,
13451347
int argc, JSValueConst *argv, int flags);
1348+
static void js_c_closure_finalizer(JSRuntime *rt, JSValue val);
1349+
static JSValue js_call_c_closure(JSContext *ctx, JSValueConst func_obj,
1350+
JSValueConst this_val,
1351+
int argc, JSValueConst *argv, int flags);
13461352
static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val);
13471353
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
13481354
JSGCObjectTypeEnum type);
@@ -1746,6 +1752,7 @@ static JSClassShortDef const js_std_class_def[] = {
17461752
{ JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
17471753
{ JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
17481754
{ JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
1755+
{ JS_ATOM_Function, js_c_closure_finalizer, NULL}, /* JS_CLASS_C_CLOSURE */
17491756
{ JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */
17501757
{ JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
17511758
{ JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
@@ -1870,6 +1877,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
18701877

18711878
rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
18721879
rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_call_c_function_data;
1880+
rt->class_array[JS_CLASS_C_CLOSURE].call = js_call_c_closure;
18731881
rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
18741882
rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_call_generator_function;
18751883
if (init_shape_hash(rt))
@@ -5547,6 +5555,75 @@ static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
55475555
mark_func(rt, &js_autoinit_get_realm(pr)->header);
55485556
}
55495557

5558+
typedef struct JSCClosureRecord {
5559+
JSCClosure *func;
5560+
uint16_t length;
5561+
uint16_t magic;
5562+
void *opaque;
5563+
void (*opaque_finalize)(void*);
5564+
} JSCClosureRecord;
5565+
5566+
static void js_c_closure_finalizer(JSRuntime *rt, JSValue val)
5567+
{
5568+
JSCClosureRecord *s = JS_GetOpaque(val, JS_CLASS_C_CLOSURE);
5569+
5570+
if (s) {
5571+
if (s->opaque_finalize)
5572+
s->opaque_finalize(s->opaque);
5573+
5574+
js_free_rt(rt, s);
5575+
}
5576+
}
5577+
5578+
static JSValue js_call_c_closure(JSContext *ctx, JSValueConst func_obj,
5579+
JSValueConst this_val,
5580+
int argc, JSValueConst *argv, int flags)
5581+
{
5582+
JSCClosureRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_CLOSURE);
5583+
JSValueConst *arg_buf;
5584+
int i;
5585+
5586+
/* XXX: could add the function on the stack for debug */
5587+
if (unlikely(argc < s->length)) {
5588+
arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
5589+
for(i = 0; i < argc; i++)
5590+
arg_buf[i] = argv[i];
5591+
for(i = argc; i < s->length; i++)
5592+
arg_buf[i] = JS_UNDEFINED;
5593+
} else {
5594+
arg_buf = argv;
5595+
}
5596+
5597+
return s->func(ctx, this_val, argc, arg_buf, s->magic, s->opaque);
5598+
}
5599+
5600+
JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func,
5601+
int length, int magic, void *opaque,
5602+
void (*opaque_finalize)(void*))
5603+
{
5604+
JSCClosureRecord *s;
5605+
JSValue func_obj;
5606+
5607+
func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
5608+
JS_CLASS_C_CLOSURE);
5609+
if (JS_IsException(func_obj))
5610+
return func_obj;
5611+
s = js_malloc(ctx, sizeof(*s));
5612+
if (!s) {
5613+
JS_FreeValue(ctx, func_obj);
5614+
return JS_EXCEPTION;
5615+
}
5616+
s->func = func;
5617+
s->length = length;
5618+
s->magic = magic;
5619+
s->opaque = opaque;
5620+
s->opaque_finalize = opaque_finalize;
5621+
JS_SetOpaque(func_obj, s);
5622+
js_function_set_properties(ctx, func_obj,
5623+
JS_ATOM_empty_string, length);
5624+
return func_obj;
5625+
}
5626+
55505627
static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
55515628
{
55525629
if (unlikely(prop_flags & JS_PROP_TMASK)) {
@@ -6455,6 +6532,15 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
64556532
}
64566533
}
64576534
break;
6535+
case JS_CLASS_C_CLOSURE: /* u.c_closure_record */
6536+
{
6537+
JSCClosureRecord *c = p->u.c_closure_record;
6538+
if (c) {
6539+
s->memory_used_count += 1;
6540+
s->memory_used_size += sizeof(*c);
6541+
}
6542+
}
6543+
break;
64586544
case JS_CLASS_REGEXP: /* u.regexp */
64596545
compute_jsstring_size(p->u.regexp.pattern, hp);
64606546
compute_jsstring_size(p->u.regexp.bytecode, hp);

quickjs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ static inline bool JS_VALUE_IS_NAN(JSValue v)
394394
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
395395
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
396396
typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data);
397+
typedef JSValue JSCClosure(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, void *opaque);
397398

398399
typedef struct JSMallocFunctions {
399400
void *(*js_calloc)(void *opaque, size_t count, size_t size);
@@ -1165,6 +1166,9 @@ JS_EXTERN JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func,
11651166
const char *name,
11661167
int length, int magic, int data_len,
11671168
JSValueConst *data);
1169+
JS_EXTERN JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func,
1170+
int length, int magic, void *opaque,
1171+
void (*opaque_finalize)(void*));
11681172

11691173
static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func,
11701174
const char *name, int length)

0 commit comments

Comments
 (0)