mirror of
https://github.com/lexmount/moli.git
synced 2026-10-17 16:01:22 +00:00
fix(streams): avoid inherited setters in readable algorithm arrays
This commit is contained in:
@@ -618,15 +618,15 @@ fn initialize_readable_stream_object_fields<'s>(
|
||||
let source_value = source
|
||||
.map(|value| value.into())
|
||||
.unwrap_or_else(|| v8::null(scope).into());
|
||||
let algorithms = v8::Array::new(scope, 4);
|
||||
let _ = algorithms.set_index(scope, READABLE_STREAM_ALGORITHM_SOURCE_INDEX, source_value);
|
||||
let _ = algorithms.set_index(scope, READABLE_STREAM_ALGORITHM_PULL_INDEX, pull_algorithm);
|
||||
let _ = algorithms.set_index(
|
||||
let algorithms = v8::Array::new_with_elements(
|
||||
scope,
|
||||
READABLE_STREAM_ALGORITHM_CANCEL_INDEX,
|
||||
cancel_algorithm,
|
||||
&[
|
||||
source_value,
|
||||
pull_algorithm,
|
||||
cancel_algorithm,
|
||||
size_algorithm,
|
||||
],
|
||||
);
|
||||
let _ = algorithms.set_index(scope, READABLE_STREAM_ALGORITHM_SIZE_INDEX, size_algorithm);
|
||||
let controller = super::super::stream_objects::new_readable_stream_controller_object(
|
||||
scope,
|
||||
stream,
|
||||
|
||||
@@ -2163,6 +2163,7 @@ mod observer_receivers;
|
||||
mod offline_audio_float;
|
||||
mod post_parse;
|
||||
mod queue_microtask;
|
||||
mod readable_algorithm_arrays;
|
||||
mod remote_playback_interface;
|
||||
mod rendering_update;
|
||||
mod script_terminal_completion;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
(async () => {
|
||||
const checks = [];
|
||||
async function check(name, test) {
|
||||
try { checks.push({name, passed: (await test()) === true}); }
|
||||
catch (error) { checks.push({name, passed: false, error: String(error)}); }
|
||||
}
|
||||
let frame;
|
||||
const owners = [['main', globalThis]];
|
||||
if (typeof document !== 'undefined') {
|
||||
frame = document.createElement('iframe');document.body.append(frame);
|
||||
owners.push(['child', frame.contentWindow]);
|
||||
}
|
||||
try {
|
||||
for (const [label, owner] of owners) for (const kind of ['default', 'bytes', 'response']) {
|
||||
for (const prototype of ['Array', 'Object']) for (const index of [0, 1, 2, 3]) {
|
||||
for (const poison of ['setter', 'throwing-setter', 'getter-only', 'readonly-data']) {
|
||||
await check(`${label}/${kind}/${prototype}/${index}/${poison}`, async () => {
|
||||
const target = owner[prototype].prototype;
|
||||
const previous = Object.getOwnPropertyDescriptor(target, String(index));
|
||||
const reason = {numericPoison: poison};
|
||||
let calls = 0, starts = 0, pulls = 0, stream, canceled, cancelReason;
|
||||
const descriptor = poison === 'readonly-data' ? {configurable: true, value: 42, writable: false}
|
||||
: poison === 'getter-only' ? {configurable: true, get() {calls++; throw reason;}}
|
||||
: {configurable: true, set() {calls++; if (poison === 'throwing-setter') throw reason;}};
|
||||
Object.defineProperty(target, String(index), descriptor);
|
||||
try {
|
||||
if (kind === 'response') stream = new owner.Response(new owner.Uint8Array([65, 66]));
|
||||
else {
|
||||
const source = {
|
||||
start(controller) {starts++; controller.enqueue(kind === 'bytes' ? new owner.Uint8Array([65]) : 'first');},
|
||||
pull(controller) {pulls++; controller.enqueue(kind === 'bytes' ? new owner.Uint8Array([66]) : 'second');controller.close();}
|
||||
};
|
||||
if (kind === 'bytes') source.type = 'bytes';
|
||||
stream = new owner.ReadableStream(source);
|
||||
canceled = new owner.ReadableStream({cancel(value) {cancelReason = value;}});
|
||||
}
|
||||
} finally {
|
||||
if (previous) Object.defineProperty(target, String(index), previous);
|
||||
else delete target[String(index)];
|
||||
}
|
||||
if (calls !== 0) return false;
|
||||
if (kind === 'response') return Object.getPrototypeOf(stream) === owner.Response.prototype && await stream.text() === 'AB';
|
||||
if (Object.getPrototypeOf(stream) !== owner.ReadableStream.prototype) return false;
|
||||
const reader = stream.getReader();
|
||||
const first = await reader.read(), second = await reader.read(), done = await reader.read();
|
||||
if (starts !== 1 || pulls !== 1 || first.done || second.done || !done.done) return false;
|
||||
if (kind === 'bytes' ? first.value[0] !== 65 || second.value[0] !== 66 : first.value !== 'first' || second.value !== 'second') return false;
|
||||
const cancelValue = {cancelValue: label};await canceled.cancel(cancelValue);
|
||||
return cancelReason === cancelValue;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {if (frame) frame.remove();}
|
||||
globalThis.__readableAlgorithmResults = {checks, total: checks.length, passed: checks.filter(row => row.passed).length, complete: true};
|
||||
return true;
|
||||
})()
|
||||
@@ -0,0 +1,26 @@
|
||||
use super::*;
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn readable_stream_construction_ignores_inherited_numeric_properties() {
|
||||
let loader = static_http_loader(std::iter::empty::<String>());
|
||||
let mut vm = new_storage_page_task_executor_test_vm_with_loader(
|
||||
"https://readable-algorithm-arrays.test/",
|
||||
&loader,
|
||||
);
|
||||
vm.exec(include_str!("readable_algorithm_arrays.js"), None)
|
||||
.unwrap();
|
||||
advance_page_task_executor_until_eval_equals(
|
||||
&mut vm,
|
||||
&loader,
|
||||
"String(globalThis.__readableAlgorithmResults?.complete===true)",
|
||||
"true",
|
||||
"readable-algorithm-array-prototype-fixture",
|
||||
)
|
||||
.await;
|
||||
assert_eq!(vm.eval("__readableAlgorithmResults.total").unwrap(), "192");
|
||||
assert_eq!(
|
||||
vm.eval("JSON.stringify(__readableAlgorithmResults.checks.filter(row=>!row.passed))")
|
||||
.unwrap(),
|
||||
"[]"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user