diff --git a/quickjs.c b/quickjs.c index 797dcfab7..94ea3ad0c 100644 --- a/quickjs.c +++ b/quickjs.c @@ -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 */ @@ -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 */ @@ -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); @@ -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 */ @@ -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)) @@ -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); + 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*)) +{ + 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); + return func_obj; +} + static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) { if (unlikely(prop_flags & JS_PROP_TMASK)) { @@ -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); diff --git a/quickjs.h b/quickjs.h index 6ba21237c..99e83419a 100644 --- a/quickjs.h +++ b/quickjs.h @@ -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); @@ -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)