@@ -19,6 +19,12 @@ typedef struct {
1919} qjs_signal_entry_t ;
2020
2121
22+ typedef struct {
23+ #define QJS_NJS_HOOK_EXIT 0
24+ JSValue hooks [1 ];
25+ } qjs_njs_t ;
26+
27+
2228typedef enum {
2329 QJS_ENCODING_UTF8 ,
2430} qjs_encoding_t ;
@@ -42,7 +48,13 @@ typedef struct {
4248extern char * * environ ;
4349
4450
45- static JSValue qjs_njs_getter (JSContext * ctx , JSValueConst this_val );
51+ static int qjs_add_intrinsic_njs (JSContext * cx , JSValueConst global );
52+ static JSValue qjs_njs_on (JSContext * ctx , JSValueConst this_val , int argc ,
53+ JSValueConst * argv );
54+ static void qjs_njs_mark (JSRuntime * rt , JSValueConst val ,
55+ JS_MarkFunc * mark_func );
56+ static void qjs_njs_finalizer (JSRuntime * rt , JSValue val );
57+
4658static JSValue qjs_process_env (JSContext * ctx , JSValueConst this_val );
4759static JSValue qjs_process_kill (JSContext * ctx , JSValueConst this_val ,
4860 int argc , JSValueConst * argv );
@@ -99,10 +111,6 @@ static qjs_encoding_label_t qjs_encoding_labels[] =
99111};
100112
101113
102- static const JSCFunctionListEntry qjs_global_proto [] = {
103- JS_CGETSET_DEF ("njs" , qjs_njs_getter , NULL ),
104- };
105-
106114static const JSCFunctionListEntry qjs_text_decoder_proto [] = {
107115 JS_PROP_STRING_DEF ("[Symbol.toStringTag]" , "TextDecoder" ,
108116 JS_PROP_CONFIGURABLE ),
@@ -126,6 +134,7 @@ static const JSCFunctionListEntry qjs_njs_proto[] = {
126134 JS_PROP_INT32_DEF ("version_number" , NJS_VERSION_NUMBER ,
127135 JS_PROP_C_W_E ),
128136 JS_PROP_STRING_DEF ("engine" , "QuickJS" , JS_PROP_C_W_E ),
137+ JS_CFUNC_DEF ("on" , 2 , qjs_njs_on ),
129138};
130139
131140static const JSCFunctionListEntry qjs_process_proto [] = {
@@ -137,6 +146,13 @@ static const JSCFunctionListEntry qjs_process_proto[] = {
137146};
138147
139148
149+ static JSClassDef qjs_njs_class = {
150+ "njs" ,
151+ .finalizer = qjs_njs_finalizer ,
152+ .gc_mark = qjs_njs_mark ,
153+ };
154+
155+
140156static JSClassDef qjs_text_decoder_class = {
141157 "TextDecoder" ,
142158 .finalizer = qjs_text_decoder_finalizer ,
@@ -186,6 +202,10 @@ qjs_new_context(JSRuntime *rt, qjs_module_t **addons)
186202
187203 global_obj = JS_GetGlobalObject (ctx );
188204
205+ if (qjs_add_intrinsic_njs (ctx , global_obj ) < 0 ) {
206+ return NULL ;
207+ }
208+
189209 if (qjs_add_intrinsic_text_decoder (ctx , global_obj ) < 0 ) {
190210 return NULL ;
191211 }
@@ -194,9 +214,6 @@ qjs_new_context(JSRuntime *rt, qjs_module_t **addons)
194214 return NULL ;
195215 }
196216
197- JS_SetPropertyFunctionList (ctx , global_obj , qjs_global_proto ,
198- njs_nitems (qjs_global_proto ));
199-
200217 prop = JS_NewAtom (ctx , "eval" );
201218 if (prop == JS_ATOM_NULL ) {
202219 return NULL ;
@@ -225,20 +242,168 @@ qjs_new_context(JSRuntime *rt, qjs_module_t **addons)
225242}
226243
227244
228- static JSValue
229- qjs_njs_getter (JSContext * ctx , JSValueConst this_val )
245+ JSValue
246+ qjs_call_exit_hook (JSContext * ctx )
230247{
231- JSValue obj ;
248+ JSValue global , obj , ret ;
249+ qjs_njs_t * njs ;
232250
233- obj = JS_NewObject (ctx );
251+ global = JS_GetGlobalObject (ctx );
252+
253+ obj = JS_GetPropertyStr (ctx , global , "njs" );
254+ if (JS_IsException (obj ) || JS_IsUndefined (obj )) {
255+ goto done ;
256+ }
257+
258+ njs = JS_GetOpaque (obj , QJS_CORE_CLASS_ID_NJS );
259+ if (njs != NULL && JS_IsFunction (ctx , njs -> hooks [QJS_NJS_HOOK_EXIT ])) {
260+ ret = JS_Call (ctx , njs -> hooks [QJS_NJS_HOOK_EXIT ], JS_UNDEFINED ,
261+ 0 , NULL );
262+
263+ JS_FreeValue (ctx , njs -> hooks [QJS_NJS_HOOK_EXIT ]);
264+ njs -> hooks [QJS_NJS_HOOK_EXIT ] = JS_UNDEFINED ;
265+
266+ if (JS_IsException (ret )) {
267+ JS_FreeValue (ctx , obj );
268+ JS_FreeValue (ctx , global );
269+ return ret ;
270+ }
271+
272+ JS_FreeValue (ctx , ret );
273+ }
274+
275+ done :
276+
277+ JS_FreeValue (ctx , obj );
278+ JS_FreeValue (ctx , global );
279+
280+ return JS_UNDEFINED ;
281+ }
282+
283+
284+ static int
285+ qjs_add_intrinsic_njs (JSContext * cx , JSValueConst global )
286+ {
287+ JSValue obj , proto ;
288+
289+ if (JS_NewClass (JS_GetRuntime (cx ), QJS_CORE_CLASS_ID_NJS ,
290+ & qjs_njs_class ) < 0 )
291+ {
292+ return -1 ;
293+ }
294+
295+ proto = JS_NewObject (cx );
296+ if (JS_IsException (proto )) {
297+ return -1 ;
298+ }
299+
300+ JS_SetPropertyFunctionList (cx , proto , qjs_njs_proto ,
301+ njs_nitems (qjs_njs_proto ));
302+
303+ JS_SetClassProto (cx , QJS_CORE_CLASS_ID_NJS , proto );
304+
305+ obj = JS_NewObjectClass (cx , QJS_CORE_CLASS_ID_NJS );
234306 if (JS_IsException (obj )) {
307+ return -1 ;
308+ }
309+
310+ if (JS_SetPropertyStr (cx , global , "njs" , obj ) < 0 ) {
311+ JS_FreeValue (cx , obj );
312+ return -1 ;
313+ }
314+
315+ return 0 ;
316+ }
317+
318+
319+ static void
320+ qjs_njs_mark (JSRuntime * rt , JSValueConst val , JS_MarkFunc * mark_func )
321+ {
322+ unsigned i ;
323+ qjs_njs_t * njs ;
324+
325+ njs = JS_GetOpaque (val , QJS_CORE_CLASS_ID_NJS );
326+ if (njs != NULL ) {
327+ for (i = 0 ; i < njs_nitems (njs -> hooks ); i ++ ) {
328+ JS_MarkValue (rt , njs -> hooks [i ], mark_func );
329+ }
330+ }
331+ }
332+
333+
334+ static void
335+ qjs_njs_finalizer (JSRuntime * rt , JSValue val )
336+ {
337+ unsigned i ;
338+ qjs_njs_t * njs ;
339+
340+ njs = JS_GetOpaque (val , QJS_CORE_CLASS_ID_NJS );
341+ if (njs != NULL ) {
342+ for (i = 0 ; i < njs_nitems (njs -> hooks ); i ++ ) {
343+ JS_FreeValueRT (rt , njs -> hooks [i ]);
344+ }
345+
346+ js_free_rt (rt , njs );
347+ }
348+ }
349+
350+
351+
352+ static JSValue
353+ qjs_njs_on (JSContext * ctx , JSValueConst this_val , int argc ,
354+ JSValueConst * argv )
355+ {
356+ unsigned i , n ;
357+ qjs_njs_t * njs ;
358+ njs_str_t name ;
359+
360+ static const njs_str_t hooks [] = {
361+ njs_str ("exit" ),
362+ };
363+
364+ njs = JS_GetOpaque (this_val , QJS_CORE_CLASS_ID_NJS );
365+ if (njs == NULL ) {
366+ njs = js_mallocz (ctx , sizeof (qjs_njs_t ));
367+ if (njs == NULL ) {
368+ return JS_ThrowOutOfMemory (ctx );
369+ }
370+
371+ JS_SetOpaque (this_val , njs );
372+ }
373+
374+ name .start = (u_char * ) JS_ToCStringLen (ctx , & name .length , argv [0 ]);
375+ if (name .start == NULL ) {
235376 return JS_EXCEPTION ;
236377 }
237378
238- JS_SetPropertyFunctionList ( ctx , obj , qjs_njs_proto ,
239- njs_nitems (qjs_njs_proto ) );
379+ i = 0 ;
380+ n = njs_nitems (hooks );
240381
241- return obj ;
382+ while (i < n ) {
383+ if (njs_strstr_eq (& name , & hooks [i ])) {
384+ break ;
385+ }
386+
387+ i ++ ;
388+ }
389+
390+ if (i == n ) {
391+ JS_ThrowTypeError (ctx , "unknown hook \"%s\"" , name .start );
392+ JS_FreeCString (ctx , (const char * ) name .start );
393+ return JS_EXCEPTION ;
394+ }
395+
396+ JS_FreeCString (ctx , (const char * ) name .start );
397+
398+ if (!JS_IsFunction (ctx , argv [1 ]) && !JS_IsNull (argv [1 ])) {
399+ JS_ThrowTypeError (ctx , "callback is not a function or null" );
400+ return JS_EXCEPTION ;
401+ }
402+
403+ JS_FreeValue (ctx , njs -> hooks [i ]);
404+ njs -> hooks [i ] = JS_DupValue (ctx , argv [1 ]);
405+
406+ return JS_UNDEFINED ;
242407}
243408
244409
0 commit comments