also looking at this
fix(runtime): realign ResolvedSource.Tag ids to the linked (pinned) Bun codegen
#117
2 files
+186
-235
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -1003,6 +1003,10 @@ pub const FileSystemFlags = enum(c_int) { | ||
| 1003 | 1003 | |
| 1004 | 1004 | _, |
| 1005 | 1005 | |
| 1006 | // Node's stringToFlags is a case-SENSITIVE, strict-equality switch over | |
| 1007 | // exactly these 22 primitive strings (any other string throws | |
| 1008 | // ERR_INVALID_ARG_VALUE). Uppercase spellings ("W", "A+", ...) must NOT be | |
| 1009 | // accepted. Ports oven-sh/bun 459c33fa9f (#32966). | |
| 1006 | 1010 | const map = bun.ComptimeStringMap(i32, .{ |
| 1007 | 1011 | .{ "r", O.RDONLY }, |
| 1008 | 1012 | .{ "rs", O.RDONLY | O.SYNC }, |
| @@ -1011,52 +1015,25 @@ pub const FileSystemFlags = enum(c_int) { | ||
| 1011 | 1015 | .{ "rs+", O.RDWR | O.SYNC }, |
| 1012 | 1016 | .{ "sr+", O.RDWR | O.SYNC }, |
| 1013 | 1017 | |
| 1014 | .{ "R", O.RDONLY }, | |
| 1015 | .{ "RS", O.RDONLY | O.SYNC }, | |
| 1016 | .{ "SR", O.RDONLY | O.SYNC }, | |
| 1017 | .{ "R+", O.RDWR }, | |
| 1018 | .{ "RS+", O.RDWR | O.SYNC }, | |
| 1019 | .{ "SR+", O.RDWR | O.SYNC }, | |
| 1020 | ||
| 1021 | 1018 | .{ "w", O.TRUNC | O.CREAT | O.WRONLY }, |
| 1022 | 1019 | .{ "wx", O.TRUNC | O.CREAT | O.WRONLY | O.EXCL }, |
| 1023 | 1020 | .{ "xw", O.TRUNC | O.CREAT | O.WRONLY | O.EXCL }, |
| 1024 | 1021 | |
| 1025 | .{ "W", O.TRUNC | O.CREAT | O.WRONLY }, | |
| 1026 | .{ "WX", O.TRUNC | O.CREAT | O.WRONLY | O.EXCL }, | |
| 1027 | .{ "XW", O.TRUNC | O.CREAT | O.WRONLY | O.EXCL }, | |
| 1028 | ||
| 1029 | 1022 | .{ "w+", O.TRUNC | O.CREAT | O.RDWR }, |
| 1030 | 1023 | .{ "wx+", O.TRUNC | O.CREAT | O.RDWR | O.EXCL }, |
| 1031 | 1024 | .{ "xw+", O.TRUNC | O.CREAT | O.RDWR | O.EXCL }, |
| 1032 | 1025 | |
| 1033 | .{ "W+", O.TRUNC | O.CREAT | O.RDWR }, | |
| 1034 | .{ "WX+", O.TRUNC | O.CREAT | O.RDWR | O.EXCL }, | |
| 1035 | .{ "XW+", O.TRUNC | O.CREAT | O.RDWR | O.EXCL }, | |
| 1036 | ||
| 1037 | 1026 | .{ "a", O.APPEND | O.CREAT | O.WRONLY }, |
| 1038 | 1027 | .{ "ax", O.APPEND | O.CREAT | O.WRONLY | O.EXCL }, |
| 1039 | 1028 | .{ "xa", O.APPEND | O.CREAT | O.WRONLY | O.EXCL }, |
| 1040 | 1029 | .{ "as", O.APPEND | O.CREAT | O.WRONLY | O.SYNC }, |
| 1041 | 1030 | .{ "sa", O.APPEND | O.CREAT | O.WRONLY | O.SYNC }, |
| 1042 | 1031 | |
| 1043 | .{ "A", O.APPEND | O.CREAT | O.WRONLY }, | |
| 1044 | .{ "AX", O.APPEND | O.CREAT | O.WRONLY | O.EXCL }, | |
| 1045 | .{ "XA", O.APPEND | O.CREAT | O.WRONLY | O.EXCL }, | |
| 1046 | .{ "AS", O.APPEND | O.CREAT | O.WRONLY | O.SYNC }, | |
| 1047 | .{ "SA", O.APPEND | O.CREAT | O.WRONLY | O.SYNC }, | |
| 1048 | ||
| 1049 | 1032 | .{ "a+", O.APPEND | O.CREAT | O.RDWR }, |
| 1050 | 1033 | .{ "ax+", O.APPEND | O.CREAT | O.RDWR | O.EXCL }, |
| 1051 | 1034 | .{ "xa+", O.APPEND | O.CREAT | O.RDWR | O.EXCL }, |
| 1052 | 1035 | .{ "as+", O.APPEND | O.CREAT | O.RDWR | O.SYNC }, |
| 1053 | 1036 | .{ "sa+", O.APPEND | O.CREAT | O.RDWR | O.SYNC }, |
| 1054 | ||
| 1055 | .{ "A+", O.APPEND | O.CREAT | O.RDWR }, | |
| 1056 | .{ "AX+", O.APPEND | O.CREAT | O.RDWR | O.EXCL }, | |
| 1057 | .{ "XA+", O.APPEND | O.CREAT | O.RDWR | O.EXCL }, | |
| 1058 | .{ "AS+", O.APPEND | O.CREAT | O.RDWR | O.SYNC }, | |
| 1059 | .{ "SA+", O.APPEND | O.CREAT | O.RDWR | O.SYNC }, | |
| 1060 | 1037 | }); |
| 1061 | 1038 | |
| 1062 | 1039 | pub fn fromJS(ctx: *jsc.JSGlobalObject, val: jsc.JSValue) bun.JSError!?FileSystemFlags { |
| @@ -1076,45 +1053,30 @@ pub const FileSystemFlags = enum(c_int) { | ||
| 1076 | 1053 | return @as(FileSystemFlags, @enumFromInt(flags)); |
| 1077 | 1054 | } |
| 1078 | 1055 | |
| 1079 | const jsType = val.jsType(); | |
| 1080 | if (jsType.isStringLike()) { | |
| 1081 | const str = try val.getZigString(ctx); | |
| 1082 | if (str.isEmpty()) { | |
| 1083 | return ctx.throwInvalidArguments("Expected flags to be a non-empty string. Learn more at https://nodejs.org/api/fs.html#fs_file_system_flags", .{}); | |
| 1084 | } | |
| 1085 | // it's definitely wrong when the string is super long | |
| 1086 | else if (str.len > 12) { | |
| 1087 | return ctx.throwInvalidArguments("Invalid flag '{f}'. Learn more at https://nodejs.org/api/fs.html#fs_file_system_flags", .{str}); | |
| 1088 | } | |
| 1056 | if (val.isUndefinedOrNull()) { | |
| 1057 | return null; | |
| 1058 | } | |
| 1089 | 1059 | |
| 1090 | const flags: i32 = brk: { | |
| 1091 | switch (str.is16Bit()) { | |
| 1092 | inline else => |is_16bit| { | |
| 1093 | const chars = if (is_16bit) str.utf16SliceAligned() else str.slice(); | |
| 1094 | ||
| 1095 | if (std.ascii.isDigit(@as(u8, @truncate(chars[0])))) { | |
| 1096 | // node allows "0o644" as a string :( | |
| 1097 | if (is_16bit) { | |
| 1098 | const slice = str.toSlice(bun.default_allocator); | |
| 1099 | defer slice.deinit(); | |
| 1100 | ||
| 1101 | break :brk @as(i32, @intCast(std.fmt.parseInt(Mode, slice.slice(), 10) catch break :brk null)); | |
| 1102 | } else { | |
| 1103 | break :brk @as(i32, @intCast(std.fmt.parseInt(Mode, chars, 10) catch break :brk null)); | |
| 1104 | } | |
| 1105 | } | |
| 1106 | }, | |
| 1060 | // Node switches on the value with strict equality, so only primitive | |
| 1061 | // strings can match; every other value (including a `new String("w")` | |
| 1062 | // wrapper, if it reaches here) throws ERR_INVALID_ARG_VALUE (not | |
| 1063 | // ERR_INVALID_ARG_TYPE), and a leading digit is NOT parsed as a raw | |
| 1064 | // open(2) flag. Ports oven-sh/bun 459c33fa9f (#32966). NB: with the pinned | |
| 1065 | // internal `fs.ts`, a String-wrapper flag is coerced/defaulted in JS | |
| 1066 | // before reaching native, so that specific case surfaces as ENOENT until | |
| 1067 | // Home bumps Bun; all primitive-string cases are validated here. | |
| 1068 | if (val.isStringLiteral()) { | |
| 1069 | const str = try val.getZigString(ctx); | |
| 1070 | // The longest valid flag string is 3 bytes ("as+", "wx+", ...). | |
| 1071 | if (str.len >= 1 and str.len <= 3) { | |
| 1072 | if (map.getWithEql(str, jsc.ZigString.eqlComptime)) |flags| { | |
| 1073 | return @enumFromInt(flags); | |
| 1107 | 1074 | } |
| 1108 | ||
| 1109 | break :brk map.getWithEql(str, jsc.ZigString.eqlComptime) orelse break :brk null; | |
| 1110 | } orelse { | |
| 1111 | return ctx.throwInvalidArguments("Invalid flag '{f}'. Learn more at https://nodejs.org/api/fs.html#fs_file_system_flags", .{str}); | |
| 1112 | }; | |
| 1113 | ||
| 1114 | return @enumFromInt(flags); | |
| 1075 | } | |
| 1115 | 1076 | } |
| 1116 | 1077 | |
| 1117 | return null; | |
| 1078 | var formatter = jsc.ConsoleObject.Formatter{ .globalThis = ctx }; | |
| 1079 | return ctx.ERR(.INVALID_ARG_VALUE, "The argument 'flags' is invalid. Received {f}", .{val.toFmt(&formatter)}).throw(); | |
| 1118 | 1080 | } |
| 1119 | 1081 | |
| 1120 | 1082 | /// Equivalent of GetValidFileMode, which is used to implement fs.access and copyFile |