Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions src/ast/base.zig
Original file line number Diff line number Diff line change
Expand Up @@ -283,13 +283,3 @@ test "Ref" {
try std.testing.expectEqual(ref.isSourceContentsSlice(), first.is_source_contents_slice);
}
}

// This is kind of the wrong place, but it's shared between files
pub const RequireOrImportMeta = struct {
// CommonJS files will return the "require_*" wrapper function and an invalid
// exports object reference. Lazily-initialized ESM files will return the
// "init_*" wrapper function and the exports object for that file.
wrapper_ref: Ref = Ref.None,
exports_ref: Ref = Ref.None,
is_wrapper_async: bool = false,
};
2 changes: 1 addition & 1 deletion src/bun.js/module_loader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ pub const ModuleLoader = struct {
return resolved_source;
}

return ResolvedSource{
return .{
.allocator = null,
.source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())),
.specifier = ZigString.init(display_specifier),
Expand Down
57 changes: 23 additions & 34 deletions src/fs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -203,22 +203,13 @@ pub const FileSystem = struct {
// }

pub fn addEntry(dir: *DirEntry, entry: std.fs.IterableDir.Entry, allocator: std.mem.Allocator, comptime Iterator: type, iterator: Iterator) !void {
var _kind: Entry.Kind = undefined;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can tell this is some of the earliest code in bun because i did not know zig very well

switch (entry.kind) {
.Directory => {
_kind = Entry.Kind.dir;
},
.SymLink => {
// This might be wrong!
_kind = Entry.Kind.file;
},
.File => {
_kind = Entry.Kind.file;
},
else => {
return;
},
}
const _kind: Entry.Kind = switch (entry.kind) {
.Directory => .dir,
// This might be wrong!
.SymLink => .file,
.File => .file,
else => return,
};
// entry.name only lives for the duration of the iteration

const name = if (entry.name.len >= strings.StringOrTinyString.Max)
Expand All @@ -231,22 +222,20 @@ pub const FileSystem = struct {
else
strings.StringOrTinyString.initLowerCase(entry.name);

var stored = try EntryStore.instance.append(
Entry{
.base_ = name,
.base_lowercase_ = name_lowercased,
.dir = dir.dir,
.mutex = Mutex.init(),
// Call "stat" lazily for performance. The "@material-ui/icons" package
// contains a directory with over 11,000 entries in it and running "stat"
// for each entry was a big performance issue for that package.
.need_stat = entry.kind == .SymLink,
.cache = Entry.Cache{
.symlink = PathString.empty,
.kind = _kind,
},
const stored = try EntryStore.instance.append(.{
.base_ = name,
.base_lowercase_ = name_lowercased,
.dir = dir.dir,
.mutex = Mutex.init(),
// Call "stat" lazily for performance. The "@material-ui/icons" package
// contains a directory with over 11,000 entries in it and running "stat"
// for each entry was a big performance issue for that package.
.need_stat = entry.kind == .SymLink,
.cache = .{
.symlink = PathString.empty,
.kind = _kind,
},
);
});

const stored_name = stored.base();

Expand All @@ -270,7 +259,7 @@ pub const FileSystem = struct {
Output.prettyln("\n {s}", .{dir});
}

return DirEntry{ .dir = dir, .data = EntryMap{} };
return .{ .dir = dir, .data = .{} };
}

pub const Err = struct {
Expand Down Expand Up @@ -363,7 +352,7 @@ pub const FileSystem = struct {
};

pub const Entry = struct {
cache: Cache = Cache{},
cache: Cache = .{},
dir: string,

base_: strings.StringOrTinyString,
Expand Down Expand Up @@ -406,7 +395,7 @@ pub const FileSystem = struct {
pub const Cache = struct {
symlink: PathString = PathString.empty,
fd: StoredFileDescriptorType = 0,
kind: Kind = Kind.file,
kind: Kind = .file,
};

pub const Kind = enum {
Expand Down
2 changes: 1 addition & 1 deletion src/js_parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18902,7 +18902,7 @@ fn NewParser_(
// }
}

return js_ast.Ast{
return .{
.runtime_imports = p.runtime_imports,
.parts = parts,
.module_scope = p.module_scope.*,
Expand Down
10 changes: 6 additions & 4 deletions src/resolver/resolver.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1819,7 +1819,7 @@ pub const Resolver = struct {
}
fn dirInfoForResolution(
r: *ThisResolver,
dir_path: []const u8,
dir_path: string,
package_id: Install.PackageID,
) !?*DirInfo {
std.debug.assert(r.package_manager != null);
Expand All @@ -1833,7 +1833,7 @@ pub const Resolver = struct {
var cached_dir_entry_result = rfs.entries.getOrPut(dir_path) catch unreachable;

var dir_entries_option: *Fs.FileSystem.RealFS.EntriesOption = undefined;
var needs_iter: bool = true;
var needs_iter = true;
var open_dir = std.fs.cwd().openIterableDir(dir_path, .{}) catch |err| {
switch (err) {
error.FileNotFound => unreachable,
Expand All @@ -1855,7 +1855,9 @@ pub const Resolver = struct {
if (needs_iter) {
const allocator = r.fs.allocator;
dir_entries_option = rfs.entries.put(&cached_dir_entry_result, .{
.entries = Fs.FileSystem.DirEntry.init(dir_path),
.entries = Fs.FileSystem.DirEntry.init(
Fs.FileSystem.DirnameStore.instance.append(string, dir_path) catch unreachable,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are few cases where we need to clone this. The caller already cloned it except for the case you're running into, which has to do with automatic package installs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay let me look into that a bit more 👍

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add a debug assertion which checks:

Fs.FileSystem.DirnameStore.instance.isSliceInBuffer(dir_path) or bun.isHeapMemory(dir_path)

Then we can find the callers of this function which are not cloning the dir name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is only one user for this function:

if (r.dirInfoForResolution(dir_path_for_resolution, resolved_package_id)) |dir_info_to_use_| {

string of which is supplied by PackageManager.pathForResolution():
const dir_path_for_resolution = manager.pathForResolution(resolved_package_id, resolution, &path_in_global_disk_cache_buf) catch |err| {

which is using a threadlocal buffer:
threadlocal var path_in_global_disk_cache_buf: [bun.MAX_PATH_BYTES]u8 = undefined;

... doesn't look very cloned to me 😅

),
}) catch unreachable;

if (FeatureFlags.store_file_descriptors) {
Expand All @@ -1870,7 +1872,7 @@ pub const Resolver = struct {

// We must initialize it as empty so that the result index is correct.
// This is important so that browser_scope has a valid index.
var dir_info_ptr = r.dir_cache.put(&dir_cache_info_result, DirInfo{}) catch unreachable;
var dir_info_ptr = r.dir_cache.put(&dir_cache_info_result, .{}) catch unreachable;

try r.dirInfoUncached(
dir_info_ptr,
Expand Down
43 changes: 41 additions & 2 deletions test/bun.js/install/bunx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ it("should install and run specified version", async () => {
expect(await exited).toBe(0);
});

it("should download dependencies to run local file", async () => {
it("should download dependency to run local file", async () => {
await writeFile(
join(x_dir, "test.js"),
`
import { minify } from "uglify-js";
const { minify } = require("uglify-js@3.17.4");

console.log(minify("print(6 * 7)").code);
`,
Expand All @@ -82,3 +82,42 @@ console.log(minify("print(6 * 7)").code);
expect(await exited).toBe(0);
expect(await readdirSorted(x_dir)).toEqual([".cache", "test.js"]);
});

it("should download dependencies to run local file", async () => {
await writeFile(
join(x_dir, "test.js"),
`
import { file } from "bun";
import decompress from "[email protected]";

const buffer = await file("${join(import.meta.dir, "baz-0.0.3.tgz")}").arrayBuffer();
for (const entry of await decompress(Buffer.from(buffer))) {
console.log(\`\${entry.type}: \${entry.path}\`);
}
`,
);
const { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "test.js"],
cwd: x_dir,
stdout: null,
stdin: "pipe",
stderr: "pipe",
env: {
...env,
BUN_INSTALL_CACHE_DIR: join(x_dir, ".cache"),
},
});
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
expect(err).toBe("");
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(out.split(/\r?\n/)).toEqual([
"directory: package/",
"file: package/index.js",
"file: package/package.json",
"",
]);
expect(await exited).toBe(0);
expect(await readdirSorted(x_dir)).toEqual([".cache", "test.js"]);
});