-
Notifications
You must be signed in to change notification settings - Fork 216
Add C Closures #1228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add C Closures #1228
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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*)) | ||||||||||||||
|
Comment on lines
+5600
to
+5602
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency with e.g. JS_SetModuleLoaderFunc:
Suggested change
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) { | ||||||||||||||
|
|
@@ -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); | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a stack check:
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