zig reduce: rename identifiers when inlining an @import
This commit is contained in:
@@ -26,6 +26,9 @@ pub const Fixups = struct {
|
||||
omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
|
||||
/// These expressions will be replaced with the string value.
|
||||
replace_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{},
|
||||
/// Change all identifier names matching the key to be value instead.
|
||||
rename_identifiers: std.StringArrayHashMapUnmanaged([]const u8) = .{},
|
||||
|
||||
/// All `@import` builtin calls which refer to a file path will be prefixed
|
||||
/// with this path.
|
||||
rebase_imported_paths: ?[]const u8 = null,
|
||||
@@ -34,7 +37,9 @@ pub const Fixups = struct {
|
||||
return f.unused_var_decls.count() +
|
||||
f.gut_functions.count() +
|
||||
f.omit_nodes.count() +
|
||||
f.replace_nodes.count();
|
||||
f.replace_nodes.count() +
|
||||
f.rename_identifiers.count() +
|
||||
@intFromBool(f.rebase_imported_paths != null);
|
||||
}
|
||||
|
||||
pub fn clearRetainingCapacity(f: *Fixups) void {
|
||||
@@ -42,6 +47,9 @@ pub const Fixups = struct {
|
||||
f.gut_functions.clearRetainingCapacity();
|
||||
f.omit_nodes.clearRetainingCapacity();
|
||||
f.replace_nodes.clearRetainingCapacity();
|
||||
f.rename_identifiers.clearRetainingCapacity();
|
||||
|
||||
f.rebase_imported_paths = null;
|
||||
}
|
||||
|
||||
pub fn deinit(f: *Fixups, gpa: Allocator) void {
|
||||
@@ -49,6 +57,7 @@ pub const Fixups = struct {
|
||||
f.gut_functions.deinit(gpa);
|
||||
f.omit_nodes.deinit(gpa);
|
||||
f.replace_nodes.deinit(gpa);
|
||||
f.rename_identifiers.deinit(gpa);
|
||||
f.* = undefined;
|
||||
}
|
||||
};
|
||||
@@ -2833,6 +2842,13 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote
|
||||
const token_tags = tree.tokens.items(.tag);
|
||||
assert(token_tags[token_index] == .identifier);
|
||||
const lexeme = tokenSliceForRender(tree, token_index);
|
||||
|
||||
if (r.fixups.rename_identifiers.get(lexeme)) |mangled| {
|
||||
try r.ais.writer().writeAll(mangled);
|
||||
try renderSpace(r, token_index, lexeme.len, space);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lexeme[0] != '@') {
|
||||
return renderToken(r, token_index, space);
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
|
||||
var transformations = std.ArrayList(Walk.Transformation).init(gpa);
|
||||
defer transformations.deinit();
|
||||
try Walk.findTransformations(&tree, &transformations);
|
||||
try Walk.findTransformations(arena, &tree, &transformations);
|
||||
sortTransformations(transformations.items, rng.random());
|
||||
|
||||
fresh: while (transformations.items.len > 0) {
|
||||
@@ -162,12 +162,19 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
subset_size = @max(1, subset_size / 2);
|
||||
|
||||
const this_set = transformations.items[start_index..][0..subset_size];
|
||||
std.debug.print("trying {d} random transformations: ", .{subset_size});
|
||||
for (this_set[0..@min(this_set.len, 20)]) |t| {
|
||||
std.debug.print("{s} ", .{@tagName(t)});
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
try transformationsToFixups(gpa, arena, root_source_file_path, this_set, &fixups);
|
||||
|
||||
rendered.clearRetainingCapacity();
|
||||
try tree.renderToArrayList(&rendered, fixups);
|
||||
try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
|
||||
|
||||
//std.debug.print("trying this code:\n{s}\n", .{rendered.items});
|
||||
|
||||
const interestingness = try runCheck(arena, interestingness_argv.items);
|
||||
std.debug.print("{d} random transformations: {s}. {d} remaining\n", .{
|
||||
subset_size, @tagName(interestingness), transformations.items.len - start_index,
|
||||
@@ -179,7 +186,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
tree.deinit(gpa);
|
||||
tree = new_tree;
|
||||
|
||||
try Walk.findTransformations(&tree, &transformations);
|
||||
try Walk.findTransformations(arena, &tree, &transformations);
|
||||
// Resetting based on the seed again means we will get the same
|
||||
// results if restarting the reduction process from this new point.
|
||||
rng = std.rand.DefaultPrng.init(seed);
|
||||
@@ -263,7 +270,6 @@ fn transformationsToFixups(
|
||||
try fixups.replace_nodes.put(gpa, node, "undefined");
|
||||
},
|
||||
.inline_imported_file => |inline_imported_file| {
|
||||
defer gpa.free(inline_imported_file.imported_string);
|
||||
const full_imported_path = try std.fs.path.join(gpa, &.{
|
||||
std.fs.path.dirname(root_source_file_path) orelse ".",
|
||||
inline_imported_file.imported_string,
|
||||
@@ -274,14 +280,34 @@ fn transformationsToFixups(
|
||||
gpa.free(other_file_ast.source);
|
||||
other_file_ast.deinit(gpa);
|
||||
}
|
||||
var other_source = std.ArrayList(u8).init(gpa);
|
||||
defer other_source.deinit();
|
||||
|
||||
var inlined_fixups: Ast.Fixups = .{};
|
||||
defer inlined_fixups.deinit(gpa);
|
||||
if (std.fs.path.dirname(inline_imported_file.imported_string)) |dirname| {
|
||||
inlined_fixups.rebase_imported_paths = dirname;
|
||||
}
|
||||
for (inline_imported_file.in_scope_names.keys()) |name| {
|
||||
// This name needs to be mangled in order to not cause an
|
||||
// ambiguous reference error.
|
||||
var i: u32 = 2;
|
||||
const mangled = while (true) : (i += 1) {
|
||||
const mangled = try std.fmt.allocPrint(gpa, "{s}{d}", .{ name, i });
|
||||
if (!inline_imported_file.in_scope_names.contains(mangled))
|
||||
break mangled;
|
||||
gpa.free(mangled);
|
||||
};
|
||||
try inlined_fixups.rename_identifiers.put(gpa, name, mangled);
|
||||
}
|
||||
defer {
|
||||
for (inlined_fixups.rename_identifiers.values()) |v| {
|
||||
gpa.free(v);
|
||||
}
|
||||
}
|
||||
|
||||
var other_source = std.ArrayList(u8).init(gpa);
|
||||
defer other_source.deinit();
|
||||
try other_source.appendSlice("struct {\n");
|
||||
try other_file_ast.renderToArrayList(&other_source, .{
|
||||
.rebase_imported_paths = std.fs.path.dirname(inline_imported_file.imported_string),
|
||||
});
|
||||
try other_file_ast.renderToArrayList(&other_source, inlined_fixups);
|
||||
try other_source.appendSlice("}");
|
||||
|
||||
try fixups.replace_nodes.put(
|
||||
|
||||
@@ -7,7 +7,9 @@ const BuiltinFn = @import("../BuiltinFn.zig");
|
||||
ast: *const Ast,
|
||||
transformations: *std.ArrayList(Transformation),
|
||||
unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
|
||||
in_scope_names: std.StringArrayHashMapUnmanaged(u32),
|
||||
gpa: std.mem.Allocator,
|
||||
arena: std.mem.Allocator,
|
||||
|
||||
pub const Transformation = union(enum) {
|
||||
/// Replace the fn decl AST Node with one whose body is only `@trap()` with
|
||||
@@ -18,25 +20,39 @@ pub const Transformation = union(enum) {
|
||||
/// Replace an expression with `undefined`.
|
||||
replace_with_undef: Ast.Node.Index,
|
||||
/// Replace an `@import` with the imported file contents wrapped in a struct.
|
||||
inline_imported_file: struct {
|
||||
inline_imported_file: InlineImportedFile,
|
||||
|
||||
pub const InlineImportedFile = struct {
|
||||
builtin_call_node: Ast.Node.Index,
|
||||
imported_string: []const u8,
|
||||
},
|
||||
/// Identifier names that must be renamed in the inlined code or else
|
||||
/// will cause ambiguous reference errors.
|
||||
in_scope_names: std.StringArrayHashMapUnmanaged(void),
|
||||
};
|
||||
};
|
||||
|
||||
pub const Error = error{OutOfMemory};
|
||||
|
||||
/// The result will be priority shuffled.
|
||||
pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Transformation)) !void {
|
||||
pub fn findTransformations(
|
||||
arena: std.mem.Allocator,
|
||||
ast: *const Ast,
|
||||
transformations: *std.ArrayList(Transformation),
|
||||
) !void {
|
||||
transformations.clearRetainingCapacity();
|
||||
|
||||
var walk: Walk = .{
|
||||
.ast = ast,
|
||||
.transformations = transformations,
|
||||
.gpa = transformations.allocator,
|
||||
.arena = arena,
|
||||
.unreferenced_globals = .{},
|
||||
.in_scope_names = .{},
|
||||
};
|
||||
defer walk.unreferenced_globals.deinit(walk.gpa);
|
||||
defer {
|
||||
walk.unreferenced_globals.deinit(walk.gpa);
|
||||
walk.in_scope_names.deinit(walk.gpa);
|
||||
}
|
||||
|
||||
try walkMembers(&walk, walk.ast.rootDecls());
|
||||
|
||||
@@ -49,14 +65,18 @@ pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Tran
|
||||
|
||||
fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {
|
||||
// First we scan for globals so that we can delete them while walking.
|
||||
try scanDecls(w, members);
|
||||
try scanDecls(w, members, .add);
|
||||
|
||||
for (members) |member| {
|
||||
try walkMember(w, member);
|
||||
}
|
||||
|
||||
try scanDecls(w, members, .remove);
|
||||
}
|
||||
|
||||
fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void {
|
||||
const ScanDeclsAction = enum { add, remove };
|
||||
|
||||
fn scanDecls(w: *Walk, members: []const Ast.Node.Index, action: ScanDeclsAction) Error!void {
|
||||
const ast = w.ast;
|
||||
const gpa = w.gpa;
|
||||
const node_tags = ast.nodes.items(.tag);
|
||||
@@ -80,9 +100,27 @@ fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void {
|
||||
|
||||
else => continue,
|
||||
};
|
||||
|
||||
assert(token_tags[name_token] == .identifier);
|
||||
const name_bytes = ast.tokenSlice(name_token);
|
||||
try w.unreferenced_globals.put(gpa, name_bytes, member_node);
|
||||
|
||||
switch (action) {
|
||||
.add => {
|
||||
try w.unreferenced_globals.put(gpa, name_bytes, member_node);
|
||||
|
||||
const gop = try w.in_scope_names.getOrPut(gpa, name_bytes);
|
||||
if (!gop.found_existing) gop.value_ptr.* = 0;
|
||||
gop.value_ptr.* += 1;
|
||||
},
|
||||
.remove => {
|
||||
const entry = w.in_scope_names.getEntry(name_bytes).?;
|
||||
if (entry.value_ptr.* <= 1) {
|
||||
assert(w.in_scope_names.swapRemove(name_bytes));
|
||||
} else {
|
||||
entry.value_ptr.* -= 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,12 +605,12 @@ fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {
|
||||
try walkExpression(w, var_decl.ast.section_node);
|
||||
}
|
||||
|
||||
assert(var_decl.ast.init_node != 0);
|
||||
if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
|
||||
try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
|
||||
if (var_decl.ast.init_node != 0) {
|
||||
if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
|
||||
try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
|
||||
}
|
||||
try walkExpression(w, var_decl.ast.init_node);
|
||||
}
|
||||
|
||||
return walkExpression(w, var_decl.ast.init_node);
|
||||
}
|
||||
|
||||
fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void {
|
||||
@@ -582,7 +620,9 @@ fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void {
|
||||
if (field.ast.align_expr != 0) {
|
||||
try walkExpression(w, field.ast.align_expr); // alignment
|
||||
}
|
||||
try walkExpression(w, field.ast.value_expr); // value
|
||||
if (field.ast.value_expr != 0) {
|
||||
try walkExpression(w, field.ast.value_expr); // value
|
||||
}
|
||||
}
|
||||
|
||||
fn walkBlock(
|
||||
@@ -690,7 +730,6 @@ fn walkBuiltinCall(
|
||||
params: []const Ast.Node.Index,
|
||||
) Error!void {
|
||||
const ast = w.ast;
|
||||
const gpa = w.gpa;
|
||||
const main_tokens = ast.nodes.items(.main_token);
|
||||
const builtin_token = main_tokens[call_node];
|
||||
const builtin_name = ast.tokenSlice(builtin_token);
|
||||
@@ -700,12 +739,17 @@ fn walkBuiltinCall(
|
||||
const operand_node = params[0];
|
||||
const str_lit_token = main_tokens[operand_node];
|
||||
const token_bytes = ast.tokenSlice(str_lit_token);
|
||||
const imported_string = std.zig.string_literal.parseAlloc(gpa, token_bytes) catch
|
||||
unreachable;
|
||||
if (std.mem.endsWith(u8, imported_string, ".zig")) {
|
||||
if (std.mem.endsWith(u8, token_bytes, ".zig\"")) {
|
||||
const imported_string = std.zig.string_literal.parseAlloc(w.arena, token_bytes) catch
|
||||
unreachable;
|
||||
try w.transformations.append(.{ .inline_imported_file = .{
|
||||
.builtin_call_node = call_node,
|
||||
.imported_string = imported_string,
|
||||
.in_scope_names = try std.StringArrayHashMapUnmanaged(void).init(
|
||||
w.arena,
|
||||
w.in_scope_names.keys(),
|
||||
&.{},
|
||||
),
|
||||
} });
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user