| 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 | |
| 6 | param_count: u32, |
| 7 | nodes: Node.List.Slice, |
| 8 | extern_symbols: []String.WithLen, |
| 9 | top_level_value_deps: []Value, |
| 10 | extra: ExtraStore, |
| 11 | |
| 12 | pub const LocalIndex = u32; |
| 13 | pub const ExtraStore = array.ExtraStore(.js_tree, .@"const"); |
| 14 | pub const EncodedData = array.EncodedData(Tag, u64); |
| 15 | pub const ExtraIndex = ExtraStore.Index; |
| 16 | |
| 17 | pub 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 | |
| 120 | pub 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 | |
| 148 | pub const PlainValue = struct { |
| 149 | value: Value, |
| 150 | unused: Unused = .zero, |
| 151 | }; |
| 152 | |
| 153 | pub const LocalRef = struct { |
| 154 | id: LocalIndex, |
| 155 | unused: Unused = .zero, |
| 156 | }; |
| 157 | |
| 158 | pub const GlobalRef = struct { |
| 159 | name: InternPool.String.WithLen, |
| 160 | }; |
| 161 | |
| 162 | pub const ValueRef = struct { |
| 163 | value: Value, |
| 164 | unused: Unused = .zero, |
| 165 | }; |
| 166 | |
| 167 | pub const StmtDeclareLocal = struct { |
| 168 | identifier_index: u32, |
| 169 | init: Node.Index.Optional, |
| 170 | }; |
| 171 | |
| 172 | pub const Bin = struct { |
| 173 | lhs: Node.Index, |
| 174 | rhs: Node.Index, |
| 175 | }; |
| 176 | |
| 177 | pub const Un = struct { |
| 178 | operand: Node.Index, |
| 179 | unused: Unused = .zero, |
| 180 | }; |
| 181 | |
| 182 | pub const Call = struct { |
| 183 | base: Node.Index, |
| 184 | args: ExtraIndex(IndexList), |
| 185 | }; |
| 186 | |
| 187 | pub 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. |
| 194 | pub 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 | |
| 230 | pub 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 | |
| 239 | pub 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 | |
| 257 | pub 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 | |
| 267 | pub const StmtBreak = struct { |
| 268 | label: u32, |
| 269 | unused: Unused = .zero, |
| 270 | }; |
| 271 | |
| 272 | pub const StmtReturn = struct { |
| 273 | operand: Node.Index.Optional, |
| 274 | unused: Unused = .zero, |
| 275 | }; |
| 276 | |
| 277 | pub const StmtThrow = struct { |
| 278 | operand: Node.Index, |
| 279 | unused: Unused = .zero, |
| 280 | }; |
| 281 | |
| 282 | pub const StmtBlock = struct { |
| 283 | label: u32, |
| 284 | content: ExtraIndex(IndexList), |
| 285 | }; |
| 286 | |
| 287 | pub 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 | |
| 294 | pub fn nodeTag(tree: Tree, index: Node.Index) Tag { |
| 295 | return tree.nodes.items(.tag)[@intFromEnum(index)]; |
| 296 | } |
| 297 | |
| 298 | pub 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 | |
| 302 | pub fn mainStatements(tree: *const Tree) []const Node.Index { |
| 303 | return tree.nodeData(.root, .root).slice(tree); |
| 304 | } |
| 305 | |
| 306 | const Tree = @This(); |
| 307 | |
| 308 | const std = @import("std"); |
| 309 | const assert = std.debug.assert; |
| 310 | |
| 311 | const InternPool = @import("../InternPool.zig"); |
| 312 | const Value = InternPool.Value; |
| 313 | |
| 314 | const NullTerminatedString = InternPool.NullTerminatedString; |
| 315 | const String = InternPool.String; |
| 316 | |
| 317 | const Comp = @import("../Comp.zig"); |
| 318 | const Decl = Comp.Decl; |
| 319 | |
| 320 | const array = @import("../array.zig"); |
| 321 | |
| 322 | const Unused = @import("../Ast.zig").Unused; |