diff options
author | 2021-06-09 13:26:30 -0700 | |
---|---|---|
committer | 2021-06-09 13:26:30 -0700 | |
commit | ecda693e3844511644a177a0bcb146bda07effb9 (patch) | |
tree | e032597bdae3e8229a88042d1ae2b978ef63e056 /src/node_module_bundle.zig | |
parent | 6a4712f4c90ef7f1bb858ea81fe3d11ea60b036e (diff) | |
download | bun-ecda693e3844511644a177a0bcb146bda07effb9.tar.gz bun-ecda693e3844511644a177a0bcb146bda07effb9.tar.zst bun-ecda693e3844511644a177a0bcb146bda07effb9.zip |
lots
Former-commit-id: 7346cdaa5a32ade26821ed97ef07f7c9ae87c0c2
Diffstat (limited to 'src/node_module_bundle.zig')
-rw-r--r-- | src/node_module_bundle.zig | 215 |
1 files changed, 209 insertions, 6 deletions
diff --git a/src/node_module_bundle.zig b/src/node_module_bundle.zig index ada125e22..ec85e4fb1 100644 --- a/src/node_module_bundle.zig +++ b/src/node_module_bundle.zig @@ -3,6 +3,19 @@ const Api = schema.Api; const std = @import("std"); usingnamespace @import("global.zig"); +pub fn modulesIn(bundle: *const Api.JavascriptBundle, pkg: *const Api.JavascriptBundledPackage) []const Api.JavascriptBundledModule { + return bundle.modules[pkg.modules_offset .. pkg.modules_offset + pkg.modules_length]; +} + +// This corresponds to Api.JavascriptBundledPackage.hash +pub const BundledPackageHash = u32; +// This is the offset in the array of packages +pub const BundledPackageID = u32; + +const PackageIDMap = std.AutoHashMap(BundledPackageHash, BundledPackageID); + +const PackageNameMap = std.StringHashMap([]BundledPackageID); + pub const NodeModuleBundle = struct { container: Api.JavascriptBundleContainer, bundle: Api.JavascriptBundle, @@ -11,14 +24,199 @@ pub const NodeModuleBundle = struct { bytes: []u8 = undefined, fd: FileDescriptorType = 0, + // Lookup packages by ID - hash(name@version) + package_id_map: PackageIDMap, + + // Lookup packages by name. Remember that you can have multiple versions of the same package. + package_name_map: PackageNameMap, + + // This is stored as a single pre-allocated, flat array so we can avoid dynamic allocations. + package_name_ids_ptr: []BundledPackageID = &([_]BundledPackageID{}), + pub const magic_bytes = "#!/usr/bin/env speedy\n\n"; threadlocal var jsbundle_prefix: [magic_bytes.len + 5]u8 = undefined; + pub fn loadPackageMap(this: *NodeModuleBundle) !void { + this.package_name_map = PackageNameMap.init(this.allocator); + var ids = PackageIDMap.init(this.allocator); + + const package_count = @truncate(u32, this.bundle.packages.len); + + // this.package_has_multiple_versions = try std.bit_set.DynamicBitSet.initFull(package_count, this.allocator); + + try ids.ensureCapacity( + package_count, + ); + this.package_name_ids_ptr = try this.allocator.alloc(BundledPackageID, this.bundle.packages.len); + var remaining_names = this.package_name_ids_ptr; + try this.package_name_map.ensureCapacity( + package_count, + ); + var prev_package_ids_for_name: []u32 = &[_]u32{}; + + for (this.bundle.packages) |package, _package_id| { + const package_id = @truncate(u32, _package_id); + std.debug.assert(package.hash != 0); + ids.putAssumeCapacityNoClobber(package.hash, @truncate(u32, package_id)); + + const package_name = this.str(package.name); + var entry = this.package_name_map.getOrPutAssumeCapacity(package_name); + + if (entry.found_existing) { + // this.package_has_multiple_versions.set(prev_package_ids_for_name[prev_package_ids_for_name.len - 1]); + // Assert that multiple packages with the same name come immediately after another + // This catches any issues with the sorting order, which would cause all sorts of weird bugs + // This also allows us to simply extend the length of the previous slice to the new length + // Saving us an allocation + if (@ptrToInt(prev_package_ids_for_name.ptr) != @ptrToInt(entry.value_ptr.ptr)) { + Output.prettyErrorln( + \\<r><red>Fatal<r>: incorrect package sorting order detected in .jsb file.\n + \\This is a bug! Please create an issue.\n + \\If this bug blocks you from doing work, for now + \\please <b>avoid having multiple versions of <cyan>"{s}"<r> in the same bundle.\n + \\\n + \\- Jarred" + , + .{ + package_name, + }, + ); + Global.crash(); + } + + const end = prev_package_ids_for_name.len + 1; + // Assert we have enough room to add another package + std.debug.assert(end < remaining_names.len); + entry.value_ptr.* = prev_package_ids_for_name.ptr[0..end]; + entry.value_ptr.*[end] = package_id; + } else { + prev_package_ids_for_name = remaining_names[0..1]; + prev_package_ids_for_name[0] = package_id; + entry.value_ptr.* = prev_package_ids_for_name; + remaining_names = remaining_names[1..]; + } + } + + this.package_id_map = ids; + } + + pub fn getPackageIDByHash(this: *const NodeModuleBundle, hash: BundledPackageID) ?u32 { + return this.package_id_map.get(hash); + } + + pub fn getPackageIDByName(this: *const NodeModuleBundle, name: string) ?[]u32 { + return this.package_name_map.get(name); + } + + pub fn getPackage(this: *const NodeModuleBundle, name: string) ?*const Api.JavascriptBundledPackage { + const package_id = this.getPackageID(name) orelse return null; + return &this.bundle.packages[@intCast(usize, package_id)]; + } + + pub fn hasModule(this: *const NodeModuleBundle, name: string) ?*const Api.JavascriptBundledPackage { + const package_id = this.getPackageID(name) orelse return null; + return &this.bundle.packages[@intCast(usize, package_id)]; + } + + pub const ModuleQuery = struct { + package: *const Api.JavascriptBundledPackage, + relative_path: string, + extensions: []string, + }; + + pub fn allocModuleImport( + this: *const NodeModuleBundle, + to: *const Api.JavascriptBundledModule, + allocator: *std.mem.Allocator, + ) !string { + return try std.fmt.allocPrint( + allocator, + "{x}/{s}", + .{ + this.bundle.packages[to.package_id].hash, + this.str(to.path), + 123, + }, + ); + } + + pub fn findModuleInPackageByPathWithoutPackageName( + this: *const NodeModuleBundle, + package: *const Api.JavascriptBundledPackage, + query: ModuleQuery, + ) ?Api.JavascriptBundledModule { + // const ModuleSearcher = struct { + // ctx: *const NodeModuleBundle, + // query: ModuleQuery, + // }; + // std.sort.binarySearch(comptime T: type, key: T, items: []const T, context: anytype, comptime compareFn: fn(context:@TypeOf(context), lhs:T, rhs:T)math.Order) + } + + pub fn findModuleInPackage( + this: *const NodeModuleBundle, + package: *const Api.JavascriptBundledPackage, + _query: string, + ) ?*const Api.JavascriptBundledModule { + const ModuleFinder = struct { + const Self = @This(); + ctx: *const NodeModuleBundle, + pkg: *const Api.JavascriptBundledPackage, + query: string, + + // Since the module doesn't necessarily exist, we use an integer overflow as the module name + pub fn moduleName(context: *const Self, module: *const Api.JavascriptBundledModule) string { + return if (module.path.offset == context.ctx.bundle.manifest_string.len) context.query else context.ctx.str(module.path); + } + + pub fn cmpAsc(context: Self, lhs: Api.JavascriptBundledModule, rhs: Api.JavascriptBundledModule) std.math.Order { + // Comapre the module name + const lhs_name = context.moduleName(&lhs); + const rhs_name = context.moduleName(&rhs); + const VoidType = void; + + const traversal_length = std.math.min(lhs_name.len, rhs_name.len); + + for (lhs_name[0..traversal_length]) |char, i| { + switch (std.math.order(char, rhs_name[i])) { + .lt, .gt => |order| { + return order; + }, + .eq => {}, + } + } + + return std.math.order(lhs_name.len, rhs_name.len); + } + }; + var to_find = Api.JavascriptBundledModule{ + .package_id = 0, + .code = .{}, + .path = .{ + .offset = @truncate(u32, this.bundle.manifest_string.len), + }, + }; + + var finder = ModuleFinder{ .ctx = this, .pkg = package, .query = _query }; + + const modules = modulesIn(&this.bundle, package); + const module_id = std.sort.binarySearch( + Api.JavascriptBundledModule, + to_find, + modules, + finder, + ModuleFinder.cmpAsc, + ) orelse return null; + return &modules[module_id]; + } + pub fn init(container: Api.JavascriptBundleContainer, allocator: *std.mem.Allocator) NodeModuleBundle { return NodeModuleBundle{ .container = container, .bundle = container.bundle.?, .allocator = allocator, + .package_id_map = undefined, + .package_name_map = undefined, + .package_name_ids_ptr = undefined, }; } @@ -43,14 +241,19 @@ pub const NodeModuleBundle = struct { var reader = schema.Reader.init(read_bytes, allocator); var container = try Api.JavascriptBundleContainer.decode(&reader); - return NodeModuleBundle{ + var bundle = NodeModuleBundle{ .allocator = allocator, .container = container, .bundle = container.bundle.?, .fd = stream.handle, .bytes = read_bytes, .bytes_ptr = file_bytes, + .package_id_map = undefined, + .package_name_map = undefined, + .package_name_ids_ptr = undefined, }; + try bundle.loadPackageMap(); + return bundle; } pub fn str(bundle: *const NodeModuleBundle, pointer: Api.StringPointer) string { @@ -58,7 +261,6 @@ pub const NodeModuleBundle = struct { } pub fn getPackageSize(this: *const NodeModuleBundle, pkg: Api.JavascriptBundledPackage) usize { - const modules = this.bundle.modules[pkg.modules_offset .. pkg.modules_offset + pkg.modules_length]; var size: usize = 0; for (modules) |module| { size += module.code.length; @@ -86,10 +288,11 @@ pub const NodeModuleBundle = struct { ); for (modules) |module| { - const size_level = switch (module.code.length) { - 0...5_000 => SizeLevel.good, - 5_001...74_999 => SizeLevel.neutral, - else => SizeLevel.bad, + const size_level: SizeLevel = + switch (module.code.length) { + 0...5_000 => .good, + 5_001...74_999 => .neutral, + else => .bad, }; Output.print(indent, .{}); |