| ... | ... | @@ -8,6 +8,7 @@ ast: *const Ast, |
| 8 | 8 | transformations: *std.ArrayList(Transformation), |
| 9 | 9 | unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index), |
| 10 | 10 | in_scope_names: std.StringArrayHashMapUnmanaged(u32), |
| 11 | replace_names: std.StringArrayHashMapUnmanaged(u32), |
| 11 | 12 | gpa: std.mem.Allocator, |
| 12 | 13 | arena: std.mem.Allocator, |
| 13 | 14 | |
| ... | ... | @@ -17,6 +18,13 @@ pub const Transformation = union(enum) { |
| 17 | 18 | gut_function: Ast.Node.Index, |
| 18 | 19 | /// Omit a global declaration. |
| 19 | 20 | delete_node: Ast.Node.Index, |
| 21 | /// Delete a local variable declaration and replace all of its references |
| 22 | /// with `undefined`. |
| 23 | delete_var_decl: struct { |
| 24 | var_decl_node: Ast.Node.Index, |
| 25 | /// Identifier nodes that reference the variable. |
| 26 | references: std.ArrayListUnmanaged(Ast.Node.Index), |
| 27 | }, |
| 20 | 28 | /// Replace an expression with `undefined`. |
| 21 | 29 | replace_with_undef: Ast.Node.Index, |
| 22 | 30 | /// Replace an `@import` with the imported file contents wrapped in a struct. |
| ... | ... | @@ -48,10 +56,12 @@ pub fn findTransformations( |
| 48 | 56 | .arena = arena, |
| 49 | 57 | .unreferenced_globals = .{}, |
| 50 | 58 | .in_scope_names = .{}, |
| 59 | .replace_names = .{}, |
| 51 | 60 | }; |
| 52 | 61 | defer { |
| 53 | 62 | walk.unreferenced_globals.deinit(walk.gpa); |
| 54 | 63 | walk.in_scope_names.deinit(walk.gpa); |
| 64 | walk.replace_names.deinit(walk.gpa); |
| 55 | 65 | } |
| 56 | 66 | |
| 57 | 67 | try walkMembers(&walk, walk.ast.rootDecls()); |
| ... | ... | @@ -133,6 +143,7 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { |
| 133 | 143 | try walkExpression(w, fn_proto); |
| 134 | 144 | const body_node = datas[decl].rhs; |
| 135 | 145 | if (!isFnBodyGutted(ast, body_node)) { |
| 146 | w.replace_names.clearRetainingCapacity(); |
| 136 | 147 | try w.transformations.append(.{ .gut_function = decl }); |
| 137 | 148 | try walkExpression(w, body_node); |
| 138 | 149 | } |
| ... | ... | @@ -187,7 +198,15 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 187 | 198 | const node_tags = ast.nodes.items(.tag); |
| 188 | 199 | const datas = ast.nodes.items(.data); |
| 189 | 200 | switch (node_tags[node]) { |
| 190 | | .identifier => try walkIdentifier(w, main_tokens[node]), |
| 201 | .identifier => { |
| 202 | const name_ident = main_tokens[node]; |
| 203 | assert(token_tags[name_ident] == .identifier); |
| 204 | const name_bytes = ast.tokenSlice(name_ident); |
| 205 | _ = w.unreferenced_globals.swapRemove(name_bytes); |
| 206 | if (w.replace_names.get(name_bytes)) |index| { |
| 207 | try w.transformations.items[index].delete_var_decl.references.append(w.arena, node); |
| 208 | } |
| 209 | }, |
| 191 | 210 | |
| 192 | 211 | .number_literal, |
| 193 | 212 | .char_literal, |
| ... | ... | @@ -646,13 +665,31 @@ fn walkBlock( |
| 646 | 665 | .local_var_decl, |
| 647 | 666 | .simple_var_decl, |
| 648 | 667 | .aligned_var_decl, |
| 649 | | => try walkLocalVarDecl(w, ast.fullVarDecl(stmt).?), |
| 668 | => { |
| 669 | const var_decl = ast.fullVarDecl(stmt).?; |
| 670 | if (var_decl.ast.init_node != 0 and |
| 671 | isUndefinedIdent(w.ast, var_decl.ast.init_node)) |
| 672 | { |
| 673 | try w.transformations.append(.{ .delete_var_decl = .{ |
| 674 | .var_decl_node = stmt, |
| 675 | .references = .{}, |
| 676 | } }); |
| 677 | const name_tok = var_decl.ast.mut_token + 1; |
| 678 | const name_bytes = ast.tokenSlice(name_tok); |
| 679 | try w.replace_names.put(w.gpa, name_bytes, @intCast(w.transformations.items.len - 1)); |
| 680 | } else { |
| 681 | try walkLocalVarDecl(w, var_decl); |
| 682 | } |
| 683 | }, |
| 650 | 684 | |
| 651 | 685 | else => { |
| 652 | | // Don't try to remove `_ = foo;` discards; those are handled separately. |
| 653 | 686 | switch (categorizeStmt(ast, stmt)) { |
| 687 | // Don't try to remove `_ = foo;` discards; those are handled separately. |
| 654 | 688 | .discard_identifier => {}, |
| 655 | | else => try w.transformations.append(.{ .delete_node = stmt }), |
| 689 | // definitely try to remove `_ = undefined;` though. |
| 690 | .discard_undefined, .trap_call, .other => { |
| 691 | try w.transformations.append(.{ .delete_node = stmt }); |
| 692 | }, |
| 656 | 693 | } |
| 657 | 694 | try walkExpression(w, stmt); |
| 658 | 695 | }, |
| ... | ... | @@ -905,6 +942,7 @@ fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool { |
| 905 | 942 | } |
| 906 | 943 | |
| 907 | 944 | const StmtCategory = enum { |
| 945 | discard_undefined, |
| 908 | 946 | discard_identifier, |
| 909 | 947 | trap_call, |
| 910 | 948 | other, |
| ... | ... | @@ -930,8 +968,14 @@ fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory { |
| 930 | 968 | }, |
| 931 | 969 | .assign => { |
| 932 | 970 | const infix = datas[stmt]; |
| 933 | | if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) |
| 934 | | return .discard_identifier; |
| 971 | if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) { |
| 972 | const name_bytes = ast.tokenSlice(main_tokens[infix.rhs]); |
| 973 | if (std.mem.eql(u8, name_bytes, "undefined")) { |
| 974 | return .discard_undefined; |
| 975 | } else { |
| 976 | return .discard_identifier; |
| 977 | } |
| 978 | } |
| 935 | 979 | return .other; |
| 936 | 980 | }, |
| 937 | 981 | else => return .other, |
| ... | ... | @@ -951,26 +995,21 @@ fn categorizeBuiltinCall( |
| 951 | 995 | } |
| 952 | 996 | |
| 953 | 997 | fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| 954 | | const node_tags = ast.nodes.items(.tag); |
| 955 | | const main_tokens = ast.nodes.items(.main_token); |
| 956 | | switch (node_tags[node]) { |
| 957 | | .identifier => { |
| 958 | | const token_index = main_tokens[node]; |
| 959 | | const name_bytes = ast.tokenSlice(token_index); |
| 960 | | return std.mem.eql(u8, name_bytes, "_"); |
| 961 | | }, |
| 962 | | else => return false, |
| 963 | | } |
| 998 | return isMatchingIdent(ast, node, "_"); |
| 964 | 999 | } |
| 965 | 1000 | |
| 966 | 1001 | fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| 1002 | return isMatchingIdent(ast, node, "undefined"); |
| 1003 | } |
| 1004 | |
| 1005 | fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool { |
| 967 | 1006 | const node_tags = ast.nodes.items(.tag); |
| 968 | 1007 | const main_tokens = ast.nodes.items(.main_token); |
| 969 | 1008 | switch (node_tags[node]) { |
| 970 | 1009 | .identifier => { |
| 971 | 1010 | const token_index = main_tokens[node]; |
| 972 | 1011 | const name_bytes = ast.tokenSlice(token_index); |
| 973 | | return std.mem.eql(u8, name_bytes, "undefined"); |
| 1012 | return std.mem.eql(u8, name_bytes, string); |
| 974 | 1013 | }, |
| 975 | 1014 | else => return false, |
| 976 | 1015 | } |