Skip to content

Commit d00468a

Browse files
Jake ChampionJakeChampion
authored andcommitted
extract native-stream-sink and source into their own files
1 parent 0393071 commit d00468a

File tree

5 files changed

+384
-343
lines changed

5 files changed

+384
-343
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// TODO: remove these once the warnings are fixed
2+
#pragma clang diagnostic push
3+
#pragma clang diagnostic ignored "-Winvalid-offsetof"
4+
#include "js/experimental/TypedData.h" // used within "js/Stream.h"
5+
#pragma clang diagnostic pop
6+
7+
#include "js/Stream.h"
8+
9+
#include "builtin.h"
10+
#include "builtins/native-stream-sink.h"
11+
#include "js-compute-builtins.h"
12+
13+
// A JS class to use as the underlying sink for native writable streams, used
14+
// for TransformStream.
15+
namespace NativeStreamSink {
16+
namespace Slots {
17+
enum {
18+
Owner, // TransformStream.
19+
Controller, // The WritableStreamDefaultController.
20+
InternalWriter, // Only used to lock the stream if it's consumed internally.
21+
StartPromise, // Used as the return value of `start`, can be undefined.
22+
// Needed to properly implement TransformStream.
23+
WriteAlgorithm,
24+
AbortAlgorithm,
25+
CloseAlgorithm,
26+
// AbortAlgorithm, TODO: implement
27+
Count
28+
};
29+
};
30+
31+
JSObject *owner(JSObject *self) { return &JS::GetReservedSlot(self, Slots::Owner).toObject(); }
32+
33+
JS::Value startPromise(JSObject *self) { return JS::GetReservedSlot(self, Slots::StartPromise); }
34+
35+
WriteAlgorithm *writeAlgorithm(JSObject *self) {
36+
return (WriteAlgorithm *)JS::GetReservedSlot(self, Slots::WriteAlgorithm).toPrivate();
37+
}
38+
39+
AbortAlgorithm *abortAlgorithm(JSObject *self) {
40+
return (AbortAlgorithm *)JS::GetReservedSlot(self, Slots::AbortAlgorithm).toPrivate();
41+
}
42+
43+
CloseAlgorithm *closeAlgorithm(JSObject *self) {
44+
return (CloseAlgorithm *)JS::GetReservedSlot(self, Slots::CloseAlgorithm).toPrivate();
45+
}
46+
47+
JSObject *controller(JSObject *self) {
48+
return &JS::GetReservedSlot(self, Slots::Controller).toObject();
49+
}
50+
51+
/**
52+
* Returns the underlying sink for the given controller iff it's an object,
53+
* nullptr otherwise.
54+
*/
55+
static JSObject *get_controller_sink(JSContext *cx, JS::HandleObject controller) {
56+
JS::RootedValue sink(cx, JS::WritableStreamControllerGetUnderlyingSink(cx, controller));
57+
return sink.isObject() ? &sink.toObject() : nullptr;
58+
}
59+
60+
JSObject *get_stream_sink(JSContext *cx, JS::HandleObject stream) {
61+
JS::RootedObject controller(cx, JS::WritableStreamGetController(cx, stream));
62+
return get_controller_sink(cx, controller);
63+
}
64+
65+
bool stream_has_native_sink(JSContext *cx, JS::HandleObject stream) {
66+
MOZ_RELEASE_ASSERT(JS::IsWritableStream(stream));
67+
68+
JSObject *sink = get_stream_sink(cx, stream);
69+
return is_instance(sink);
70+
}
71+
72+
const unsigned ctor_length = 0;
73+
74+
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name);
75+
76+
bool start(JSContext *cx, unsigned argc, JS::Value *vp) {
77+
METHOD_HEADER(1)
78+
79+
MOZ_ASSERT(args[0].isObject());
80+
JS::RootedObject controller(cx, &args[0].toObject());
81+
MOZ_ASSERT(get_controller_sink(cx, controller) == self);
82+
83+
JS::SetReservedSlot(self, Slots::Controller, args[0]);
84+
85+
// For TransformStream, StartAlgorithm returns the same Promise for both the
86+
// readable and writable stream. All other native initializations of
87+
// WritableStream have StartAlgorithm return undefined.
88+
//
89+
// Instead of introducing both the StartAlgorithm as a pointer and
90+
// startPromise as a value, we just store the latter or undefined, and always
91+
// return it.
92+
args.rval().set(startPromise(self));
93+
return true;
94+
}
95+
96+
bool write(JSContext *cx, unsigned argc, JS::Value *vp) {
97+
METHOD_HEADER(1)
98+
99+
JS::RootedObject owner(cx, NativeStreamSink::owner(self));
100+
JS::HandleValue chunk(args[0]);
101+
102+
WriteAlgorithm *write = writeAlgorithm(self);
103+
return write(cx, args, self, owner, chunk);
104+
}
105+
106+
bool abort(JSContext *cx, unsigned argc, JS::Value *vp) {
107+
METHOD_HEADER(1)
108+
109+
JS::RootedObject owner(cx, NativeStreamSink::owner(self));
110+
JS::HandleValue reason(args[0]);
111+
112+
AbortAlgorithm *abort = abortAlgorithm(self);
113+
return abort(cx, args, self, owner, reason);
114+
}
115+
116+
bool close(JSContext *cx, unsigned argc, JS::Value *vp) {
117+
METHOD_HEADER(0)
118+
119+
JS::RootedObject owner(cx, NativeStreamSink::owner(self));
120+
121+
CloseAlgorithm *close = closeAlgorithm(self);
122+
return close(cx, args, self, owner);
123+
}
124+
125+
const JSFunctionSpec methods[] = {JS_FN("start", start, 1, 0), JS_FN("write", write, 2, 0),
126+
JS_FN("abort", abort, 2, 0), JS_FN("close", close, 1, 0),
127+
JS_FS_END};
128+
129+
const JSPropertySpec properties[] = {JS_PS_END};
130+
131+
CLASS_BOILERPLATE_NO_CTOR(NativeStreamSink)
132+
133+
JSObject *create(JSContext *cx, JS::HandleObject owner, JS::HandleValue startPromise,
134+
WriteAlgorithm *write, CloseAlgorithm *close, AbortAlgorithm *abort) {
135+
JS::RootedObject sink(cx, JS_NewObjectWithGivenProto(cx, &class_, proto_obj));
136+
if (!sink)
137+
return nullptr;
138+
139+
JS::SetReservedSlot(sink, Slots::Owner, JS::ObjectValue(*owner));
140+
JS::SetReservedSlot(sink, Slots::StartPromise, startPromise);
141+
JS::SetReservedSlot(sink, Slots::WriteAlgorithm, JS::PrivateValue((void *)write));
142+
JS::SetReservedSlot(sink, Slots::AbortAlgorithm, JS::PrivateValue((void *)abort));
143+
JS::SetReservedSlot(sink, Slots::CloseAlgorithm, JS::PrivateValue((void *)close));
144+
return sink;
145+
}
146+
} // namespace NativeStreamSink
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef JS_COMPUTE_RUNTIME_NATIVE_STREAM_SINK_H
2+
#define JS_COMPUTE_RUNTIME_NATIVE_STREAM_SINK_H
3+
4+
namespace NativeStreamSink {
5+
typedef bool WriteAlgorithm(JSContext *cx, JS::CallArgs args, JS::HandleObject stream,
6+
JS::HandleObject owner, JS::HandleValue chunk);
7+
typedef bool AbortAlgorithm(JSContext *cx, JS::CallArgs args, JS::HandleObject stream,
8+
JS::HandleObject owner, JS::HandleValue reason);
9+
typedef bool CloseAlgorithm(JSContext *cx, JS::CallArgs args, JS::HandleObject stream,
10+
JS::HandleObject owner);
11+
bool is_instance(JSObject *obj);
12+
// Register the class.
13+
bool init_class(JSContext *cx, JS::HandleObject global);
14+
JSObject *create(JSContext *cx, JS::HandleObject owner, JS::HandleValue startPromise,
15+
WriteAlgorithm *write, CloseAlgorithm *close, AbortAlgorithm *abort);
16+
JSObject *get_stream_sink(JSContext *cx, JS::HandleObject stream);
17+
JSObject *owner(JSObject *self);
18+
19+
} // namespace NativeStreamSink
20+
#endif
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// TODO: remove these once the warnings are fixed
2+
#pragma clang diagnostic push
3+
#pragma clang diagnostic ignored "-Winvalid-offsetof"
4+
#include "js/experimental/TypedData.h" // used within "js/Stream.h"
5+
#pragma clang diagnostic pop
6+
7+
#include "js/Stream.h"
8+
9+
#include "builtin.h"
10+
#include "builtins/native-stream-sink.h"
11+
#include "builtins/native-stream-source.h"
12+
#include "js-compute-builtins.h"
13+
14+
// A JS class to use as the underlying source for native readable streams, used
15+
// for Request/Response bodies and TransformStream.
16+
namespace NativeStreamSource {
17+
18+
JSObject *owner(JSObject *self) {
19+
MOZ_ASSERT(is_instance(self));
20+
return &JS::GetReservedSlot(self, Slots::Owner).toObject();
21+
}
22+
23+
JSObject *stream(JSObject *self) { return RequestOrResponse::body_stream(owner(self)); }
24+
25+
JS::Value startPromise(JSObject *self) {
26+
MOZ_ASSERT(is_instance(self));
27+
return JS::GetReservedSlot(self, Slots::StartPromise);
28+
}
29+
30+
PullAlgorithm *pullAlgorithm(JSObject *self) {
31+
MOZ_ASSERT(is_instance(self));
32+
return (PullAlgorithm *)JS::GetReservedSlot(self, Slots::PullAlgorithm).toPrivate();
33+
}
34+
35+
CancelAlgorithm *cancelAlgorithm(JSObject *self) {
36+
MOZ_ASSERT(is_instance(self));
37+
return (CancelAlgorithm *)JS::GetReservedSlot(self, Slots::CancelAlgorithm).toPrivate();
38+
}
39+
40+
JSObject *controller(JSObject *self) {
41+
MOZ_ASSERT(is_instance(self));
42+
return &JS::GetReservedSlot(self, Slots::Controller).toObject();
43+
}
44+
45+
/**
46+
* Returns the underlying source for the given controller iff it's an object,
47+
* nullptr otherwise.
48+
*/
49+
static JSObject *get_controller_source(JSContext *cx, JS::HandleObject controller) {
50+
JS::RootedValue source(cx);
51+
bool success __attribute__((unused));
52+
success = JS::ReadableStreamControllerGetUnderlyingSource(cx, controller, &source);
53+
MOZ_ASSERT(success);
54+
return source.isObject() ? &source.toObject() : nullptr;
55+
}
56+
57+
JSObject *get_stream_source(JSContext *cx, JS::HandleObject stream) {
58+
MOZ_ASSERT(JS::IsReadableStream(stream));
59+
JS::RootedObject controller(cx, JS::ReadableStreamGetController(cx, stream));
60+
return get_controller_source(cx, controller);
61+
}
62+
63+
bool stream_has_native_source(JSContext *cx, JS::HandleObject stream) {
64+
JSObject *source = get_stream_source(cx, stream);
65+
return is_instance(source);
66+
}
67+
68+
bool stream_is_body(JSContext *cx, JS::HandleObject stream) {
69+
JSObject *stream_source = get_stream_source(cx, stream);
70+
return NativeStreamSource::is_instance(stream_source) &&
71+
RequestOrResponse::is_instance(owner(stream_source));
72+
}
73+
74+
void set_stream_piped_to_ts_writable(JSContext *cx, JS::HandleObject stream,
75+
JS::HandleObject writable) {
76+
JS::RootedObject source(cx, NativeStreamSource::get_stream_source(cx, stream));
77+
MOZ_ASSERT(is_instance(source));
78+
JS::RootedObject sink(cx, NativeStreamSink::get_stream_sink(cx, writable));
79+
JS::RootedObject transform_stream(cx, NativeStreamSink::owner(sink));
80+
MOZ_ASSERT(transform_stream);
81+
JS::SetReservedSlot(source, Slots::PipedToTransformStream, JS::ObjectValue(*transform_stream));
82+
}
83+
84+
JSObject *piped_to_transform_stream(JSObject *self) {
85+
MOZ_ASSERT(is_instance(self));
86+
return JS::GetReservedSlot(self, Slots::PipedToTransformStream).toObjectOrNull();
87+
}
88+
89+
bool lock_stream(JSContext *cx, JS::HandleObject stream) {
90+
MOZ_ASSERT(JS::IsReadableStream(stream));
91+
92+
bool locked;
93+
JS::ReadableStreamIsLocked(cx, stream, &locked);
94+
if (locked) {
95+
JS_ReportErrorLatin1(cx, "Can't lock an already locked ReadableStream");
96+
return false;
97+
}
98+
99+
JS::RootedObject self(cx, get_stream_source(cx, stream));
100+
MOZ_ASSERT(is_instance(self));
101+
102+
auto mode = JS::ReadableStreamReaderMode::Default;
103+
JS::RootedObject reader(cx, JS::ReadableStreamGetReader(cx, stream, mode));
104+
if (!reader)
105+
return false;
106+
107+
JS::SetReservedSlot(self, Slots::InternalReader, JS::ObjectValue(*reader));
108+
return true;
109+
}
110+
111+
const unsigned ctor_length = 0;
112+
113+
bool check_receiver(JSContext *cx, JS::HandleValue receiver, const char *method_name);
114+
115+
bool start(JSContext *cx, unsigned argc, JS::Value *vp) {
116+
METHOD_HEADER(1)
117+
118+
MOZ_ASSERT(args[0].isObject());
119+
JS::RootedObject controller(cx, &args[0].toObject());
120+
MOZ_ASSERT(get_controller_source(cx, controller) == self);
121+
122+
JS::SetReservedSlot(self, NativeStreamSource::Slots::Controller, args[0]);
123+
124+
// For TransformStream, StartAlgorithm returns the same Promise for both the
125+
// readable and writable stream. All other native initializations of
126+
// ReadableStream have StartAlgorithm return undefined. Instead of introducing
127+
// both the StartAlgorithm as a pointer and startPromise as a value, we just
128+
// store the latter or undefined, and always return it.
129+
args.rval().set(startPromise(self));
130+
return true;
131+
}
132+
133+
bool pull(JSContext *cx, unsigned argc, JS::Value *vp) {
134+
METHOD_HEADER(1)
135+
136+
JS::RootedObject owner(cx, NativeStreamSource::owner(self));
137+
JS::RootedObject controller(cx, &args[0].toObject());
138+
MOZ_ASSERT(controller == NativeStreamSource::controller(self));
139+
MOZ_ASSERT(get_controller_source(cx, controller) == self.get());
140+
141+
PullAlgorithm *pull = pullAlgorithm(self);
142+
return pull(cx, args, self, owner, controller);
143+
}
144+
145+
bool cancel(JSContext *cx, unsigned argc, JS::Value *vp) {
146+
METHOD_HEADER(0)
147+
148+
JS::RootedObject owner(cx, NativeStreamSource::owner(self));
149+
JS::HandleValue reason(args.get(0));
150+
151+
CancelAlgorithm *cancel = cancelAlgorithm(self);
152+
return cancel(cx, args, self, owner, reason);
153+
}
154+
155+
const JSFunctionSpec methods[] = {JS_FN("start", start, 1, 0), JS_FN("pull", pull, 1, 0),
156+
JS_FN("cancel", cancel, 1, 0), JS_FS_END};
157+
158+
const JSPropertySpec properties[] = {JS_PS_END};
159+
160+
CLASS_BOILERPLATE_NO_CTOR(NativeStreamSource)
161+
162+
JSObject *create(JSContext *cx, JS::HandleObject owner, JS::HandleValue startPromise,
163+
PullAlgorithm *pull, CancelAlgorithm *cancel) {
164+
JS::RootedObject source(cx, JS_NewObjectWithGivenProto(cx, &class_, proto_obj));
165+
if (!source)
166+
return nullptr;
167+
168+
JS::SetReservedSlot(source, Slots::Owner, JS::ObjectValue(*owner));
169+
JS::SetReservedSlot(source, Slots::StartPromise, startPromise);
170+
JS::SetReservedSlot(source, Slots::PullAlgorithm, JS::PrivateValue((void *)pull));
171+
JS::SetReservedSlot(source, Slots::CancelAlgorithm, JS::PrivateValue((void *)cancel));
172+
JS::SetReservedSlot(source, Slots::PipedToTransformStream, JS::NullValue());
173+
return source;
174+
}
175+
} // namespace NativeStreamSource
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#ifndef JS_COMPUTE_RUNTIME_NATIVE_STREAM_SOURCE_H
2+
#define JS_COMPUTE_RUNTIME_NATIVE_STREAM_SOURCE_H
3+
4+
namespace NativeStreamSource {
5+
namespace Slots {
6+
enum {
7+
Owner, // Request or Response object, or TransformStream.
8+
Controller, // The ReadableStreamDefaultController.
9+
InternalReader, // Only used to lock the stream if it's consumed internally.
10+
StartPromise, // Used as the return value of `start`, can be undefined.
11+
// Needed to properly implement TransformStream.
12+
PullAlgorithm,
13+
CancelAlgorithm,
14+
PipedToTransformStream, // The TransformStream this source's stream is piped
15+
// to, if any. Only applies if the source backs a
16+
// RequestOrResponse's body.
17+
Count
18+
};
19+
};
20+
bool is_instance(JSObject *obj);
21+
// Register the class.
22+
bool init_class(JSContext *cx, JS::HandleObject global);
23+
typedef bool PullAlgorithm(JSContext *cx, JS::CallArgs args, JS::HandleObject stream,
24+
JS::HandleObject owner, JS::HandleObject controller);
25+
typedef bool CancelAlgorithm(JSContext *cx, JS::CallArgs args, JS::HandleObject stream,
26+
JS::HandleObject owner, JS::HandleValue reason);
27+
28+
JSObject *create(JSContext *cx, JS::HandleObject owner, JS::HandleValue startPromise,
29+
PullAlgorithm *pull, CancelAlgorithm *cancel);
30+
JSObject *get_stream_source(JSContext *cx, JS::HandleObject stream);
31+
JSObject *owner(JSObject *self);
32+
JSObject *stream(JSObject *self);
33+
bool stream_has_native_source(JSContext *cx, JS::HandleObject stream);
34+
bool stream_is_body(JSContext *cx, JS::HandleObject stream);
35+
bool lock_stream(JSContext *cx, JS::HandleObject stream);
36+
JSObject *piped_to_transform_stream(JSObject *source);
37+
JSObject *controller(JSObject *self);
38+
void set_stream_piped_to_ts_writable(JSContext *cx, JS::HandleObject stream,
39+
JS::HandleObject writable);
40+
} // namespace NativeStreamSource
41+
#endif

0 commit comments

Comments
 (0)