| ... | @@ -32,6 +32,9 @@ bin_file_path: []const u8, | ... | @@ -32,6 +32,9 @@ bin_file_path: []const u8, |
| 32 | /// Decl pointers to details about them being exported. | 32 | /// Decl pointers to details about them being exported. |
| 33 | /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. | 33 | /// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table. |
| 34 | decl_exports: std.AutoHashMap(*Decl, []*Export), | 34 | decl_exports: std.AutoHashMap(*Decl, []*Export), |
| | 35 | /// We track which export is associated with the given symbol name for quick |
| | 36 | /// detection of symbol collisions. |
| | 37 | symbol_exports: std.StringHashMap(*Export), |
| 35 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl | 38 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl |
| 36 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that | 39 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that |
| 37 | /// is performing the export of another Decl. | 40 | /// is performing the export of another Decl. |
| ... | @@ -772,6 +775,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { | ... | @@ -772,6 +775,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 772 | .optimize_mode = options.optimize_mode, | 775 | .optimize_mode = options.optimize_mode, |
| 773 | .decl_table = DeclTable.init(gpa), | 776 | .decl_table = DeclTable.init(gpa), |
| 774 | .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa), | 777 | .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa), |
| | 778 | .symbol_exports = std.StringHashMap(*Export).init(gpa), |
| 775 | .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa), | 779 | .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa), |
| 776 | .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa), | 780 | .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa), |
| 777 | .failed_files = std.AutoHashMap(*Scope, *ErrorMsg).init(gpa), | 781 | .failed_files = std.AutoHashMap(*Scope, *ErrorMsg).init(gpa), |
| ... | @@ -829,6 +833,7 @@ pub fn deinit(self: *Module) void { | ... | @@ -829,6 +833,7 @@ pub fn deinit(self: *Module) void { |
| 829 | } | 833 | } |
| 830 | self.export_owners.deinit(); | 834 | self.export_owners.deinit(); |
| 831 | } | 835 | } |
| | 836 | self.symbol_exports.deinit(); |
| 832 | self.root_scope.destroy(allocator); | 837 | self.root_scope.destroy(allocator); |
| 833 | self.* = undefined; | 838 | self.* = undefined; |
| 834 | } | 839 | } |
| ... | @@ -1869,6 +1874,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { | ... | @@ -1869,6 +1874,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 1869 | if (self.failed_exports.remove(exp)) |entry| { | 1874 | if (self.failed_exports.remove(exp)) |entry| { |
| 1870 | entry.value.destroy(self.allocator); | 1875 | entry.value.destroy(self.allocator); |
| 1871 | } | 1876 | } |
| | 1877 | _ = self.symbol_exports.remove(exp.options.name); |
| 1872 | self.allocator.destroy(exp); | 1878 | self.allocator.destroy(exp); |
| 1873 | } | 1879 | } |
| 1874 | self.allocator.free(kv.value); | 1880 | self.allocator.free(kv.value); |
| ... | @@ -2104,20 +2110,6 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const | ... | @@ -2104,20 +2110,6 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2104 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), | 2110 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), |
| 2105 | } | 2111 | } |
| 2106 | | 2112 | |
| 2107 | var already_exported = false; | | |
| 2108 | { | | |
| 2109 | var it = self.decl_exports.iterator(); | | |
| 2110 | while (it.next()) |kv| { | | |
| 2111 | const export_list = kv.value; | | |
| 2112 | for (export_list) |e| { | | |
| 2113 | if (std.mem.eql(u8, e.options.name, symbol_name)) { | | |
| 2114 | already_exported = true; | | |
| 2115 | break; | | |
| 2116 | } | | |
| 2117 | } | | |
| 2118 | } | | |
| 2119 | } | | |
| 2120 | | | |
| 2121 | try self.decl_exports.ensureCapacity(self.decl_exports.size + 1); | 2113 | try self.decl_exports.ensureCapacity(self.decl_exports.size + 1); |
| 2122 | try self.export_owners.ensureCapacity(self.export_owners.size + 1); | 2114 | try self.export_owners.ensureCapacity(self.export_owners.size + 1); |
| 2123 | | 2115 | |
| ... | @@ -2153,7 +2145,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const | ... | @@ -2153,7 +2145,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2153 | de_gop.kv.value[de_gop.kv.value.len - 1] = new_export; | 2145 | de_gop.kv.value[de_gop.kv.value.len - 1] = new_export; |
| 2154 | errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1); | 2146 | errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1); |
| 2155 | | 2147 | |
| 2156 | if (already_exported) { | 2148 | if (self.symbol_exports.get(symbol_name)) |_| { |
| 2157 | try self.failed_exports.ensureCapacity(self.failed_exports.size + 1); | 2149 | try self.failed_exports.ensureCapacity(self.failed_exports.size + 1); |
| 2158 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( | 2150 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( |
| 2159 | self.allocator, | 2151 | self.allocator, |
| ... | @@ -2161,9 +2153,12 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const | ... | @@ -2161,9 +2153,12 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2161 | "exported symbol collision: {}", | 2153 | "exported symbol collision: {}", |
| 2162 | .{symbol_name}, | 2154 | .{symbol_name}, |
| 2163 | )); | 2155 | )); |
| | 2156 | // TODO: add a note |
| 2164 | new_export.status = .failed; | 2157 | new_export.status = .failed; |
| 2165 | return; | 2158 | return; |
| 2166 | } | 2159 | } |
| | 2160 | |
| | 2161 | try self.symbol_exports.putNoClobber(symbol_name, new_export); |
| 2167 | self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) { | 2162 | self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) { |
| 2168 | error.OutOfMemory => return error.OutOfMemory, | 2163 | error.OutOfMemory => return error.OutOfMemory, |
| 2169 | else => { | 2164 | else => { |