Skip to content

Commit b1fa39e

Browse files
authored
Merge pull request #46 from JuliaLang/c42f/core-hook-fixes
Reduce invalidation/recompliation for parser usage in sysimage
2 parents ffd3e47 + 04a3dfd commit b1fa39e

File tree

1 file changed

+71
-25
lines changed

1 file changed

+71
-25
lines changed

src/hooks.jl

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,36 @@
1-
_debug_log = nothing
1+
# This file provides an adaptor to match the API expected by the Julia runtime
2+
# code in the binding Core._parse
3+
4+
# Use caller's world age.
5+
const _caller_world = typemax(UInt)
6+
const _parser_world_age = Ref{UInt}(_caller_world)
27

3-
# Adaptor for the API/ABI expected by the Julia runtime code.
48
function core_parser_hook(code, filename, lineno, offset, options)
9+
# `hook` is always _core_parser_hook, but that's hidden from the compiler
10+
# via a Ref to prevent invalidation / recompilation when other packages are
11+
# loaded. This wouldn't seem like it should be necessary given the use of
12+
# invoke_in_world, but it is in Julia-1.7.3. I'm not sure exactly which
13+
# latency it's removing.
14+
hook = _core_parser_hook_ref[]
15+
if _parser_world_age[] != _caller_world
16+
Base.invoke_in_world(_parser_world_age[], hook,
17+
code, filename, lineno, offset, options)
18+
else
19+
hook(code, filename, lineno, offset, options)
20+
end
21+
end
22+
23+
# Core._parse gained a `lineno` argument in
24+
# https://github.com/JuliaLang/julia/pull/43876
25+
# Prior to this, the following signature was needed:
26+
function core_parser_hook(code, filename, offset, options)
27+
core_parser_hook(code, filename, LineNumberNode(0), offset, options)
28+
end
29+
30+
# Debug log file for dumping parsed code
31+
const _debug_log = Ref{Union{Nothing,IO}}(nothing)
32+
33+
function _core_parser_hook(code, filename, lineno, offset, options)
534
try
635
# TODO: Check that we do all this input wrangling without copying the
736
# code buffer
@@ -10,13 +39,13 @@ function core_parser_hook(code, filename, lineno, offset, options)
1039
(ptr,len) = code
1140
code = String(unsafe_wrap(Array, ptr, len))
1241
end
13-
if !isnothing(_debug_log)
14-
print(_debug_log, """
42+
if !isnothing(_debug_log[])
43+
print(_debug_log[], """
1544
#-#-#-------------------------------
1645
# ENTER filename=$filename, lineno=$lineno, offset=$offset, options=$options"
1746
#-#-#-------------------------------
1847
""")
19-
write(_debug_log, code)
48+
write(_debug_log[], code)
2049
end
2150

2251
io = IOBuffer(code)
@@ -58,8 +87,8 @@ function core_parser_hook(code, filename, lineno, offset, options)
5887
# of one cancel here.
5988
last_offset = last_byte(stream)
6089

61-
if !isnothing(_debug_log)
62-
println(_debug_log, """
90+
if !isnothing(_debug_log[])
91+
println(_debug_log[], """
6392
#-#-#-
6493
# EXIT last_offset=$last_offset
6594
#-#-#-
@@ -69,42 +98,59 @@ function core_parser_hook(code, filename, lineno, offset, options)
6998
# Rewrap result in an svec for use by the C code
7099
return Core.svec(ex, last_offset)
71100
catch exc
101+
if !isnothing(_debug_log[])
102+
println(_debug_log[], """
103+
#-#-#-
104+
# ERROR EXIT
105+
# $exc
106+
#-#-#-
107+
""")
108+
end
72109
@error("JuliaSyntax parser failed — falling back to flisp!",
73110
exception=(exc,catch_backtrace()),
74111
offset=offset,
75112
code=code)
76-
end
77-
return Core.Compiler.fl_parse(code, filename, offset, options)
78-
end
79113

80-
# Core._parse gained a `lineno` argument in
81-
# https://github.com/JuliaLang/julia/pull/43876
82-
# Prior to this, the following signature was needed:
83-
function core_parser_hook(code, filename, offset, options)
84-
core_parser_hook(code, filename, LineNumberNode(0), offset, options)
114+
if VERSION >= v"1.8.0-DEV.1370" # https://github.com/JuliaLang/julia/pull/43876
115+
return Core.Compiler.fl_parse(code, filename, lineno, offset, options)
116+
else
117+
return Core.Compiler.fl_parse(code, filename, offset, options)
118+
end
119+
end
85120
end
86121

87122
# Hack:
88123
# Meta.parse() attempts to construct a ParseError from a string if it receives
89-
# `Expr(:error)`.
124+
# `Expr(:error)`. Add an override to the ParseError constructor to prevent this.
125+
# FIXME: Improve this in Base somehow?
90126
Base.Meta.ParseError(e::JuliaSyntax.ParseError) = e
91127

92128
const _default_parser = Core._parse
129+
# NB: Never reassigned, but the compiler doesn't know this!
130+
const _core_parser_hook_ref = Ref{Function}(_core_parser_hook)
93131

94132
"""
133+
enable_in_core!([enable=true; freeze_world_age, debug_filename])
134+
95135
Connect the JuliaSyntax parser to the Julia runtime so that it replaces the
96-
flisp parser for all parsing work.
136+
flisp parser for all parsing work. That is, JuliaSyntax will be used for
137+
`include()` `Meta.parse()`, the REPL, etc. To disable, set use
138+
`enable_in_core!(false)`.
97139
98-
That is, JuliaSyntax will be used for `include()` `Meta.parse()`, the REPL, etc.
140+
Keyword arguments:
141+
* `freeze_world_age` - Use a fixed world age for the parser to prevent
142+
recompilation of the parser due to any user-defined methods (default `true`).
143+
* `debug_filename` - File name of parser debug log (defaults to `nothing` or
144+
the value of `ENV["JULIA_SYNTAX_DEBUG_FILE"]`).
99145
"""
100-
function enable_in_core!(enable=true)
101-
debug_filename = get(ENV, "JULIA_SYNTAX_DEBUG_FILE", nothing)
102-
global _debug_log
146+
function enable_in_core!(enable=true; freeze_world_age = true,
147+
debug_filename = get(ENV, "JULIA_SYNTAX_DEBUG_FILE", nothing))
148+
_parser_world_age[] = freeze_world_age ? Base.get_world_counter() : _caller_world
103149
if enable && !isnothing(debug_filename)
104-
_debug_log = open(debug_filename, "w")
105-
elseif !enable && !isnothing(_debug_log)
106-
close(_debug_log)
107-
_debug_log = nothing
150+
_debug_log[] = open(debug_filename, "w")
151+
elseif !enable && !isnothing(_debug_log[])
152+
close(_debug_log[])
153+
_debug_log[] = nothing
108154
end
109155
parser = enable ? core_parser_hook : _default_parser
110156
Base.eval(Core, :(_parse = $parser))

0 commit comments

Comments
 (0)