Skip to content

Commit f0889c8

Browse files
committed
Make binary values structured
Summary: # Context When displaying variables, evaluation results, etc, values that have structured are shown truncated with ellipsis, and can then be expanded to show subfields, etc. # Problem Binary values are currently treated as an atomic value and have no structure. That means that it is not possible to observe the rest of the values. Moreover, because we only provide an `evaluateName` only for structured values, it is also not possible to copy it to the clipboard completely. # This diff Make binary values expandable. Notice that `binary:at()` indexes from 0, not 1, so unlike lists, we number elements from 0. Reviewed By: michalmuskala Differential Revision: D84987599 fbshipit-source-id: 0ba7e4d32b8c2b50372a17c3afe5ef161d8602ae
1 parent 896f6ac commit f0889c8

2 files changed

Lines changed: 110 additions & 43 deletions

File tree

edb/src/edb_dap_eval_delegate.erl

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ structure_callback(Accessor, EvalName, Window) ->
312312
List when is_list(List) -> list_structure(List, Accessor, EvalName, Window);
313313
Tuple when is_tuple(Tuple) -> tuple_structure(Tuple, Accessor, EvalName, Window);
314314
Map when is_map(Map) -> map_structure(Map, Accessor, EvalName, Window);
315-
Fun when is_function(Fun) -> fun_structure(Fun, Accessor, EvalName, Window);
315+
Bin when is_binary(Bin) -> binary_structure(Bin, Window);
316+
Fun when is_function(Fun) -> fun_structure(Fun, Accessor, EvalName);
316317
_ -> []
317318
end
318319
end
@@ -408,26 +409,11 @@ slice_map_iterator(Iterator0, 1, Count) when is_integer(Count) ->
408409
{K, V, Iterator1} -> [{K, V} | slice_map_iterator(Iterator1, 1, Count - 1)]
409410
end.
410411

411-
-spec extend_accessor(Accessor, EvalName, Step, StepStr) -> {accessor(), eval_name()} when
412-
Accessor :: accessor(),
413-
EvalName :: eval_name(),
414-
Step :: fun((term()) -> term()),
415-
StepStr :: fun((binary()) -> binary()).
416-
extend_accessor(Accessor, EvalName, Step, StepStr) ->
417-
Accessor1 = fun(StackFrameVars) -> Step(Accessor(StackFrameVars)) end,
418-
EvalName1 =
419-
case EvalName of
420-
none -> none;
421-
_ -> StepStr(EvalName)
422-
end,
423-
{Accessor1, EvalName1}.
424-
425-
-spec fun_structure(Fun, Accessor, EvalName, Window) -> [variable()] when
412+
-spec fun_structure(Fun, Accessor, EvalName) -> [variable()] when
426413
Fun :: fun(),
427414
Accessor :: accessor(),
428-
EvalName :: eval_name(),
429-
Window :: window().
430-
fun_structure(Fun, Accessor, EvalName, _Window) ->
415+
EvalName :: eval_name().
416+
fun_structure(Fun, Accessor, EvalName) ->
431417
FunInfo = maps:from_list(erlang:fun_info(Fun)),
432418
EnvStep = fun(_) -> maps:get(env, FunInfo) end,
433419
EnvStepStr = fun(E) -> format("erlang:element(2, erlang:fun_info(~s, env))", [E]) end,
@@ -450,6 +436,42 @@ fun_structure(Fun, Accessor, EvalName, _Window) ->
450436
}
451437
].
452438

439+
-spec binary_structure(Bin, Window) -> [variable()] when
440+
Bin :: binary(),
441+
Window :: window().
442+
binary_structure(Bin, Window) ->
443+
Indices =
444+
case Window of
445+
#{start := Start, count := infinity} ->
446+
lists:seq(Start - 1, byte_size(Bin) - 1);
447+
#{start := Start, count := Count} ->
448+
End = min(byte_size(Bin), Start + Count - 1) - 1,
449+
lists:seq(Start - 1, End)
450+
end,
451+
[
452+
#{
453+
name => integer_to_binary(Index),
454+
value_rep => value_rep({value, Value}),
455+
structure => none
456+
}
457+
|| Index <- Indices,
458+
Value <- [binary:at(Bin, Index)]
459+
].
460+
461+
-spec extend_accessor(Accessor, EvalName, Step, StepStr) -> {accessor(), eval_name()} when
462+
Accessor :: accessor(),
463+
EvalName :: eval_name(),
464+
Step :: fun((term()) -> term()),
465+
StepStr :: fun((binary()) -> binary()).
466+
extend_accessor(Accessor, EvalName, Step, StepStr) ->
467+
Accessor1 = fun(StackFrameVars) -> Step(Accessor(StackFrameVars)) end,
468+
EvalName1 =
469+
case EvalName of
470+
none -> none;
471+
_ -> StepStr(EvalName)
472+
end,
473+
{Accessor1, EvalName1}.
474+
453475
% -----------------------------------------------------------------------------
454476
% Callback for the "evaluate" request
455477
% -----------------------------------------------------------------------------
@@ -549,6 +571,12 @@ structure({value, Val}, {ValAccessor, EvalName}) when is_map(Val), map_size(Val)
549571
accessor => ValAccessor,
550572
evaluate_name => EvalName
551573
};
574+
structure({value, Val}, {ValAccessor, EvalName}) when is_binary(Val) ->
575+
#{
576+
count => byte_size(Val),
577+
accessor => ValAccessor,
578+
evaluate_name => EvalName
579+
};
552580
structure({value, Val}, {ValAccessor, EvalName}) when is_function(Val) ->
553581
NumItemsInExpansionOfClosure = 2,
554582
#{

edb/test/edb_dap_scopes_SUITE.erl

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,18 @@ test_structured_variables(Config) ->
125125
modules => [
126126
{source, [~"""
127127
-module(foo). %L01\n
128-
-export([go/2]). %L02\n
129-
go(L, X) -> %L03\n
128+
-export([go/3]). %L02\n
129+
go(L, X, B) -> %L03\n
130130
M = [4, [], {6, 7}], %L04\n
131131
F = fun(_A) -> length(L) == length(M) orelse ok end, %L05\n
132132
persistent_term:put(fun_repr, io_lib:format("~p", [F])),%L06\n
133-
{L ++ M, X, F}. %L07\n
133+
{L ++ M, X, B, F}. %L07\n
134134
"""]}
135135
]
136136
}),
137137
ok = edb_dap_test_support:configure(Client, [{FooSrc, [{line, 7}]}]),
138138
{ok, _ThreadId, [#{id := TopFrameId} | _]} = edb_dap_test_support:spawn_and_wait_for_bp(
139-
Client, Peer, {foo, go, [[1, 2, 3], #{life => 42}]}
139+
Client, Peer, {foo, go, [[1, 2, 3], #{life => 42}, ~"abcdefghijklmnopqrstuvwxyz"]}
140140
),
141141

142142
Scopes = edb_dap_test_support:get_scopes(Client, TopFrameId),
@@ -147,13 +147,13 @@ test_structured_variables(Config) ->
147147
name => ~"Locals",
148148
expensive => false,
149149
presentationHint => ~"locals",
150-
variablesReference => 5
150+
variablesReference => 6
151151
},
152152
~"Process" =>
153153
#{
154154
name => ~"Process",
155155
expensive => false,
156-
variablesReference => 7
156+
variablesReference => 8
157157
}
158158
},
159159
Scopes
@@ -166,29 +166,35 @@ test_structured_variables(Config) ->
166166

167167
?assertEqual(
168168
#{
169+
~"B" => #{
170+
name => ~"B",
171+
value => ~"<<\"abcdefghijklmnop\"...>>",
172+
evaluateName => ~"B",
173+
variablesReference => 1
174+
},
169175
~"F" => #{
170176
name => ~"F",
171177
value => format("~s", [FunRepr]),
172178
evaluateName => ~"F",
173-
variablesReference => 1
179+
variablesReference => 2
174180
},
175181
~"L" => #{
176182
name => ~"L",
177183
value => ~"[1,2,3]",
178184
evaluateName => ~"L",
179-
variablesReference => 2
185+
variablesReference => 3
180186
},
181187
~"M" => #{
182188
name => ~"M",
183189
value => ~"[4,[],{6,...}]",
184190
evaluateName => ~"M",
185-
variablesReference => 3
191+
variablesReference => 4
186192
},
187193
~"X" => #{
188194
name => ~"X",
189195
value => ~"#{life => 42}",
190196
evaluateName => ~"X",
191-
variablesReference => 4
197+
variablesReference => 5
192198
}
193199
},
194200
LocalVars
@@ -204,7 +210,7 @@ test_structured_variables(Config) ->
204210
name => ~"3",
205211
value => ~"{6,7}",
206212
evaluateName => ~"lists:nth(3, M)",
207-
variablesReference => 8
213+
variablesReference => 9
208214
}
209215
},
210216
ChildrenListVars
@@ -219,19 +225,33 @@ test_structured_variables(Config) ->
219225
ChildrenMapVars
220226
),
221227

228+
ChildBinaryVarRef = maps:get(variablesReference, maps:get(~"B", LocalVars)),
229+
ChildrenBinaryVars = edb_dap_test_support:get_variables(Client, ChildBinaryVarRef),
230+
?assertEqual(
231+
#{
232+
integer_to_binary(I) => #{
233+
name => integer_to_binary(I),
234+
value => integer_to_binary($a + I),
235+
variablesReference => 0
236+
}
237+
|| I <- lists:seq(0, 25)
238+
},
239+
ChildrenBinaryVars
240+
),
241+
222242
ChildFunVarRef = maps:get(variablesReference, maps:get(~"F", LocalVars)),
223243
ChildrenFunVars = edb_dap_test_support:get_variables(Client, ChildFunVarRef),
224244
?assertEqual(
225245
#{
226246
~"fun" => #{
227247
name => ~"fun",
228-
value => ~"foo:'-go/2-fun-0-'/1",
248+
value => ~"foo:'-go/3-fun-0-'/1",
229249
variablesReference => 0
230250
},
231251
~"env" => #{
232252
name => ~"env",
233253
value => ~"[[1,2,3],[4,[]|...]]",
234-
variablesReference => 9,
254+
variablesReference => 10,
235255
evaluateName => ~"erlang:element(2, erlang:fun_info(F, env))"
236256
}
237257
},
@@ -263,9 +283,9 @@ test_structured_variables_with_pagination(Config) ->
263283
modules => [
264284
{source, [~"""
265285
-module(foo). %L01\n
266-
-export([go/3]). %L02\n
267-
go(L, T, M) -> %L03\n
268-
{L, T, M}. %L04\n
286+
-export([go/4]). %L02\n
287+
go(L, T, M, B) -> %L03\n
288+
{L, T, M, B}. %L04\n
269289
"""]}
270290
]
271291
}),
@@ -274,8 +294,9 @@ test_structured_variables_with_pagination(Config) ->
274294
L = [10, 20, 30, 40, 50, 60, 70, 80, 90],
275295
T = {4, [], {6, 7}, 8, {}, 9},
276296
M = #{life => 42, death => 43, etc => 44, more => {45, 46, 47}, universe => 99},
297+
B = ~"abcdefghijklmnopqrstuvwxyz",
277298
{ok, _ThreadId, [#{id := TopFrameId} | _]} = edb_dap_test_support:spawn_and_wait_for_bp(
278-
Client, Peer, {foo, go, [L, T, M]}
299+
Client, Peer, {foo, go, [L, T, M, B]}
279300
),
280301

281302
% When client supports variable paging, scopes include variable count
@@ -285,8 +306,8 @@ test_structured_variables_with_pagination(Config) ->
285306
name => ~"Locals",
286307
expensive => false,
287308
presentationHint => ~"locals",
288-
variablesReference => 4,
289-
indexedVariables => 3
309+
variablesReference => 5,
310+
indexedVariables => 4
290311
},
291312
LocalsScope
292313
),
@@ -300,22 +321,29 @@ test_structured_variables_with_pagination(Config) ->
300321
name => ~"L",
301322
evaluateName => ~"L",
302323
value => ~"[10,20,30,40|...]",
303-
variablesReference => 1,
324+
variablesReference => 2,
304325
indexedVariables => 9
305326
},
306327
~"T" => #{
307328
name => ~"T",
308329
evaluateName => ~"T",
309330
value => ~"{4,[],{6,...},8,...}",
310-
variablesReference => 3,
331+
variablesReference => 4,
311332
indexedVariables => 6
312333
},
313334
~"M" => #{
314335
name => ~"M",
315336
evaluateName => ~"M",
316337
value => ~"#{death => 43,etc => 44,life => 42,more => {45,46,47},...}",
317-
variablesReference => 2,
338+
variablesReference => 3,
318339
indexedVariables => 5
340+
},
341+
~"B" => #{
342+
name => ~"B",
343+
evaluateName => ~"B",
344+
value => ~"<<\"abcdefghijklmnop\"...>>",
345+
variablesReference => 1,
346+
indexedVariables => 26
319347
}
320348
},
321349
LocalVars
@@ -341,7 +369,7 @@ test_structured_variables_with_pagination(Config) ->
341369
name => ~"3",
342370
value => ~"{6,7}",
343371
evaluateName => ~"erlang:element(3, T)",
344-
variablesReference => 7,
372+
variablesReference => 8,
345373
indexedVariables => 2
346374
},
347375
#{name => ~"4", value => ~"8", variablesReference => 0},
@@ -359,13 +387,24 @@ test_structured_variables_with_pagination(Config) ->
359387
#{
360388
name => ~"more",
361389
value => ~"{45,46,47}",
362-
variablesReference => 8,
390+
variablesReference => 9,
363391
evaluateName => ~"maps:get(more, M)",
364392
indexedVariables => 3
365393
}
366394
],
367395
get_variables_page(Client, MapVarsRef, #{start => 1, count => 3})
368396
),
397+
398+
% Test pagination for binaries
399+
BinVarsRef = maps:get(variablesReference, maps:get(~"B", LocalVars)),
400+
?assertEqual(
401+
[
402+
#{name => <<"1">>, value => <<"98">>, variablesReference => 0},
403+
#{name => <<"2">>, value => <<"99">>, variablesReference => 0},
404+
#{name => <<"3">>, value => <<"100">>, variablesReference => 0}
405+
],
406+
get_variables_page(Client, BinVarsRef, #{start => 1, count => 3})
407+
),
369408
ok.
370409

371410
test_reports_registers_scope_when_locals_not_available(Config) ->

0 commit comments

Comments
 (0)