-
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace TracePoint with const_added for explicit namespaces
- Loading branch information
Showing
13 changed files
with
101 additions
and
249 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# frozen_string_literal: true | ||
|
||
module Zeitwerk::ConstAdded | ||
def const_added(cname) | ||
if loader = Zeitwerk::ExplicitNamespace.__loader_for(self, cname) | ||
loader.on_namespace_loaded(const_get(cname, false)) | ||
end | ||
super | ||
end | ||
|
||
Module.prepend(self) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,84 @@ | ||
# frozen_string_literal: true | ||
|
||
module Zeitwerk | ||
# Centralizes the logic for the trace point used to detect the creation of | ||
# explicit namespaces, needed to descend into matching subdirectories right | ||
# after the constant has been defined. | ||
# Centralizes the logic needed to descend into matching subdirectories right | ||
# after the constant for an explicit namespace has been defined. | ||
# | ||
# The implementation assumes an explicit namespace is managed by one loader. | ||
# Loaders that reopen namespaces owned by other projects are responsible for | ||
# loading their constant before setup. This is documented. | ||
module ExplicitNamespace # :nodoc: all | ||
# Maps cpaths of explicit namespaces with their corresponding loader. | ||
# Entries are added as the namespaces are found, and removed as they are | ||
# autoloaded. | ||
# | ||
# @sig Hash[String => Zeitwerk::Loader] | ||
@cpaths = {} | ||
|
||
class << self | ||
include RealModName | ||
extend Internal | ||
|
||
# Maps constant paths that correspond to explicit namespaces according to | ||
# the file system, to the loader responsible for them. | ||
# | ||
# @sig Hash[String, Zeitwerk::Loader] | ||
attr_reader :cpaths | ||
private :cpaths | ||
|
||
# @sig Mutex | ||
attr_reader :mutex | ||
private :mutex | ||
|
||
# @sig TracePoint | ||
attr_reader :tracer | ||
private :tracer | ||
|
||
# Asserts `cpath` corresponds to an explicit namespace for which `loader` | ||
# is responsible. | ||
# Registers `cpath` as being the constant path of an explicit namespace | ||
# managed by `loader`. | ||
# | ||
# @sig (String, Zeitwerk::Loader) -> void | ||
internal def register(cpath, loader) | ||
mutex.synchronize do | ||
cpaths[cpath] = loader | ||
# We check enabled? because, looking at the C source code, enabling an | ||
# enabled tracer does not seem to be a simple no-op. | ||
tracer.enable unless tracer.enabled? | ||
end | ||
@cpaths[cpath] = loader | ||
end | ||
|
||
# @sig (String) -> Zeitwerk::Loader? | ||
internal def loader_for(mod, cname) | ||
cpath = mod.equal?(Object) ? cname.name : "#{real_mod_name(mod)}::#{cname}" | ||
@cpaths.delete(cpath) | ||
end | ||
|
||
# @sig (Zeitwerk::Loader) -> void | ||
internal def unregister_loader(loader) | ||
cpaths.delete_if { |_cpath, l| l == loader } | ||
disable_tracer_if_unneeded | ||
@cpaths.delete_if { _2.equal?(loader) } | ||
end | ||
|
||
# This is an internal method only used by the test suite. | ||
# | ||
# @sig (String) -> bool | ||
internal def registered?(cpath) | ||
cpaths.key?(cpath) | ||
@cpaths[cpath] | ||
end | ||
|
||
# This is an internal method only used by the test suite. | ||
# | ||
# @sig () -> void | ||
private def disable_tracer_if_unneeded | ||
mutex.synchronize do | ||
tracer.disable if cpaths.empty? | ||
end | ||
internal def clear | ||
@cpaths.clear | ||
end | ||
|
||
# @sig (TracePoint) -> void | ||
private def tracepoint_class_callback(event) | ||
# If the class is a singleton class, we won't do anything with it so we | ||
# can bail out immediately. This is several orders of magnitude faster | ||
# than accessing its name. | ||
return if event.self.singleton_class? | ||
module Synchronized | ||
extend Internal | ||
|
||
MUTEX = Mutex.new | ||
|
||
# It might be tempting to return if name.nil?, to avoid the computation | ||
# of a hash code and delete call. But Ruby does not trigger the :class | ||
# event on Class.new or Module.new, so that would incur in an extra call | ||
# for nothing. | ||
# | ||
# On the other hand, if we were called, cpaths is not empty. Otherwise | ||
# the tracer is disabled. So we do need to go ahead with the hash code | ||
# computation and delete call. | ||
if loader = cpaths.delete(real_mod_name(event.self)) | ||
loader.on_namespace_loaded(event.self) | ||
disable_tracer_if_unneeded | ||
internal def register(...) | ||
MUTEX.synchronize { super } | ||
end | ||
end | ||
end | ||
|
||
@cpaths = {} | ||
@mutex = Mutex.new | ||
internal def loader_for(...) | ||
MUTEX.synchronize { super } | ||
end | ||
|
||
internal def unregister_loader(...) | ||
MUTEX.synchronize { super } | ||
end | ||
|
||
# We go through a method instead of defining a block mainly to have a better | ||
# label when profiling. | ||
@tracer = TracePoint.new(:class, &method(:tracepoint_class_callback)) | ||
internal def registered?(...) | ||
MUTEX.synchronize { super } | ||
end | ||
|
||
internal def clear | ||
MUTEX.synchronize { super } | ||
end | ||
end | ||
|
||
prepend Synchronized unless RUBY_ENGINE == "ruby" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.