@@ -25,10 +25,18 @@ typedef struct ExtensionExposure {
2525 struct ExtensionExposure * next ;
2626} ExtensionExposure ;
2727
28+ typedef struct RegisteredOperator {
29+ char * name ;
30+ prefix_operator_fn fn ;
31+ int flags ;
32+ struct RegisteredOperator * next ;
33+ } RegisteredOperator ;
34+
2835typedef struct LoadedExtension {
2936 char * canonical_path ;
3037 DynLibHandle handle ;
3138 prefix_extension_init_fn init_fn ;
39+ RegisteredOperator * ops ;
3240 ExtensionExposure * exposures ;
3341 struct LoadedExtension * next ;
3442} LoadedExtension ;
@@ -38,6 +46,7 @@ static char* g_interpreter_dir = NULL;
3846static char * g_cwd_dir = NULL ;
3947static const char * g_loading_extension_name = NULL ;
4048static const char * g_loading_scope_name = NULL ;
49+ static LoadedExtension * g_current_loading_extension = NULL ;
4150
4251// Registered event handlers and periodic hooks
4352typedef struct EventHandler {
@@ -383,6 +392,24 @@ static LoadedExtension* loaded_find_by_path(const char* canonical_path) {
383392static int ctx_register_operator (const char * name , prefix_operator_fn fn , int flags ) {
384393 if (!name || name [0 ] == '\0' || !fn ) return -1 ;
385394
395+ /* Record the operator for the currently-loading extension so we can
396+ expose aliases later when the same extension is imported into other
397+ module scopes without reinitializing the library. */
398+ if (g_current_loading_extension ) {
399+ RegisteredOperator * ro = calloc (1 , sizeof (RegisteredOperator ));
400+ if (ro ) {
401+ ro -> name = strdup (name );
402+ if (ro -> name ) {
403+ ro -> fn = fn ;
404+ ro -> flags = flags ;
405+ ro -> next = g_current_loading_extension -> ops ;
406+ g_current_loading_extension -> ops = ro ;
407+ } else {
408+ free (ro );
409+ }
410+ }
411+ }
412+
386413 char * final_name = NULL ;
387414 if ((flags & PREFIX_EXTENSION_MODULE_RESTRICTED ) != 0 && g_loading_extension_name && g_loading_extension_name [0 ] != '\0' ) {
388415 const char * ext_name = g_loading_extension_name ;
@@ -528,16 +555,30 @@ static int extension_register_exposure(LoadedExtension* le,
528555 return -1 ;
529556 }
530557
531- (void )scope_name ;
532-
533- char * key = exposure_key_for (ext_name );
534- if (!key ) {
558+ char * base_key = exposure_key_for (ext_name );
559+ if (!base_key ) {
535560 set_error (error_out , "Out of memory" );
536561 return -1 ;
537562 }
538563
539- if (exposure_exists (le , key )) {
540- free (key );
564+ char * scope_key = NULL ;
565+ if (scope_name && scope_name [0 ] != '\0' ) {
566+ size_t s = strlen (scope_name );
567+ size_t e = strlen (ext_name );
568+ /* use ':' as an internal separator to form a unique exposure key */
569+ scope_key = malloc (s + 1 + e + 1 );
570+ if (!scope_key ) { free (base_key ); set_error (error_out , "Out of memory" ); return -1 ; }
571+ memcpy (scope_key , scope_name , s );
572+ scope_key [s ] = ':' ;
573+ memcpy (scope_key + s + 1 , ext_name , e );
574+ scope_key [s + 1 + e ] = '\0' ;
575+ }
576+
577+ int base_exists = exposure_exists (le , base_key );
578+ int scope_exists = scope_key ? exposure_exists (le , scope_key ) : 0 ;
579+ if (base_exists && (!scope_key || scope_exists )) {
580+ free (base_key );
581+ free (scope_key );
541582 return 0 ;
542583 }
543584
@@ -550,19 +591,58 @@ static int extension_register_exposure(LoadedExtension* le,
550591 ctx .register_event_handler = ctx_register_event_handler ;
551592 ctx .register_repl_handler = ctx_register_repl_handler ;
552593
553- g_loading_extension_name = ext_name ;
554- g_loading_scope_name = (scope_name && scope_name [0 ] != '\0' ) ? scope_name : NULL ;
555- le -> init_fn (& ctx );
556- g_loading_extension_name = NULL ;
557- g_loading_scope_name = NULL ;
594+ /* If this is the first time the extension is exposed (base), run its
595+ init function so it can register operators. Otherwise, the operators
596+ should already be recorded in le->ops and we only need to create
597+ scope-qualified aliases. */
598+ if (!base_exists ) {
599+ g_current_loading_extension = le ;
600+ g_loading_extension_name = ext_name ;
601+ g_loading_scope_name = (scope_name && scope_name [0 ] != '\0' ) ? scope_name : NULL ;
602+ le -> init_fn (& ctx );
603+ g_current_loading_extension = NULL ;
604+ g_loading_extension_name = NULL ;
605+ g_loading_scope_name = NULL ;
606+
607+ if (exposure_add (le , base_key ) != 0 ) {
608+ free (base_key );
609+ free (scope_key );
610+ set_error (error_out , "Out of memory" );
611+ return -1 ;
612+ }
613+ }
558614
559- if (exposure_add (le , key ) != 0 ) {
560- free (key );
561- set_error (error_out , "Out of memory" );
562- return -1 ;
615+ /* If a scope was provided and we haven't yet exposed the extension under
616+ that scope, create aliases for module-restricted operators. */
617+ if (scope_key && !scope_exists ) {
618+ for (RegisteredOperator * ro = le -> ops ; ro ; ro = ro -> next ) {
619+ if ((ro -> flags & PREFIX_EXTENSION_MODULE_RESTRICTED ) != 0 ) {
620+ size_t s = strlen (scope_name );
621+ size_t e = strlen (ext_name );
622+ size_t n = strlen (ro -> name );
623+ size_t total = s + 1 + e + 1 + n + 1 ;
624+ char * alias = malloc (total );
625+ if (!alias ) continue ;
626+ size_t p = 0 ;
627+ memcpy (alias + p , scope_name , s ); p += s ; alias [p ++ ] = '.' ;
628+ memcpy (alias + p , ext_name , e ); p += e ; alias [p ++ ] = '.' ;
629+ memcpy (alias + p , ro -> name , n ); p += n ; alias [p ] = '\0' ;
630+ /* ignore registration errors for aliases */
631+ builtins_register_operator (alias , (BuiltinImplFn )ro -> fn , 0 , -1 , NULL , 0 );
632+ free (alias );
633+ }
634+ }
635+
636+ if (exposure_add (le , scope_key ) != 0 ) {
637+ free (base_key );
638+ free (scope_key );
639+ set_error (error_out , "Out of memory" );
640+ return -1 ;
641+ }
563642 }
564643
565- free (key );
644+ free (base_key );
645+ free (scope_key );
566646 return 0 ;
567647}
568648
@@ -704,6 +784,14 @@ void extensions_shutdown(void) {
704784 ex = ex_next ;
705785 }
706786
787+ RegisteredOperator * ro = e -> ops ;
788+ while (ro ) {
789+ RegisteredOperator * rn = ro -> next ;
790+ free (ro -> name );
791+ free (ro );
792+ ro = rn ;
793+ }
794+
707795 dyn_close_library (e -> handle );
708796 free (e -> canonical_path );
709797 free (e );
0 commit comments