Skip to content

Commit 64b5afa

Browse files
authored
Clean up fixed world age mechanism to use closures (#290)
Using a closure here to hold the fixed world age seems nicer than a global within the JuliaSyntax module.
1 parent 200145f commit 64b5afa

File tree

2 files changed

+42
-36
lines changed

2 files changed

+42
-36
lines changed

src/hooks.jl

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ function _incomplete_tag(n::SyntaxNode, codelen)
7676
end
7777

7878
#-------------------------------------------------------------------------------
79-
if isdefined(Core, :_setparser!)
80-
const _set_core_parse_hook = Core._setparser!
81-
else
82-
function _set_core_parse_hook(parser)
79+
function _set_core_parse_hook(parser)
80+
@static if isdefined(Core, :_setparser!)
81+
Core._setparser!(parser)
82+
else
8383
# HACK! Fool the runtime into allowing us to set Core._parse, even during
8484
# incremental compilation. (Ideally we'd just arrange for Core._parse to be
8585
# set to the JuliaSyntax parser. But how do we signal that to the dumping
@@ -100,28 +100,23 @@ else
100100
end
101101
end
102102

103-
# Use caller's world age.
104-
const _latest_world = typemax(UInt)
105-
const _parser_world_age = Ref{UInt}(_latest_world)
106103

107-
function core_parser_hook(code, filename, lineno, offset, options)
108-
# NB: We need an inference barrier of one type or another here to prevent
109-
# invalidations. The invokes provide this currently.
110-
if _parser_world_age[] != _latest_world
111-
Base.invoke_in_world(_parser_world_age[], _core_parser_hook,
112-
code, filename, lineno, offset, options)
104+
# Wrap the function `f` so that it's always invoked in the given `world_age`
105+
#
106+
# NB: We need an inference barrier of one type or another here to prevent
107+
# invalidations. The invokes provide this currently.
108+
function fix_world_age(f, world_age::UInt)
109+
if world_age == typemax(UInt)
110+
function invoke_latestworld(args...; kws...)
111+
Base.invokelatest(f, args...; kws...)
112+
end
113113
else
114-
Base.invokelatest(_core_parser_hook, code, filename, lineno, offset, options)
114+
function invoke_fixedworld(args...; kws...)
115+
Base.invoke_in_world(world_age, f, args...; kws...)
116+
end
115117
end
116118
end
117119

118-
# Core._parse gained a `lineno` argument in
119-
# https://github.com/JuliaLang/julia/pull/43876
120-
# Prior to this, the following signature was needed:
121-
function core_parser_hook(code, filename, offset, options)
122-
core_parser_hook(code, filename, 1, offset, options)
123-
end
124-
125120
function _has_nested_error(ex)
126121
if ex isa Expr
127122
if ex.head == :error
@@ -137,7 +132,7 @@ end
137132
# Debug log file for dumping parsed code
138133
const _debug_log = Ref{Union{Nothing,IO}}(nothing)
139134

140-
function _core_parser_hook(code, filename::String, lineno::Int, offset::Int, options::Symbol)
135+
function core_parser_hook(code, filename::String, lineno::Int, offset::Int, options::Symbol)
141136
try
142137
# TODO: Check that we do all this input wrangling without copying the
143138
# code buffer
@@ -266,6 +261,13 @@ function _core_parser_hook(code, filename::String, lineno::Int, offset::Int, opt
266261
end
267262
end
268263

264+
# Core._parse gained a `lineno` argument in
265+
# https://github.com/JuliaLang/julia/pull/43876
266+
# Prior to this, the following signature was needed:
267+
function core_parser_hook(code, filename, offset, options)
268+
core_parser_hook(code, filename, 1, offset, options)
269+
end
270+
269271
# Hack:
270272
# Meta.parse() attempts to construct a ParseError from a string if it receives
271273
# `Expr(:error)`. Add an override to the ParseError constructor to prevent this.
@@ -293,14 +295,18 @@ function enable_in_core!(enable=true; freeze_world_age = true,
293295
if VERSION < v"1.6"
294296
error("Cannot use JuliaSyntax as the main Julia parser in Julia version $VERSION < 1.6")
295297
end
296-
_parser_world_age[] = freeze_world_age ? Base.get_world_counter() : _latest_world
297298
if enable && !isnothing(debug_filename)
298299
_debug_log[] = open(debug_filename, "w")
299300
elseif !enable && !isnothing(_debug_log[])
300301
close(_debug_log[])
301302
_debug_log[] = nothing
302303
end
303-
_set_core_parse_hook(enable ? core_parser_hook : _default_parser)
304+
if enable
305+
world_age = freeze_world_age ? Base.get_world_counter() : typemax(UInt)
306+
_set_core_parse_hook(fix_world_age(core_parser_hook, world_age))
307+
else
308+
_set_core_parse_hook(_default_parser)
309+
end
304310
nothing
305311
end
306312

test/hooks.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
@testset "Hooks for Core integration" begin
22
@testset "whitespace parsing" begin
3-
@test JuliaSyntax._core_parser_hook("", "somefile", 1, 0, :statement) == Core.svec(nothing, 0)
4-
@test JuliaSyntax._core_parser_hook("", "somefile", 1, 0, :statement) == Core.svec(nothing, 0)
3+
@test JuliaSyntax.core_parser_hook("", "somefile", 1, 0, :statement) == Core.svec(nothing, 0)
4+
@test JuliaSyntax.core_parser_hook("", "somefile", 1, 0, :statement) == Core.svec(nothing, 0)
55

6-
@test JuliaSyntax._core_parser_hook(" ", "somefile", 1, 2, :statement) == Core.svec(nothing,2)
7-
@test JuliaSyntax._core_parser_hook(" #==# ", "somefile", 1, 6, :statement) == Core.svec(nothing,6)
6+
@test JuliaSyntax.core_parser_hook(" ", "somefile", 1, 2, :statement) == Core.svec(nothing,2)
7+
@test JuliaSyntax.core_parser_hook(" #==# ", "somefile", 1, 6, :statement) == Core.svec(nothing,6)
88

9-
@test JuliaSyntax._core_parser_hook(" x \n", "somefile", 1, 0, :statement) == Core.svec(:x,4)
10-
@test JuliaSyntax._core_parser_hook(" x \n", "somefile", 1, 0, :atom) == Core.svec(:x,2)
9+
@test JuliaSyntax.core_parser_hook(" x \n", "somefile", 1, 0, :statement) == Core.svec(:x,4)
10+
@test JuliaSyntax.core_parser_hook(" x \n", "somefile", 1, 0, :atom) == Core.svec(:x,2)
1111
end
1212

1313
@testset "filename and lineno" begin
14-
ex = JuliaSyntax._core_parser_hook("@a", "somefile", 1, 0, :statement)[1]
14+
ex = JuliaSyntax.core_parser_hook("@a", "somefile", 1, 0, :statement)[1]
1515
@test Meta.isexpr(ex, :macrocall)
1616
@test ex.args[2] == LineNumberNode(1, "somefile")
1717

18-
ex = JuliaSyntax._core_parser_hook("@a", "otherfile", 2, 0, :statement)[1]
18+
ex = JuliaSyntax.core_parser_hook("@a", "otherfile", 2, 0, :statement)[1]
1919
@test ex.args[2] == LineNumberNode(2, "otherfile")
2020

2121
# Errors also propagate file & lineno
22-
err = JuliaSyntax._core_parser_hook("[x)", "f1", 1, 0, :statement)[1].args[1]
22+
err = JuliaSyntax.core_parser_hook("[x)", "f1", 1, 0, :statement)[1].args[1]
2323
@test err isa JuliaSyntax.ParseError
2424
@test err.source.filename == "f1"
2525
@test err.source.first_line == 1
26-
err = JuliaSyntax._core_parser_hook("[x)", "f2", 2, 0, :statement)[1].args[1]
26+
err = JuliaSyntax.core_parser_hook("[x)", "f2", 2, 0, :statement)[1].args[1]
2727
@test err isa JuliaSyntax.ParseError
2828
@test err.source.filename == "f2"
2929
@test err.source.first_line == 2
3030
end
3131

3232
@testset "toplevel errors" begin
33-
ex = JuliaSyntax._core_parser_hook("a\nb\n[x,\ny)", "somefile", 1, 0, :all)[1]
33+
ex = JuliaSyntax.core_parser_hook("a\nb\n[x,\ny)", "somefile", 1, 0, :all)[1]
3434
@test ex.head == :toplevel
3535
@test ex.args[1:5] == [
3636
LineNumberNode(1, "somefile"),
@@ -129,6 +129,6 @@
129129
JuliaSyntax.enable_in_core!(false)
130130

131131
# Should not throw
132-
@test JuliaSyntax._core_parser_hook("+=", "somefile", 1, 0, :statement)[1] isa Expr
132+
@test JuliaSyntax.core_parser_hook("+=", "somefile", 1, 0, :statement)[1] isa Expr
133133
end
134134
end

0 commit comments

Comments
 (0)