Skip to content

Commit

Permalink
Merge pull request #16049 from JuliaLang/jn/static_show_circ
Browse files Browse the repository at this point in the history
improve circular reference printing
  • Loading branch information
vtjnash committed Apr 26, 2016
2 parents 35e239c + 25d466b commit 6168da5
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 88 deletions.
27 changes: 15 additions & 12 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ end

showdict(t::Associative; kw...) = showdict(STDOUT, t; kw...)
function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false)
(:SHOWN_SET => t) in io && (print(io, "#= circular reference =#"); return)

recur_io = IOContext(io, :SHOWN_SET => t)
limit::Bool = limit_output(io)
if compact
Expand All @@ -75,14 +73,16 @@ function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false)
print(io, typeof(t))
end
print(io, '(')
first = true
n = 0
for pair in t
first || print(io, ',')
first = false
show(recur_io, pair)
n+=1
limit && n >= 10 && (print(io, ""); break)
if !show_circular(io, t)
first = true
n = 0
for pair in t
first || print(io, ',')
first = false
show(recur_io, pair)
n+=1
limit && n >= 10 && (print(io, ""); break)
end
end
print(io, ')')
end
Expand All @@ -92,7 +92,8 @@ function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false)
# Otherwise show more descriptively, with one line per key/value pair
print(io, summary(t))
isempty(t) && return
print(io, ":")
print(io, ":\n ")
show_circular(io, t) && return
if limit
sz = displaysize(io)
rows, cols = sz[1] - 3, sz[2]
Expand All @@ -113,8 +114,10 @@ function showdict{K,V}(io::IO, t::Associative{K,V}; compact = false)
rows = cols = 0
end

first = true
for (i, (k, v)) in enumerate(t)
print(io, "\n ")
first || print(io, "\n ")
first = false
limit && i > rows && (print(io, rpad("", keylen), " => ⋮"); break)

if limit
Expand Down
128 changes: 68 additions & 60 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ _limit_output = false # delete with with_output_limit deprecation

displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize] : displaysize(io.io)

show_circular(io::IO, x::ANY) = false
function show_circular(io::IOContext, x::ANY)
d = 1
for (k, v) in io.dict
if k === :SHOWN_SET && v === x
print(io, "#= circular reference @-$d =#")
return true
end
d += 1
end
return false
end

show(io::IO, x::ANY) = show_default(io, x)
function show_default(io::IO, x::ANY)
Expand All @@ -77,9 +89,7 @@ function show_default(io::IO, x::ANY)
print(io, '(')
nf = nfields(t)
if nf != 0 || t.size==0
if (:SHOWN_SET => x) in io
print(io, "#= circular reference =#")
else
if !show_circular(io, x)
recur_io = IOContext(io, :SHOWN_SET => x)
for i=1:nf
f = fieldname(t, i)
Expand Down Expand Up @@ -246,36 +256,35 @@ end
function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, delim_one,
i1=1, l=length(itr))
print(io, op)
newline = true
first = true
i = i1
if l > 0
while true
if !isassigned(itr, i)
print(io, undef_ref_str)
multiline = false
else
x = itr[i]
multiline = isa(x,AbstractArray) && ndims(x)>1 && !isempty(x)
newline && multiline && println(io)
if !isbits(x) && is(x, itr)
print(io, "#= circular reference =#")
if !show_circular(io, itr)
recur_io = IOContext(io, :SHOWN_SET => itr)
newline = true
first = true
i = i1
if l > 0
while true
if !isassigned(itr, i)
print(io, undef_ref_str)
multiline = false
else
showcompact_lim(io, x)
x = itr[i]
multiline = isa(x,AbstractArray) && ndims(x)>1 && !isempty(x)
newline && multiline && println(io)
showcompact_lim(recur_io, x)
end
i += 1
if i > i1+l-1
delim_one && first && print(io, delim)
break
end
first = false
print(io, delim)
if multiline
println(io); println(io)
newline = false
else
newline = true
end
end
i += 1
if i > i1+l-1
delim_one && first && print(io, delim)
break
end
first = false
print(io, delim)
if multiline
println(io); println(io)
newline = false
else
newline = true
end
end
end
Expand All @@ -284,35 +293,34 @@ end

function show_delim_array(io::IO, itr, op, delim, cl, delim_one, i1=1, n=typemax(Int))
print(io, op)
state = start(itr)
newline = true
first = true
while i1 > 1 && !done(itr,state)
_, state = next(itr, state)
i1 -= 1
end
if !done(itr,state)
while true
x, state = next(itr,state)
multiline = isa(x,AbstractArray) && ndims(x)>1 && !isempty(x)
newline && multiline && println(io)
if !isbits(x) && is(x, itr)
print(io, "#= circular reference =#")
else
show(io, x)
end
i1 += 1
if done(itr,state) || i1 > n
delim_one && first && print(io, delim)
break
end
first = false
print(io, delim)
if multiline
println(io); println(io)
newline = false
else
newline = true
if !show_circular(io, itr)
recur_io = IOContext(io, :SHOWN_SET => itr)
state = start(itr)
newline = true
first = true
while i1 > 1 && !done(itr,state)
_, state = next(itr, state)
i1 -= 1
end
if !done(itr,state)
while true
x, state = next(itr,state)
multiline = isa(x,AbstractArray) && ndims(x)>1 && !isempty(x)
newline && multiline && println(io)
show(recur_io, x)
i1 += 1
if done(itr,state) || i1 > n
delim_one && first && print(io, delim)
break
end
first = false
print(io, delim)
if multiline
println(io); println(io)
newline = false
else
newline = true
end
end
end
end
Expand Down
56 changes: 40 additions & 16 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1192,23 +1192,22 @@ static size_t jl_show_svec(JL_STREAM *out, jl_svec_t *t, char *head, char *opn,
return n;
}

#define MAX_DEPTH 25
struct recur_list {
struct recur_list *prev;
jl_value_t *v;
};

static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, int depth);
static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, struct recur_list *depth);

// `v` might be pointing to a field inlined in a structure therefore
// `jl_typeof(v)` may not be the same with `vt` and only `vt` should be
// used to determine the type of the value.
// This is necessary to make sure that this function doesn't allocate any
// memory through the Julia GC
static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v,
jl_datatype_t *vt, int depth)
static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt,
struct recur_list *depth)
{
if (depth > MAX_DEPTH) { // cheap way of bailing out of cycles
return jl_printf(out, "•");
}
size_t n = 0;
depth++;
if ((uintptr_t)vt < 4096U) {
n += jl_printf(out, "<?#%p::%p>", v, vt);
}
Expand Down Expand Up @@ -1304,7 +1303,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v,
#endif
}
else if (vt == jl_float32_type) {
n += jl_printf(out, "%g", *(float*)v);
n += jl_printf(out, "%gf", *(float*)v);
}
else if (vt == jl_float64_type) {
n += jl_printf(out, "%g", *(double*)v);
Expand Down Expand Up @@ -1409,6 +1408,24 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v,
size_t j, tlen = jl_array_len(v);
jl_array_t *av = (jl_array_t*)v;
jl_datatype_t *el_type = (jl_datatype_t*)jl_tparam0(vt);
int nlsep = 0;
if (av->flags.ptrarray) {
// print arrays with newlines, unless the elements are probably small
for (j = 0; j < tlen; j++) {
jl_value_t *p = jl_cellref(av, j);
if (p != NULL && (uintptr_t)p >= 4096U) {
jl_value_t *p_ty = jl_typeof(p);
if ((uintptr_t)p_ty >= 4096U) {
if (!jl_isbits(p_ty)) {
nlsep = 1;
break;
}
}
}
}
}
if (nlsep && tlen > 1)
n += jl_printf(out, "\n ");
for (j = 0; j < tlen; j++) {
if (av->flags.ptrarray) {
n += jl_static_show_x(out, jl_cellref(v, j), depth);
Expand All @@ -1417,10 +1434,9 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v,
char *ptr = ((char*)av->data) + j * av->elsize;
n += jl_static_show_x_(out, (jl_value_t*)ptr, el_type, depth);
}
if (j != tlen-1)
n += jl_printf(out, ", ");
if (j != tlen - 1)
n += jl_printf(out, nlsep ? ",\n " : ", ");
}
if (j < tlen) n += jl_printf(out, " ...");
n += jl_printf(out, "]");
}
else if (vt == jl_loaderror_type) {
Expand Down Expand Up @@ -1483,17 +1499,25 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v,
return n;
}

static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, int depth)
static size_t jl_static_show_x(JL_STREAM *out, jl_value_t *v, struct recur_list *depth)
{
// mimic jl_show, but never calling a julia method and
// (hopefully) never allocate through julia gc
// never allocate through julia gc
if (v == NULL) {
return jl_printf(out, "#<null>");
}
else if ((uintptr_t)v < 4096U) {
return jl_printf(out, "#<%d>", (int)(uintptr_t)v);
}
return jl_static_show_x_(out, v, (jl_datatype_t*)jl_typeof(v), depth);
unsigned int dist = 1;
struct recur_list this_item = {depth, v}, *p = depth;
while (p) {
if (p->v == v)
return jl_printf(out, "<circular reference @-%u>", dist);
dist++;
p = p->prev;
}
return jl_static_show_x_(out, v, (jl_datatype_t*)jl_typeof(v), &this_item);
}

JL_DLLEXPORT size_t jl_static_show(JL_STREAM *out, jl_value_t *v)
Expand Down Expand Up @@ -1544,7 +1568,7 @@ JL_DLLEXPORT void jl_(void *jl_value)
jl_jmp_buf buf;
jl_safe_restore = &buf;
if (!jl_setjmp(buf, 0)) {
(void)jl_static_show((JL_STREAM*)STDERR_FILENO, (jl_value_t*)jl_value);
jl_static_show((JL_STREAM*)STDERR_FILENO, (jl_value_t*)jl_value);
jl_printf((JL_STREAM*)STDERR_FILENO,"\n");
}
else {
Expand Down
6 changes: 6 additions & 0 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -408,3 +408,9 @@ A = reshape(1:16,4,4)
@test sprint(show, :(break)) == ":(break)"
@test_repr "continue"
@test_repr "break"

let x = [], y = []
push!(x, y)
push!(y, x)
@test replstr(x) == "1-element Array{Any,1}:\n Any[Any[Any[#= circular reference @-2 =#]]]"
end

0 comments on commit 6168da5

Please sign in to comment.