9797# '
9898# ' A module which has not declared any exports is treated as a \emph{legacy
9999# ' module} and exports \emph{all} default-visible names (that is, all names that
100- # ' do not start with a dot (\code{.}). This usage is present only for backwards
100+ # ' do not start with a dot (\code{.})) . This usage is present only for backwards
101101# ' compatibility with plain \R scripts, and its usage is \emph{not recommended}
102102# ' when writing new modules.
103103# '
@@ -314,12 +314,15 @@ use_one = function (declaration, alias, caller, use_call) {
314314# ' @param info the physical module information
315315# ' @rdname importing
316316load_and_register = function (spec , info , caller ) {
317- mod_ns = load_mod(info )
317+ ret = load_mod(info )
318+ mod_ns = ret $ mod_ns
319+ is_apex = ret $ is_apex
320+ on.exit(lock_all_environments(is_apex ))
318321 register_as_import(spec , info , mod_ns , caller )
319322
320323 if (is_mod_still_loading(info )) {
321324 # Module was cached but hasn’t fully loaded yet. This happens for
322- # cyclic imports (1. A -> 2. B -> 3. A)) in step (3). To proceed, we
325+ # cyclic imports (1. A -> 2. B -> 3. A) in step (3). To proceed, we
323326 # take note of the issue and wait until we bounce back to step (1) to
324327 # perform deferred finalization.
325328
@@ -374,7 +377,7 @@ export_and_attach = function (spec, info, mod_ns, caller) {
374377 finalize_deferred(info )
375378
376379 mod_exports = mod_exports(info , spec , mod_ns )
377- lockEnvironment (mod_exports , bindings = TRUE )
380+ defer_locking (mod_exports )
378381
379382 assign_alias(spec , mod_exports , caller )
380383 attach_to_caller(spec , info , mod_exports , mod_ns , caller )
@@ -398,38 +401,50 @@ load_from_source = function (info, mod_ns) {
398401 make_S3_methods_known(mod_ns )
399402}
400403
401- # ' @return \code{load_mod} returns the module or package namespace environment
402- # ' of the specified module or package info.
404+ # ' @return \code{load_mod} returns a named \code{list(mod_ns, is_apex)}
405+ # ' containing the module or package namespace environment of the specified
406+ # ' module or package info, as well as a flag specifying whether the loaded
407+ # ' module is an “apex” module, i.e. not loaded from within another module. This
408+ # ' information is then used to lock all environments that were created during
409+ # ' the loading. — This must happen after nested modules are fully loaded, since
410+ # ' deferred finalization otherwise cannot modify these environments during
411+ # ' cyclic imports.
403412# ' @rdname importing
404413load_mod = function (info ) {
405414 UseMethod(' load_mod' )
406415}
407416
408417`load_mod.box$mod_info` = function (info ) {
409- if (is_mod_loaded(info )) return (loaded_mod(info ))
418+ if (is_mod_loaded(info )) {
419+ return (list (mod_ns = loaded_mod(info ), is_apex = is_at_apex()))
420+ }
410421
411422 # Load module/package and dependencies; register the module now, to allow
412423 # cyclic imports without recursing indefinitely — but deregister upon
413424 # failure to load.
414- on.exit(deregister_mod(info ))
425+
426+ on.exit({
427+ deregister_mod(info )
428+ lock_all_environments(is_apex )
429+ })
415430
416431 mod_ns = make_namespace(info )
432+ is_apex = defer_locking(mod_ns )
417433 register_mod(info , mod_ns )
418434 load_from_source(info , mod_ns )
419435 mod_loading_finished(info , mod_ns )
420436
421437 # Call `.on_load` hook just after loading is finished but before exporting
422438 # symbols, so that `.on_load` can modify these symbols.
423439 call_hook(mod_ns , ' .on_load' , mod_ns )
424- lockEnvironment(mod_ns , bindings = TRUE )
425440
426441 on.exit()
427- mod_ns
442+ list ( mod_ns = mod_ns , is_apex = is_apex )
428443}
429444
430445`load_mod.box$pkg_info` = function (info ) {
431446 pkg = info $ name
432- base :: .getNamespace(pkg ) %|| % loadNamespace(pkg )
447+ list ( mod_ns = base :: .getNamespace(pkg ) %|| % loadNamespace(pkg ), is_apex = is_at_apex() )
433448}
434449
435450# ' @return \code{mod_exports} returns an export environment containing the
@@ -517,6 +532,7 @@ assign_temp_alias = function (spec, caller) {
517532 create_mod_alias = is.null(spec $ attach ) || spec $ explicit
518533 if (! create_mod_alias ) return ()
519534
535+ self = environment()
520536 callers = list ()
521537
522538 binding = function (mod_exports ) {
@@ -527,25 +543,47 @@ assign_temp_alias = function (spec, caller) {
527543 sys.calls()
528544 )), 1L )
529545 frame = sys.frame(mod_exports_frame_index )
530- env = frame $ env
531- assign(' callers' , append(callers , env ), envir = parent.env(environment()))
546+ self $ callers = c(callers , frame $ env )
532547
533548 # FIXME: Do we need to create transitive placeholder active bindings?
534- structure(list (), class = ' placeholder' )
549+ structure(list (), class = ' box$ placeholder' )
535550 } else {
536551 # Resolve assignments
537552 for (env in callers ) {
538- box_unlock_binding(spec $ alias , env )
539553 assign(spec $ alias , mod_exports , envir = env )
540- lockBinding( spec $ alias , env )
554+ defer_locking( env )
541555 }
556+
542557 # Replace myself
543- unlock_environment(caller )
544558 rm(list = spec $ alias , envir = caller )
545559 assign(spec $ alias , mod_exports , envir = caller )
546- lockEnvironment(caller )
547560 }
548561 }
549562
550563 makeActiveBinding(spec $ alias , structure(binding , class = ' box$placeholder' ), caller )
551564}
565+
566+ lock_info = new.env(parent = emptyenv())
567+ lock_info $ envs = list ()
568+
569+ defer_locking = function (ns ) {
570+ is_apex = is_at_apex()
571+ lock_info $ envs = c(lock_info $ envs , list (ns ))
572+ is_apex
573+ }
574+
575+ is_at_apex = function () {
576+ length(lock_info $ envs ) == 0L
577+ }
578+
579+ lock_all_environments = function (is_apex ) {
580+ if (! is_apex ) {
581+ return ()
582+ }
583+
584+ for (ns in lock_info $ envs ) {
585+ lockEnvironment(ns , bindings = TRUE )
586+ }
587+
588+ lock_info $ envs = list ()
589+ }
0 commit comments