diff --git a/.gitignore b/.gitignore index 8c960ec808d9e3..71f8c81ae4d3c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.jl.cov *.jl.*.cov *.jl.mem +expected.out +failed.out diff --git a/.travis.yml b/.travis.yml index 4e8b9b3b8fd7a6..c142db04e9699a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: julia os: - linux julia: - - 0.6 + - 0.7 - nightly notifications: email: false diff --git a/REQUIRE b/REQUIRE index 9307368fec1aa6..c80a6d73ae3680 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,2 +1,3 @@ -julia 0.6 -DebuggerFramework 0.1.1 +julia 0.7 +DebuggerFramework 0.1.2 +REPL diff --git a/src/ASTInterpreter2.jl b/src/ASTInterpreter2.jl index cd220dfa09fc9a..f202eaa469f5d9 100644 --- a/src/ASTInterpreter2.jl +++ b/src/ASTInterpreter2.jl @@ -4,12 +4,14 @@ module ASTInterpreter2 using DebuggerFramework using DebuggerFramework: FileLocInfo, BufferLocInfo, Suppressed using Base.Meta -using Base: LineEdit -import Base: + +using REPL.LineEdit +using REPL +import Base: +, deepcopy_internal +using Core: CodeInfo, SSAValue, SlotNumber, TypeMapEntry, SimpleVector, LineInfoNode, GotoNode, Slot, GeneratedFunctionStub +using Markdown export @enter, @make_stack -include("linearize.jl") include("interpret.jl") struct JuliaProgramCounter @@ -20,11 +22,12 @@ end struct JuliaStackFrame meth::Method code::CodeInfo - locals::Vector{Nullable{Any}} + locals::Vector{Any} ssavalues::Vector{Any} + used::BitSet sparams::Vector{Any} exception_frames::Vector{Int} - last_exception::Ref{Nullable{Any}} + last_exception::Ref{Any} pc::JuliaProgramCounter # A vector from names to the slotnumber of that name # for which a reference was last encountered. @@ -36,54 +39,18 @@ struct JuliaStackFrame end function JuliaStackFrame(frame::JuliaStackFrame, pc::JuliaProgramCounter; wrapper = frame.wrapper, generator=frame.generator, fullpath=frame.fullpath) JuliaStackFrame(frame.meth, frame.code, frame.locals, - frame.ssavalues, frame.sparams, + frame.ssavalues, frame.used, frame.sparams, frame.exception_frames, frame.last_exception, pc, frame.last_reference, wrapper, generator, fullpath) end is_loc_meta(expr, kind) = isexpr(expr, :meta) && length(expr.args) >= 1 && expr.args[1] === kind -function determine_line_and_file(frame, highlight::Int=0) - file = frame.meth.file - line = frame.meth.line - foundline = false - extra_locs = Any[] - # Find a line number node previous to this expression - if highlight !== 0 && !isempty(highlight) - i = highlight - while i >= 1 - expr = frame.code.code[i] - if !foundline && isa(expr, LineNumberNode) - line = expr.line - foundline = true - elseif !foundline && isexpr(expr, :line) - line = expr.args[1] - foundline = true - elseif foundline && is_loc_meta(expr, :push_loc) - file = expr.args[2] - extra_locs = determine_line_and_file(frame, i-1) - break - elseif is_loc_meta(expr, :pop_loc) - npops = 1 - while npops >= 1 - i -= 1 - expr = frame.code.code[i] - is_loc_meta(expr, :pop_loc) && (npops += 1) - is_loc_meta(expr, :push_loc) && (npops -= 1) - end - end - i -= 1 - end - end - [extra_locs; (file, line)] -end function DebuggerFramework.locdesc(frame::JuliaStackFrame, specslottypes = false) sprint() do io - slottypes = frame.code.slottypes argnames = frame.code.slotnames[2:frame.meth.nargs] - spectypes = specslottypes && (slottypes != nothing) ? - slottypes[2:frame.meth.nargs] : Any[Any for i=1:length(argnames)] + spectypes = Any[Any for i=1:length(argnames)] print(io, frame.meth.name,'(') first = true for (argname, argT) in zip(argnames, spectypes) @@ -101,25 +68,25 @@ end function DebuggerFramework.print_locals(io::IO, frame::JuliaStackFrame) for i = 1:length(frame.locals) - if !isnull(frame.locals[i]) + if !isa(frame.locals[i], Nothing) # #self# is only interesting if it has values inside of it. We already know # which function we're in otherwise. - val = get(frame.locals[i]) + val = something(frame.locals[i]) if frame.code.slotnames[i] == Symbol("#self#") && (isa(val, Type) || sizeof(val) == 0) continue end - DebuggerFramework.print_var(io, frame.code.slotnames[i], frame.locals[i], nothing) + DebuggerFramework.print_var(io, frame.code.slotnames[i], something(frame.locals[i]), nothing) end end for i = 1:length(frame.sparams) - DebuggerFramework.print_var(io, frame.meth.sparam_syms[i], Nullable{Any}(frame.sparams[i]), nothing) + DebuggerFramework.print_var(io, frame.meth.sparam_syms[i], frame.sparams[i], nothing) end end const SEARCH_PATH = [] -__init__() = append!(SEARCH_PATH,[joinpath(JULIA_HOME,"../share/julia/base/"), - joinpath(JULIA_HOME,"../include/")]) +__init__() = append!(SEARCH_PATH,[joinpath(Sys.BINDIR,"../share/julia/base/"), + joinpath(Sys.BINDIR,"../include/")]) function loc_for_fname(file, line, defline) if startswith(string(file),"REPL[") hist_idx = parse(Int,string(file)[6:end-1]) @@ -138,8 +105,7 @@ function loc_for_fname(file, line, defline) end function DebuggerFramework.locinfo(frame::JuliaStackFrame) - file, line = determine_line_and_file(frame, frame.pc.next_stmt)[end] - loc_for_fname(file, line, frame.meth.line) + loc_for_fname(frame.meth.file, location(frame), frame.meth.line) end function lookup_var_if_var(frame, x) @@ -154,9 +120,9 @@ function DebuggerFramework.eval_code(state, frame::JuliaStackFrame, command) local_vars = Any[] local_vals = Any[] for i = 1:length(frame.locals) - if !isnull(frame.locals[i]) + if !isa(frame.locals[i], Nothing) push!(local_vars, frame.code.slotnames[i]) - push!(local_vals, QuoteNode(get(frame.locals[i]))) + push!(local_vals, QuoteNode(something(frame.locals[i]))) end end for i = 1:length(frame.sparams) @@ -164,15 +130,17 @@ function DebuggerFramework.eval_code(state, frame::JuliaStackFrame, command) push!(local_vals, QuoteNode(frame.sparams[i])) end res = gensym() - eval_expr = Expr(:let, Expr(:block, - Expr(:(=), res, expr), - Expr(:tuple, res, Expr(:tuple, local_vars...))), - map(x->Expr(:(=), x...), zip(local_vars, local_vals))...) - eval_res, res = eval(frame.meth.module, eval_expr) + eval_expr = Expr(:let, + Expr(:block, map(x->Expr(:(=), x...), zip(local_vars, local_vals))...), + Expr(:block, + Expr(:(=), res, expr), + Expr(:tuple, res, Expr(:tuple, local_vars...)) + )) + eval_res, res = Core.eval(frame.meth.module, eval_expr) j = 1 for i = 1:length(frame.locals) - if !isnull(frame.locals[i]) - frame.locals[i] = Nullable{Any}(res[j]) + if !isa(frame.locals[i], Nothing) + frame.locals[i] = Some(res[j]) j += 1 end end @@ -223,9 +191,9 @@ function DebuggerFramework.language_specific_prompt(state, frame::JuliaStackFram julia_prompt = LineEdit.Prompt(DebuggerFramework.promptname(state.level, "julia"); # Copy colors from the prompt object prompt_prefix = state.repl.prompt_color, - prompt_suffix = (state.repl.envcolors ? Base.input_color : repl.input_color), - complete = Base.REPL.REPLCompletionProvider(), - on_enter = Base.REPL.return_callback) + prompt_suffix = (state.repl.envcolors ? Base.input_color : state.repl.input_color), + complete = REPL.REPLCompletionProvider(), + on_enter = REPL.return_callback) # 0.7 compat if isdefined(state.main_mode, :repl) julia_prompt.repl = state.main_mode.repl @@ -241,7 +209,7 @@ function DebuggerFramework.language_specific_prompt(state, frame::JuliaStackFram xbuf = copy(buf) command = String(take!(buf)) ok, result = DebuggerFramework.eval_code(state, command) - Base.REPL.print_response(state.repl, ok ? result : result[1], ok ? nothing : result[2], true, true) + REPL.print_response(state.repl, ok ? result : result[1], ok ? nothing : result[2], true, true) println(state.repl.t) if !ok @@ -255,13 +223,13 @@ function DebuggerFramework.language_specific_prompt(state, frame::JuliaStackFram end LineEdit.reset_state(s) end - julia_prompt.keymap_dict = LineEdit.keymap([Base.REPL.mode_keymap(state.main_mode);state.standard_keymap]) + julia_prompt.keymap_dict = LineEdit.keymap([REPL.mode_keymap(state.main_mode);state.standard_keymap]) state.language_modes[:julia] = julia_prompt return julia_prompt end function JuliaStackFrame(meth::Method) - JuliaStackFrame(meth, Vector{Nullable{Any}}(), + JuliaStackFrame(meth, Vector{Any}(), Vector{Any}(), Vector{Any}(), Vector{Any}(), Dict{Symbol, Int}(), false, false, true) end @@ -289,11 +257,7 @@ function is_function_def(ex) end function is_generated(meth) - if VERSION < v"0.7-" - meth.isstaged - else - isdefined(meth, :generator) - end + isdefined(meth, :generator) end function determine_method_for_expr(expr; enter_generated = false) @@ -330,26 +294,17 @@ function determine_method_for_expr(expr; enter_generated = false) sig = method.sig isa(method, TypeMapEntry) && (method = method.func) # Get static parameters - if VERSION < v"0.7-" - (ti, lenv) = ccall(:jl_match_method, Any, (Any, Any), - argtypes, sig)::SimpleVector - else - (ti, lenv) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), - argtypes, sig)::SimpleVector - end + (ti, lenv) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), + argtypes, sig)::SimpleVector if is_generated(method) && !enter_generated # If we're stepping into a staged function, we need to use # the specialization, rather than stepping thorugh the # unspecialized method. - code = Core.Inference.get_staged(Core.Inference.code_for_method(method, argtypes, lenv, typemax(UInt), false)) + code = Core.Compiler.get_staged(Core.Compiler.code_for_method(method, argtypes, lenv, typemax(UInt), false)) else if is_generated(method) args = map(_Typeof, args) - if VERSION < v"0.7-" - code = get_source(method) - else - code = get_source(method.generator.gen) - end + code = get_source(method.generator) else code = get_source(method) end @@ -367,6 +322,21 @@ function get_source(meth) end end +function get_source(g::GeneratedFunctionStub) + b = g(g.argnames...) + b isa CodeInfo && return b + return eval(b) +end + +function Base.deepcopy_internal(x::LineInfoNode, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x] + end + deeper(x) = deepcopy_internal(x, stackdict) + stackdict[x] = LineInfoNode(x.mod, deeper(x.method), + deeper(x.file), deeper(x.line), deeper(x.inlined_at)) +end + function copy_codeinfo(code::CodeInfo) old_code = code.code code.code = UInt8[] @@ -378,25 +348,25 @@ end function prepare_locals(meth, code, argvals = (), generator = false) code = copy_codeinfo(code) - linearize!(code) # Construct the environment from the arguments argnames = code.slotnames[1:meth.nargs] - locals = Array{Nullable{Any}}(length(code.slotflags)) + locals = Array{Any}(undef, length(code.slotflags)) ng = isa(code.ssavaluetypes, Int) ? code.ssavaluetypes : length(code.ssavaluetypes) - ssavalues = Array{Any}(ng) - sparams = Array{Any}(length(meth.sparam_syms)) + ssavalues = Array{Any}(undef, ng) + sparams = Array{Any}(undef, length(meth.sparam_syms)) for i = 1:meth.nargs if meth.isva && i == length(argnames) - locals[i] = length(argvals) >= i ? tuple(argvals[i:end]...) : Nullable{Any}(()) + locals[i] = length(argvals) >= i ? Some(tuple(argvals[i:end]...)) : Some(()) break end - locals[i] = length(argvals) >= i ? Nullable{Any}(argvals[i]) : Nullable{Any}() + locals[i] = length(argvals) >= i ? Some(argvals[i]) : Some(()) end # add local variables initially undefined for i = (meth.nargs+1):length(code.slotnames) - locals[i] = Nullable{Any}() + locals[i] = nothing end - JuliaStackFrame(meth, code, locals, ssavalues, sparams, Int[], Nullable{Any}(), + used = find_used(code) + JuliaStackFrame(meth, code, locals, ssavalues, used, sparams, Int[], nothing, JuliaProgramCounter(1), Dict{Symbol,Int}(), false, generator, true) end @@ -429,13 +399,13 @@ function maybe_step_through_wrapper!(stack) pc = next_call!(frame, pc) end stack[1] = JuliaStackFrame(frame, pc; wrapper=true) - unshift!(stack, enter_call_expr(Expr(:call, map(x->lookup_var_if_var(frame, x), last.args)...))) + pushfirst!(stack, enter_call_expr(Expr(:call, map(x->lookup_var_if_var(frame, x), last.args)...))) return maybe_step_through_wrapper!(stack) end stack end -lower(mod, arg) = VERSION < v"0.7-" ? expand(arg) : Meta.lower(mod, arg) +lower(mod, arg) = false ? expand(arg) : Meta.lower(mod, arg) # This is a version of gen_call_with_extracted_types, except that is passes back the call expression # for further processing. @@ -443,9 +413,14 @@ function extract_args(__module__, ex0) if isa(ex0, Expr) kws = collect(filter(x->isexpr(x,:kw),ex0.args)) if !isempty(kws) + names = [] + values = Tuple(map(x-> begin + push!(names,x.args[1]) + x.args[2] + end,kws)) + names = Tuple(names) return Expr(:tuple,:(Core.kwfunc($(ex0.args[1]))), - Expr(:call,Base.vector_any,mapreduce( - x->[QuoteNode(x.args[1]),x.args[2]],vcat,kws)...), + Expr(:call, NamedTuple{names,typeof(values)}, values), map(x->isexpr(x, :parameters) ? QuoteNode(x) : x, filter(x->!isexpr(x, :kw),ex0.args))...) else @@ -494,16 +469,10 @@ function _make_stack(mod, arg) end macro make_stack(arg) - if VERSION < v"0.7-" - __module__ = current_module() - end _make_stack(__module__, arg) end macro enter(arg) - if VERSION < v"0.7-" - __module__ = current_module() - end quote let stack = $(_make_stack(__module__,arg)) DebuggerFramework.RunDebugger(stack) diff --git a/src/commands.jl b/src/commands.jl index 69fd00d80ebbc4..eb6a402bddf66a 100644 --- a/src/commands.jl +++ b/src/commands.jl @@ -8,8 +8,11 @@ function perform_return!(state) if returning_frame.generator # Don't do anything here, just return us to where we were else - if isexpr(pc_expr(calling_frame), :(=)) - do_assignment!(calling_frame, pc_expr(calling_frame).args[1], val) + prev = pc_expr(calling_frame) + if isexpr(prev, :(=)) + do_assignment!(calling_frame, prev.args[1], val) + elseif isassign(calling_frame) + do_assignment!(calling_frame, getlhs(calling_frame.pc), val) end state.stack[2] = JuliaStackFrame(calling_frame, maybe_next_call!(calling_frame, calling_frame.pc + 1)) @@ -18,7 +21,7 @@ function perform_return!(state) @assert !returning_frame.generator state.overall_result = val end - shift!(state.stack) + popfirst!(state.stack) if !isempty(state.stack) && state.stack[1].wrapper state.stack[1] = JuliaStackFrame(state.stack[1], finish!(state.stack[1])) perform_return!(state) @@ -27,7 +30,7 @@ end function propagate_exception!(state, exc) while !isempty(state.stack) - shift!(state.stack) + popfirst!(state.stack) isempty(state.stack) && break if isa(state.stack[1], JuliaStackFrame) if !isempty(state.stack[1].exception_frames) @@ -79,12 +82,12 @@ function DebuggerFramework.execute_command(state, frame::JuliaStackFrame, cmd::U if (cmd == Val{:s}() || cmd == Val{:sg}()) new_frame = JuliaStackFrame(new_frame, maybe_next_call!(new_frame)) end - # Don't step into Core.Inference - if new_frame.meth.module == Core.Inference + # Don't step into Core.Compiler + if new_frame.meth.module == Core.Compiler ok = false else state.stack[1] = JuliaStackFrame(frame, pc) - unshift!(state.stack, new_frame) + pushfirst!(state.stack, new_frame) return true end else @@ -157,7 +160,7 @@ end function DebuggerFramework.execute_command(state, frane::JuliaStackFrame, ::Val{:?}, cmd) display( - Base.@md_str """ + @md_str """ Basic Commands:\\ - `n` steps to the next line\\ - `s` steps into the next call\\ diff --git a/src/interpret.jl b/src/interpret.jl index 28c2c229eb431f..f4be9b0c21d5fb 100644 --- a/src/interpret.jl +++ b/src/interpret.jl @@ -1,10 +1,15 @@ # Implements a simple interpreter for julia's lowered AST +getlhs(pc) = SSAValue(pc.next_stmt) + +isassign(fr) = isassign(fr, fr.pc) +isassign(fr, pc) = (pc.next_stmt in fr.used) + lookup_var(frame, val::SSAValue) = frame.ssavalues[val.id+1] lookup_var(frame, ref::GlobalRef) = getfield(ref.mod, ref.name) -lookup_var(frame, slot::SlotNumber) = get(frame.locals[slot.id]) +lookup_var(frame, slot::SlotNumber) = something(frame.locals[slot.id]) function lookup_var(frame, e::Expr) - isexpr(e, :the_exception) && return get(frame.last_exception[]) + isexpr(e, :the_exception) && return frame.last_exception[] isexpr(e, :boundscheck) && return true isexpr(e, :static_parameter) || error() frame.sparams[e.args[1]] @@ -23,7 +28,7 @@ instantiate_type_in_env(arg, spsig, spvals) = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), arg, spsig, spvals) function evaluate_call(frame, call_expr) - args = Array{Any}(length(call_expr.args)) + args = Array{Any}(undef, length(call_expr.args)) for i = 1:length(args) arg = call_expr.args[i] if isa(arg, QuoteNode) @@ -32,6 +37,8 @@ function evaluate_call(frame, call_expr) args[i] = lookup_var(frame, arg) elseif isexpr(arg, :&) args[i] = Expr(:&, lookup_var(frame, arg.args[1])) + elseif isa(arg, Expr) + args[i] = eval_rhs(frame, arg) else args[i] = arg end @@ -48,7 +55,7 @@ function evaluate_call(frame, call_expr) instantiate_type_in_env(arg, frame.meth.sig, frame.sparams) end...) end - ret = eval(frame.meth.module, Expr(:foreigncall, args...)) + ret = Core.eval(frame.meth.module, Expr(:foreigncall, args...)) else f = to_function(args[1]) if isa(f, CodeInfo) @@ -66,37 +73,57 @@ function do_assignment!(frame, lhs, rhs) if isa(lhs, SSAValue) frame.ssavalues[lhs.id+1] = rhs elseif isa(lhs, Slot) - frame.locals[lhs.id] = Nullable{Any}(rhs) + frame.locals[lhs.id] = Some(rhs) frame.last_reference[frame.code.slotnames[lhs.id]] = lhs.id elseif isa(lhs, GlobalRef) - eval(lhs.mod,:($(lhs.name) = $(QuoteNode(rhs)))) + Base.eval(lhs.mod,:($(lhs.name) = $(QuoteNode(rhs)))) + end +end + +eval_rhs(frame, node) = eval(node) +function eval_rhs(frame, node::Expr) + if isexpr(node, :new) + new_expr = Expr(:new, map(x->QuoteNode(lookup_var_if_var(frame, x)), + node.args)...) + rhs = Core.eval(frame.meth.module, new_expr) + elseif isexpr(node, :isdefined) + rhs = check_isdefined(frame, node.args[1]) + else + rhs = (isexpr(node, :call) || isexpr(node, :foreigncall)) ? + evaluate_call(frame, node) : + lookup_var_if_var(frame, node) end + if isa(rhs, QuoteNode) + rhs = rhs.value + end + return rhs +end + +eval_rhs(frame, node::Union{SSAValue, GlobalRef, SlotNumber}) = lookup_var(frame, node) +eval_rhs(frame, node::QuoteNode) = node.value +check_isdefined(frame, node::Slot) = isdefined(frame.locals, slot.id) +function check_isdefined(frame, node::Expr) + node.head == :static_parameter && return isdefined(frame.sparams, node.args[1]) end function _step_expr(frame, pc) node = pc_expr(frame, pc) try - if isa(node, Expr) + if isassign(frame, pc) + lhs = getlhs(pc) + rhs = eval_rhs(frame, node) + do_assignment!(frame, lhs, rhs) + elseif isa(node, Expr) if node.head == :(=) lhs = node.args[1] - if isexpr(node.args[2], :new) - new_expr = Expr(:new, map(x->QuoteNode(lookup_var_if_var(frame, x)), node.args[2].args)...) - rhs = eval(frame.meth.module, new_expr) - else - rhs = (isexpr(node.args[2], :call) || isexpr(node.args[2], :foreigncall)) ? evaluate_call(frame, node.args[2]) : - lookup_var_if_var(frame, node.args[2]) - end - if isa(rhs, QuoteNode) - rhs = rhs.value - end + rhs = eval_rhs(frame, node.args[2]) do_assignment!(frame, lhs, rhs) # Special case hack for readability. # ret = rhs elseif node.head == :& elseif node.head == :gotoifnot - arg = node.args[1] - arg = isa(arg, Bool) ? arg : lookup_var(frame, arg) + arg = eval_rhs(frame, node.args[1]) if !isa(arg, Bool) throw(TypeError(frame.meth.name, "if", Bool, node.args[1])) end @@ -114,6 +141,7 @@ function _step_expr(frame, pc) pop!(frame.exception_frames) end elseif node.head == :static_parameter + elseif node.head == :gc_preserve_end || node.head == :gc_preserve_begin elseif node.head == :return return nothing else @@ -124,7 +152,7 @@ function _step_expr(frame, pc) elseif isa(node, QuoteNode) ret = node.value else - ret = eval(node) + ret = eval_rhs(frame, node) end catch err isempty(frame.exception_frames) && rethrow(err) @@ -177,6 +205,15 @@ end pc_expr(frame, pc) = frame.code.code[pc.next_stmt] pc_expr(frame) = pc_expr(frame, frame.pc) +function find_used(code::CodeInfo) + used = BitSet() + stmts = code.code + for stmt in stmts + Core.Compiler.scan_ssa_use!(push!, used, stmt) + end + return used +end + function maybe_next_call!(frame, pc) call_or_return(node) = is_call(node) || isexpr(node, :return) call_or_return(pc_expr(frame, pc)) || @@ -185,13 +222,13 @@ function maybe_next_call!(frame, pc) end maybe_next_call!(frame) = maybe_next_call!(frame, frame.pc) +location(frame) = location(frame, frame.pc) +location(frame, pc) = frame.code.codelocs[pc.next_stmt] + frame.meth.line - 1 function next_line!(frame, stack = nothing) - didchangeline = false - fls = determine_line_and_file(frame, frame.pc.next_stmt) - line = fls[1][2] + initial = location(frame) first = true pc = frame.pc - while !didchangeline + while location(frame, pc) == initial # If this is a return node, interrupt execution. This is the same # special case as in `s`. (!first && isexpr(pc_expr(frame, pc), :return)) && return pc @@ -200,8 +237,6 @@ function next_line!(frame, stack = nothing) if isgotonode(pc_expr(frame, pc)) pc = _step_expr(frame, pc) pc == nothing && return nothing - fls = determine_line_and_file(frame, pc.next_stmt) - didchangeline = line != fls[1][2] elseif stack !== nothing && iswrappercall(pc_expr(frame, pc)) # With splatting it can happen that we do something like ssa = tuple(#self#), _apply(ssa), which # confuses the logic here, just step into the first call that's not a builtin @@ -212,7 +247,7 @@ function next_line!(frame, stack = nothing) call_expr = Expr(:call, map(x->lookup_var_if_var(frame, x), call_expr.args)...) new_frame = enter_call_expr(call_expr) if new_frame !== nothing - unshift!(stack, new_frame) + pushfirst!(stack, new_frame) frame = new_frame pc = frame.pc break diff --git a/src/linearize.jl b/src/linearize.jl deleted file mode 100644 index 0dfce283d5f3a9..00000000000000 --- a/src/linearize.jl +++ /dev/null @@ -1,164 +0,0 @@ -# Linearizer. Future versions of julia will emit fully-linear IR, so this way upgrading should be easier. -# This is taken from inference.jl on julia master -function newvar!(code::CodeInfo, typ) - if isa(code.ssavaluetypes, Int) - id = code.ssavaluetypes::Int - code.ssavaluetypes = id + 1 - else - id = length(code.ssavaluetypes) - push!(code.ssavaluetypes, typ) - end - return SSAValue(id) -end - -is_meta_expr_head(head::Symbol) = -(head === :inbounds || head === :boundscheck || head === :meta || - head === :line || head === :simdloop) -is_meta_expr(ex::Expr) = is_meta_expr_head(ex.head) - -function is_ccall_static(e::Expr) - if e.head === :call - length(e.args) == 3 || return false - for i in 2:3 - a = e.args[i] - (isa(a, Expr) || isa(a, Slot) || isa(a, SSAValue)) && return false - end - return true - elseif e.head === :static_parameter - return true - end - return false -end - -function linearize_arg!(args, i, stmts, code::CodeInfo) - a = args[i] - if isa(a, Symbol) - a = a::Symbol - typ = Any - elseif isa(a, GlobalRef) - a = a::GlobalRef - typ = Any - elseif isa(a, Expr) - typ = (a::Expr).typ - else - return - end - ssa = newvar!(code, typ) - push!(stmts, :($ssa = $a)) - args[i] = ssa - return -end - -function get_label_map(body::Vector{Any}) - labelmap = Dict{Int, Int}() - for i = 1:length(body) - el = body[i] - if isa(el, LabelNode) - labelmap[el.label] = i - end - end - return labelmap -end - -function relabel!(body::Vector{Any}) - mapping = get_label_map(body) - for i = 1:length(body) - el = body[i] - if isa(el, LabelNode) - labelnum = mapping[el.label] - @assert labelnum !== 0 - body[i] = LabelNode(labelnum) - elseif isa(el, GotoNode) - labelnum = mapping[el.label] - @assert labelnum !== 0 - body[i] = GotoNode(labelnum) - elseif isa(el, Expr) - if el.head === :gotoifnot - labelnum = mapping[el.args[2]::Int] - if labelnum === 0 - # Might still have side effects - body[i] = el.args[1] - else - el.args[2] = labelnum - end - elseif el.head === :enter - labelnum = mapping[el.args[1]::Int] - @assert labelnum !== 0 - el.args[1] = labelnum - end - end - end -end - -function linearize!(code::CodeInfo) - body = code.code - len = length(body) - next_i = 1 - stmts = [] - while next_i <= len - i = next_i - next_i += 1 - ex = body[i] - isa(ex, Expr) || continue - ex = ex::Expr - head = ex.head - is_meta_expr_head(head) && continue - if head === :(=) - ex = ex.args[2] - isa(ex, Expr) || continue - ex = ex::Expr - head = ex.head - end - args = ex.args - if head === :foreigncall - if isa(args[1], Expr) && !is_ccall_static(args[1]::Expr) - linearize_arg!(args, 1, stmts, code) - end - for j in 2:length(args) - a = args[j] - isa(a, Expr) || continue - if a.head === :& - linearize_arg!(a.args, 1, stmts, code) - else - linearize_arg!(args, j, stmts, code) - end - end - elseif (head === :import || head === :using || head === :importall || head === :export || - head === :isdefined || head === :const || is_meta_expr_head(head)) - continue - elseif head === :call - if ex.args[1] == Core.Intrinsics.llvmcall - for j in 5:length(args) - linearize_arg!(args, j, stmts, code) - end - elseif ex.args[1] == Core.Intrinsics.cglobal - if isa(args[2], Expr) && !is_ccall_static(args[2]::Expr) - linearize_arg!(args, 2, stmts, code) - end - for j in 3:length(args) - linearize_arg!(args, j, stmts, code) - end - else - for j in 1:length(args) - linearize_arg!(args, j, stmts, code) - end - end - else - for j in 1:length(args) - if j == 1 && head === :method - argj = args[j] - if isa(argj, Slot) || isa(argj, Symbol) || isa(argj, GlobalRef) - continue - end - end - linearize_arg!(args, j, stmts, code) - end - end - isempty(stmts) && continue - next_i = i - splice!(body, i:(i - 1), stmts) - len += length(stmts) - empty!(stmts) - end - relabel!(body) -end \ No newline at end of file diff --git a/test/REQUIRE b/test/REQUIRE index d3e33bef4bf2c1..12635247d0f04c 100644 --- a/test/REQUIRE +++ b/test/REQUIRE @@ -1 +1,2 @@ VT100 0.2.0 +TerminalRegressionTests diff --git a/test/interpret.jl b/test/interpret.jl index f9b4f9b34b98be..cc6370a3542b59 100644 --- a/test/interpret.jl +++ b/test/interpret.jl @@ -1,6 +1,6 @@ using ASTInterpreter2 using ASTInterpreter2: enter_call_expr -using Base.Test +using Test function CallTest() UnitRange{Int64}(2,2) diff --git a/test/runtests.jl b/test/runtests.jl index 286fa072ffc4eb..9dfd4471343089 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,11 @@ using ASTInterpreter2 -using Base.Test +using Test +using REPL + +using DebuggerFramework include("evaling.jl") include("stepping.jl") include("interpret.jl") include("ui.jl") -include("misc.jl") \ No newline at end of file +include("misc.jl") diff --git a/test/stepping.jl b/test/stepping.jl index e48f6edd90e954..2454885edec0c8 100644 --- a/test/stepping.jl +++ b/test/stepping.jl @@ -1,10 +1,11 @@ using ASTInterpreter2 using Base.Meta +using REPL using DebuggerFramework: execute_command, dummy_state struct DummyState; end -Base.LineEdit.transition(s::DummyState, _) = nothing +REPL.LineEdit.transition(s::DummyState, _) = nothing # Steps through the whole expression using `s` function step_through(frame) @@ -67,7 +68,7 @@ end # Work around the fact that we can't detect macro expansions if the macro # is defined in the same file -include_string(""" +include_string(Main, """ function test_macro() a = sin(5) b = asin(a) diff --git a/test/ui.jl b/test/ui.jl index 6cc69e8d0bde9f..f34fd830c882d5 100644 --- a/test/ui.jl +++ b/test/ui.jl @@ -1,4 +1,4 @@ -using ASTInterpreter2 +using ASTInterpreter2, REPL # From base, but copied here to make sure we don't fail bacause base changed function my_gcd(a::T, b::T) where T<:Union{Int64,UInt64,Int128,UInt128} @@ -22,20 +22,16 @@ function my_gcd(a::T, b::T) where T<:Union{Int64,UInt64,Int128,UInt128} r % T end -if is_unix() - include(Pkg.dir("VT100","test","TerminalRegressionTests.jl")) +if Sys.isunix() + using TerminalRegressionTests const thisdir = dirname(@__FILE__) TerminalRegressionTests.automated_test( joinpath(thisdir,"ui/history.multiout"), ["n\n","`", "a\n", "\e[A", "\e[A", "\x3", "\x4"]) do emuterm - repl = Base.REPL.LineEditREPL(emuterm, true) - repl.interface = Base.REPL.setup_interface(repl) - if VERSION < v"0.7.0-DEV.1309" - repl.specialdisplay = (args...)->display(Base.REPL.REPLDisplay(repl), args...) - else - repl.specialdisplay = Base.REPL.REPLDisplay(repl) - end + repl = REPL.LineEditREPL(emuterm, true) + repl.interface = REPL.setup_interface(repl) + repl.specialdisplay = REPL.REPLDisplay(repl) stack = ASTInterpreter2.@make_stack my_gcd(10, 20) stack[1] = ASTInterpreter2.JuliaStackFrame(stack[1], stack[1].pc; fullpath=false) DebuggerFramework.RunDebugger(stack, repl, emuterm)