Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum {
JS_CLASS_BYTECODE_FUNCTION, /* u.func */
JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
JS_CLASS_C_CLOSURE, /* u.c_closure_record */
JS_CLASS_GENERATOR_FUNCTION, /* u.func */
JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
JS_CLASS_REGEXP, /* u.regexp */
Expand Down Expand Up @@ -980,6 +981,7 @@ struct JSObject {
void *opaque;
struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
struct JSCClosureRecord *c_closure_record; /* JS_CLASS_C_CLOSURE */
struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
Expand Down Expand Up @@ -1343,6 +1345,10 @@ static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
static JSValue js_call_c_function_data(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_val,
int argc, JSValueConst *argv, int flags);
static void js_c_closure_finalizer(JSRuntime *rt, JSValue val);
static JSValue js_call_c_closure(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_val,
int argc, JSValueConst *argv, int flags);
static JSAtom js_symbol_to_atom(JSContext *ctx, JSValueConst val);
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
JSGCObjectTypeEnum type);
Expand Down Expand Up @@ -1746,6 +1752,7 @@ static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
{ JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
{ JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
{ JS_ATOM_Function, js_c_closure_finalizer, NULL}, /* JS_CLASS_C_CLOSURE */
{ JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */
{ JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
{ JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
Expand Down Expand Up @@ -1870,6 +1877,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)

rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_call_c_function_data;
rt->class_array[JS_CLASS_C_CLOSURE].call = js_call_c_closure;
rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_call_generator_function;
if (init_shape_hash(rt))
Expand Down Expand Up @@ -5547,6 +5555,75 @@ static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
mark_func(rt, &js_autoinit_get_realm(pr)->header);
}

typedef struct JSCClosureRecord {
JSCClosure *func;
uint16_t length;
uint16_t magic;
void *opaque;
void (*opaque_finalize)(void*);
} JSCClosureRecord;

static void js_c_closure_finalizer(JSRuntime *rt, JSValue val)
{
JSCClosureRecord *s = JS_GetOpaque(val, JS_CLASS_C_CLOSURE);

if (s) {
if (s->opaque_finalize)
s->opaque_finalize(s->opaque);

js_free_rt(rt, s);
}
}

static JSValue js_call_c_closure(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_val,
int argc, JSValueConst *argv, int flags)
{
JSCClosureRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_CLOSURE);
JSValueConst *arg_buf;
int i;

/* XXX: could add the function on the stack for debug */
if (unlikely(argc < s->length)) {
arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a stack check:

Suggested change
arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
stack_size = arg_buf[0]) * s->length;
if (js_check_stack_overflow(rt, stack_size))
return JS_ThrowStackOverflow(ctx);
arg_buf = alloca(sizeof(stack_size);

As well, add a temporary JSStackFrame here, otherwise the function doesn't show up in stack traces. I fixed that recently for JSCFunctionData callbacks so you can just crib it from there: c85fd87

for(i = 0; i < argc; i++)
arg_buf[i] = argv[i];
for(i = argc; i < s->length; i++)
arg_buf[i] = JS_UNDEFINED;
} else {
arg_buf = argv;
}

return s->func(ctx, this_val, argc, arg_buf, s->magic, s->opaque);
}

JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func,
int length, int magic, void *opaque,
void (*opaque_finalize)(void*))
Comment on lines +5600 to +5602
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with e.g. JS_SetModuleLoaderFunc:

Suggested change
JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func,
int length, int magic, void *opaque,
void (*opaque_finalize)(void*))
JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func,
void (*opaque_finalize)(void*),
int length, int magic, void *opaque)

And I'd add a typedef for the finalizer callback.

{
JSCClosureRecord *s;
JSValue func_obj;

func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
JS_CLASS_C_CLOSURE);
if (JS_IsException(func_obj))
return func_obj;
s = js_malloc(ctx, sizeof(*s));
if (!s) {
JS_FreeValue(ctx, func_obj);
return JS_EXCEPTION;
}
s->func = func;
s->length = length;
s->magic = magic;
s->opaque = opaque;
s->opaque_finalize = opaque_finalize;
JS_SetOpaque(func_obj, s);
js_function_set_properties(ctx, func_obj,
JS_ATOM_empty_string, length);
Comment on lines +5622 to +5623
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added JS_NewCFunctionData2 recently because people do in fact sometimes want to set the function name, so let's do that right from the beginning here. :-)

return func_obj;
}

static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
{
if (unlikely(prop_flags & JS_PROP_TMASK)) {
Expand Down Expand Up @@ -6455,6 +6532,15 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
}
}
break;
case JS_CLASS_C_CLOSURE: /* u.c_closure_record */
{
JSCClosureRecord *c = p->u.c_closure_record;
if (c) {
s->memory_used_count += 1;
s->memory_used_size += sizeof(*c);
}
}
break;
case JS_CLASS_REGEXP: /* u.regexp */
compute_jsstring_size(p->u.regexp.pattern, hp);
compute_jsstring_size(p->u.regexp.bytecode, hp);
Expand Down
4 changes: 4 additions & 0 deletions quickjs.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ static inline bool JS_VALUE_IS_NAN(JSValue v)
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValueConst *func_data);
typedef JSValue JSCClosure(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, void *opaque);

typedef struct JSMallocFunctions {
void *(*js_calloc)(void *opaque, size_t count, size_t size);
Expand Down Expand Up @@ -1165,6 +1166,9 @@ JS_EXTERN JSValue JS_NewCFunctionData2(JSContext *ctx, JSCFunctionData *func,
const char *name,
int length, int magic, int data_len,
JSValueConst *data);
JS_EXTERN JSValue JS_NewCClosure(JSContext *ctx, JSCClosure *func,
int length, int magic, void *opaque,
void (*opaque_finalize)(void*));

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