aboutsummaryrefslogtreecommitdiff
path: root/src/install
diff options
context:
space:
mode:
authorGravatar Alex Lam S.L <alexlamsl@gmail.com> 2023-08-25 03:17:48 +0300
committerGravatar GitHub <noreply@github.com> 2023-08-24 17:17:48 -0700
commite115638cba41e77a667d5798a0ca8cae9b38db9f (patch)
tree035fe54e593ada58ff7e2dd226c17e85b09ca7aa /src/install
parent6e57556fad85b71b0985e93a88c55b78ba19fad2 (diff)
downloadbun-e115638cba41e77a667d5798a0ca8cae9b38db9f.tar.gz
bun-e115638cba41e77a667d5798a0ca8cae9b38db9f.tar.zst
bun-e115638cba41e77a667d5798a0ca8cae9b38db9f.zip
[install] fix crash when installing package that uses loose semver pre-release (#4302)
- also fix parsing of `1.2.3pre+build` fixes #4266
Diffstat (limited to 'src/install')
-rw-r--r--src/install/install.zig2
-rw-r--r--src/install/lockfile.zig2
-rw-r--r--src/install/npm.zig38
-rw-r--r--src/install/semver.zig34
4 files changed, 42 insertions, 34 deletions
diff --git a/src/install/install.zig b/src/install/install.zig
index b0cdf35c8..baa35b869 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -2287,7 +2287,7 @@ pub const PackageManager = struct {
if (entry.kind != .directory and entry.kind != .sym_link) continue;
const name = entry.name;
const sliced = SlicedString.init(name, name);
- const parsed = Semver.Version.parse(sliced, allocator);
+ const parsed = Semver.Version.parse(sliced);
if (!parsed.valid or parsed.wildcard != .none) continue;
// not handling OOM
// TODO: wildcard
diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig
index 8eb411739..363418976 100644
--- a/src/install/lockfile.zig
+++ b/src/install/lockfile.zig
@@ -2860,7 +2860,7 @@ pub const Package = extern struct {
};
if (workspace_json.has_found_version) {
const version = SlicedString.init(workspace_json.found_version, workspace_json.found_version);
- const result = Semver.Version.parse(version, allocator);
+ const result = Semver.Version.parse(version);
if (result.valid and result.wildcard == .none) {
entry.version = result.version.fill();
}
diff --git a/src/install/npm.zig b/src/install/npm.zig
index 0a25fe636..9f3f2952c 100644
--- a/src/install/npm.zig
+++ b/src/install/npm.zig
@@ -846,8 +846,16 @@ pub const PackageManifest = struct {
const versions = versions_q.expr.data.e_object.properties.slice();
for (versions) |prop| {
const version_name = prop.key.?.asString(allocator) orelse continue;
+ const sliced_version = SlicedString.init(version_name, version_name);
+ const parsed_version = Semver.Version.parse(sliced_version);
- if (strings.indexOfChar(version_name, '-') != null) {
+ if (Environment.allow_assert) std.debug.assert(parsed_version.valid);
+ if (!parsed_version.valid) {
+ log.addErrorFmt(&source, prop.value.?.loc, allocator, "Failed to parse dependency {s}", .{version_name}) catch unreachable;
+ continue;
+ }
+
+ if (parsed_version.version.tag.hasPre()) {
pre_versions_len += 1;
extern_string_count += 1;
} else {
@@ -1022,25 +1030,23 @@ pub const PackageManifest = struct {
var dependency_names = all_dependency_names_and_values;
var prev_extern_bin_group = extern_strings_bin_entries;
- var version_string__: String = String{};
for (versions) |prop| {
const version_name = prop.key.?.asString(allocator) orelse continue;
+ var sliced_version = SlicedString.init(version_name, version_name);
+ var parsed_version = Semver.Version.parse(sliced_version);
- var sliced_string = SlicedString.init(version_name, version_name);
-
- // We only need to copy the version tags if it's a pre/post
- if (std.mem.indexOfAny(u8, version_name, "-+") != null) {
- version_string__ = string_builder.append(String, version_name);
- sliced_string = version_string__.sliced(string_buf);
- }
-
- const parsed_version = Semver.Version.parse(sliced_string, allocator);
if (Environment.allow_assert) std.debug.assert(parsed_version.valid);
-
- if (!parsed_version.valid) {
- log.addErrorFmt(&source, prop.value.?.loc, allocator, "Failed to parse dependency {s}", .{version_name}) catch unreachable;
- continue;
+ // We only need to copy the version tags if it contains pre and/or build
+ if (parsed_version.version.tag.hasBuild() or parsed_version.version.tag.hasPre()) {
+ const version_string = string_builder.append(String, version_name);
+ sliced_version = version_string.sliced(string_buf);
+ parsed_version = Semver.Version.parse(sliced_version);
+ if (Environment.allow_assert) {
+ std.debug.assert(parsed_version.valid);
+ std.debug.assert(parsed_version.version.tag.hasBuild() or parsed_version.version.tag.hasPre());
+ }
}
+ if (!parsed_version.valid) continue;
var package_version = PackageVersion{};
@@ -1451,7 +1457,7 @@ pub const PackageManifest = struct {
const sliced_string = dist_tag_value_literal.value.sliced(string_buf);
- dist_tag_versions[dist_tag_i] = Semver.Version.parse(sliced_string, allocator).version.fill();
+ dist_tag_versions[dist_tag_i] = Semver.Version.parse(sliced_string).version.fill();
dist_tag_i += 1;
}
}
diff --git a/src/install/semver.zig b/src/install/semver.zig
index 1ec76b370..04818cfd3 100644
--- a/src/install/semver.zig
+++ b/src/install/semver.zig
@@ -689,13 +689,13 @@ pub const Version = extern struct {
const self = formatter.version;
try std.fmt.format(writer, "{?d}.{?d}.{?d}", .{ self.major, self.minor, self.patch });
- if (!self.tag.pre.isEmpty()) {
+ if (self.tag.hasPre()) {
const pre = self.tag.pre.slice(formatter.input);
try writer.writeAll("-");
try writer.writeAll(pre);
}
- if (!self.tag.build.isEmpty()) {
+ if (self.tag.hasBuild()) {
const build = self.tag.build.slice(formatter.input);
try writer.writeAll("+");
try writer.writeAll(build);
@@ -814,11 +814,11 @@ pub const Version = extern struct {
var multi_tag_warn = false;
// TODO: support multiple tags
- pub fn parse(allocator: Allocator, sliced_string: SlicedString) TagResult {
- return parseWithPreCount(allocator, sliced_string, 0);
+ pub fn parse(sliced_string: SlicedString) TagResult {
+ return parseWithPreCount(sliced_string, 0);
}
- pub fn parseWithPreCount(_: Allocator, sliced_string: SlicedString, initial_pre_count: u32) TagResult {
+ pub fn parseWithPreCount(sliced_string: SlicedString, initial_pre_count: u32) TagResult {
var input = sliced_string.slice;
var build_count: u32 = 0;
var pre_count: u32 = initial_pre_count;
@@ -876,7 +876,7 @@ pub const Version = extern struct {
},
'+' => {
// qualifier ::= ( '-' pre )? ( '+' build )?
- if (state == .pre) {
+ if (state == .pre or state == .none and initial_pre_count > 0) {
result.tag.pre = sliced_string.sub(input[start..i]).external();
if (comptime Environment.isDebug) {
std.debug.assert(!strings.containsChar(result.tag.pre.slice(sliced_string.buf), '-'));
@@ -901,7 +901,6 @@ pub const Version = extern struct {
if (state == .none and initial_pre_count > 0) {
state = .pre;
start = 0;
- result.tag.pre = sliced_string.sub(input[start..i]).external();
}
switch (state) {
@@ -932,7 +931,7 @@ pub const Version = extern struct {
stopped_at: u32 = 0,
};
- pub fn parse(sliced_string: SlicedString, allocator: Allocator) ParseResult {
+ pub fn parse(sliced_string: SlicedString) ParseResult {
var input = sliced_string.slice;
var result = ParseResult{};
@@ -1024,7 +1023,7 @@ pub const Version = extern struct {
}) {
i += 1;
}
- const tag_result = Tag.parse(allocator, sliced_string.sub(input[part_start_i..]));
+ const tag_result = Tag.parse(sliced_string.sub(input[part_start_i..]));
result.version.tag = tag_result.tag;
i += tag_result.len;
break;
@@ -1071,9 +1070,12 @@ pub const Version = extern struct {
// Some weirdo npm packages in the wild have a version like "1.0.0rc.1"
// npm just expects that to work...even though it has no "-" qualifier.
- if (result.wildcard == .none and part_i >= 2 and ((c >= 'A' and c <= 'Z') or (c >= 'a' and c <= 'z'))) {
+ if (result.wildcard == .none and part_i >= 2 and switch (c) {
+ 'a'...'z', 'A'...'Z', '_' => true,
+ else => false,
+ }) {
part_start_i = i;
- const tag_result = Tag.parseWithPreCount(allocator, sliced_string.sub(input[part_start_i..]), 1);
+ const tag_result = Tag.parseWithPreCount(sliced_string.sub(input[part_start_i..]), 1);
result.version.tag = tag_result.tag;
i += tag_result.len;
is_done = true;
@@ -1781,7 +1783,7 @@ pub const Query = struct {
}
if (!skip_round) {
- const parse_result = Version.parse(sliced.sub(input[i..]), allocator);
+ const parse_result = Version.parse(sliced.sub(input[i..]));
const version = parse_result.version.fill();
if (version.tag.hasBuild()) list.flags.setValue(Group.Flags.build, true);
if (version.tag.hasPre()) list.flags.setValue(Group.Flags.pre, true);
@@ -1817,7 +1819,7 @@ pub const Query = struct {
i += @as(usize, @intFromBool(!hyphenate));
if (hyphenate) {
- const second_parsed = Version.parse(sliced.sub(input[i..]), allocator);
+ const second_parsed = Version.parse(sliced.sub(input[i..]));
var second_version = second_parsed.version.fill();
if (second_version.tag.hasBuild()) list.flags.setValue(Group.Flags.build, true);
if (second_version.tag.hasPre()) list.flags.setValue(Group.Flags.pre, true);
@@ -1905,7 +1907,7 @@ pub const Query = struct {
const expect = if (Environment.isTest) struct {
pub var counter: usize = 0;
pub fn isRangeMatch(input: string, version_str: string) bool {
- var parsed = Version.parse(SlicedString.init(version_str, version_str), default_allocator);
+ var parsed = Version.parse(SlicedString.init(version_str, version_str));
std.debug.assert(parsed.valid);
// std.debug.assert(strings.eql(parsed.version.raw.slice(version_str), version_str));
@@ -1956,7 +1958,7 @@ const expect = if (Environment.isTest) struct {
pub fn version(input: string, v: [3]?u32, src: std.builtin.SourceLocation) void {
Output.initTest();
defer counter += 1;
- const result = Version.parse(SlicedString.init(input, input), default_allocator);
+ const result = Version.parse(SlicedString.init(input, input));
std.debug.assert(result.valid);
if (v[0] != result.version.major or v[1] != result.version.minor or v[2] != result.version.patch) {
@@ -1980,7 +1982,7 @@ const expect = if (Environment.isTest) struct {
Output.initTest();
defer counter += 1;
- var result = Version.parse(SlicedString.init(input, input), default_allocator);
+ var result = Version.parse(SlicedString.init(input, input));
if (!v.eql(result.version.fill())) {
Output.panic("<r><red>Fail<r> Expected version <b>\"{s}\"<r> to match <b>\"{?d}.{?d}.{?d}\" but received <red>\"{?d}.{?d}.{?d}\"<r>\nAt: <blue><b>{s}:{d}:{d}<r><d> in {s}<r>", .{
input,