Compare commits

...

5 Commits

Author SHA1 Message Date
dave caruso
1fb5c502fd remove generated files 2024-09-11 16:24:14 -07:00
dave caruso
a04f6b8522 make createUninitialized return error.OutOfMemory 2024-09-11 16:23:16 -07:00
dave caruso
f17c71f3d6 Merge remote-tracking branch 'origin/main' into jarred/propagate-oom 2024-09-11 16:03:19 -07:00
Dylan Conway
acc258a210 Merge branch 'main' into jarred/propagate-oom 2024-09-10 19:58:33 -07:00
Jarred Sumner
cd26dacd59 Propagate OOM with strings more carefully 2024-09-10 19:48:06 -07:00
10 changed files with 91 additions and 58 deletions

View File

@@ -278,7 +278,7 @@ pub const RuntimeTranspilerCache = struct {
break :brk .{ .utf8 = utf8 };
},
.latin1 => brk: {
var latin1, const bytes = bun.String.createUninitialized(.latin1, this.metadata.output_byte_length);
var latin1, const bytes = try bun.String.createUninitialized(.latin1, this.metadata.output_byte_length);
errdefer latin1.deref();
const read_bytes = try file.preadAll(bytes, this.metadata.output_byte_offset);
@@ -295,7 +295,7 @@ pub const RuntimeTranspilerCache = struct {
break :brk .{ .string = latin1 };
},
.utf16 => brk: {
var string, const chars = bun.String.createUninitialized(.utf16, this.metadata.output_byte_length / 2);
var string, const chars = try bun.String.createUninitialized(.utf16, this.metadata.output_byte_length / 2);
errdefer string.deref();
const read_bytes = try file.preadAll(std.mem.sliceAsBytes(chars), this.metadata.output_byte_offset);

View File

@@ -220,7 +220,7 @@ extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject* globalObject
return JSValue::encode(Bun::toJS(globalObject, *bunString));
}
extern "C" BunString BunString__fromUTF16Unitialized(size_t length)
extern "C" BunString BunString__fromUTF16Uninitialized(size_t length)
{
ASSERT(length > 0);
UChar* ptr;
@@ -231,7 +231,7 @@ extern "C" BunString BunString__fromUTF16Unitialized(size_t length)
return { BunStringTag::WTFStringImpl, { .wtf = impl.leakRef() } };
}
extern "C" BunString BunString__fromLatin1Unitialized(size_t length)
extern "C" BunString BunString__fromLatin1Uninitialized(size_t length)
{
ASSERT(length > 0);
LChar* ptr;

View File

@@ -5801,11 +5801,9 @@ pub const NodeFS = struct {
},
},
.string => brk: {
const str = bun.SliceWithUnderlyingString.transcodeFromOwnedSlice(@constCast(ret.result.string), args.encoding);
if (str.underlying.tag == .Dead and str.utf8.len == 0) {
const str = bun.SliceWithUnderlyingString.transcodeFromOwnedSlice(@constCast(ret.result.string), args.encoding) catch {
return .{ .err = Syscall.Error.fromCode(.NOMEM, .read).withPathLike(args.path) };
}
};
break :brk .{ .result = .{ .string = str } };
},

View File

@@ -608,7 +608,10 @@ pub const Encoding = enum(u8) {
.base64 => {
var base64_buf: [std.base64.standard.Encoder.calcSize(max_size * 4)]u8 = undefined;
const encoded_len = bun.base64.encode(&base64_buf, input);
const encoded, const bytes = bun.String.createUninitialized(.latin1, encoded_len);
const encoded, const bytes = bun.String.createUninitialized(.latin1, encoded_len) catch {
globalObject.throwOutOfMemory();
return .zero;
};
defer encoded.deref();
@memcpy(@constCast(bytes), base64_buf[0..encoded_len]);
return encoded.toJS(globalObject);

View File

@@ -655,7 +655,10 @@ pub const Crypto = struct {
globalThis: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
const str, var bytes = bun.String.createUninitialized(.latin1, 36);
const str, var bytes = bun.String.createUninitialized(.latin1, 36) catch {
globalThis.throwOutOfMemory();
return .zero;
};
defer str.deref();
const uuid = globalThis.bunVM().rareData().nextUUID();
@@ -668,7 +671,10 @@ pub const Crypto = struct {
_: *Crypto,
globalThis: *JSC.JSGlobalObject,
) JSC.JSValue {
const str, var bytes = bun.String.createUninitialized(.latin1, 36);
const str, var bytes = bun.String.createUninitialized(.latin1, 36) catch {
globalThis.throwOutOfMemory();
return .zero;
};
defer str.deref();
// randomUUID must have been called already many times before this kicks

View File

@@ -35,6 +35,8 @@ const JSGlobalObject = JSC.JSGlobalObject;
const VirtualMachine = JSC.VirtualMachine;
const Task = @import("../javascript.zig").Task;
const OOM = bun.OOM;
const picohttp = bun.picohttp;
pub const TextEncoder = struct {
@@ -1104,7 +1106,8 @@ pub const Encoder = struct {
};
}
pub fn toBunStringFromOwnedSlice(input: []u8, encoding: JSC.Node.Encoding) bun.String {
/// Even in the error cases, the input bytes will be freed
pub fn toBunStringFromOwnedSlice(input: []u8, encoding: JSC.Node.Encoding) OOM!bun.String {
if (input.len == 0)
return bun.String.empty;
@@ -1114,11 +1117,8 @@ pub const Encoder = struct {
return bun.String.createExternalGloballyAllocated(.latin1, input);
}
const str, const chars = bun.String.createUninitialized(.latin1, input.len);
defer bun.default_allocator.free(input);
if (str.tag == .Dead) {
return str;
}
const str, const chars = try bun.String.createUninitialized(.latin1, input.len);
strings.copyLatin1IntoASCII(chars, input);
return str;
},
@@ -1126,9 +1126,9 @@ pub const Encoder = struct {
return bun.String.createExternalGloballyAllocated(.latin1, input);
},
.buffer, .utf8 => {
const converted = strings.toUTF16Alloc(bun.default_allocator, input, false, false) catch {
const converted = strings.toUTF16Alloc(bun.default_allocator, input, false, false) catch |err| {
bun.default_allocator.free(input);
return bun.String.dead;
return err;
};
if (converted) |utf16| {
@@ -1152,11 +1152,7 @@ pub const Encoder = struct {
.hex => {
defer bun.default_allocator.free(input);
const str, const chars = bun.String.createUninitialized(.latin1, input.len * 2);
if (str.tag == .Dead) {
return str;
}
const str, const chars = try bun.String.createUninitialized(.latin1, input.len * 2);
const wrote = strings.encodeBytesToHex(chars, input);
@@ -1174,17 +1170,15 @@ pub const Encoder = struct {
// appears inconsistent with Node.js.
.base64url => {
defer bun.default_allocator.free(input);
const out, const chars = bun.String.createUninitialized(.latin1, bun.base64.urlSafeEncodeLen(input));
if (out.tag != .Dead) {
_ = bun.base64.encodeURLSafe(chars, input);
}
const out, const chars = try bun.String.createUninitialized(.latin1, bun.base64.urlSafeEncodeLen(input));
_ = bun.base64.encodeURLSafe(chars, input);
return out;
},
.base64 => {
defer bun.default_allocator.free(input);
const to_len = bun.base64.encodeLen(input);
const to = bun.default_allocator.alloc(u8, to_len) catch return bun.String.dead;
const to = try bun.default_allocator.alloc(u8, to_len);
const wrote = bun.base64.encode(to, input);
return bun.String.createExternalGloballyAllocated(.latin1, to[0..wrote]);
},
@@ -1200,14 +1194,20 @@ pub const Encoder = struct {
switch (comptime encoding) {
.ascii => {
var str, const chars = bun.String.createUninitialized(.latin1, len);
var str, const chars = bun.String.createUninitialized(.latin1, len) catch {
global.throwOutOfMemory();
return .zero;
};
defer str.deref();
strings.copyLatin1IntoASCII(chars, input);
return str.toJS(global);
},
.latin1 => {
var str, const chars = bun.String.createUninitialized(.latin1, len);
var str, const chars = bun.String.createUninitialized(.latin1, len) catch {
global.throwOutOfMemory();
return .zero;
};
defer str.deref();
@memcpy(chars, input);
@@ -1227,7 +1227,10 @@ pub const Encoder = struct {
// Avoid incomplete characters
if (len / 2 == 0) return ZigString.Empty.toJS(global);
var output, const chars = bun.String.createUninitialized(.utf16, len / 2);
var output, const chars = bun.String.createUninitialized(.utf16, len / 2) catch {
global.throwOutOfMemory();
return .zero;
};
defer output.deref();
var output_bytes = std.mem.sliceAsBytes(chars);
output_bytes[output_bytes.len - 1] = 0;
@@ -1237,7 +1240,10 @@ pub const Encoder = struct {
},
.hex => {
var str, const chars = bun.String.createUninitialized(.latin1, len * 2);
var str, const chars = bun.String.createUninitialized(.latin1, len * 2) catch {
global.throwOutOfMemory();
return .zero;
};
defer str.deref();
const wrote = strings.encodeBytesToHex(chars, input);
@@ -1246,7 +1252,10 @@ pub const Encoder = struct {
},
.base64url => {
var out, const chars = bun.String.createUninitialized(.latin1, bun.base64.urlSafeEncodeLen(input));
var out, const chars = bun.String.createUninitialized(.latin1, bun.base64.urlSafeEncodeLen(input)) catch {
global.throwOutOfMemory();
return .zero;
};
defer out.deref();
_ = bun.base64.encodeURLSafe(chars, input);
return out.toJS(global);
@@ -1254,7 +1263,11 @@ pub const Encoder = struct {
.base64 => {
const to_len = bun.base64.encodeLen(input);
var to = allocator.alloc(u8, to_len) catch return ZigString.init("Out of memory").toErrorInstance(global);
var to = allocator.alloc(u8, to_len) catch {
global.throwOutOfMemory();
return .zero;
};
const wrote = bun.base64.encode(to, input);
return ZigString.init(to[0..wrote]).toExternalValue(global);
},

View File

@@ -474,7 +474,7 @@ pub const Request = struct {
}
if (strings.isAllASCII(host) and strings.isAllASCII(req_url)) {
this.url, const bytes = bun.String.createUninitialized(.latin1, url_bytelength);
this.url, const bytes = try bun.String.createUninitialized(.latin1, url_bytelength);
_ = std.fmt.bufPrint(bytes, "{s}{any}{s}", .{
this.getProtocol(),
fmt,
@@ -484,13 +484,11 @@ pub const Request = struct {
};
} else {
// slow path
const temp_url = std.fmt.allocPrint(bun.default_allocator, "{s}{any}{s}", .{
this.url = try bun.String.createFormat("{s}{any}{s}", .{
this.getProtocol(),
fmt,
req_url,
}) catch bun.outOfMemory();
defer bun.default_allocator.free(temp_url);
this.url = bun.String.createUTF8(temp_url);
});
}
const href = bun.JSC.URL.hrefFromString(this.url);

View File

@@ -2517,7 +2517,10 @@ pub const E = struct {
}
if (s.is_utf16) {
var out, const chars = bun.String.createUninitialized(.utf16, s.len());
var out, const chars = bun.String.createUninitialized(.utf16, s.len()) catch {
globalObject.throwOutOfMemory();
return .zero;
};
defer out.deref();
@memcpy(chars, s.slice16());
return out.toJS(globalObject);
@@ -2529,7 +2532,10 @@ pub const E = struct {
const decoded = js_lexer.decodeStringLiteralEscapeSequencesToUTF16(s.slice(allocator), allocator) catch unreachable;
defer allocator.free(decoded);
var out, const chars = bun.String.createUninitialized(.utf16, decoded.len);
var out, const chars = bun.String.createUninitialized(.utf16, decoded.len) catch {
globalObject.throwOutOfMemory();
return .zero;
};
defer out.deref();
@memcpy(chars, decoded);

View File

@@ -363,7 +363,9 @@ pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length
return .ok;
}
var string, const bytes = bun.String.createUninitialized(.latin1, slice.len);
var string, const bytes = bun.String.createUninitialized(.latin1, slice.len) catch {
return .generic_failure;
};
defer string.deref();
@memcpy(bytes, slice);
@@ -424,7 +426,9 @@ pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, l
result.set(env, bun.String.empty.toJS(env));
}
var string, const chars = bun.String.createUninitialized(.utf16, slice.len);
var string, const chars = bun.String.createUninitialized(.utf16, slice.len) catch {
return .generic_failure;
};
defer string.deref();
@memcpy(chars, slice);

View File

@@ -271,6 +271,7 @@ pub const StringImplAllocator = struct {
pub const Tag = enum(u8) {
/// String is not valid. Observed on some failed operations.
/// To prevent crashes, this value acts similarly to .Empty (such as length = 0)
/// Prefer `error.OutOfMemory` instead of this value.
Dead = 0,
/// String is backed by a WTF::StringImpl from JavaScriptCore.
/// Can be in either `latin1` or `utf16le` encodings.
@@ -314,8 +315,8 @@ pub const String = extern struct {
extern fn BunString__fromBytes(bytes: [*]const u8, len: usize) String;
extern fn BunString__fromUTF16(bytes: [*]const u16, len: usize) String;
extern fn BunString__fromUTF16ToLatin1(bytes: [*]const u16, len: usize) String;
extern fn BunString__fromLatin1Unitialized(len: usize) String;
extern fn BunString__fromUTF16Unitialized(len: usize) String;
extern fn BunString__fromLatin1Uninitialized(len: usize) String;
extern fn BunString__fromUTF16Uninitialized(len: usize) String;
pub fn ascii(bytes: []const u8) String {
return String{ .tag = .ZigString, .value = .{ .ZigString = ZigString.init(bytes) } };
@@ -369,9 +370,13 @@ pub const String = extern struct {
return createUTF8(utf8_slice);
}
fn createUninitializedLatin1(len: usize) struct { String, []u8 } {
fn createUninitializedLatin1(len: usize) !struct { String, []u8 } {
bun.assert(len > 0);
const string = BunString__fromLatin1Unitialized(len);
const string = BunString__fromLatin1Uninitialized(len);
if (string.tag == .Dead) {
return error.OutOfMemory;
}
const wtf = string.value.WTFStringImpl;
return .{
string,
@@ -379,9 +384,13 @@ pub const String = extern struct {
};
}
fn createUninitializedUTF16(len: usize) struct { String, []u16 } {
fn createUninitializedUTF16(len: usize) !struct { String, []u16 } {
bun.assert(len > 0);
const string = BunString__fromUTF16Unitialized(len);
const string = BunString__fromUTF16Uninitialized(len);
if (string.tag == .Dead) {
return error.OutOfMemory;
}
const wtf = string.value.WTFStringImpl;
return .{
string,
@@ -406,12 +415,10 @@ pub const String = extern struct {
///
/// This is not allowed on zero-length strings, in this case you should
/// check earlier and use String.empty in that case.
///
/// If the length is too large, this will return a dead string.
pub fn createUninitialized(
comptime kind: WTFStringEncoding,
len: usize,
) struct { String, [](kind.Byte()) } {
) OOM!struct { String, [](kind.Byte()) } {
bun.assert(len > 0);
return switch (comptime kind) {
.latin1 => createUninitializedLatin1(len),
@@ -478,10 +485,8 @@ pub const String = extern struct {
}
if (this.isUTF16()) {
const new, const bytes = createUninitialized(.utf16, this.length());
if (new.tag != .Dead) {
@memcpy(bytes, this.value.ZigString.utf16Slice());
}
const new, const bytes = createUninitialized(.utf16, this.length()) catch bun.outOfMemory();
@memcpy(bytes, this.value.ZigString.utf16Slice());
return new;
}
@@ -1366,7 +1371,7 @@ pub const SliceWithUnderlyingString = struct {
/// Transcode a byte array to an encoded String, avoiding unnecessary copies.
///
/// owned_input_bytes ownership is transferred to this function
pub fn transcodeFromOwnedSlice(owned_input_bytes: []u8, encoding: JSC.Node.Encoding) SliceWithUnderlyingString {
pub fn transcodeFromOwnedSlice(owned_input_bytes: []u8, encoding: JSC.Node.Encoding) OOM!SliceWithUnderlyingString {
if (owned_input_bytes.len == 0) {
return .{
.utf8 = ZigString.Slice.empty,
@@ -1375,7 +1380,7 @@ pub const SliceWithUnderlyingString = struct {
}
return .{
.underlying = JSC.WebCore.Encoder.toBunStringFromOwnedSlice(owned_input_bytes, encoding),
.underlying = try JSC.WebCore.Encoder.toBunStringFromOwnedSlice(owned_input_bytes, encoding),
};
}