1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef frontend_SourceNotes_h
#define frontend_SourceNotes_h
#include <stdint.h>
#include "jstypes.h"
typedef uint8_t jssrcnote;
namespace js {
/*
* Source notes generated along with bytecode for decompiling and debugging.
* A source note is a uint8_t with 5 bits of type and 3 of offset from the pc
* of the previous note. If 3 bits of offset aren't enough, extended delta
* notes (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset
* bits are emitted before the next note. Some notes have operand offsets
* encoded immediately after them, in note bytes or byte-triples.
*
* Source Note Extended Delta
* +7-6-5-4-3+2-1-0+ +7-6-5+4-3-2-1-0+
* |note-type|delta| |1 1| ext-delta |
* +---------+-----+ +---+-----------+
*
* At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE,
* SRC_COLSPAN, SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode.
*
* NB: the js_SrcNoteSpec array in BytecodeEmitter.cpp is indexed by this
* enum, so its initializers need to match the order here.
*/
class SrcNote {
public:
// SRC_FOR: Source note for JSOP_NOP at the top of C-style for loop,
// which is placed after init expression/declaration ops.
class For {
public:
enum Fields {
// The offset of the condition expression ops from JSOP_NOP.
CondOffset,
// The offset of the update expression ops from JSOP_NOP.
UpdateOffset,
// The offset of JSOP_GOTO/JSOP_IFNE at the end of the loop from
// JSOP_NOP.
BackJumpOffset,
Count,
};
};
// SRC_WHILE: Source note for JSOP_GOTO at the top of while loop,
// which jumps to JSOP_LOOPENTRY.
class While {
public:
enum Fields {
// The offset of JSOP_IFNE at the end of the loop from JSOP_GOTO.
BackJumpOffset,
Count,
};
};
// SRC_DO_WHILE: Source note for JSOP_LOOPHEAD at the top of do-while loop
class DoWhile {
public:
enum Fields {
// The offset of the condition ops from JSOP_LOOPHEAD.
CondOffset,
// The offset of JSOP_IFNE at the end of the loop from
// JSOP_LOOPHEAD.
BackJumpOffset,
Count,
};
};
// SRC_FOR_IN: Source note for JSOP_GOTO at the top of for-in loop,
// which jumps to JSOP_LOOPENTRY.
class ForIn {
public:
enum Fields {
// The offset of JSOP_IFEQ at the end of the loop from JSOP_GOTO.
BackJumpOffset,
Count,
};
};
// SRC_FOR_OF: Source note for JSOP_GOTO at the top of for-of loop,
// which jumps to JSOP_LOOPENTRY.
class ForOf {
public:
enum Fields {
// The offset of JSOP_IFEQ at the end of the loop from JSOP_GOTO.
BackJumpOffset,
Count,
};
};
// SRC_TABLESWITCH: Source note for JSOP_TABLESWITCH.
class TableSwitch {
public:
enum Fields {
// The offset of the end of switch (the first non-JumpTarget op
// after switch) from JSOP_TABLESWITCH.
EndOffset,
Count
};
};
// SRC_CONDSWITCH: Source note for JSOP_CONDSWITCH.
class CondSwitch {
public:
enum Fields {
// The offset of the end of switch (the first non-JumpTarget op
// after switch) from JSOP_CONDSWITCH.
EndOffset,
// The offset of JSOP_CASE for the first case from JSOP_CONDSWITCH.
FirstCaseOffset,
Count
};
};
// SRC_NEXTCASE: Source note for JSOP_CASE in a JSOP_CONDSWITCH.
class NextCase {
public:
enum Fields {
// Offset of the next JSOP_CASE from this JSOP_CASE. This field is
// 0 if this is the last JSOP_CASE.
NextCaseOffset,
Count
};
};
// SRC_TRY: Source note for JSOP_TRY.
class Try {
public:
enum Fields {
// The offset of the JSOP_GOTO at the end of the try block from
// JSOP_TRY.
EndOfTryJumpOffset,
Count
};
};
// SRC_COLSPAN: Source note for arbitrary ops.
class ColSpan {
public:
enum Fields {
// The column span (the diff between the column corresponds to the
// current op and last known column).
Span,
Count
};
};
// SRC_SETLINE: Source note for arbitrary ops.
class SetLine {
public:
enum Fields {
// The file-absolute source line number of the current op.
Line,
Count
};
};
};
// clang-format off
#define FOR_EACH_SRC_NOTE_TYPE(M) \
M(SRC_NULL, "null", 0) /* Terminates a note vector. */ \
M(SRC_IF, "if", 0) /* JSOP_IFEQ bytecode is from an if-then. */ \
M(SRC_IF_ELSE, "if-else", 0) /* JSOP_IFEQ bytecode is from an if-then-else. */ \
M(SRC_COND, "cond", 0) /* JSOP_IFEQ is from conditional ?: operator. */ \
M(SRC_FOR, "for", SrcNote::For::Count) \
M(SRC_WHILE, "while", SrcNote::While::Count) \
M(SRC_DO_WHILE, "do-while", SrcNote::DoWhile::Count) \
M(SRC_FOR_IN, "for-in", SrcNote::ForIn::Count) \
M(SRC_FOR_OF, "for-of", SrcNote::ForOf::Count) \
M(SRC_CONTINUE, "continue", 0) /* JSOP_GOTO is a continue. */ \
M(SRC_BREAK, "break", 0) /* JSOP_GOTO is a break. */ \
M(SRC_BREAK2LABEL, "break2label", 0) /* JSOP_GOTO for 'break label'. */ \
M(SRC_SWITCHBREAK, "switchbreak", 0) /* JSOP_GOTO is a break in a switch. */ \
M(SRC_TABLESWITCH, "tableswitch", SrcNote::TableSwitch::Count) \
M(SRC_CONDSWITCH, "condswitch", SrcNote::CondSwitch::Count) \
M(SRC_NEXTCASE, "nextcase", SrcNote::NextCase::Count) \
M(SRC_ASSIGNOP, "assignop", 0) /* += or another assign-op follows. */ \
M(SRC_CLASS_SPAN, "class", 2) /* The starting and ending offsets for the class, used \
for toString correctness for default ctors. */ \
M(SRC_TRY, "try", SrcNote::Try::Count) \
/* All notes above here are "gettable". See SN_IS_GETTABLE below. */ \
M(SRC_COLSPAN, "colspan", SrcNote::ColSpan::Count) \
M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \
M(SRC_SETLINE, "setline", SrcNote::SetLine::Count) \
M(SRC_BREAKPOINT, "breakpoint", 0) /* Bytecode is a recommended breakpoint. */ \
M(SRC_STEP_SEP, "step-sep", 0) /* Bytecode is the first in a new steppable area. */ \
M(SRC_XDELTA, "xdelta", 0) /* 24-31 are for extended delta notes. */
// clang-format on
enum SrcNoteType {
#define DEFINE_SRC_NOTE_TYPE(sym, name, arity) sym,
FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_TYPE)
#undef DEFINE_SRC_NOTE_TYPE
SRC_LAST,
SRC_LAST_GETTABLE = SRC_TRY
};
static_assert(SRC_XDELTA == 24, "SRC_XDELTA should be 24");
/* A source note array is terminated by an all-zero element. */
inline void SN_MAKE_TERMINATOR(jssrcnote* sn) { *sn = SRC_NULL; }
inline bool SN_IS_TERMINATOR(jssrcnote* sn) { return *sn == SRC_NULL; }
} // namespace js
#define SN_TYPE_BITS 5
#define SN_DELTA_BITS 3
#define SN_XDELTA_BITS 6
#define SN_TYPE_MASK (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS)
#define SN_DELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS))
#define SN_XDELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS))
#define SN_MAKE_NOTE(sn, t, d) \
(*(sn) = (jssrcnote)(((t) << SN_DELTA_BITS) | ((d)&SN_DELTA_MASK)))
#define SN_MAKE_XDELTA(sn, d) \
(*(sn) = (jssrcnote)((SRC_XDELTA << SN_DELTA_BITS) | ((d)&SN_XDELTA_MASK)))
#define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA)
#define SN_TYPE(sn) \
((js::SrcNoteType)(SN_IS_XDELTA(sn) ? SRC_XDELTA : *(sn) >> SN_DELTA_BITS))
#define SN_SET_TYPE(sn, type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn))
#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) <= SRC_LAST_GETTABLE)
#define SN_DELTA(sn) \
((ptrdiff_t)(SN_IS_XDELTA(sn) ? *(sn)&SN_XDELTA_MASK : *(sn)&SN_DELTA_MASK))
#define SN_SET_DELTA(sn, delta) \
(SN_IS_XDELTA(sn) ? SN_MAKE_XDELTA(sn, delta) \
: SN_MAKE_NOTE(sn, SN_TYPE(sn), delta))
#define SN_DELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_DELTA_BITS))
#define SN_XDELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS))
/*
* Offset fields follow certain notes and are frequency-encoded: an offset in
* [0,0x7f] consumes one byte, an offset in [0x80,0x7fffffff] takes four, and
* the high bit of the first byte is set.
*/
#define SN_4BYTE_OFFSET_FLAG 0x80
#define SN_4BYTE_OFFSET_MASK 0x7f
#define SN_OFFSET_BITS 31
#define SN_MAX_OFFSET (((size_t)1 << SN_OFFSET_BITS) - 1)
inline bool SN_REPRESENTABLE_OFFSET(ptrdiff_t offset) {
return 0 <= offset && size_t(offset) <= SN_MAX_OFFSET;
}
/*
* SRC_COLSPAN values represent changes to the column number. Colspans are
* signed: negative changes arise in describing constructs like for(;;) loops,
* that generate code in non-source order. (Negative colspans also have a
* history of indicating bugs in updating ParseNodes' source locations.)
*
* We store colspans using the same variable-length encoding as offsets,
* described above. However, unlike offsets, colspans are signed, so we truncate
* colspans (SN_COLSPAN_TO_OFFSET) for storage as offsets, and sign-extend
* offsets into colspans when we read them (SN_OFFSET_TO_COLSPAN).
*/
#define SN_COLSPAN_SIGN_BIT (1 << (SN_OFFSET_BITS - 1))
#define SN_MIN_COLSPAN (-SN_COLSPAN_SIGN_BIT)
#define SN_MAX_COLSPAN (SN_COLSPAN_SIGN_BIT - 1)
inline bool SN_REPRESENTABLE_COLSPAN(ptrdiff_t colspan) {
return SN_MIN_COLSPAN <= colspan && colspan <= SN_MAX_COLSPAN;
}
inline ptrdiff_t SN_OFFSET_TO_COLSPAN(ptrdiff_t offset) {
// There should be no bits set outside the field we're going to sign-extend.
MOZ_ASSERT(!(offset & ~((1U << SN_OFFSET_BITS) - 1)));
// Sign-extend the least significant SN_OFFSET_BITS bits.
return (offset ^ SN_COLSPAN_SIGN_BIT) - SN_COLSPAN_SIGN_BIT;
}
inline ptrdiff_t SN_COLSPAN_TO_OFFSET(ptrdiff_t colspan) {
// Truncate the two's complement colspan, for storage as an offset.
ptrdiff_t offset = colspan & ((1U << SN_OFFSET_BITS) - 1);
// When we read this back, we'd better get the value we stored.
MOZ_ASSERT(SN_OFFSET_TO_COLSPAN(offset) == colspan);
return offset;
}
#define SN_LENGTH(sn) \
((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 : js::SrcNoteLength(sn))
#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn))
struct JSSrcNoteSpec {
const char* name; /* name for disassembly/debugging output */
int8_t arity; /* number of offset operands */
};
extern JS_FRIEND_DATA const JSSrcNoteSpec js_SrcNoteSpec[];
namespace js {
extern JS_FRIEND_API unsigned SrcNoteLength(jssrcnote* sn);
/*
* Get and set the offset operand identified by which (0 for the first, etc.).
*/
extern JS_FRIEND_API ptrdiff_t GetSrcNoteOffset(jssrcnote* sn, unsigned which);
} // namespace js
#endif /* frontend_SourceNotes_h */