diff --git a/src/construct.jl b/src/construct.jl index 95ebc300..d0bb4a85 100644 --- a/src/construct.jl +++ b/src/construct.jl @@ -47,11 +47,13 @@ end get_source(meth::Method) = Base.uncompressed_ast(meth) -function get_source(g::GeneratedFunctionStub, env) - b = g(env..., g.argnames...) - b isa CodeInfo && return b - return eval(b) -end +# The following creates the CodeInfo holding the generated expression. May be +# useful if we decide to revert to that behavior. +# function get_source(g::GeneratedFunctionStub, env) +# b = @which g(env..., g.argnames...) +# b isa CodeInfo && return b +# return eval(b) +# end function copy_codeinfo(code::CodeInfo) @static if VERSION < v"1.1.0-DEV.762" @@ -129,6 +131,7 @@ function prepare_framecode(method::Method, @nospecialize(argtypes); enter_genera framecode = get(framedict, method, nothing) end if framecode === nothing + method0 = method if is_generated(method) && !enter_generated # If we're stepping into a staged function, we need to use # the specialization, rather than stepping through the @@ -138,7 +141,11 @@ function prepare_framecode(method::Method, @nospecialize(argtypes); enter_genera generator = false else if is_generated(method) - code = get_source(method.generator, lenv) + g = method.generator + methsg = collect(methods(g.gen)) + @assert length(methsg) == 1 + method = first(methsg) + code = get_source(method) generator = true else code = get_source(method) @@ -146,8 +153,8 @@ function prepare_framecode(method::Method, @nospecialize(argtypes); enter_genera end end framecode = FrameCode(method, code; generator=generator) - if is_generated(method) && !enter_generated - genframedict[(method, argtypes)] = framecode + if is_generated(method0) && !generator + genframedict[(method0, argtypes)] = framecode else framedict[method] = framecode end @@ -225,8 +232,10 @@ function prepare_call(@nospecialize(f), allargs; enter_generated = false) isa(ret, Compiled) && return ret # Typical return framecode, lenv = ret - if is_generated(method) && enter_generated + if framecode.generator args = Any[_Typeof(a) for a in args] + selfarg = Base.unwrap_unionall(scopeof(framecode).sig).parameters[1] # #self# + args = Any[selfarg, lenv..., args...] end return framecode, args, lenv, argtypes end @@ -292,7 +301,12 @@ end Construct a new `Frame` for `framecode`, given lowered-code arguments `frameargs` and static parameters `lenv`. See [`JuliaInterpreter.prepare_call`](@ref) for information about how to prepare the inputs. """ -function prepare_frame(framecode::FrameCode, args::Vector{Any}, lenv::SimpleVector) +function prepare_frame(framecode::FrameCode, args::Vector{Any}, lenv::SimpleVector; enter_generated=false) + s = scopeof(framecode) + if framecode.generator + args = Any[_Typeof(a) for a in args] + args = Any[Base.unwrap_unionall(s.sig).parameters[1], lenv..., args...] # first is #self# + end framedata = prepare_framedata(framecode, args) resize!(framedata.sparams, length(lenv)) # Add static parameters to environment @@ -304,8 +318,8 @@ function prepare_frame(framecode::FrameCode, args::Vector{Any}, lenv::SimpleVect return Frame(framecode, framedata) end -function prepare_frame_caller(caller::Frame, framecode::FrameCode, args::Vector{Any}, lenv::SimpleVector) - caller.callee = frame = prepare_frame(framecode, args, lenv) +function prepare_frame_caller(caller::Frame, framecode::FrameCode, args::Vector{Any}, lenv::SimpleVector; enter_generated=false) + caller.callee = frame = prepare_frame(framecode, args, lenv; enter_generated=enter_generated) frame.caller = caller return frame end diff --git a/src/interpret.jl b/src/interpret.jl index 48cc054c..794362bb 100644 --- a/src/interpret.jl +++ b/src/interpret.jl @@ -208,7 +208,7 @@ function evaluate_call_recurse!(@nospecialize(recurse), frame::Frame, call_expr: end return framecode # this was a Builtin end - newframe = prepare_frame_caller(frame, framecode, fargs, lenv) + newframe = prepare_frame_caller(frame, framecode, fargs, lenv; enter_generated=enter_generated) npc = newframe.pc shouldbreak(newframe, npc) && return BreakpointRef(newframe.framecode, npc) # if the following errors, handle_err will pop the stack and recycle newframe diff --git a/test/debug.jl b/test/debug.jl index f4fe26ec..6e26bd7a 100644 --- a/test/debug.jl +++ b/test/debug.jl @@ -18,7 +18,7 @@ end function step_through_frame(frame_creator) rets = [] for cmd in ALL_COMMANDS - frame = frame_creator() + frame = frame_creator() ret = step_through_command(frame, cmd) push!(rets, ret) end @@ -28,11 +28,15 @@ end step_through(f, args...; kwargs...) = step_through_frame(() -> enter_call(f, args...; kwargs...)) step_through(expr::Expr) = step_through_frame(() -> enter_call_expr(expr)) -@generated function generatedfoo(T) - :(return $T) +@generated function generatedfoo(x) + # A deliberately-complicated way to perform this operation + ex = Expr(:block) + push!(ex.args, :(return x)) + return ex end callgenerated() = generatedfoo(1) @generated function generatedparams(a::Array{T,N}) where {T,N} + zz = 1 :(return ($T,$N)) end callgeneratedparams() = generatedparams([1 2; 3 4]) @@ -91,23 +95,28 @@ struct B{T} end @test isa(pc, BreakpointRef) @test JuliaInterpreter.scopeof(f).name == :generatedfoo stmt = JuliaInterpreter.pc_expr(f) - @test stmt.head == :return && stmt.args[1] === Int + @test stmt.head == :return @test debug_command(frame, :c) === nothing @test frame.callee === nothing - @test get_return(frame) === Int + @test get_return(frame) === 1 # This time, step into the generated function itself frame = enter_call_expr(:($(callgenerated)())) f, pc = debug_command(frame, :sg) @test isa(pc, BreakpointRef) - @test JuliaInterpreter.scopeof(f).name == :generatedfoo stmt = JuliaInterpreter.pc_expr(f) - @test stmt.head == :return && @lookup(f, stmt.args[1]) === 1 + if stmt === nothing + f, pc = debug_command(f, :se) + stmt = JuliaInterpreter.pc_expr(f) + end + @test stmt.head == :(=) f2, pc = debug_command(f, :finish) @test JuliaInterpreter.scopeof(f2).name == :callgenerated # Now finish the regular function @test debug_command(frame, :finish) === nothing @test frame.callee === nothing - @test get_return(frame) === 1 + excmp = quote return x end + deleteat!(excmp.args, 1) # delete LineNumberNode + @test get_return(frame) == excmp # Parametric generated function (see #157) frame = fr = JuliaInterpreter.enter_call(callgeneratedparams) @@ -115,10 +124,16 @@ struct B{T} end fr, pc = debug_command(fr, :se) end fr, pc = debug_command(fr, :sg) - @test JuliaInterpreter.scopeof(fr).name == :generatedparams + JuliaInterpreter.finish!(fr) + vzz = filter(v -> v.name == :zz, JuliaInterpreter.locals(fr))[1] + @test vzz.value == 1 fr, pc = debug_command(fr, :finish) @test debug_command(fr, :finish) === nothing - @test JuliaInterpreter.get_return(fr) == (Int, 2) + ex = get_return(fr) + isa(ex.args[1], LineNumberNode) && deleteat!(ex.args, 1) + excmp = quote return ($Int, 2) end + deleteat!(excmp.args, 1) + @test ex == excmp end @testset "Optional arguments" begin