Skip to content

Commit

Permalink
make printing random LambdaInfo objects less segfault-y
Browse files Browse the repository at this point in the history
  • Loading branch information
vtjnash committed Jul 21, 2016
1 parent 3625a5a commit 7e56827
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 58 deletions.
11 changes: 6 additions & 5 deletions base/methodshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ function argtype_decl(env, n, sig, i, nargs, isva) # -> (argname, argtype)
end
end
return s, string_with_env(env, "Vararg{", tt, ",", tn, "}")
elseif t == String
return s, "String"
end
return s, string_with_env(env, t)
end
Expand All @@ -43,15 +41,15 @@ function arg_decl_parts(m::Method)
end
li = m.lambda_template
file, line = "", 0
if li !== nothing
if li !== nothing && isdefined(li, :slotnames)
argnames = li.slotnames[1:li.nargs]
decls = Any[argtype_decl(:tvar_env => tv, argnames[i], m.sig, i, li.nargs, li.isva)
for i = 1:li.nargs]
if isdefined(li, :def)
file, line = li.def.file, li.def.line
end
else
decls = Any["" for i = 1:length(m.sig.parameters)]
decls = Any[("", "") for i = 1:length(m.sig.parameters)]
end
return tv, decls, file, line
end
Expand All @@ -76,7 +74,10 @@ function show(io::IO, m::Method; kwtype::Nullable{DataType}=Nullable{DataType}()
tv, decls, file, line = arg_decl_parts(m)
ft = m.sig.parameters[1]
d1 = decls[1]
if ft <: Function &&
if m.sig === Tuple
print(io, m.name)
decls = Any[(), ("...", "")]
elseif ft <: Function &&
isdefined(ft.name.module, ft.name.mt.name) &&
ft == typeof(getfield(ft.name.module, ft.name.mt.name))
print(io, ft.name.mt.name)
Expand Down
17 changes: 17 additions & 0 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,23 @@ function show(io::IO, ::MIME"text/plain", f::Function)
end
end

function show(io::IO, ::MIME"text/plain", l::LambdaInfo)
show(io, l)
# Fix slot names and types in function body
ast = uncompressed_ast(l)
if ast !== nothing
println(io)
lambda_io = IOContext(io, :LAMBDAINFO => l)
if isdefined(l, :slotnames)
lambda_io = IOContext(lambda_io, :LAMBDA_SLOTNAMES => lambdainfo_slotnames(l))
end
body = Expr(:body)
body.args = ast
body.typ = l.rettype
show(lambda_io, body)
end
end

function show(io::IO, ::MIME"text/plain", r::LinSpace)
# show for linspace, e.g.
# linspace(1,3,7)
Expand Down
45 changes: 15 additions & 30 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ function show(io::IO, p::Pair)
isa(p.second,Pair) && print(io, "(")
show(io, p.second)
isa(p.second,Pair) && print(io, ")")
nothing
end

function show(io::IO, m::Module)
Expand Down Expand Up @@ -259,30 +260,21 @@ function lambdainfo_slotnames(l::LambdaInfo)
end
printnames[i] = printname
end
printnames
return printnames
end

function show(io::IO, l::LambdaInfo)
if isdefined(l, :def)
if (l === l.def.lambda_template)
if l === l.def.lambda_template
print(io, "LambdaInfo template for ")
show(io, l.def)
println(io)
else
print(io, "LambdaInfo for ")
show_lambda_types(io, l.specTypes.parameters)
println(io)
show_lambda_types(io, l)
end
else
println(io, "Toplevel LambdaInfo thunk")
end
# Fix slot names and types in function body
lambda_io = IOContext(IOContext(io, :LAMBDAINFO => l),
:LAMBDA_SLOTNAMES => lambdainfo_slotnames(l))
body = Expr(:body)
body.args = uncompressed_ast(l)
body.typ = l.rettype
show(lambda_io, body)
print(io, "Toplevel LambdaInfo thunk")
end
end

function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl,
Expand Down Expand Up @@ -563,15 +555,6 @@ show_unquoted(io::IO, ex::LabelNode, ::Int, ::Int) = print(io, ex.label, ":
show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int) = print(io, "goto ", ex.label)
show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = print(io, ex.mod, '.', ex.name)

function show_unquoted(io::IO, ex::LambdaInfo, ::Int, ::Int)
if isdefined(ex, :specTypes)
print(io, "LambdaInfo for ")
show_lambda_types(io, ex.specTypes.parameters)
else
show(io, ex)
end
end

function show_unquoted(io::IO, ex::Slot, ::Int, ::Int)
typ = isa(ex,TypedSlot) ? ex.typ : Any
slotid = ex.id
Expand Down Expand Up @@ -981,20 +964,22 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int)
show(io, ex.head)
for arg in args
print(io, ", ")
if isa(arg, LambdaInfo) && isdefined(arg, :specTypes)
show_lambda_types(io, arg.specTypes.parameters)
else
show(io, arg)
end
show(io, arg)
end
print(io, "))")
end
show_type && show_expr_type(io, ex.typ, emphstate)
nothing
end

function show_lambda_types(io::IO, sig::SimpleVector)
# print a method signature tuple
function show_lambda_types(io::IO, li::LambdaInfo)
# print a method signature tuple for a lambda definition
if li.specTypes === Tuple
print(io, li.def.name, "(...)")
return
end

sig = li.specTypes.parameters
ft = sig[1]
if ft <: Function && isempty(ft.parameters) &&
isdefined(ft.name.module, ft.name.mt.name) &&
Expand Down
21 changes: 9 additions & 12 deletions base/stacktraces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Stack information representing execution context, with the following fields:
The name of the function containing the execution context.
- `outer_linfo::Nullable{LambdaInfo}`
- `linfo::Nullable{LambdaInfo}`
The LambdaInfo containing the execution context (if it could be found).
Expand All @@ -29,18 +29,14 @@ Stack information representing execution context, with the following fields:
The line number in the file containing the execution context.
- `inlined_file::Symbol`
The path to the file containing the context for inlined code.
- `inlined_line::Int`
The line number in the file containing the context for inlined code.
- `from_c::Bool`
True if the code is from C.
- `inlined::Bool`
True if the code is from an inlined frame.
- `pointer::Int64`
Representation of the pointer to the execution context as returned by `backtrace`.
Expand All @@ -57,6 +53,7 @@ immutable StackFrame # this type should be kept platform-agnostic so that profil
linfo::Nullable{LambdaInfo}
"true if the code is from C"
from_c::Bool
"true if the code is from an inlined frame"
inlined::Bool
"representation of the pointer to the execution context as returned by `backtrace`"
pointer::Int64 # Large enough to be read losslessly on 32- and 64-bit machines.
Expand Down Expand Up @@ -195,10 +192,10 @@ function show_spec_linfo(io::IO, frame::StackFrame)
end
else
linfo = get(frame.linfo)
if isdefined(linfo, :specTypes)
Base.show_lambda_types(io, linfo.specTypes.parameters)
if isdefined(linfo, :def)
Base.show_lambda_types(io, linfo)
else
print(io, linfo.name)
Base.show(io, linfo)
end
end
end
Expand Down
9 changes: 5 additions & 4 deletions src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,10 @@ JL_DLLEXPORT jl_lambda_info_t *jl_new_lambda_info_uninit(void)
(jl_lambda_info_t*)jl_gc_alloc(ptls, sizeof(jl_lambda_info_t),
jl_lambda_info_type);
li->code = NULL;
li->slotnames = li->slotflags = NULL;
li->slottypes = li->ssavaluetypes = NULL;
li->slotnames = NULL;
li->slotflags = NULL;
li->slottypes = NULL;
li->ssavaluetypes = NULL;
li->rettype = (jl_value_t*)jl_any_type;
li->sparam_syms = jl_emptysvec;
li->sparam_vals = jl_emptysvec;
Expand Down Expand Up @@ -586,7 +588,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(void)
m->specializations.unknown = jl_nothing;
m->sig = NULL;
m->tvars = NULL;
m->ambig = NULL;
m->ambig = jl_nothing;
m->roots = NULL;
m->module = ptls->current_module;
m->lambda_template = NULL;
Expand All @@ -612,7 +614,6 @@ jl_method_t *jl_new_method(jl_lambda_info_t *definition, jl_sym_t *name, jl_tupl
if (jl_svec_len(tvars) == 1)
tvars = (jl_svec_t*)jl_svecref(tvars, 0);
m->tvars = tvars;
m->ambig = jl_nothing;
JL_GC_PUSH1(&m);
// the front end may add this lambda to multiple methods; make a copy if so
jl_method_t *oldm = definition->def;
Expand Down
13 changes: 10 additions & 3 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,19 @@ jl_value_t *jl_mk_builtin_func(const char *name, jl_fptr_t fptr)
jl_value_t *f = jl_new_generic_function_with_supertype(sname, jl_core_module, jl_builtin_type, 0);
jl_lambda_info_t *li = jl_new_lambda_info_uninit();
li->fptr = fptr;
// TODO jb/functions: what should li->code be?
li->code = jl_nothing; jl_gc_wb(li, li->code);
li->code = jl_nothing;
li->slottypes = jl_nothing;
li->specTypes = jl_anytuple_type;
li->ssavaluetypes = jl_box_long(0);
jl_gc_wb(li, li->ssavaluetypes);

li->def = jl_new_method_uninit();
li->def->name = sname;
// li->def->module will be set to jl_core_module by init.c
li->def->lambda_template = li;
li->def->ambig = jl_nothing;
li->def->sig = jl_anytuple_type;
li->def->tvars = jl_emptysvec;

jl_methtable_t *mt = jl_gf_mtable(f);
jl_typemap_insert(&mt->cache, (jl_value_t*)mt, jl_anytuple_type, jl_emptysvec, NULL, jl_emptysvec, (jl_value_t*)li, 0, &lambda_cache, NULL);
return f;
Expand Down
2 changes: 1 addition & 1 deletion src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -3833,7 +3833,7 @@ void jl_init_types(void)
jl_bool_type,
jl_bool_type,
jl_bool_type),
0, 1, 8);
0, 1, 9);

jl_lambda_info_type =
jl_new_datatype(jl_symbol("LambdaInfo"),
Expand Down
16 changes: 13 additions & 3 deletions test/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ function test_typed_ast_printing(f::ANY, types::ANY, must_used_vars)
for name in must_used_vars
@test name in slotnames
end
for str in (sprint(io->code_warntype(io, f, types)),
sprint(io->show(io, li)))
for str in (sprint(code_warntype, f, types),
stringmime("text/plain", li))
# Test to make sure the clearing of file path below works
@test string(li.def.file) == @__FILE__
for var in must_used_vars
Expand All @@ -401,7 +401,7 @@ function test_typed_ast_printing(f::ANY, types::ANY, must_used_vars)
end
end
# Make sure printing an AST outside LambdaInfo still works.
str = sprint(io->show(io, Base.uncompressed_ast(li)))
str = sprint(show, Base.uncompressed_ast(li))
# Check that we are printing the slot numbers when we don't have the context
# Use the variable names that we know should be present in the optimized AST
for i in 2:length(li.slotnames)
Expand All @@ -418,6 +418,16 @@ test_typed_ast_printing(g15714, Tuple{Vector{Float32}},
@test used_dup_var_tested15714
@test used_unique_var_tested15714

let li = typeof(getfield).name.mt.cache.func::LambdaInfo,
lrepr = string(li),
mrepr = string(li.def),
lmime = stringmime("text/plain", li)

@test lrepr == "LambdaInfo template for getfield(...)"
@test mrepr == "getfield(...)"
end


# Linfo Tracing test
tracefoo(x, y) = x+y
didtrace = false
Expand Down
11 changes: 11 additions & 0 deletions test/stacktraces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,14 @@ for (frame, func, inlined) in zip(trace, [g,h,f], (can_inline, can_inline, false
@test frame.inlined === inlined
end
end

let li = expand(quote let x = 1 end end).args[1]::LambdaInfo,
sf = StackFrame(:a, :b, 3, li, false, false, 0),
repr = string(sf)
@test repr == " in Toplevel LambdaInfo thunk at b:3"
end
let li = typeof(getfield).name.mt.cache.func::LambdaInfo,
sf = StackFrame(:a, :b, 3, li, false, false, 0),
repr = string(sf)
@test repr == " in getfield(...) at b:3"
end

0 comments on commit 7e56827

Please sign in to comment.