Skip to content

Minor cleanups to core parser hook #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 19, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 27 additions & 29 deletions src/hooks.jl
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
# This file provides an adaptor to match the API expected by the Julia runtime
# code in the binding Core._parse

function _set_core_parse_hook(parser)
# HACK! Fool the runtime into allowing us to set Core._parse, even during
# incremental compilation. (Ideally we'd just arrange for Core._parse to be
# set to the JuliaSyntax parser. But how do we signal that to the dumping
# code outside of the initial creation of Core?)
i = findfirst(==(:incremental), fieldnames(Base.JLOptions))
ptr = convert(Ptr{fieldtype(Base.JLOptions, i)},
cglobal(:jl_options, Base.JLOptions) + fieldoffset(Base.JLOptions, i))
incremental = unsafe_load(ptr)
if incremental != 0
unsafe_store!(ptr, 0)
end
if isdefined(Core, :set_parser)
const _set_core_parse_hook = Core.set_parser
else
function _set_core_parse_hook(parser)
# HACK! Fool the runtime into allowing us to set Core._parse, even during
# incremental compilation. (Ideally we'd just arrange for Core._parse to be
# set to the JuliaSyntax parser. But how do we signal that to the dumping
# code outside of the initial creation of Core?)
i = findfirst(==(:incremental), fieldnames(Base.JLOptions))
ptr = convert(Ptr{fieldtype(Base.JLOptions, i)},
cglobal(:jl_options, Base.JLOptions) + fieldoffset(Base.JLOptions, i))
incremental = unsafe_load(ptr)
if incremental != 0
unsafe_store!(ptr, 0)
end

Base.eval(Core, :(_parse = $parser))
Base.eval(Core, :(_parse = $parser))

if incremental != 0
unsafe_store!(ptr, incremental)
if incremental != 0
unsafe_store!(ptr, incremental)
end
end
end

# Use caller's world age.
const _caller_world = typemax(UInt)
const _parser_world_age = Ref{UInt}(_caller_world)
const _latest_world = typemax(UInt)
const _parser_world_age = Ref{UInt}(_latest_world)

function core_parser_hook(code, filename, lineno, offset, options)
# `hook` is always _core_parser_hook, but that's hidden from the compiler
# via a Ref to prevent invalidation / recompilation when other packages are
# loaded. This wouldn't seem like it should be necessary given the use of
# invoke_in_world, but it is in Julia-1.7.3. I'm not sure exactly which
# latency it's removing.
hook = _core_parser_hook_ref[]
if _parser_world_age[] != _caller_world
Base.invoke_in_world(_parser_world_age[], hook,
# NB: We need an inference barrier of one type or another here to prevent
# invalidations. The invokes provide this currently.
if _parser_world_age[] != _latest_world
Base.invoke_in_world(_parser_world_age[], _core_parser_hook,
code, filename, lineno, offset, options)
else
hook(code, filename, lineno, offset, options)
Base.invokelatest(_core_parser_hook, code, filename, lineno, offset, options)
end
end

Expand Down Expand Up @@ -146,8 +146,6 @@ end
Base.Meta.ParseError(e::JuliaSyntax.ParseError) = e

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

"""
enable_in_core!([enable=true; freeze_world_age, debug_filename])
Expand All @@ -165,7 +163,7 @@ Keyword arguments:
"""
function enable_in_core!(enable=true; freeze_world_age = true,
debug_filename = get(ENV, "JULIA_SYNTAX_DEBUG_FILE", nothing))
_parser_world_age[] = freeze_world_age ? Base.get_world_counter() : _caller_world
_parser_world_age[] = freeze_world_age ? Base.get_world_counter() : _latest_world
if enable && !isnothing(debug_filename)
_debug_log[] = open(debug_filename, "w")
elseif !enable && !isnothing(_debug_log[])
Expand Down