From cb804219c5f6343340b58f58de3d6b9008dc71a7 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Wed, 9 Aug 2023 20:23:58 +0100 Subject: [PATCH 1/4] Performance improvements Main change is not calling `length(src)` which is linear in `src`. `linestarts` could be converted to a StaticArray with size 21 but I don't think that would do much. --- src/CodeTracking.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/CodeTracking.jl b/src/CodeTracking.jl index f1bcea9..62969d7 100644 --- a/src/CodeTracking.jl +++ b/src/CodeTracking.jl @@ -219,7 +219,7 @@ see [`definition(Expr, method::Method)`](@ref) instead. See also [`code_string`](@ref). """ function definition(::Type{String}, method::Method) - methodname = method.name + methodname::Symbol = method.name if methodname == :kwcall # Julia 1.9+ # it seems better to have nkw, but see https://github.com/JuliaLang/julia/issues/48786 # The first `::typeof(f)` seems possibly unsafe because some kwargs could themselves function-typed @@ -245,15 +245,13 @@ function definition(::Type{String}, method::Method) # Step forward to the definition of this method, keeping track of positions of newlines # Issue: in-code `'\n'`. To fix, presumably we'd have to parse the entire file. eol = isequal('\n') - linestarts = Int[] + linestarts = zeros(Int, line) istart = 1 - for _ = 1:line-1 - push!(linestarts, istart) + for i in 1:line-1 + linestarts[i] = istart istart = findnext(eol, src, istart) + 1 end - push!(linestarts, length(src) + 1) - # Parse the function definition (hoping that we've found the right location to start) - ex, iend = Meta.parse(src, istart; raise=false) + linestarts[end] = istart # The function declaration may have been on a previous line, # allow some slop lineindex = lastindex(linestarts) @@ -267,14 +265,15 @@ function definition(::Type{String}, method::Method) end istart = linestarts[lineindex] try + # Parse the function definition (hoping that we've found the right location to start) ex, iend = Meta.parse(src, istart) + is_func_expr(ex, method) && return clean_source(src[istart:prevind(src, iend)]), line catch end lineindex -= 1 line -= 1 end - lineindex <= linestop && return nothing - return clean_source(src[istart:prevind(src, iend)]), line + return nothing end function clean_source(src) From b8aa2ad45d772289d768afb36b3185091d7dc6b0 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Wed, 16 Aug 2023 19:53:52 +0100 Subject: [PATCH 2/4] Allocated fixed size vector --- src/CodeTracking.jl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/CodeTracking.jl b/src/CodeTracking.jl index 62969d7..b365ed5 100644 --- a/src/CodeTracking.jl +++ b/src/CodeTracking.jl @@ -245,13 +245,16 @@ function definition(::Type{String}, method::Method) # Step forward to the definition of this method, keeping track of positions of newlines # Issue: in-code `'\n'`. To fix, presumably we'd have to parse the entire file. eol = isequal('\n') - linestarts = zeros(Int, line) + slop = 20 + linestarts = zeros(Int, slop) istart = 1 - for i in 1:line-1 - linestarts[i] = istart + for _ in 1:line-slop-1 + istart = findnext(eol, src, istart) + 1 + end + for i in 1:min(line, slop) istart = findnext(eol, src, istart) + 1 + linestarts[i] = istart end - linestarts[end] = istart # The function declaration may have been on a previous line, # allow some slop lineindex = lastindex(linestarts) @@ -266,7 +269,8 @@ function definition(::Type{String}, method::Method) istart = linestarts[lineindex] try # Parse the function definition (hoping that we've found the right location to start) - ex, iend = Meta.parse(src, istart) + ex, iend = Meta.parse(src, istart; raise=!firstrun) + firstrun = false is_func_expr(ex, method) && return clean_source(src[istart:prevind(src, iend)]), line catch end From 6f72fe6a1a6f906f0f8a36cf0fb7060f5133d9ab Mon Sep 17 00:00:00 2001 From: Zentrik Date: Wed, 16 Aug 2023 22:26:25 +0100 Subject: [PATCH 3/4] Fix bugs --- src/CodeTracking.jl | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/CodeTracking.jl b/src/CodeTracking.jl index b365ed5..6d26dda 100644 --- a/src/CodeTracking.jl +++ b/src/CodeTracking.jl @@ -248,32 +248,29 @@ function definition(::Type{String}, method::Method) slop = 20 linestarts = zeros(Int, slop) istart = 1 - for _ in 1:line-slop-1 - istart = findnext(eol, src, istart) + 1 - end - for i in 1:min(line, slop) + lineindex = 1 + for i in 1:line-1 + if i > line - slop + linestarts[lineindex] = istart + lineindex += 1 + end istart = findnext(eol, src, istart) + 1 - linestarts[i] = istart end + linestarts[lineindex] = istart # The function declaration may have been on a previous line, # allow some slop - lineindex = lastindex(linestarts) - linestop = max(0, lineindex - 20) - while !is_func_expr(ex, method) && lineindex > linestop - if isexpr(ex, :call) && length(ex.args) > 1 && first(ex.args) == :eval && isexpr(last(ex.args), :quote) && length(last(ex.args).args) > 0 - actual_ex = first(last(ex.args).args) - if is_func_expr(actual_ex, method) - return clean_source(string(actual_ex)), line - end - end + while lineindex > 0 istart = linestarts[lineindex] - try - # Parse the function definition (hoping that we've found the right location to start) - ex, iend = Meta.parse(src, istart; raise=!firstrun) - firstrun = false - is_func_expr(ex, method) && return clean_source(src[istart:prevind(src, iend)]), line - catch + # Parse the function definition (hoping that we've found the right location to start) + ex, iend = Meta.parse(src, istart; raise=false) + + is_func_expr(ex, method) && return clean_source(src[istart:prevind(src, iend)]), line + + if isexpr(ex, :call) && length(ex.args) > 1 && first(ex.args) == :eval && isexpr(last(ex.args), :quote) && length(last(ex.args).args) > 0 + ex = first(last(ex.args).args) + is_func_expr(ex, method) && return clean_source(string(ex)), line end + lineindex -= 1 line -= 1 end From d4bc3966d5cd1de6c2a27ef55e9b80f9dbc245b6 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Sun, 20 Aug 2023 09:53:11 +0100 Subject: [PATCH 4/4] Add try catch back in Also remove type assertion just in case --- src/CodeTracking.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/CodeTracking.jl b/src/CodeTracking.jl index 6d26dda..8b43754 100644 --- a/src/CodeTracking.jl +++ b/src/CodeTracking.jl @@ -219,7 +219,7 @@ see [`definition(Expr, method::Method)`](@ref) instead. See also [`code_string`](@ref). """ function definition(::Type{String}, method::Method) - methodname::Symbol = method.name + methodname = method.name if methodname == :kwcall # Julia 1.9+ # it seems better to have nkw, but see https://github.com/JuliaLang/julia/issues/48786 # The first `::typeof(f)` seems possibly unsafe because some kwargs could themselves function-typed @@ -262,7 +262,11 @@ function definition(::Type{String}, method::Method) while lineindex > 0 istart = linestarts[lineindex] # Parse the function definition (hoping that we've found the right location to start) - ex, iend = Meta.parse(src, istart; raise=false) + ex, iend = try + Meta.parse(src, istart) + catch + nothing, nothing + end is_func_expr(ex, method) && return clean_source(src[istart:prevind(src, iend)]), line