mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Call web stream methods past own overrides, and await a transformer's thenable start
A method value of a web stream class, such as a saved WritableStream.prototype.close, called on an instance whose own property of the same name wraps it, ran the own property again, so the wrapper recursed. The classes' method values now call the class's method directly (ScrDynHandleClass.method_invoke); member calls still look at own properties first. TransformStream resolved its start promise with a transformer start's result without adopting a thenable, so both sides started at once and a rejection was lost. The start promise now resolves as a promise's resolve function does (scr_dyn_promise_resolve_with): a thenable adopts through its resolve job, a throwing `then` read rejects, and the stream starts or errors when it settles, in Node's order. Lowerer.namesWebStreamClass, a cache made when first needed, is listed in Lowerer.unjournaled.
This commit is contained in:
@@ -4259,6 +4259,7 @@ export class Lowerer {
|
||||
"createRequireBindingTargets",
|
||||
"createRequireElementTargets",
|
||||
"computedGlobalReads",
|
||||
"namesWebStreamClass",
|
||||
"timeValueDateDecisions",
|
||||
"timeValueDateWrites",
|
||||
"dynamicOverloadAnswers",
|
||||
|
||||
@@ -1551,8 +1551,11 @@ static ScrDyn *scr_dyn_then_of(const ScrDyn *value, const ScrDyn *reader) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Resolves `target` with `value` (borrowed). */
|
||||
static void scr_dyn_resolve_value(ScrPromise *target, ScrDyn *value, const ScrDyn *reader) {
|
||||
/* Resolves `target` with `value` (borrowed). Inlined into both callers, so
|
||||
* the code of scr_dyn_then_resolve_call does not depend on whether a
|
||||
* program links scr_dyn_promise_resolve_with. */
|
||||
__attribute__((always_inline)) static inline void scr_dyn_resolve_value(ScrPromise *target, ScrDyn *value,
|
||||
const ScrDyn *reader) {
|
||||
if (value->kind == SCR_DYN_PROMISE) {
|
||||
scr_promise_resolve_dyn(target, scr_dyn_retain(value));
|
||||
return;
|
||||
@@ -1669,6 +1672,10 @@ ScrPromise *scr_dyn_promise_resolve(const ScrDyn *value, const ScrDyn *reader) {
|
||||
return scr_dyn_promise_entry(NULL, value, reader);
|
||||
}
|
||||
|
||||
void scr_dyn_promise_resolve_with(ScrPromise *target, ScrDyn *value) {
|
||||
scr_dyn_resolve_value(target, value, NULL);
|
||||
}
|
||||
|
||||
static void scr_dyn_settled_store(ScrArr *a, double i, ScrPromise *src) {
|
||||
ScrDyn *record = scr_dyn_new_obj();
|
||||
bool fulfilled = !scr_promise_is_rejected(src);
|
||||
|
||||
@@ -2061,6 +2061,12 @@ typedef struct ScrDynHandleClass {
|
||||
/* The prototype's methods and getters in definition order (the order
|
||||
* Object.keys reports); NULL = the methods, then the getters. */
|
||||
const char *const *order;
|
||||
/* A call of one of the class's method values
|
||||
* (`WritableStream.prototype.close.call(stream)`): the class's method,
|
||||
* which an own property of the same name on the handle does not
|
||||
* replace. NULL = ScrDynHandleOps.invoke. */
|
||||
ScrDyn *(*method_invoke)(void *h, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
const char *what);
|
||||
} ScrDynHandleClass;
|
||||
|
||||
/* Installs a tag's ops with the class its handles belong to; both outlive
|
||||
@@ -2087,6 +2093,11 @@ ScrDyn *scr_dyn_handle_class_prototype_of(const ScrDyn *handle);
|
||||
* receiver (+1); NULL when the chain has none or with the exception
|
||||
* pending. */
|
||||
ScrDyn *scr_dyn_handle_class_member(ScrDynHandleTag tag, void *h, const char *key, size_t len);
|
||||
/* A method value's call on a handle of its tag (`self`, borrowed): the
|
||||
* class's method_invoke, or the ops' invoke for a tag without one. +1, or
|
||||
* NULL with the exception pending. */
|
||||
ScrDyn *scr_dyn_handle_class_method_call(const ScrDynHandleOps *ops, ScrDyn *self, const char *method,
|
||||
ScrDyn *const *args, size_t argc);
|
||||
|
||||
/* The operations a Transform handle gives the stream unit (scr_stream.c):
|
||||
* the slot where the handle keeps its stream (+1) with the stream's
|
||||
@@ -2163,6 +2174,11 @@ ScrPromise *scr_dyn_promise_any(const ScrDyn *entries, const ScrDyn *reader);
|
||||
* itself, a thenable adopts through its resolve job, a throwing `then`
|
||||
* read rejects, and any other value fulfills. +1. */
|
||||
ScrPromise *scr_dyn_promise_resolve(const ScrDyn *value, const ScrDyn *reader);
|
||||
/* Resolves `target` with a dyn value (borrowed), as a promise's resolve
|
||||
* function does: a native promise adopts, a thenable adopts through its
|
||||
* resolve job, a throwing `then` read rejects, and any other value
|
||||
* fulfills. */
|
||||
void scr_dyn_promise_resolve_with(ScrPromise *target, ScrDyn *value);
|
||||
|
||||
/* Native predicates for typed reference capsules and boxed errors. */
|
||||
bool scr_dyn_ref_typeof_is(const ScrDyn *d, const char *name);
|
||||
|
||||
@@ -183,6 +183,14 @@ ScrDyn *scr_dyn_handle_class_member(ScrDynHandleTag tag, void *h, const char *ke
|
||||
return value;
|
||||
}
|
||||
|
||||
ScrDyn *scr_dyn_handle_class_method_call(const ScrDynHandleOps *ops, ScrDyn *self, const char *method,
|
||||
ScrDyn *const *args, size_t argc) {
|
||||
const ScrDynHandleClass *klass = hc_classes[self->v.handle.tag];
|
||||
if (klass && klass->method_invoke)
|
||||
return klass->method_invoke(self->v.handle.ptr, self, method, args, argc, method);
|
||||
return ops->invoke(self->v.handle.ptr, self, method, args, argc, method);
|
||||
}
|
||||
|
||||
static void hc_define_method(ScrDyn *prototype, ScrDynHandleTag tag, const char *name) {
|
||||
ScrDyn *fn = scr_dyn_handle_class_method(tag, name);
|
||||
if (fn) hc_define(prototype, name, fn, true, true, true);
|
||||
|
||||
@@ -7004,7 +7004,14 @@ static ScrDyn *scr_dynh_method_call(ScrClosure *closure, ScrDyn *const *args, si
|
||||
ScrDyn *self = scr_dyn_this_get();
|
||||
ScrDyn *result;
|
||||
if (self->kind == SCR_DYN_HANDLE && self->v.handle.tag == tag) {
|
||||
#ifdef SCR_HANDLE_CLASSES
|
||||
/* A class's method value reaches the class's method past the handle's
|
||||
* own properties; this build is the one programs with such classes
|
||||
* link. */
|
||||
result = scr_dyn_handle_class_method_call(ops, self, method->name, args, argc);
|
||||
#else
|
||||
result = ops->invoke(self->v.handle.ptr, self, method->name, args, argc, method->name);
|
||||
#endif
|
||||
} else {
|
||||
const char *message = method->invalid_this ? method->invalid_this
|
||||
: ops->invalid_this ? ops->invalid_this
|
||||
|
||||
@@ -3520,7 +3520,10 @@ static ScrDyn *ws_transform_construct(ScrDyn *const *args, size_t argc) {
|
||||
ws_release(t);
|
||||
goto done;
|
||||
}
|
||||
scr_promise_resolve_dyn(start_promise, value);
|
||||
/* startPromise.resolve(result): a thenable result adopts through its
|
||||
* resolve job, so both sides start when it settles. */
|
||||
scr_dyn_promise_resolve_with(start_promise, value);
|
||||
scr_dyn_release(value);
|
||||
} else {
|
||||
ws_promise_fulfill_undefined(start_promise);
|
||||
}
|
||||
@@ -4128,6 +4131,18 @@ static ScrDyn *ws_arg(ScrDyn *const *args, size_t argc, size_t i) {
|
||||
return i < argc ? args[i] : scr_dyn_undefined();
|
||||
}
|
||||
|
||||
/* A member call on a handle (ScrDynHandleOps.invoke): an own property of
|
||||
* the name first, then the class's method (<name>_method). The class's
|
||||
* method values call <name>_method directly (ScrDynHandleClass's
|
||||
* method_invoke), so a wrapper an own property installs reaches it. */
|
||||
#define WS_MEMBER_INVOKE(NAME) \
|
||||
static ScrDyn *NAME##_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc, \
|
||||
const char *what) { \
|
||||
ScrDyn *own_result; \
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result; \
|
||||
return NAME##_method(ptr, self, method, args, argc, what); \
|
||||
}
|
||||
|
||||
static ScrDyn *ws_promise_box(ScrPromise *p) {
|
||||
ScrDyn *box = scr_dyn_new_promise(p);
|
||||
scr_promise_release(p);
|
||||
@@ -4168,10 +4183,8 @@ static ScrDyn *ws_readable_get_reader(WsReadable *s, ScrDyn *options) {
|
||||
return box;
|
||||
}
|
||||
|
||||
static ScrDyn *ws_readable_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
static ScrDyn *ws_readable_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
(void)self;
|
||||
(void)what;
|
||||
WsReadable *s = ptr;
|
||||
@@ -4279,6 +4292,7 @@ static ScrDyn *ws_readable_invoke(void *ptr, ScrDyn *self, const char *method, S
|
||||
}
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_readable)
|
||||
|
||||
static ScrDyn *ws_readable_get(void *ptr, const char *key, size_t len) {
|
||||
WsReadable *s = ptr;
|
||||
@@ -4310,7 +4324,7 @@ static const char *const ws_readable_order[] = {"locked", "cancel", "getReader",
|
||||
"tee", "values", NULL};
|
||||
|
||||
static const ScrDynHandleClass ws_readable_class = {
|
||||
"ReadableStream", 0, ws_readable_construct, ws_readable_accessors, ws_readable_statics, ws_readable_order,
|
||||
"ReadableStream", 0, ws_readable_construct, ws_readable_accessors, ws_readable_statics, ws_readable_order, ws_readable_method,
|
||||
};
|
||||
|
||||
/* ReadableStreamDefaultReader */
|
||||
@@ -4354,10 +4368,8 @@ static ScrDyn *ws_reader_read(WsReader *r) {
|
||||
return ws_promise_box(result);
|
||||
}
|
||||
|
||||
static ScrDyn *ws_reader_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
static ScrDyn *ws_reader_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
(void)self;
|
||||
(void)what;
|
||||
WsReader *r = ptr;
|
||||
@@ -4373,6 +4385,7 @@ static ScrDyn *ws_reader_invoke(void *ptr, ScrDyn *self, const char *method, Scr
|
||||
}
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_reader)
|
||||
|
||||
static ScrDyn *ws_reader_get(void *ptr, const char *key, size_t len) {
|
||||
WsReader *r = ptr;
|
||||
@@ -4390,15 +4403,13 @@ static const ScrDynHandleMethod ws_reader_methods[] = {
|
||||
static const char *const ws_reader_order[] = {"read", "releaseLock", "closed", "cancel", NULL};
|
||||
|
||||
static const ScrDynHandleClass ws_reader_class = {
|
||||
"ReadableStreamDefaultReader", 1, ws_reader_construct, ws_reader_accessors, NULL, ws_reader_order,
|
||||
"ReadableStreamDefaultReader", 1, ws_reader_construct, ws_reader_accessors, NULL, ws_reader_order, ws_reader_method,
|
||||
};
|
||||
|
||||
/* ReadableStreamDefaultController */
|
||||
|
||||
static ScrDyn *ws_rcontroller_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args,
|
||||
static ScrDyn *ws_rcontroller_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
(void)self;
|
||||
(void)what;
|
||||
WsRController *c = ptr;
|
||||
@@ -4425,6 +4436,7 @@ static ScrDyn *ws_rcontroller_invoke(void *ptr, ScrDyn *self, const char *method
|
||||
}
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_rcontroller)
|
||||
|
||||
static ScrDyn *ws_rcontroller_get(void *ptr, const char *key, size_t len) {
|
||||
if (ws_name(key, len, "desiredSize")) return ws_rcontroller_desired_size(ptr);
|
||||
@@ -4441,14 +4453,12 @@ static const ScrDynHandleMethod ws_rcontroller_methods[] = {
|
||||
static const char *const ws_rcontroller_order[] = {"desiredSize", "close", "enqueue", "error", NULL};
|
||||
|
||||
static const ScrDynHandleClass ws_rcontroller_class = {
|
||||
"ReadableStreamDefaultController", 0, NULL, ws_rcontroller_accessors, NULL, ws_rcontroller_order,
|
||||
"ReadableStreamDefaultController", 0, NULL, ws_rcontroller_accessors, NULL, ws_rcontroller_order, ws_rcontroller_method,
|
||||
};
|
||||
|
||||
/* WritableStream */
|
||||
static ScrDyn *ws_writable_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
static ScrDyn *ws_writable_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
WsWritable *s = ptr;
|
||||
size_t len = strlen(method);
|
||||
if (ws_name(method, len, "abort")) {
|
||||
@@ -4470,6 +4480,7 @@ static ScrDyn *ws_writable_invoke(void *ptr, ScrDyn *self, const char *method, S
|
||||
}
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_writable)
|
||||
|
||||
static ScrDyn *ws_writable_get(void *ptr, const char *key, size_t len) {
|
||||
WsWritable *s = ptr;
|
||||
@@ -4487,7 +4498,7 @@ static const ScrDynHandleMethod ws_writable_methods[] = {
|
||||
static const char *const ws_writable_order[] = {"locked", "abort", "close", "getWriter", NULL};
|
||||
|
||||
static const ScrDynHandleClass ws_writable_class = {
|
||||
"WritableStream", 0, ws_writable_construct, ws_writable_accessors, NULL, ws_writable_order,
|
||||
"WritableStream", 0, ws_writable_construct, ws_writable_accessors, NULL, ws_writable_order, ws_writable_method,
|
||||
};
|
||||
|
||||
/* WritableStreamDefaultWriter */
|
||||
@@ -4507,10 +4518,8 @@ static ScrDyn *ws_writer_construct(ScrDyn *const *args, size_t argc) {
|
||||
|
||||
static const char ws_writer_unbound[] = "Writer is not bound to a WritableStream";
|
||||
|
||||
static ScrDyn *ws_writer_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
static ScrDyn *ws_writer_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
WsWriter *w = ptr;
|
||||
size_t len = strlen(method);
|
||||
if (ws_name(method, len, "abort")) {
|
||||
@@ -4533,6 +4542,7 @@ static ScrDyn *ws_writer_invoke(void *ptr, ScrDyn *self, const char *method, Scr
|
||||
}
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_writer)
|
||||
|
||||
static ScrDyn *ws_writer_get(void *ptr, const char *key, size_t len) {
|
||||
WsWriter *w = ptr;
|
||||
@@ -4560,14 +4570,12 @@ static const char *const ws_writer_order[] = {"closed", "desiredSize", "ready",
|
||||
"close", "releaseLock", "write", NULL};
|
||||
|
||||
static const ScrDynHandleClass ws_writer_class = {
|
||||
"WritableStreamDefaultWriter", 1, ws_writer_construct, ws_writer_accessors, NULL, ws_writer_order,
|
||||
"WritableStreamDefaultWriter", 1, ws_writer_construct, ws_writer_accessors, NULL, ws_writer_order, ws_writer_method,
|
||||
};
|
||||
|
||||
/* WritableStreamDefaultController */
|
||||
static ScrDyn *ws_wcontroller_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args,
|
||||
static ScrDyn *ws_wcontroller_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
WsWController *c = ptr;
|
||||
size_t len = strlen(method);
|
||||
if (ws_name(method, len, "error")) {
|
||||
@@ -4576,6 +4584,7 @@ static ScrDyn *ws_wcontroller_invoke(void *ptr, ScrDyn *self, const char *method
|
||||
}
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_wcontroller)
|
||||
|
||||
static ScrDyn *ws_wcontroller_get(void *ptr, const char *key, size_t len) {
|
||||
if (ws_name(key, len, "signal")) return ws_wcontroller_signal(ptr);
|
||||
@@ -4590,7 +4599,7 @@ static const ScrDynHandleMethod ws_wcontroller_methods[] = {{"error", 0, false,
|
||||
static const char *const ws_wcontroller_order[] = {"signal", "error", NULL};
|
||||
|
||||
static const ScrDynHandleClass ws_wcontroller_class = {
|
||||
"WritableStreamDefaultController", 0, NULL, ws_wcontroller_accessors, NULL, ws_wcontroller_order,
|
||||
"WritableStreamDefaultController", 0, NULL, ws_wcontroller_accessors, NULL, ws_wcontroller_order, ws_wcontroller_method,
|
||||
};
|
||||
|
||||
/* TransformStream */
|
||||
@@ -4603,24 +4612,21 @@ static ScrDyn *ws_transform_get(void *ptr, const char *key, size_t len) {
|
||||
return scr_dyn_handle_class_member(SCR_DYNH_WS_TRANSFORM, ptr, key, len);
|
||||
}
|
||||
|
||||
static ScrDyn *ws_transform_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
static ScrDyn *ws_transform_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
(void)ptr;
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_transform)
|
||||
|
||||
|
||||
static const ScrDynHandleClass ws_transform_class = {
|
||||
"TransformStream", 0, ws_transform_construct, ws_transform_accessors, NULL, NULL,
|
||||
"TransformStream", 0, ws_transform_construct, ws_transform_accessors, NULL, NULL, ws_transform_method,
|
||||
};
|
||||
|
||||
/* TransformStreamDefaultController */
|
||||
static ScrDyn *ws_tcontroller_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args,
|
||||
static ScrDyn *ws_tcontroller_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
WsTController *c = ptr;
|
||||
size_t len = strlen(method);
|
||||
if (ws_name(method, len, "enqueue")) {
|
||||
@@ -4637,6 +4643,7 @@ static ScrDyn *ws_tcontroller_invoke(void *ptr, ScrDyn *self, const char *method
|
||||
}
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_tcontroller)
|
||||
|
||||
static ScrDyn *ws_tcontroller_get(void *ptr, const char *key, size_t len) {
|
||||
WsTController *c = ptr;
|
||||
@@ -4654,7 +4661,7 @@ static const ScrDynHandleMethod ws_tcontroller_methods[] = {
|
||||
static const char *const ws_tcontroller_order[] = {"desiredSize", "enqueue", "error", "terminate", NULL};
|
||||
|
||||
static const ScrDynHandleClass ws_tcontroller_class = {
|
||||
"TransformStreamDefaultController", 0, NULL, ws_tcontroller_accessors, NULL, ws_tcontroller_order,
|
||||
"TransformStreamDefaultController", 0, NULL, ws_tcontroller_accessors, NULL, ws_tcontroller_order, ws_tcontroller_method,
|
||||
};
|
||||
|
||||
/* ── TextEncoderStream and TextDecoderStream (webstreams/encoding.js) */
|
||||
@@ -4930,19 +4937,18 @@ static ScrDyn *ws_text_get(void *ptr, const char *key, size_t len) {
|
||||
return scr_dyn_handle_class_member(tag, ptr, key, len);
|
||||
}
|
||||
|
||||
static ScrDyn *ws_text_invoke(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
static ScrDyn *ws_text_method(void *ptr, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
ScrDyn *own_result;
|
||||
if (ws_own_invoke(ptr, self, method, args, argc, what, &own_result)) return own_result;
|
||||
return ws_invoke_member(self, method, args, argc, what);
|
||||
}
|
||||
WS_MEMBER_INVOKE(ws_text)
|
||||
|
||||
static const ScrDynHandleClass ws_text_encoder_class = {
|
||||
"TextEncoderStream", 0, ws_text_encoder_construct, ws_text_encoder_accessors, NULL, NULL,
|
||||
"TextEncoderStream", 0, ws_text_encoder_construct, ws_text_encoder_accessors, NULL, NULL, ws_text_method,
|
||||
};
|
||||
|
||||
static const ScrDynHandleClass ws_text_decoder_class = {
|
||||
"TextDecoderStream", 0, ws_text_decoder_construct, ws_text_decoder_accessors, NULL, NULL,
|
||||
"TextDecoderStream", 0, ws_text_decoder_construct, ws_text_decoder_accessors, NULL, NULL, ws_text_method,
|
||||
};
|
||||
|
||||
/* ── queuing strategies ────────────────────────────────────────────── */
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Web TransformStream and pipes from untyped JavaScript: transformers with
|
||||
// start, transform, flush and cancel, backpressure between the sides,
|
||||
// controller errors and terminate, pipeTo and pipeThrough with their
|
||||
// options, abort signals, own properties on streams, and encoder and
|
||||
// options, abort signals, own properties on streams and method wrappers
|
||||
// over them, a transformer start that returns a thenable, and encoder and
|
||||
// decoder streams, in Node's promise order.
|
||||
import { TransformStream as WebTransformStream, TransformStreamDefaultController } from "node:stream/web";
|
||||
|
||||
@@ -127,6 +128,66 @@ out("prototype", Object.keys(TransformStream.prototype).join(","), Object.keys(T
|
||||
out("own-properties", /** @type {any} */ (sink).tag, typeof /** @type {any} */ (source).getReader, caught(() => { /** @type {any} */ (source).locked = true; }));
|
||||
}
|
||||
|
||||
{
|
||||
// An own property that wraps the saved prototype method reaches the
|
||||
// class's method.
|
||||
const calls = [];
|
||||
const wrap = (target, name, method) => {
|
||||
target[name] = function (...args) {
|
||||
calls.push(name);
|
||||
return method.apply(this, args);
|
||||
};
|
||||
};
|
||||
const writable = new WritableStream({ write(chunk) { calls.push(`sink:${chunk}`); } });
|
||||
wrap(writable, "getWriter", WritableStream.prototype.getWriter);
|
||||
const writer = writable.getWriter();
|
||||
wrap(writer, "write", WritableStreamDefaultWriter.prototype.write);
|
||||
wrap(writer, "close", WritableStreamDefaultWriter.prototype.close);
|
||||
await writer.write("a");
|
||||
await writer.close();
|
||||
const readable = ReadableStream.from(["b"]);
|
||||
wrap(readable, "getReader", ReadableStream.prototype.getReader);
|
||||
const reader = readable.getReader();
|
||||
wrap(reader, "read", ReadableStreamDefaultReader.prototype.read);
|
||||
const first = await reader.read();
|
||||
const t = new TransformStream({
|
||||
transform(chunk, controller) {
|
||||
wrap(controller, "enqueue", TransformStreamDefaultController.prototype.enqueue);
|
||||
controller.enqueue(`${chunk}!`);
|
||||
},
|
||||
});
|
||||
t.writable.getWriter().write("c");
|
||||
const transformed = await t.readable.getReader().read();
|
||||
out("wrappers", calls.join(","), first.value, transformed.value, writable.locked);
|
||||
}
|
||||
|
||||
{
|
||||
// A transformer start that returns a thenable: both sides start when it
|
||||
// settles, and its rejection errors the stream.
|
||||
const order = [];
|
||||
const later = { then(resolve) { order.push("then"); setTimeout(() => { order.push("resolve"); resolve(); }, 0); } };
|
||||
const t = new TransformStream({ start() { return later; }, transform(chunk, c) { order.push(`transform:${chunk}`); c.enqueue(chunk); } });
|
||||
Promise.resolve().then(() => order.push("tick1")).then(() => order.push("tick2")).then(() => order.push("tick3"));
|
||||
const written = t.writable.getWriter().write("x").then(() => order.push("written"));
|
||||
const read = await t.readable.getReader().read();
|
||||
await written;
|
||||
out("start-thenable", order.join(","), read.value);
|
||||
const ticks = [];
|
||||
const sync = new TransformStream({ start() { return { then(resolve) { ticks.push("then"); resolve(); } }; }, transform(chunk, c) { ticks.push("transform"); c.enqueue(chunk); } });
|
||||
Promise.resolve().then(() => ticks.push("tick1")).then(() => ticks.push("tick2")).then(() => ticks.push("tick3")).then(() => ticks.push("tick4"));
|
||||
sync.writable.getWriter().write("z").then(() => ticks.push("written"));
|
||||
sync.readable.getReader().read().then(() => ticks.push("read"));
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
out("start-thenable-ticks", ticks.join(","));
|
||||
const failing = new TransformStream({ start() { return { then(_, reject) { reject(new Error("start failed")); } }; } });
|
||||
out("start-thenable-reject", await settled(failing.writable.getWriter().write("y")), await settled(failing.readable.getReader().read()));
|
||||
const throwing = new TransformStream({ start() { return { get then() { throw new Error("then getter"); } }; } });
|
||||
out("start-then-getter", await settled(throwing.writable.getWriter().write("y")));
|
||||
const plain = new TransformStream({ start() { return { then: 1 }; } });
|
||||
const pending = plain.readable.getReader().read();
|
||||
out("start-object", await settled(plain.writable.getWriter().write("p")), (await pending).value);
|
||||
}
|
||||
|
||||
out(
|
||||
"invalid",
|
||||
await settled(ReadableStream.from([]).pipeTo(/** @type {any} */ ({}))),
|
||||
|
||||
Reference in New Issue
Block a user