2 * Helpers for creating and querying pc2line debug data, which
3 * converts a bytecode program counter to a source line number.
5 * The run-time pc2line data is bit-packed, and documented in:
7 * doc/function-objects.rst
10 #include "duk_internal.h"
12 #if defined(DUK_USE_PC2LINE)
14 /* Generate pc2line data for an instruction sequence, leaving a buffer on stack top. */
15 DUK_INTERNAL
void duk_hobject_pc2line_pack(duk_hthread
*thr
, duk_compiler_instr
*instrs
, duk_uint_fast32_t length
) {
16 duk_context
*ctx
= (duk_context
*) thr
;
17 duk_hbuffer_dynamic
*h_buf
;
18 duk_bitencoder_ctx be_ctx_alloc
;
19 duk_bitencoder_ctx
*be_ctx
= &be_ctx_alloc
;
22 duk_uint_fast32_t num_header_entries
;
23 duk_uint_fast32_t curr_offset
;
24 duk_int_fast32_t curr_line
, next_line
, diff_line
;
25 duk_uint_fast32_t curr_pc
;
26 duk_uint_fast32_t hdr_index
;
28 DUK_ASSERT(length
<= DUK_COMPILER_MAX_BYTECODE_LENGTH
);
30 /* XXX: add proper spare handling to dynamic buffer, to minimize
31 * reallocs; currently there is no spare at all.
34 num_header_entries
= (length
+ DUK_PC2LINE_SKIP
- 1) / DUK_PC2LINE_SKIP
;
35 curr_offset
= (duk_uint_fast32_t
) (sizeof(duk_uint32_t
) + num_header_entries
* sizeof(duk_uint32_t
) * 2);
37 duk_push_dynamic_buffer(ctx
, (duk_size_t
) curr_offset
);
38 h_buf
= (duk_hbuffer_dynamic
*) duk_get_hbuffer(ctx
, -1);
39 DUK_ASSERT(h_buf
!= NULL
);
40 DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h_buf
) && !DUK_HBUFFER_HAS_EXTERNAL(h_buf
));
42 hdr
= (duk_uint32_t
*) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr
->heap
, h_buf
);
43 DUK_ASSERT(hdr
!= NULL
);
44 hdr
[0] = (duk_uint32_t
) length
; /* valid pc range is [0, length[ */
47 while (curr_pc
< length
) {
48 new_size
= (duk_size_t
) (curr_offset
+ DUK_PC2LINE_MAX_DIFF_LENGTH
);
49 duk_hbuffer_resize(thr
, h_buf
, new_size
);
51 hdr
= (duk_uint32_t
*) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr
->heap
, h_buf
);
52 DUK_ASSERT(hdr
!= NULL
);
53 DUK_ASSERT(curr_pc
< length
);
54 hdr_index
= 1 + (curr_pc
/ DUK_PC2LINE_SKIP
) * 2;
55 curr_line
= (duk_int_fast32_t
) instrs
[curr_pc
].line
;
56 hdr
[hdr_index
+ 0] = (duk_uint32_t
) curr_line
;
57 hdr
[hdr_index
+ 1] = (duk_uint32_t
) curr_offset
;
60 DUK_DDD(DUK_DDDPRINT("hdr[%ld]: pc=%ld line=%ld offset=%ld",
61 (long) (curr_pc
/ DUK_PC2LINE_SKIP
),
63 (long) hdr
[hdr_index
+ 0],
64 (long) hdr
[hdr_index
+ 1]));
67 DUK_MEMZERO(be_ctx
, sizeof(*be_ctx
));
68 be_ctx
->data
= ((duk_uint8_t
*) hdr
) + curr_offset
;
69 be_ctx
->length
= (duk_size_t
) DUK_PC2LINE_MAX_DIFF_LENGTH
;
73 if ( ((curr_pc
% DUK_PC2LINE_SKIP
) == 0) || /* end of diff run */
74 (curr_pc
>= length
) ) { /* end of bytecode */
77 DUK_ASSERT(curr_pc
< length
);
78 next_line
= (duk_int32_t
) instrs
[curr_pc
].line
;
79 diff_line
= next_line
- curr_line
;
82 DUK_DDD(DUK_DDDPRINT("curr_line=%ld, next_line=%ld -> diff_line=%ld",
83 (long) curr_line
, (long) next_line
, (long) diff_line
));
88 duk_be_encode(be_ctx
, 0, 1);
89 } else if (diff_line
>= 1 && diff_line
<= 4) {
91 duk_be_encode(be_ctx
, (0x02 << 2) + (diff_line
- 1), 4);
92 } else if (diff_line
>= -0x80 && diff_line
<= 0x7f) {
94 DUK_ASSERT(diff_line
+ 0x80 >= 0 && diff_line
+ 0x80 <= 0xff);
95 duk_be_encode(be_ctx
, (0x06 << 8) + (diff_line
+ 0x80), 11);
98 * Encode in two parts to avoid bitencode 24-bit limitation
100 duk_be_encode(be_ctx
, (0x07 << 16) + ((next_line
>> 16) & 0xffffU
), 19);
101 duk_be_encode(be_ctx
, next_line
& 0xffffU
, 16);
104 curr_line
= next_line
;
107 duk_be_finish(be_ctx
);
108 DUK_ASSERT(!be_ctx
->truncated
);
110 /* be_ctx->offset == length of encoded bitstream */
111 curr_offset
+= (duk_uint_fast32_t
) be_ctx
->offset
;
115 new_size
= (duk_size_t
) curr_offset
;
116 duk_hbuffer_resize(thr
, h_buf
, new_size
);
118 (void) duk_to_fixed_buffer(ctx
, -1, NULL
);
120 DUK_DDD(DUK_DDDPRINT("final pc2line data: pc_limit=%ld, length=%ld, %lf bits/opcode --> %!ixT",
121 (long) length
, (long) new_size
, (double) new_size
* 8.0 / (double) length
,
122 (duk_tval
*) duk_get_tval(ctx
, -1)));
125 /* PC is unsigned. If caller does PC arithmetic and gets a negative result,
126 * it will map to a large PC which is out of bounds and causes a zero to be
129 DUK_LOCAL duk_uint_fast32_t
duk__hobject_pc2line_query_raw(duk_hthread
*thr
, duk_hbuffer_fixed
*buf
, duk_uint_fast32_t pc
) {
130 duk_bitdecoder_ctx bd_ctx_alloc
;
131 duk_bitdecoder_ctx
*bd_ctx
= &bd_ctx_alloc
;
133 duk_uint_fast32_t start_offset
;
134 duk_uint_fast32_t pc_limit
;
135 duk_uint_fast32_t hdr_index
;
136 duk_uint_fast32_t pc_base
;
138 duk_uint_fast32_t curr_line
;
140 DUK_ASSERT(buf
!= NULL
);
141 DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer
*) buf
) && !DUK_HBUFFER_HAS_EXTERNAL((duk_hbuffer
*) buf
));
145 * Use the index in the header to find the right starting point
148 hdr_index
= pc
/ DUK_PC2LINE_SKIP
;
149 pc_base
= hdr_index
* DUK_PC2LINE_SKIP
;
152 if (DUK_HBUFFER_FIXED_GET_SIZE(buf
) <= sizeof(duk_uint32_t
)) {
153 DUK_DD(DUK_DDPRINT("pc2line lookup failed: buffer is smaller than minimal header"));
157 hdr
= (duk_uint32_t
*) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr
->heap
, buf
);
159 if (pc
>= pc_limit
) {
160 /* Note: pc is unsigned and cannot be negative */
161 DUK_DD(DUK_DDPRINT("pc2line lookup failed: pc out of bounds (pc=%ld, limit=%ld)",
162 (long) pc
, (long) pc_limit
));
166 curr_line
= hdr
[1 + hdr_index
* 2];
167 start_offset
= hdr
[1 + hdr_index
* 2 + 1];
168 if ((duk_size_t
) start_offset
> DUK_HBUFFER_FIXED_GET_SIZE(buf
)) {
169 DUK_DD(DUK_DDPRINT("pc2line lookup failed: start_offset out of bounds (start_offset=%ld, buffer_size=%ld)",
170 (long) start_offset
, (long) DUK_HBUFFER_GET_SIZE((duk_hbuffer
*) buf
)));
175 * Iterate the bitstream (line diffs) until PC is reached
178 DUK_MEMZERO(bd_ctx
, sizeof(*bd_ctx
));
179 bd_ctx
->data
= ((duk_uint8_t
*) hdr
) + start_offset
;
180 bd_ctx
->length
= (duk_size_t
) (DUK_HBUFFER_FIXED_GET_SIZE(buf
) - start_offset
);
183 DUK_DDD(DUK_DDDPRINT("pc2line lookup: pc=%ld -> hdr_index=%ld, pc_base=%ld, n=%ld, start_offset=%ld",
184 (long) pc
, (long) hdr_index
, (long) pc_base
, (long) n
, (long) start_offset
));
189 DUK_DDD(DUK_DDDPRINT("lookup: n=%ld, curr_line=%ld", (long) n
, (long) curr_line
));
192 if (duk_bd_decode_flag(bd_ctx
)) {
193 if (duk_bd_decode_flag(bd_ctx
)) {
194 if (duk_bd_decode_flag(bd_ctx
)) {
195 /* 1 1 1 <32 bits> */
197 t
= duk_bd_decode(bd_ctx
, 16); /* workaround: max nbits = 24 now */
198 t
= (t
<< 16) + duk_bd_decode(bd_ctx
, 16);
203 t
= duk_bd_decode(bd_ctx
, 8);
204 curr_line
= curr_line
+ t
- 0x80;
209 t
= duk_bd_decode(bd_ctx
, 2);
210 curr_line
= curr_line
+ t
+ 1;
219 DUK_DDD(DUK_DDDPRINT("pc2line lookup result: pc %ld -> line %ld", (long) pc
, (long) curr_line
));
223 DUK_D(DUK_DPRINT("pc2line conversion failed for pc=%ld", (long) pc
));
227 DUK_INTERNAL duk_uint_fast32_t
duk_hobject_pc2line_query(duk_context
*ctx
, duk_idx_t idx_func
, duk_uint_fast32_t pc
) {
228 duk_hbuffer_fixed
*pc2line
;
229 duk_uint_fast32_t line
;
231 /* XXX: now that pc2line is used by the debugger quite heavily in
232 * checked execution, this should be optimized to avoid value stack
233 * and perhaps also implement some form of pc2line caching (see
234 * future work in debugger.rst).
237 duk_get_prop_stridx(ctx
, idx_func
, DUK_STRIDX_INT_PC2LINE
);
238 pc2line
= (duk_hbuffer_fixed
*) duk_get_hbuffer(ctx
, -1);
239 if (pc2line
!= NULL
) {
240 DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer
*) pc2line
) && !DUK_HBUFFER_HAS_EXTERNAL((duk_hbuffer
*) pc2line
));
241 line
= duk__hobject_pc2line_query_raw((duk_hthread
*) ctx
, pc2line
, (duk_uint_fast32_t
) pc
);
250 #endif /* DUK_USE_PC2LINE */