1//! JavaScript Function Representation is the last representation before javascript
2//! source bytes are created. One notable thing here is that all local identifiers
3//! are numbered, instead of being named. The first `param_count` local identifiers
4//! refer to parameters, and the rest refer to the respective block that defined them.
5
6param_count: u32,
7nodes: Node.List.Slice,
8extern_symbols: []String.WithLen,
9top_level_value_deps: []Value,
10extra: ExtraStore,
11
12pub const LocalIndex = u32;
13pub const ExtraStore = array.ExtraStore(.js_tree, .@"const");
14pub const EncodedData = array.EncodedData(Tag, u64);
15pub const ExtraIndex = ExtraStore.Index;
16
17pub const Tag = enum {
18 root,
19 /// Only allow simple value types: int, float, string, bool
20 /// This value type is inlined into the call site
21 plain_value,
22 /// Reference to a top level value. (give a Value)
23 /// This is used for function values / large constant objects
24 top_level_value,
25 /// Reference to a local identifier (give an VarId)
26 local,
27 /// Reference to a global identifier (give a NullTerminatedString)
28 global,
29 // Bin ops
30 assign,
31 assign_add,
32 assign_sub,
33 assign_mul,
34 assign_div,
35 add,
36 sub,
37 mul,
38 div,
39 eq,
40 lt,
41 gt,
42 ge,
43 le,
44 @"and",
45 @"or",
46 // Exprs
47 call,
48 js_new,
49 field,
50 ternary,
51 object_literal,
52 array,
53 index,
54 comma_chain,
55 // Prefix ops
56 negate,
57 bool_not,
58 pre_inc,
59 pre_dec,
60 // Statements
61 /// `let ... = ...`
62 stmt_declare_local,
63 /// `if (...) ... else ...`
64 stmt_if,
65 /// `break ... ...`
66 stmt_break,
67 /// `return ...` (break from block #0)
68 stmt_return,
69 /// `...: { ... }`
70 stmt_block,
71 /// `throw x`
72 stmt_throw,
73
74 pub fn Data(comptime tag: Tag) type {
75 return switch (tag) {
76 .root => IndexList,
77 .plain_value => PlainValue,
78 .top_level_value => ValueRef,
79 .local => LocalRef,
80 .global => GlobalRef,
81 .assign,
82 .assign_add,
83 .assign_sub,
84 .assign_mul,
85 .assign_div,
86 .add,
87 .sub,
88 .mul,
89 .div,
90 .eq,
91 .lt,
92 .gt,
93 .ge,
94 .le,
95 .index,
96 .@"and",
97 .@"or",
98 => Bin,
99 .ternary => Ternary,
100 .negate,
101 .bool_not,
102 .pre_inc,
103 .pre_dec,
104 => Un,
105 .object_literal => ObjectLiteral,
106 .array => IndexList,
107 .call, .js_new => Call,
108 .field => Field,
109 .comma_chain => IndexList,
110 .stmt_if => StmtIf,
111 .stmt_break => StmtBreak,
112 .stmt_return => StmtReturn,
113 .stmt_throw => StmtThrow,
114 .stmt_declare_local => StmtDeclareLocal,
115 .stmt_block => StmtBlock,
116 };
117 }
118};
119
120pub const Node = struct {
121 tag: Tag,
122 data: EncodedData,
123
124 pub const List = std.MultiArrayList(Node);
125
126 pub const Index = enum(u32) {
127 root = 0,
128 _,
129
130 pub fn toOptional(i: Index) Optional {
131 return @enumFromInt(@intFromEnum(i));
132 }
133
134 pub const Optional = enum(u32) {
135 none = std.math.maxInt(u32),
136 _,
137
138 pub fn unwrap(oi: Optional) ?Index {
139 return if (oi == .none)
140 null
141 else
142 @enumFromInt(@intFromEnum(oi));
143 }
144 };
145 };
146};
147
148pub const PlainValue = struct {
149 value: Value,
150 unused: Unused = .zero,
151};
152
153pub const LocalRef = struct {
154 id: LocalIndex,
155 unused: Unused = .zero,
156};
157
158pub const GlobalRef = struct {
159 name: InternPool.String.WithLen,
160};
161
162pub const ValueRef = struct {
163 value: Value,
164 unused: Unused = .zero,
165};
166
167pub const StmtDeclareLocal = struct {
168 identifier_index: u32,
169 init: Node.Index.Optional,
170};
171
172pub const Bin = struct {
173 lhs: Node.Index,
174 rhs: Node.Index,
175};
176
177pub const Un = struct {
178 operand: Node.Index,
179 unused: Unused = .zero,
180};
181
182pub const Call = struct {
183 base: Node.Index,
184 args: ExtraIndex(IndexList),
185};
186
187pub const Field = struct {
188 base: Node.Index,
189 field: NullTerminatedString,
190};
191
192/// Extra list is `property_count * 2` items long, with the first half being
193/// `ObjectLiteral.Key`s, and the second being value nodes.
194pub const ObjectLiteral = struct {
195 extra_index: u32,
196 property_count: u32,
197
198 pub const Key = packed struct(u32) {
199 kind: Kind,
200 data: u31,
201
202 pub const Kind = enum(u1) {
203 static,
204 computed,
205 };
206
207 pub const Decoded = union(enum) {
208 static: NullTerminatedString,
209 computed: Node.Index,
210
211 pub fn encode(decoded: Decoded) Key {
212 return switch (decoded) {
213 inline else => |data, tag| .{
214 .kind = @field(Kind, @tagName(tag)),
215 .data = @intCast(@intFromEnum(data)),
216 },
217 };
218 }
219 };
220
221 pub fn decode(key: Key) Decoded {
222 return switch (key.kind) {
223 .static => .{ .static = @enumFromInt(key.data) },
224 .computed => .{ .computed = @enumFromInt(key.data) },
225 };
226 }
227 };
228};
229
230pub const IndexList = struct {
231 start: u32,
232 len: u32,
233
234 pub fn slice(list: IndexList, tree: *const Tree) []const Node.Index {
235 return @ptrCast(tree.extra.list.items[list.start..][0..list.len]);
236 }
237};
238
239pub const StmtIf = struct {
240 cond: Node.Index,
241 data: ExtraIndex(Data),
242
243 pub const Data = struct {
244 yes: IndexList,
245 no: IndexList,
246 flags: Flags,
247 };
248
249 pub const Flags = packed struct(u32) {
250 block_label: u16,
251 yes_label: bool,
252 no_label: bool,
253 _: u14 = 0,
254 };
255};
256
257pub const Ternary = struct {
258 cond: Node.Index,
259 data: ExtraIndex(Data),
260
261 pub const Data = struct {
262 yes: Node.Index,
263 no: Node.Index,
264 };
265};
266
267pub const StmtBreak = struct {
268 label: u32,
269 unused: Unused = .zero,
270};
271
272pub const StmtReturn = struct {
273 operand: Node.Index.Optional,
274 unused: Unused = .zero,
275};
276
277pub const StmtThrow = struct {
278 operand: Node.Index,
279 unused: Unused = .zero,
280};
281
282pub const StmtBlock = struct {
283 label: u32,
284 content: ExtraIndex(IndexList),
285};
286
287pub fn deinit(tree: *Tree, gpa: std.mem.Allocator) void {
288 tree.nodes.deinit(gpa);
289 gpa.free(tree.top_level_value_deps);
290 gpa.free(tree.extern_symbols);
291 tree.extra.deinit(gpa);
292}
293
294pub fn nodeTag(tree: Tree, index: Node.Index) Tag {
295 return tree.nodes.items(.tag)[@intFromEnum(index)];
296}
297
298pub fn nodeData(tree: Tree, comptime tag: Tag, index: Node.Index) tag.Data() {
299 return tree.nodes.items(.data)[@intFromEnum(index)].decode(tag, tree.nodeTag(index));
300}
301
302pub fn mainStatements(tree: *const Tree) []const Node.Index {
303 return tree.nodeData(.root, .root).slice(tree);
304}
305
306const Tree = @This();
307
308const std = @import("std");
309const assert = std.debug.assert;
310
311const InternPool = @import("../InternPool.zig");
312const Value = InternPool.Value;
313
314const NullTerminatedString = InternPool.NullTerminatedString;
315const String = InternPool.String;
316
317const Comp = @import("../Comp.zig");
318const Decl = Comp.Decl;
319
320const array = @import("../array.zig");
321
322const Unused = @import("../Ast.zig").Unused;