Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

elaborate on error "Expected Sink" #15234

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bake/bun-framework-react/ssr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function renderToHtml(
});
// The root is this "Root" component that unwraps the streamed promise
// with `use`, and then returning the parsed React component for the UI.
const Root = () => React.use(promise);
const Root: any = () => React.use(promise);

// `renderToPipeableStream` is what actually generates HTML.
// Here is where React is told what script tags to inject.
Expand Down
3 changes: 2 additions & 1 deletion src/bun.js/api/bun/subprocess.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,8 @@ pub const Subprocess = struct {
if (JSC.Codegen.JSSubprocess.stdinGetCached(this_jsvalue)) |existing_value| {
if (existing_stdin_value.isCell()) {
if (stdin == null) {
stdin = @as(?*JSC.WebCore.FileSink, @alignCast(@ptrCast(JSC.WebCore.FileSink.JSSink.fromJS(globalThis, existing_value))));
// TODO: review this cast
stdin = @alignCast(@ptrCast(JSC.WebCore.FileSink.JSSink.fromJS(existing_value)));
}

existing_stdin_value = existing_value;
Expand Down
12 changes: 8 additions & 4 deletions src/bun.js/bindings/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3739,7 +3739,6 @@ pub const JSMap = opaque {
};
};

// TODO: this should not need to be `pub`
pub const JSValueReprInt = i64;

/// ABI-compatible with EncodedJSValue
Expand All @@ -3750,19 +3749,24 @@ pub const JSValue = enum(i64) {
true = FFI.TrueI64,
false = 0x6,

// TODO: Remove
/// Typically means an exception was thrown.
zero = 0,

/// JSValue::ValueDeleted
// TODO: Remove
/// This corresponds to `JSValue::ValueDeleted` in C++ It is never OK to use
/// this value except in the return value of `JSC__JSValue__getIfPropertyExistsImpl`
/// and `JSC__JSValue__fastGet`
///
/// Deleted is a special encoding used in JSC hash map internals used for
/// the null state. It is re-used here for encoding the "not present" state.
/// the null state. It is re-used here for encoding the "not present" state
/// in `JSC__JSValue__getIfPropertyExistsImpl`
property_does_not_exist_on_object = 0x4,
_,

/// When JavaScriptCore throws something, it returns a null cell (0). The
/// exception is set on the global object. ABI-compatible with EncodedJSValue.
pub const MaybeException = enum(i64) {
pub const MaybeException = enum(JSValueReprInt) {
zero = 0,
_,

Expand Down
7 changes: 1 addition & 6 deletions src/bun.js/bindings/headers.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions src/bun.js/bindings/headers.zig

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 44 additions & 34 deletions src/bun.js/webcore/streams.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1607,14 +1607,14 @@ pub const SinkDestructor = struct {
}
};

pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
pub fn NewJSSink(comptime SinkType: type, comptime abi_name: []const u8) type {
return struct {
sink: SinkType,

const ThisSink = @This();

pub const shim = JSC.Shimmer("", name_, @This());
pub const name = std.fmt.comptimePrint("{s}", .{name_});
pub const shim = JSC.Shimmer("", abi_name, @This());
pub const name = abi_name;

// This attaches it to JS
pub const SinkSignal = extern struct {
Expand Down Expand Up @@ -1661,12 +1661,6 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
return shim.cppFn("createObject", .{ globalThis, object, destructor });
}

pub fn fromJS(globalThis: *JSGlobalObject, value: JSValue) ?*anyopaque {
JSC.markBinding(@src());

return shim.cppFn("fromJS", .{ globalThis, value });
}

pub fn setDestroyCallback(value: JSValue, callback: usize) void {
JSC.markBinding(@src());

Expand Down Expand Up @@ -1720,32 +1714,48 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
shim.cppFn("detachPtr", .{ptr});
}

inline fn getThis(globalThis: *JSGlobalObject, callframe: *const JSC.CallFrame) ?*ThisSink {
return @as(
*ThisSink,
@ptrCast(@alignCast(
fromJS(
globalThis,
callframe.this(),
) orelse return null,
)),
);
// The code generator encodes two distinct failure types using 0 and 1
const FromJSResult = enum(usize) {
/// The sink has been closed and the wrapped type is freed.
detached = 0,
/// JS exception has not yet been thrown
cast_failed = 1,
/// *ThisSink
_,
};
const fromJSExtern = @extern(
*const fn (value: JSValue) callconv(.C) FromJSResult,
.{ .name = abi_name ++ "__fromJS" },
);

pub fn fromJS(value: JSValue) ?*ThisSink {
switch (fromJSExtern(value)) {
.detached, .cast_failed => return null,
else => |ptr| return @ptrFromInt(@intFromEnum(ptr)),
}
}

fn invalidThis(globalThis: *JSGlobalObject) JSValue {
const err = JSC.toTypeError(.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis);
globalThis.vm().throwError(globalThis, err);
return .zero;
fn getThis(global: *JSGlobalObject, callframe: *const JSC.CallFrame) !*ThisSink {
switch (fromJSExtern(callframe.this())) {
.detached => {
global.throw("This " ++ abi_name ++ " has already been closed. A \"direct\" ReadableStream terminates its underlying socket once `async pull()` returns.", .{});
return error.JSError;
},
.cast_failed => {
const err = JSC.toTypeError(.ERR_INVALID_THIS, "Expected " ++ abi_name, .{}, global);
return global.vm().throwError2(global, err);
},
else => |ptr| return @ptrFromInt(@intFromEnum(ptr)),
}
}

pub fn unprotect(this: *@This()) void {
_ = this; // autofix

_ = this;
}

pub fn write(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
JSC.markBinding(@src());
var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis);
const this = try getThis(globalThis, callframe);

if (comptime @hasDecl(SinkType, "getPendingError")) {
if (this.sink.getPendingError()) |err| {
Expand Down Expand Up @@ -1838,7 +1848,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {

pub fn close(globalThis: *JSGlobalObject, sink_ptr: ?*anyopaque) callconv(.C) JSValue {
JSC.markBinding(@src());
var this = @as(*ThisSink, @ptrCast(@alignCast(sink_ptr orelse return invalidThis(globalThis))));
const this: *ThisSink = @ptrCast(@alignCast(sink_ptr orelse return .undefined));

if (comptime @hasDecl(SinkType, "getPendingError")) {
if (this.sink.getPendingError()) |err| {
Expand All @@ -1853,7 +1863,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
pub fn flush(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
JSC.markBinding(@src());

var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis);
const this = try getThis(globalThis, callframe);

if (comptime @hasDecl(SinkType, "getPendingError")) {
if (this.sink.getPendingError()) |err| {
Expand Down Expand Up @@ -1888,7 +1898,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
pub fn start(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
JSC.markBinding(@src());

var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis);
const this = try getThis(globalThis, callframe);

if (comptime @hasDecl(SinkType, "getPendingError")) {
if (this.sink.getPendingError()) |err| {
Expand All @@ -1897,10 +1907,10 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
}
}

if (comptime @hasField(StreamStart, name_)) {
if (comptime @hasField(StreamStart, abi_name)) {
return this.sink.start(
if (callframe.argumentsCount() > 0)
try StreamStart.fromJSWithTag(globalThis, callframe.argument(0), comptime @field(StreamStart, name_))
try StreamStart.fromJSWithTag(globalThis, callframe.argument(0), comptime @field(StreamStart, abi_name))
else
StreamStart{ .empty = {} },
).toJS(globalThis);
Expand All @@ -1917,7 +1927,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
pub fn end(globalThis: *JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
JSC.markBinding(@src());

var this = getThis(globalThis, callframe) orelse return invalidThis(globalThis);
const this = try getThis(globalThis, callframe);

if (comptime @hasDecl(SinkType, "getPendingError")) {
if (this.sink.getPendingError()) |err| {
Expand Down Expand Up @@ -3077,10 +3087,10 @@ pub const FileSink = struct {
pub const IOWriter = bun.io.StreamingWriter(@This(), onWrite, onError, onReady, onClose);
pub const Poll = IOWriter;

fn Bun__ForceFileSinkToBeSynchronousOnWindows(globalObject: *JSC.JSGlobalObject, jsvalue: JSC.JSValue) callconv(.C) void {
fn Bun__ForceFileSinkToBeSynchronousOnWindows(jsvalue: JSC.JSValue) callconv(.C) void {
comptime bun.assert(Environment.isWindows);

var this: *FileSink = @alignCast(@ptrCast(JSSink.fromJS(globalObject, jsvalue) orelse return));
var this: *FileSink = @alignCast(@ptrCast(JSSink.fromJS(jsvalue) orelse return));
this.force_sync_on_windows = true;
}

Expand Down
Loading