[bun.js] Bun.write for macOS

This commit is contained in:
Jarred Sumner
2022-03-23 04:26:49 -07:00
parent 5e5f0bd293
commit 07d77c1e00
14 changed files with 232 additions and 76 deletions

View File

@@ -303,7 +303,7 @@ pub const HTMLRewriter = struct {
const is_pending = input.needsToReadFile();
defer if (!is_pending) input.detach();
if (input.needsToReadFile()) {
if (is_pending) {
input.doReadFileInternal(*BufferOutputSink, sink, onFinishedLoadingWrap, global);
} else if (sink.runOutputSink(input.sharedView(), false)) |error_value| {
return error_value;
@@ -321,8 +321,6 @@ pub const HTMLRewriter = struct {
}
pub fn onFinishedLoading(sink: *BufferOutputSink, bytes: anyerror![]u8) void {
var input = sink.input;
defer input.detach();
const data = bytes catch |err| {
if (sink.response.body.value == .Locked and @ptrToInt(sink.response.body.value.Locked.task) == @ptrToInt(sink)) {
sink.response.body.value = .{ .Empty = .{} };
@@ -378,16 +376,10 @@ pub const HTMLRewriter = struct {
this.response.body.value = .{
.Blob = JSC.WebCore.Blob.init(bytes, this.bytes.allocator, this.global),
};
if (prev_value.Locked.promise) |promise| {
prev_value.Locked.promise = null;
promise.asInternalPromise().?.resolve(this.global, JSC.JSValue.fromRef(
Response.makeMaybePooled(
this.global.ref(),
this.response,
),
));
}
prev_value.resolve(
&this.response.body.value,
this.global,
);
}
pub fn write(this: *BufferOutputSink, bytes: []const u8) void {

View File

@@ -298,7 +298,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
}
} else {
var sbytes: std.os.off_t = adjusted_count;
const signed_offset = @bitCast(i64, this.sendfile.offset);
const signed_offset = @bitCast(i64, @as(u64, this.sendfile.offset));
// var sf_hdr_trailer: std.os.darwin.sf_hdtr = .{
// .headers = &separator_iovec,
@@ -320,9 +320,9 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
null,
0,
));
this.sendfile.offset += sbytes;
this.sendfile.remain -= @intCast(JSC.WebCore.Blob.SizeType, sbytes);
const wrote = @intCast(Blob.SizeType, sbytes);
this.sendfile.offset += wrote;
this.sendfile.remain -= wrote;
if (errcode != .AGAIN or this.aborted or this.sendfile.remain == 0 or sbytes == 0) {
if (errcode != .AGAIN and errcode != .SUCCESS) {
Output.prettyErrorln("Error: {s}", .{@tagName(errcode)});

View File

@@ -1868,6 +1868,10 @@ pub const ArrayBuffer = extern struct {
return this.ptr[this.offset .. this.offset + this.len];
}
pub inline fn byteSlice(this: *const @This()) []u8 {
return this.ptr[this.offset .. this.offset + this.byte_len];
}
pub inline fn asU16(this: *const @This()) []u16 {
return std.mem.bytesAsSlice(u16, @alignCast(@alignOf([*]u16), this.ptr[this.offset..this.byte_len]));
}

View File

@@ -221,6 +221,12 @@ JSC__JSInternalPromise* JSC__JSValue__asInternalPromise(JSC__JSValue JSValue0)
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
return JSC::jsCast<JSC::JSInternalPromise*>(value);
}
JSC__JSPromise* JSC__JSValue__asPromise(JSC__JSValue JSValue0)
{
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
return JSC::jsCast<JSC::JSPromise*>(value);
}
JSC__JSValue JSC__JSValue__createInternalPromise(JSC__JSGlobalObject* globalObject)
{
JSC::VM& vm = globalObject->vm();
@@ -1616,19 +1622,15 @@ int64_t JSC__JSValue__toInt64(JSC__JSValue val)
return result;
}
if (auto* heapBigInt = _val.asHeapBigInt()) {
if (heapBigInt != nullptr) {
if (_val.isHeapBigInt()) {
if (auto* heapBigInt = _val.asHeapBigInt()) {
return heapBigInt->toBigInt64(heapBigInt);
}
}
return _val.asAnyInt();
}
JSC__JSValue JSC__JSValue__createObject2(JSC__JSGlobalObject* globalObject, const ZigString* arg1,
const ZigString* arg2, JSC__JSValue JSValue3,
JSC__JSValue JSValue4)

View File

@@ -1836,6 +1836,7 @@ pub const JSValue = enum(u64) {
c_uint => @intCast(c_uint, toU32(this)),
c_int => @intCast(c_int, toInt32(this)),
?*JSInternalPromise => asInternalPromise(this),
?*JSPromise => asPromise(this),
// TODO: BigUint64?
u64 => @as(u64, toU32(this)),
@@ -1935,6 +1936,14 @@ pub const JSValue = enum(u64) {
});
}
pub fn asPromise(
value: JSValue,
) ?*JSPromise {
return cppFn("asPromise", .{
value,
});
}
pub fn jsNumber(number: anytype) JSValue {
return jsNumberWithType(@TypeOf(number), number);
}
@@ -2371,7 +2380,7 @@ pub const JSValue = enum(u64) {
return @intToPtr(*anyopaque, @enumToInt(this));
}
pub const Extern = [_][]const u8{ "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "getReadableStreamState", "getWritableStreamState", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" };
pub const Extern = [_][]const u8{ "asPromise", "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "getReadableStreamState", "getWritableStreamState", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" };
};
extern "c" fn Microtask__run(*Microtask, *JSGlobalObject) void;

View File

@@ -1,4 +1,4 @@
//-- AUTOGENERATED FILE -- 1647946969
//-- AUTOGENERATED FILE -- 1648033260
// clang-format off
#pragma once

View File

@@ -1,5 +1,5 @@
// clang-format: off
//-- AUTOGENERATED FILE -- 1647946969
//-- AUTOGENERATED FILE -- 1648033260
#pragma once
#include <stddef.h>
@@ -434,6 +434,8 @@ CPP_DECL JSC__JSCell* JSC__JSValue__asCell(JSC__JSValue JSValue0);
CPP_DECL JSC__JSInternalPromise* JSC__JSValue__asInternalPromise(JSC__JSValue JSValue0);
CPP_DECL double JSC__JSValue__asNumber(JSC__JSValue JSValue0);
CPP_DECL bJSC__JSObject JSC__JSValue__asObject(JSC__JSValue JSValue0);
CPP_DECL JSC__JSPromise* JSC__JSValue__asPromise(JSC__JSValue JSValue0);
CPP_DECL JSC__JSPromise* JSC__JSValue__asPromise(JSC__JSValue JSValue0);
CPP_DECL JSC__JSString* JSC__JSValue__asString(JSC__JSValue JSValue0);
CPP_DECL JSC__JSValue JSC__JSValue__createEmptyObject(JSC__JSGlobalObject* arg0, size_t arg1);
CPP_DECL JSC__JSValue JSC__JSValue__createInternalPromise(JSC__JSGlobalObject* arg0);

View File

@@ -273,6 +273,7 @@ pub extern fn JSC__JSValue__asCell(JSValue0: JSC__JSValue) [*c]JSC__JSCell;
pub extern fn JSC__JSValue__asInternalPromise(JSValue0: JSC__JSValue) [*c]JSC__JSInternalPromise;
pub extern fn JSC__JSValue__asNumber(JSValue0: JSC__JSValue) f64;
pub extern fn JSC__JSValue__asObject(JSValue0: JSC__JSValue) bJSC__JSObject;
pub extern fn JSC__JSValue__asPromise(JSValue0: JSC__JSValue) [*c]JSC__JSPromise;
pub extern fn JSC__JSValue__asString(JSValue0: JSC__JSValue) [*c]JSC__JSString;
pub extern fn JSC__JSValue__createEmptyObject(arg0: [*c]JSC__JSGlobalObject, arg1: usize) JSC__JSValue;
pub extern fn JSC__JSValue__createInternalPromise(arg0: [*c]JSC__JSGlobalObject) JSC__JSValue;

View File

@@ -328,7 +328,7 @@ pub fn copyfile(from: [:0]const u8, to: [:0]const u8, flags: c_int) Maybe(void)
unreachable;
}
pub fn fcopyfile(fd_in: std.os.fd_t, fd_out: std.os.fd_t, flags: c_int) Maybe(void) {
pub fn fcopyfile(fd_in: std.os.fd_t, fd_out: std.os.fd_t, flags: u32) Maybe(void) {
if (comptime !Environment.isMac) @compileError("macOS only");
while (true) {

View File

@@ -157,6 +157,12 @@ pub const Response = struct {
status_text: string = "",
redirected: bool = false,
pub fn getBodyValue(
this: *Response,
) *Body.Value {
return &this.body.value;
}
pub inline fn statusCode(this: *const Response) u16 {
return this.body.init.status_code;
}
@@ -1995,13 +2001,19 @@ pub const Blob = struct {
pub fn getFdMac(this: *This) AsyncIO.OpenError!JSC.Node.FileDescriptor {
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
this.opened_fd = AsyncIO.openSync(
this.file_store.pathlike.path.sliceZ(&buf),
open_flags_,
) catch |err| {
this.errno = err;
return err;
var path = if (@hasField(This, "file_store"))
this.file_store.pathlike.path.sliceZ(&buf)
else
this.file_blob.store.?.data.file.pathlike.path.sliceZ(&buf);
this.opened_fd = switch (JSC.Node.Syscall.open(path, open_flags_, JSC.Node.default_permission)) {
.result => |fd| fd,
.err => |err| {
this.errno = AsyncIO.asError(err.errno);
return @errSetCast(AsyncIO.OpenError, this.errno.?);
},
};
return this.opened_fd;
}
@@ -2437,8 +2449,8 @@ pub const Blob = struct {
onCompleteCallback: OnWriteFileCallback = undefined,
wrote: usize = 0,
pub const ResultType = anyerror!Blob;
pub const OnWriteFileCallback = fn (ctx: *anyopaque, blob: ResultType) void;
pub const ResultType = anyerror!SizeType;
pub const OnWriteFileCallback = fn (ctx: *anyopaque, count: ResultType) void;
pub usingnamespace FileOpenerMixin(WriteFile);
pub usingnamespace FileCloserMixin(WriteFile);
@@ -2522,6 +2534,7 @@ pub const Blob = struct {
var cb_ctx = this.onCompleteCtx;
this.bytes_blob.store.?.deref();
this.file_blob.store.?.deref();
if (this.errno) |err| {
bun.default_allocator.destroy(this);
@@ -2529,10 +2542,8 @@ pub const Blob = struct {
return;
}
var blob = this.file_blob;
bun.default_allocator.destroy(this);
cb(cb_ctx, blob);
cb(cb_ctx, @truncate(SizeType, this.wrote));
}
pub fn run(this: *WriteFile, task: *WriteFileTask) void {
this.runAsyncFrame = async this.runAsync(task);
@@ -2580,7 +2591,7 @@ pub const Blob = struct {
if (wrote_len == 0) break;
}
this.file_blob.size = @truncate(SizeType, total_written);
this.wrote = @truncate(SizeType, total_written);
if (needs_close) {
this.doClose() catch {};
@@ -2613,7 +2624,7 @@ pub const Blob = struct {
globalThis: *JSGlobalObject,
pub const ResultType = anyerror!Blob;
pub const ResultType = anyerror!SizeType;
pub const Callback = fn (ctx: *anyopaque, len: ResultType) void;
pub const CopyFilePromiseTask = JSC.ConcurrentPromiseTask(CopyFile);
@@ -2651,6 +2662,7 @@ pub const Blob = struct {
bun.default_allocator.free(bun.constStrToU8(this.source_file_store.pathlike.path.slice()));
}
}
this.store.?.deref();
bun.default_allocator.destroy(this);
}
@@ -2673,21 +2685,14 @@ pub const Blob = struct {
}
pub fn then(this: *CopyFile, promise: *JSC.JSInternalPromise) void {
defer this.source_store.?.deref();
this.source_store.?.deref();
if (this.errno != null) {
this.reject(promise);
return;
}
var blob = Blob{
.offset = this.read_off,
.size = this.read_len,
.store = this.store,
};
blob.allocator = bun.default_allocator;
var ptr = bun.default_allocator.create(Blob) catch unreachable;
ptr.* = blob;
promise.resolve(this.globalThis, JSC.JSValue.fromRef(Blob.Class.make(this.globalThis.ref(), ptr)));
promise.resolve(this.globalThis, JSC.JSValue.jsNumberFromUint64(this.read_len));
}
pub fn run(this: *CopyFile) void {
this.runAsync();
@@ -2802,14 +2807,23 @@ pub const Blob = struct {
pub fn doFCopyFile(this: *CopyFile) anyerror!void {
switch (JSC.Node.Syscall.fcopyfile(this.source_fd, this.destination_fd, os.system.COPYFILE_DATA)) {
else => |errno| {
this.errno = AsyncIO.asError(errno);
.err => |errno| {
this.errno = AsyncIO.asError(errno.errno);
return this.errno.?;
},
.result => {},
}
}
pub fn doClonefile(this: *CopyFile) anyerror!void {
switch (JSC.Node.Syscall.clonefile(this.destination_file_store.pathlike.path.sliceZAssume(), this.source_file_store.pathlike.path.sliceZAssume())) {
.err => |errno| {
return AsyncIO.asError(errno.errno);
},
.result => {},
}
}
pub fn runAsync(this: *CopyFile) void {
// defer task.onFinish();
@@ -2829,7 +2843,7 @@ pub const Blob = struct {
// First, we attempt to clonefile() on macOS
// This is the fastest way to copy a file.
if (comptime Environment.isMac) {
if (this.offset == 0) {
if (this.offset == 0 and this.source_file_store.pathlike == .path and this.destination_file_store.pathlike == .path) {
do_clonefile: {
// stat the output file, make sure it:
@@ -2848,12 +2862,12 @@ pub const Blob = struct {
},
.err => |err| {
// If we can't stat it, we also can't copy it.
this.errno = err;
this.errno = AsyncIO.asError(err.errno);
return;
},
}
if (this.doCloneFile()) {
if (this.doClonefile()) {
if (this.max_length != Blob.max_size and this.max_length < @intCast(SizeType, stat_.?.size)) {
// If this fails...well, there's not much we can do about it.
_ = bun.C.truncate(
@@ -3448,11 +3462,11 @@ pub const Blob = struct {
pub const WriteFilePromise = struct {
promise: *JSPromise,
globalThis: *JSGlobalObject,
pub fn run(handler: *@This(), blob_: Blob.Store.WriteFile.ResultType) void {
pub fn run(handler: *@This(), count: Blob.Store.WriteFile.ResultType) void {
var promise = handler.promise;
var globalThis = handler.globalThis;
bun.default_allocator.destroy(handler);
var blob = blob_ catch |err| {
var wrote = count catch |err| {
var error_string = ZigString.init(
std.fmt.allocPrint(bun.default_allocator, "Failed to write file \"{s}\"", .{std.mem.span(@errorName(err))}) catch unreachable,
);
@@ -3461,10 +3475,7 @@ pub const Blob = struct {
return;
};
var ptr = bun.default_allocator.create(Blob) catch unreachable;
ptr.* = blob;
promise.resolve(globalThis, JSC.JSValue.fromRef(Blob.Class.make(globalThis.ref(), ptr)));
promise.resolve(globalThis, JSC.JSValue.jsNumberFromUint64(wrote));
}
};
@@ -3775,7 +3786,7 @@ pub const Blob = struct {
JSC.JSValue.JSType.BigUint64Array,
JSC.JSValue.JSType.DataView,
=> {
var buf = try bun.default_allocator.dupe(u8, top_value.asArrayBuffer(global).?.slice());
var buf = try bun.default_allocator.dupe(u8, top_value.asArrayBuffer(global).?.byteSlice());
return Blob.init(buf, bun.default_allocator, global);
},
@@ -3866,7 +3877,7 @@ pub const Blob = struct {
JSC.JSValue.JSType.DataView,
=> {
var buf = item.asArrayBuffer(global).?;
joiner.append(buf.slice(), 0, null);
joiner.append(buf.byteSlice(), 0, null);
continue;
},
.Array, .DerivedArray => {
@@ -4073,6 +4084,15 @@ pub const Body = struct {
task: ?*anyopaque = null,
callback: ?fn (ctx: *anyopaque, value: *Value) void = null,
deinit: bool = false,
action: Action = Action.none,
pub const Action = enum {
none,
getText,
getJSON,
getArrayBuffer,
getBlob,
};
};
pub const Value = union(Tag) {
@@ -4091,6 +4111,47 @@ pub const Body = struct {
};
pub const empty = Value{ .Empty = .{} };
pub fn resolve(this: *Value, new: *Value, global: *JSGlobalObject) void {
if (this.* == .Locked) {
var locked = this.Locked;
if (locked.callback) |callback| {
locked.callback = null;
callback(locked.task.?, new);
}
if (locked.promise) |promise| {
var blob = new.use();
switch (locked.action) {
.getText => {
promise.asPromise().?.resolve(global, JSValue.fromRef(blob.getTextTransfer(global.ref())));
},
.getJSON => {
promise.asPromise().?.resolve(global, blob.toJSON(global));
blob.detach();
},
.getArrayBuffer => {
promise.asPromise().?.resolve(global, JSValue.fromRef(blob.getArrayBufferTransfer(global.ref())));
},
.getBlob => {
var ptr = bun.default_allocator.create(Blob) catch unreachable;
ptr.* = blob;
ptr.allocator = bun.default_allocator;
promise.asPromise().?.resolve(global, JSC.JSValue.fromRef(Blob.Class.make(global.ref(), ptr)));
},
else => {
var ptr = bun.default_allocator.create(Blob) catch unreachable;
ptr.* = blob;
ptr.allocator = bun.default_allocator;
promise.asInternalPromise().?.resolve(global, JSC.JSValue.fromRef(Blob.Class.make(global.ref(), ptr)));
},
}
JSC.C.JSValueUnprotect(global.ref(), promise.asObjectRef());
locked.promise = null;
}
}
}
pub fn slice(this: Value) []const u8 {
return switch (this) {
.Blob => this.Blob.sharedView(),
@@ -4118,8 +4179,9 @@ pub const Body = struct {
if (locked.promise) |promise| {
if (promise.asInternalPromise()) |internal| {
internal.reject(global, error_instance);
} else if (promise.asPromise()) |internal| {
internal.reject(global, error_instance);
}
JSC.C.JSValueUnprotect(global.ref(), promise.asObjectRef());
locked.promise = null;
}
@@ -4535,6 +4597,12 @@ pub const Request = struct {
);
}
pub fn getBodyValue(
this: *Request,
) *Body.Value {
return &this.body;
}
pub fn getBodyUsed(
this: *Request,
_: js.JSContextRef,
@@ -4602,6 +4670,14 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
_: js.ExceptionRef,
) js.JSValueRef {
var value = this.getBodyValue();
if (value.* == .Locked) {
value.Locked.action = .getText;
var promise = JSC.JSPromise.create(ctx.ptr());
value.Locked.promise = promise.asValue(ctx.ptr());
return value.Locked.promise.?.asObjectRef();
}
var blob = this.body.use();
return blob.getTextTransfer(ctx);
}
@@ -4614,6 +4690,14 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
var value = this.getBodyValue();
if (value.* == .Locked) {
value.Locked.action = .getJSON;
var promise = JSC.JSPromise.create(ctx.ptr());
value.Locked.promise = promise.asValue(ctx.ptr());
return value.Locked.promise.?.asObjectRef();
}
var blob = this.body.use();
return blob.getJSON(ctx, null, null, &.{}, exception);
}
@@ -4625,6 +4709,15 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
_: js.ExceptionRef,
) js.JSValueRef {
var value = this.getBodyValue();
if (value.* == .Locked) {
value.Locked.action = .getArrayBuffer;
var promise = JSC.JSPromise.create(ctx.ptr());
value.Locked.promise = promise.asValue(ctx.ptr());
return value.Locked.promise.?.asObjectRef();
}
var blob = this.body.use();
return blob.getArrayBufferTransfer(ctx);
}
@@ -4637,6 +4730,14 @@ fn BlobInterface(comptime Type: type) type {
_: []const js.JSValueRef,
_: js.ExceptionRef,
) js.JSValueRef {
var value = this.getBodyValue();
if (value.* == .Locked) {
value.Locked.action = .getBlob;
var promise = JSC.JSPromise.create(ctx.ptr());
value.Locked.promise = promise.asValue(ctx.ptr());
return value.Locked.promise.?.asObjectRef();
}
var blob = this.body.use();
var ptr = getAllocator(ctx).create(Blob) catch unreachable;
ptr.* = blob;