Skip to content

Commit 6f44ff8

Browse files
committed
Merge pull request #1557 from pguyot/w10/add-support-for-large-line-numbers
Add support for large line numbers in stack traces (up to 2147483647) These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents bc21d17 + 5836553 commit 6f44ff8

File tree

5 files changed

+2109
-62
lines changed

5 files changed

+2109
-62
lines changed

src/libAtomVM/module.c

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@
4343
#define LITT_UNCOMPRESSED_SIZE_OFFSET 8
4444
#define LITT_HEADER_SIZE 12
4545

46-
// TODO Constants similar to these are defined in opcodesswitch.h and should
47-
// be refactored so they can be used here, as well.
48-
#define TAG_COMPACT_INT 0x01
49-
#define TAG_COMPACT_ATOM 0x02
50-
#define TAG_EXTENDED_INT 0x09
51-
#define TAG_EXTENDED_ATOM 0x0A
52-
5346
#define CHECK_FREE_SPACE(space, error) \
5447
if ((size_t) ((pos + space) - data) > len) { \
5548
fprintf(stderr, error); \
@@ -449,22 +442,39 @@ static bool module_check_line_refs(Module *mod, const uint8_t **data, size_t len
449442
}
450443
uint8_t tag = *pos;
451444
switch (tag & 0x0F) {
452-
case TAG_COMPACT_INT: {
445+
case COMPACT_INTEGER: {
453446
++i;
454447
++pos;
455448
break;
456449
}
457-
case TAG_EXTENDED_INT: {
450+
case COMPACT_LARGE_INTEGER: {
458451
++pos;
452+
switch (tag & COMPACT_LARGE_IMM_MASK) {
453+
case COMPACT_11BITS_VALUE: {
454+
++pos;
455+
break;
456+
}
457+
case COMPACT_NBITS_VALUE: {
458+
int sz = (tag >> 5) + 2;
459+
if (UNLIKELY(sz > 4)) {
460+
fprintf(stderr, "Invalid line_ref: expected extended int with sz <= 4 (line number <= 2^31)");
461+
return false;
462+
}
463+
pos += sz;
464+
break;
465+
}
466+
default:
467+
fprintf(stderr, "Invalid line_ref: expected extended int -- tag = %u", (unsigned int) tag);
468+
return false;
469+
}
459470
if ((size_t) (pos - *data) > len) {
460471
fprintf(stderr, "Invalid line_ref: expected extended int.\n");
461472
return false;
462473
}
463474
++i;
464-
++pos;
465475
break;
466476
}
467-
case TAG_COMPACT_ATOM: {
477+
case COMPACT_ATOM: {
468478
uint16_t location_ix = ((tag & 0xF0) >> 4);
469479
if (location_ix > mod->locations_count) {
470480
fprintf(stderr, "Invalid line_ref: location_ix = %d is greater than locations_count = %d.\n", (int) location_ix, (int) mod->locations_count);
@@ -473,11 +483,16 @@ static bool module_check_line_refs(Module *mod, const uint8_t **data, size_t len
473483
++pos;
474484
break;
475485
}
476-
case TAG_EXTENDED_ATOM: {
486+
case COMPACT_LARGE_ATOM: {
487+
// We don't support more than 11bits (2048) locations.
488+
if (UNLIKELY((tag & COMPACT_LARGE_IMM_MASK) != COMPACT_11BITS_VALUE)) {
489+
fprintf(stderr, "Invalid line_ref: location_ix is larger than 2048.\n");
490+
return false;
491+
}
477492
uint16_t high_order_3_bits = (tag & 0xE0);
478493
++pos;
479494
if ((size_t) (pos - *data) > len) {
480-
fprintf(stderr, "Invalid line_ref: expected extended int.\n");
495+
fprintf(stderr, "Invalid line_ref: expected extended atom.\n");
481496
return false;
482497
}
483498
uint8_t next_byte = *pos;
@@ -490,7 +505,6 @@ static bool module_check_line_refs(Module *mod, const uint8_t **data, size_t len
490505
break;
491506
}
492507
default:
493-
// TODO handle integer compact encodings > 2048
494508
fprintf(stderr, "Unsupported line_ref tag: %u\n", tag);
495509
return false;
496510
}
@@ -520,7 +534,7 @@ static bool module_check_locations(Module *mod, const uint8_t *data, size_t len)
520534
return true;
521535
}
522536

523-
static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint16_t *out_line, uint16_t *out_location)
537+
static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint32_t *out_line, uint16_t *out_location)
524538
{
525539
// First is undefined
526540
if (line_ref == 0) {
@@ -535,9 +549,9 @@ static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint16_t *out_li
535549
while (i <= mod->line_refs_count) {
536550
uint8_t tag = *pos;
537551
switch (tag & 0x0F) {
538-
case TAG_COMPACT_INT: {
552+
case COMPACT_INTEGER: {
539553
if (i == line_ref) {
540-
uint16_t line_idx = ((tag & 0xF0) >> 4);
554+
uint32_t line_idx = ((tag & 0xF0) >> 4);
541555
*out_line = line_idx;
542556
*out_location = location_ix;
543557
return true;
@@ -546,32 +560,49 @@ static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint16_t *out_li
546560
++pos;
547561
break;
548562
}
549-
case TAG_EXTENDED_INT: {
563+
case COMPACT_LARGE_INTEGER: {
564+
uint32_t line_idx;
565+
switch (tag & COMPACT_LARGE_IMM_MASK) {
566+
case COMPACT_11BITS_VALUE: {
567+
uint16_t high_order_3_bits = (tag & 0xE0);
568+
line_idx = ((high_order_3_bits << 3) | pos[1]);
569+
pos += 2;
570+
break;
571+
}
572+
case COMPACT_NBITS_VALUE: {
573+
pos++;
574+
int sz = (tag >> 5) + 2;
575+
line_idx = 0;
576+
for (int i = 0; i < sz; i++) {
577+
line_idx = line_idx * 256 + pos[i];
578+
}
579+
pos += sz;
580+
break;
581+
}
582+
default:
583+
UNREACHABLE();
584+
}
550585
if (i == line_ref) {
551-
uint16_t high_order_3_bits = (tag & 0xE0);
552-
uint16_t line_idx = ((high_order_3_bits << 3) | pos[1]);
553586
*out_line = line_idx;
554587
*out_location = location_ix;
555588
return true;
556589
}
557-
pos += 2;
558590
++i;
559591
break;
560592
}
561-
case TAG_COMPACT_ATOM: {
593+
case COMPACT_ATOM: {
562594
location_ix = ((tag & 0xF0) >> 4);
563595
++pos;
564596
break;
565597
}
566-
case TAG_EXTENDED_ATOM: {
598+
case COMPACT_LARGE_ATOM: {
567599
uint16_t high_order_3_bits = (tag & 0xE0);
568600
location_ix = ((high_order_3_bits << 3) | pos[1]);
569601
pos += 2;
570602
break;
571603
}
572604
default:
573-
// TODO handle integer compact encodings > 2048
574-
return false;
605+
UNREACHABLE();
575606
}
576607
}
577608

@@ -680,7 +711,7 @@ void module_insert_line_ref_offset(Module *mod, int line_ref, int offset)
680711
list_append(&mod->line_ref_offsets, &ref_offset->head);
681712
}
682713

683-
static bool module_find_line_ref(Module *mod, uint16_t line_ref, uint16_t *line, size_t *filename_len, const uint8_t **filename)
714+
static bool module_find_line_ref(Module *mod, uint16_t line_ref, uint32_t *line, size_t *filename_len, const uint8_t **filename)
684715
{
685716
uint16_t location_ix;
686717
if (UNLIKELY(!module_get_line_ref(mod, line_ref, line, &location_ix))) {
@@ -689,7 +720,7 @@ static bool module_find_line_ref(Module *mod, uint16_t line_ref, uint16_t *line,
689720
return module_get_location(mod, location_ix, filename_len, filename);
690721
}
691722

692-
bool module_find_line(Module *mod, unsigned int offset, uint16_t *line, size_t *filename_len, const uint8_t **filename)
723+
bool module_find_line(Module *mod, unsigned int offset, uint32_t *line, size_t *filename_len, const uint8_t **filename)
693724
{
694725
int i = 0;
695726
struct LineRefOffset *head = GET_LIST_ENTRY(&mod->line_ref_offsets, struct LineRefOffset, head);

src/libAtomVM/module.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ void module_insert_line_ref_offset(Module *mod, int line_ref, int offset);
410410
* @param filename on output the filename or NULL if it's module.erl. Can be NULL.
411411
* @return \c true if the line was found
412412
*/
413-
bool module_find_line(Module *mod, unsigned int offset, uint16_t *line, size_t *filename_len, const uint8_t **filename);
413+
bool module_find_line(Module *mod, unsigned int offset, uint32_t *line, size_t *filename_len, const uint8_t **filename);
414414

415415
/**
416416
* @return true if the module has line information, false, otherwise.

src/libAtomVM/stacktrace.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e
139139
prev_mod = cp_mod;
140140
prev_mod_offset = mod_offset;
141141
if (module_has_line_chunk(cp_mod)) {
142-
uint16_t line;
142+
uint32_t line;
143143
const uint8_t *filename;
144144
size_t filename_len;
145145
if (LIKELY(module_find_line(cp_mod, (unsigned int) mod_offset, &line, &filename_len, &filename))) {
@@ -163,7 +163,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e
163163
prev_mod = cl_mod;
164164
prev_mod_offset = mod_offset;
165165
if (module_has_line_chunk(cl_mod)) {
166-
uint16_t line;
166+
uint32_t line;
167167
const uint8_t *filename;
168168
size_t filename_len;
169169
if (LIKELY(module_find_line(cl_mod, (unsigned int) mod_offset, &line, &filename_len, &filename))) {
@@ -180,7 +180,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e
180180

181181
num_frames++;
182182
if (module_has_line_chunk(mod)) {
183-
uint16_t line;
183+
uint32_t line;
184184
const uint8_t *filename;
185185
size_t filename_len;
186186
if (LIKELY(module_find_line(mod, (unsigned int) current_offset, &line, &filename_len, &filename))) {
@@ -342,7 +342,7 @@ term stacktrace_build(Context *ctx, term *stack_info, uint32_t live)
342342

343343
term aux_data = term_nil();
344344
if (module_has_line_chunk(cp_mod)) {
345-
uint16_t line;
345+
uint32_t line;
346346
const uint8_t *filename;
347347
size_t filename_len;
348348
if (LIKELY(module_find_line(cp_mod, (unsigned int) mod_offset, &line, &filename_len, &filename))) {

tests/erlang_tests/test_stacktrace.erl

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
-module(test_stacktrace).
2121

2222
-export([start/0, maybe_crash/1]).
23+
-export([
24+
throw_with_file_and_line/0,
25+
throw_with_other_file_and_line/0,
26+
throw_with_other_file_and_line_large_value/0
27+
]).
2328

2429
-include("test_stacktrace.hrl").
2530

@@ -36,6 +41,7 @@ start() ->
3641
ok = test_catch(),
3742
ok = maybe_test_filelineno(),
3843
ok = maybe_test_filelineno_other_file(),
44+
ok = maybe_test_filelineno_large(),
3945
0.
4046

4147
test_local_throw() ->
@@ -233,15 +239,15 @@ test_catch() ->
233239
do_some_stuff(Result),
234240
Result.
235241

236-
maybe_test_filelineno() ->
242+
maybe_test_filelineno(Fun) ->
237243
ok =
238244
try
239-
throw_with_file_and_line(),
245+
?MODULE:Fun(),
240246
fail
241247
catch
242248
throw:{File, Line}:Stacktrace ->
243249
[Frame | _] = Stacktrace,
244-
{?MODULE, throw_with_file_and_line, 0, AuxData} = Frame,
250+
{?MODULE, Fun, 0, AuxData} = Frame,
245251
case {get_value(file, AuxData), get_value(line, AuxData)} of
246252
{undefined, undefined} ->
247253
ok;
@@ -262,34 +268,16 @@ maybe_test_filelineno() ->
262268
end
263269
end.
264270

271+
maybe_test_filelineno() ->
272+
maybe_test_filelineno(throw_with_file_and_line).
273+
265274
maybe_test_filelineno_other_file() ->
266-
ok =
267-
try
268-
throw_with_other_file_and_line(),
269-
fail
270-
catch
271-
throw:{File, Line}:Stacktrace ->
272-
[Frame | _] = Stacktrace,
273-
{?MODULE, throw_with_other_file_and_line, 0, AuxData} = Frame,
274-
case {get_value(file, AuxData), get_value(line, AuxData)} of
275-
{undefined, undefined} ->
276-
ok;
277-
{F, L} ->
278-
Ef =
279-
case is_binary(F) of
280-
true ->
281-
erlang:binary_to_list(F);
282-
_ ->
283-
F
284-
end,
285-
case File == Ef andalso Line == L of
286-
true ->
287-
ok;
288-
_ ->
289-
{unexpected_file_line, F, L}
290-
end
291-
end
292-
end.
275+
maybe_test_filelineno(throw_with_other_file_and_line).
276+
277+
% This test actually succeeds even if large line numbers are not supported
278+
% because all line numbers are then disabled.
279+
maybe_test_filelineno_large() ->
280+
maybe_test_filelineno(throw_with_other_file_and_line_large_value).
293281

294282
get_value(_Key, []) ->
295283
undefined;

0 commit comments

Comments
 (0)