require-hooks 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/require-hooks/mode/bootsnap.rb +80 -42
- data/lib/require-hooks/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9357c7e14301ad7ab541c2e2fabbda1f03df56e748aee55245dfc44ef98519f4
|
|
4
|
+
data.tar.gz: 87b05cf3ec16a3c9c4e9c1a46a3ec267ca03717923fe8abdccd4e46deb212791
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9e828449e13517a11c03d9521c4170507f08862e6a4e75fa01360a2c17191dbecfa3a745a1433bd7546f190c1599f17be7a4dc76869bbd798fd1e47e859a130
|
|
7
|
+
data.tar.gz: e1e80635ac8c7ceb90c1580797145c3c15636d463635f3ccf57578024619af150f4dbe94eb279aff7dc62c231ed1de3733b5db3e7a1b38a21002e8ef0ca8a063
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -124,7 +124,7 @@ require "require-hooks/setup"
|
|
|
124
124
|
|
|
125
125
|
The _around load_ hooks are executed for all files independently of whether they are cached or not. Source transformation and hijacking is only done for non-cached files.
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
Require Hooks separates cached instructions by hook configuration. New source transformers and hijackers invalidate affected cache entries automatically.
|
|
128
128
|
|
|
129
129
|
## Limitations
|
|
130
130
|
|
|
@@ -28,75 +28,109 @@ module RequireHooks
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
#
|
|
32
|
-
module
|
|
33
|
-
def
|
|
31
|
+
# Bootsnap before 1.24 does not support compiler namespaces.
|
|
32
|
+
module LegacyCacheExt
|
|
33
|
+
def fetch(path, cache_dir: self.cache_dir)
|
|
34
34
|
ctx = RequireHooks.context_for(path)
|
|
35
|
-
|
|
35
|
+
cache_dir = File.join(cache_dir, RequireHooks::Bootsnap.version_hash) unless ctx.empty?
|
|
36
|
+
|
|
37
|
+
super
|
|
38
|
+
end
|
|
39
|
+
end
|
|
36
40
|
|
|
41
|
+
# Bootsnap 1.24+ compiler wrapper. Its namespace isolates transformed ISeqs
|
|
42
|
+
# without changing Bootsnap's process-wide cache directory.
|
|
43
|
+
class VersionedCompiler
|
|
44
|
+
attr_reader :namespace
|
|
45
|
+
|
|
46
|
+
def initialize(compiler)
|
|
47
|
+
@compiler = compiler
|
|
48
|
+
@namespace = "#{compiler.namespace}-require-hooks-#{RequireHooks::Bootsnap.version_hash}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def input_to_storage(source, path, *args)
|
|
52
|
+
iseq = compile(RequireHooks.context_for(path), path, nil)
|
|
53
|
+
return @compiler.input_to_storage(source, path, *args) unless iseq
|
|
54
|
+
|
|
55
|
+
iseq.to_binary
|
|
56
|
+
rescue SyntaxError, TypeError
|
|
57
|
+
::Bootsnap::CompileCache::UNCOMPILABLE
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def storage_to_output(*args)
|
|
61
|
+
@compiler.storage_to_output(*args)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def input_to_output(source, path, args)
|
|
65
|
+
compile(RequireHooks.context_for(path), path, args) || @compiler.input_to_output(source, path, args)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def compile(ctx, path, args)
|
|
37
71
|
new_contents = ctx.perform_source_transform(path)
|
|
38
72
|
hijacked = ctx.try_hijack_load(path, new_contents)
|
|
39
73
|
|
|
40
74
|
if hijacked
|
|
41
|
-
raise TypeError, "Unsupported bytecode format for #{path}: #{
|
|
42
|
-
return hijacked
|
|
43
|
-
elsif new_contents
|
|
44
|
-
return RubyVM::InstructionSequence.compile(new_contents, path, path, 1, @compile_options).to_binary
|
|
75
|
+
raise TypeError, "Unsupported bytecode format for #{path}: #{hijacked.class}" unless hijacked.is_a?(::RubyVM::InstructionSequence)
|
|
76
|
+
return hijacked
|
|
45
77
|
end
|
|
46
78
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
79
|
+
return unless new_contents
|
|
80
|
+
|
|
81
|
+
@compiler.input_to_output(new_contents, path, args) ||
|
|
82
|
+
RubyVM::InstructionSequence.compile(new_contents, path, path, 1)
|
|
50
83
|
end
|
|
51
84
|
end
|
|
52
85
|
|
|
53
86
|
module LoadIseqExt
|
|
54
|
-
class << self
|
|
55
|
-
attr_accessor :orig_cache_dir
|
|
56
|
-
end
|
|
57
|
-
|
|
58
87
|
# Around hooks must be performed every time we trigger a file load, even if
|
|
59
88
|
# the file is already cached.
|
|
60
89
|
def load_iseq(path)
|
|
61
90
|
ctx = RequireHooks.context_for(path)
|
|
62
|
-
|
|
63
|
-
if ctx.empty?
|
|
64
|
-
::Bootsnap::CompileCache::ISeq.cache_dir = LoadIseqExt.orig_cache_dir if LoadIseqExt.orig_cache_dir
|
|
65
|
-
return super
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
LoadIseqExt.orig_cache_dir ||= ::Bootsnap::CompileCache::ISeq.cache_dir
|
|
69
|
-
::Bootsnap::CompileCache::ISeq.cache_dir = File.join(LoadIseqExt.orig_cache_dir, RequireHooks::Bootsnap.version_hash)
|
|
91
|
+
return super if ctx.empty?
|
|
70
92
|
|
|
71
93
|
ctx.run_around_load_callbacks(path) do
|
|
72
|
-
|
|
73
|
-
iseq = super
|
|
94
|
+
iseq = super
|
|
74
95
|
|
|
75
|
-
|
|
96
|
+
# Bootsnap returns nil when the coverage is on,
|
|
97
|
+
# we fallback to our custom #compile_with_coverage
|
|
98
|
+
unless iseq
|
|
99
|
+
next unless defined?(Coverage) && Coverage.running?
|
|
76
100
|
|
|
77
|
-
|
|
78
|
-
# we fallback to our custom #compile_with_coverage
|
|
79
|
-
unless iseq
|
|
80
|
-
next unless defined?(Coverage) && Coverage.running?
|
|
81
|
-
|
|
82
|
-
iseq = RequireHooks::Iseq.compile_with_coverage(ctx, path)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
iseq.eval
|
|
86
|
-
EMPTY_ISEQ
|
|
87
|
-
ensure
|
|
88
|
-
::Bootsnap::CompileCache::ISeq.cache_dir = LoadIseqExt.orig_cache_dir
|
|
101
|
+
iseq = RequireHooks::Iseq.compile_with_coverage(ctx, path)
|
|
89
102
|
end
|
|
103
|
+
|
|
104
|
+
iseq.eval
|
|
105
|
+
EMPTY_ISEQ
|
|
90
106
|
end
|
|
91
107
|
end
|
|
92
108
|
end
|
|
93
109
|
|
|
94
110
|
class << self
|
|
111
|
+
def install_compiler_selector
|
|
112
|
+
iseq = ::Bootsnap::CompileCache::ISeq
|
|
113
|
+
@original_compiler_selector = iseq.compiler_selector
|
|
114
|
+
@compilers = {}.compare_by_identity
|
|
115
|
+
iseq.compiler_selector = method(:compiler_for)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def compiler_for(path)
|
|
119
|
+
iseq = ::Bootsnap::CompileCache::ISeq
|
|
120
|
+
compiler = @original_compiler_selector&.call(path) || iseq.default_compiler
|
|
121
|
+
return compiler if RequireHooks.context_for(path).empty?
|
|
122
|
+
|
|
123
|
+
@compilers[compiler] ||= VersionedCompiler.new(compiler)
|
|
124
|
+
end
|
|
125
|
+
|
|
95
126
|
def version_hash
|
|
96
|
-
@
|
|
127
|
+
@version_hash ||= RequireHooks.contexts.values.map(&:to_cache_key).join("-")
|
|
97
128
|
end
|
|
98
129
|
|
|
99
|
-
|
|
130
|
+
def version_hash=(version_hash)
|
|
131
|
+
@version_hash = version_hash
|
|
132
|
+
@compilers&.clear
|
|
133
|
+
end
|
|
100
134
|
end
|
|
101
135
|
end
|
|
102
136
|
|
|
@@ -111,6 +145,10 @@ module RequireHooks
|
|
|
111
145
|
end
|
|
112
146
|
end
|
|
113
147
|
|
|
114
|
-
Bootsnap::CompileCache::ISeq
|
|
115
|
-
|
|
148
|
+
if defined?(Bootsnap::CompileCache::ISeq::Compiler)
|
|
149
|
+
RequireHooks::Bootsnap.install_compiler_selector
|
|
150
|
+
else
|
|
151
|
+
Bootsnap::CompileCache::ISeq.singleton_class.prepend(RequireHooks::Bootsnap::CompileCacheExt)
|
|
152
|
+
Bootsnap::CompileCache::ISeq.singleton_class.prepend(RequireHooks::Bootsnap::LegacyCacheExt)
|
|
153
|
+
end
|
|
116
154
|
RubyVM::InstructionSequence.singleton_class.prepend(RequireHooks::Bootsnap::LoadIseqExt)
|