@@ -2640,7 +2640,48 @@ function _require_from_serialized(uuidkey::PkgId, path::String, ocachepath::Unio
26402640end
26412641
26422642# load a serialized file directly from append_bundled_depot_path for uuidkey without stalechecks
2643- function require_stdlib (package_uuidkey:: PkgId , ext:: Union{Nothing, String} = nothing )
2643+ """
2644+ require_stdlib(package_uuidkey::PkgId, [ext::String, from::Module])
2645+
2646+ !!! warning "May load duplicate copies of stdlib packages."
2647+
2648+ This requires that all stdlib packages loaded are compatible with having concurrent
2649+ copies of themselves loaded into memory. It also places additional restrictions on
2650+ the kinds of type-piracy that are allowed in stdlibs, since type-piracy can cause the
2651+ dispatch table to become visibly "torn" across multiple different packages.
2652+
2653+ The specific requirements are:
2654+
2655+ The import side (caller of `require_stdlib`) must not leak any stdlib types, esp.
2656+ to any context that may have a conflicting copy of the stdlib(s) (or vice-versa).
2657+ - e.g., if an output is forwarded to user code, it must contain only Base types.
2658+ - e.g., if an output contains types from the stdlib, it must be consumed "internally"
2659+ before reaching user code.
2660+
2661+ The imported code (loaded stdlibs) must be very careful about type piracy:
2662+ - It must not access any global state that may differ between stdlib copies in
2663+ type-pirated methods.
2664+ - It must not return any stdlib types from any type-pirated public methods (since
2665+ a loaded duplicate would overwrite the Base method again, returning different
2666+ types that don't correspond to the user-accessible copy of the stdlib).
2667+ - It must not pass / discriminate stdlib types in type-pirated methods, except
2668+ indirectly via methods defined in Base and implemented (w/o type-piracy) in
2669+ all copies of the stdlib over their respective types.
2670+
2671+ The idea behind the above restrictions is that any type-pirated methods in the stdlib
2672+ must return a result that is simultaneously correct for all of the stdlib's loaded
2673+ copies, including accounting for global state differences and split type identities.
2674+
2675+ Furthermore, any imported code must not leak any stdlib types to globals and containers
2676+ (e.g. Vectors and mutable structs) in upstream Modules, since this will also lead to
2677+ type-confusion when the type is later pulled out in user / stdlib code.
2678+
2679+ For examples of issues like the above, see:
2680+ [1] https://github.com/JuliaLang/Pkg.jl/issues/4017#issuecomment-2377589989
2681+ [2] https://github.com/JuliaLang/StyledStrings.jl/issues/91#issuecomment-2379602914
2682+ """
2683+ require_stdlib (package_uuidkey:: PkgId ) = require_stdlib (package_uuidkey, nothing , Base)
2684+ function require_stdlib (package_uuidkey:: PkgId , ext:: Union{Nothing, String} , from:: Module )
26442685 if generating_output (#= incremental=# true )
26452686 # Otherwise this would lead to awkward dependency issues by loading a package that isn't in the Project/Manifest
26462687 error (" This interactive function requires a stdlib to be loaded, and package code should instead use it directly from that stdlib." )
@@ -2652,35 +2693,33 @@ function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=noth
26522693 if newm isa Module
26532694 return newm
26542695 end
2655- # first since this is a stdlib, try to look there directly first
26562696 env = Sys. STDLIB
2657- # sourcepath = ""
2658- if ext === nothing
2659- sourcepath = normpath (env, this_uuidkey. name, " src" , this_uuidkey. name * " .jl" )
2660- else
2661- sourcepath = find_ext_path (normpath (joinpath (env, package_uuidkey. name)), ext)
2662- end
2663- # mbypath = manifest_uuid_path(env, this_uuidkey)
2664- # if mbypath isa String && isfile_casesensitive(mbypath)
2665- # sourcepath = mbypath
2666- # else
2667- # # if the user deleted the stdlib folder, we next try using their environment
2668- # sourcepath = locate_package_env(this_uuidkey)
2669- # if sourcepath !== nothing
2670- # sourcepath, env = sourcepath
2671- # end
2672- # end
2673- # if sourcepath === nothing
2674- # throw(ArgumentError("""
2675- # Package $(repr("text/plain", this_uuidkey)) is required but does not seem to be installed.
2676- # """))
2677- # end
2678- set_pkgorigin_version_path (this_uuidkey, sourcepath)
2679- depot_path = append_bundled_depot_path! (empty (DEPOT_PATH ))
2680- newm = start_loading (this_uuidkey, UInt128 (0 ), true )
2681- newm === nothing || return newm
26822697 try
2683- newm = _require_search_from_serialized (this_uuidkey, sourcepath, UInt128 (0 ), false ; DEPOT_PATH = depot_path)
2698+ depot_path = append_bundled_depot_path! (empty (DEPOT_PATH ))
2699+ from_stdlib = true # set to false if `from` is a normal package so we do not want the internal loader for the extension either
2700+ if ext isa String
2701+ from_uuid = PkgId (from)
2702+ from_m = get (loaded_modules, from_uuid, nothing )
2703+ if from_m === from
2704+ # if from_uuid is either nothing or points to something else, assume we should use require_stdlib
2705+ # otherwise check cachepath for from to see if it looks like it is from depot_path, since try_build_ids
2706+ cachepath = get (PkgOrigin, pkgorigins, from_uuid). cachepath
2707+ entrypath, entryfile = cache_file_entry (from_uuid)
2708+ from_stdlib = any (x -> startswith (entrypath, x), depot_path)
2709+ end
2710+ end
2711+ if from_stdlib
2712+ # first since this is a stdlib, try to look there directly first
2713+ if ext === nothing
2714+ sourcepath = normpath (env, this_uuidkey. name, " src" , this_uuidkey. name * " .jl" )
2715+ else
2716+ sourcepath = find_ext_path (normpath (joinpath (env, package_uuidkey. name)), ext)
2717+ end
2718+ set_pkgorigin_version_path (this_uuidkey, sourcepath)
2719+ newm = start_loading (this_uuidkey, UInt128 (0 ), true )
2720+ newm === nothing || return newm
2721+ newm = _require_search_from_serialized (this_uuidkey, sourcepath, UInt128 (0 ), false ; DEPOT_PATH = depot_path)
2722+ end
26842723 finally
26852724 end_loading (this_uuidkey, newm)
26862725 end
@@ -2690,10 +2729,12 @@ function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=noth
26902729 run_package_callbacks (this_uuidkey)
26912730 else
26922731 # if the user deleted their bundled depot, next try to load it completely normally
2732+ # if it is an extension, we first need to indicate where to find its parant via EXT_PRIMED
2733+ ext isa String && (EXT_PRIMED[this_uuidkey] = PkgId[package_uuidkey])
26932734 newm = _require_prelocked (this_uuidkey)
26942735 end
26952736 return newm
2696- end
2737+ end # release lock
26972738end
26982739
26992740
0 commit comments