11'use strict';
22
33const {
4+ ArrayFrom,
5+ ArrayIsArray,
46 ArrayPrototypePush,
5- ArrayPrototypeShift,
7+ MapPrototypeClear,
8+ MapPrototypeEntries,
9+ MapPrototypeSet,
10+ ObjectDefineProperty,
11+ ObjectGetOwnPropertyDescriptor,
12+ ObjectGetOwnPropertyNames,
13+ ObjectGetPrototypeOf,
14+ ObjectKeys,
15+ ObjectPrototype,
16+ ObjectPrototypeHasOwnProperty,
17+ ObjectSetPrototypeOf,
18+ PromiseReject,
19+ PromiseResolve,
20+ PromiseWithResolvers,
21+ SafeMap,
22+ SetPrototypeAdd,
23+ SetPrototypeClear,
24+ SetPrototypeValues,
625 Symbol,
726} = primordials;
827const Readable = require('internal/streams/readable');
28+ const { deserializeError, serializeError } = require('internal/error_serdes');
29+ const {
30+ codes: {
31+ ERR_INVALID_STATE,
32+ },
33+ } = require('internal/errors');
34+ const { isError } = require('internal/util');
35+ const { isMap, isSet } = require('internal/util/types');
36+ const { structuredClone } = require('internal/worker/js_transferable');
937
1038const kEmitMessage = Symbol('kEmitMessage');
11- const kBenchmarksStreamDrain = Symbol('kBenchmarksStreamDrain');
39+
40+ function repairError(source, clone, seen) {
41+ const serialized = deserializeError(serializeError(source));
42+ let sourceName;
43+ try {
44+ sourceName = source.name;
45+ } catch {
46+ // The serialized form already omits properties whose getters throw.
47+ }
48+ let repaired = clone;
49+ if (!isError(repaired) ||
50+ (sourceName !== undefined && repaired.name !== sourceName)) {
51+ repaired = serialized;
52+ }
53+ if (repaired === null || typeof repaired !== 'object') return repaired;
54+ seen.set(source, repaired);
55+
56+ if (serialized !== null && typeof serialized === 'object') {
57+ const serializedKeys = ObjectGetOwnPropertyNames(serialized);
58+ for (let i = 0; i < serializedKeys.length; i++) {
59+ const key = serializedKeys[i];
60+ if (ObjectPrototypeHasOwnProperty(repaired, key)) continue;
61+ const descriptor = ObjectGetOwnPropertyDescriptor(serialized, key);
62+ ObjectSetPrototypeOf(descriptor, null);
63+ ObjectDefineProperty(repaired, key, descriptor);
64+ }
65+ }
66+
67+ const keys = ObjectGetOwnPropertyNames(source);
68+ for (let i = 0; i < keys.length; i++) {
69+ const key = keys[i];
70+ const descriptor = ObjectGetOwnPropertyDescriptor(source, key);
71+ if (!ObjectPrototypeHasOwnProperty(descriptor, 'value') ||
72+ typeof descriptor.value === 'function' ||
73+ typeof descriptor.value === 'symbol') {
74+ continue;
75+ }
76+ const existing = ObjectGetOwnPropertyDescriptor(repaired, key);
77+ const value = descriptor.value !== null &&
78+ typeof descriptor.value === 'object' ?
79+ repairClone(descriptor.value, existing?.value, seen) : descriptor.value;
80+ if ((existing !== undefined && existing.value === value) ||
81+ existing?.configurable === false) {
82+ continue;
83+ }
84+ ObjectDefineProperty(repaired, key, {
85+ __proto__: null,
86+ configurable: descriptor.configurable,
87+ enumerable: descriptor.enumerable,
88+ value,
89+ writable: descriptor.writable,
90+ });
91+ }
92+ return repaired;
93+ }
94+
95+ function repairClone(source, clone, seen) {
96+ if (source === null || (typeof source !== 'object' &&
97+ typeof source !== 'function')) {
98+ return clone === undefined ? source : clone;
99+ }
100+ if (typeof source === 'function') return clone;
101+ if (seen.has(source)) return seen.get(source);
102+ if (isError(source)) return repairError(source, clone, seen);
103+ if (clone === null || typeof clone !== 'object') {
104+ try {
105+ clone = structuredClone(source);
106+ } catch {
107+ const prototype = ObjectGetPrototypeOf(source);
108+ if (!ArrayIsArray(source) && prototype !== null &&
109+ prototype !== ObjectPrototype) {
110+ return clone;
111+ }
112+ clone = ArrayIsArray(source) ? [] : { __proto__: prototype };
113+ }
114+ }
115+ seen.set(source, clone);
116+ if (isMap(source) && isMap(clone)) {
117+ const sourceEntries = ArrayFrom(MapPrototypeEntries(source));
118+ const cloneEntries = ArrayFrom(MapPrototypeEntries(clone));
119+ const repairedEntries = [];
120+ for (let i = 0; i < sourceEntries.length; i++) {
121+ ArrayPrototypePush(repairedEntries, [
122+ repairClone(sourceEntries[i][0], cloneEntries[i][0], seen),
123+ repairClone(sourceEntries[i][1], cloneEntries[i][1], seen),
124+ ]);
125+ }
126+ MapPrototypeClear(clone);
127+ for (let i = 0; i < repairedEntries.length; i++) {
128+ MapPrototypeSet(clone, repairedEntries[i][0], repairedEntries[i][1]);
129+ }
130+ return clone;
131+ }
132+ if (isSet(source) && isSet(clone)) {
133+ const sourceValues = ArrayFrom(SetPrototypeValues(source));
134+ const cloneValues = ArrayFrom(SetPrototypeValues(clone));
135+ const repairedValues = [];
136+ for (let i = 0; i < sourceValues.length; i++) {
137+ ArrayPrototypePush(
138+ repairedValues, repairClone(sourceValues[i], cloneValues[i], seen));
139+ }
140+ SetPrototypeClear(clone);
141+ for (let i = 0; i < repairedValues.length; i++) {
142+ SetPrototypeAdd(clone, repairedValues[i]);
143+ }
144+ return clone;
145+ }
146+ const prototype = ObjectGetPrototypeOf(source);
147+ if (prototype === null) ObjectSetPrototypeOf(clone, null);
148+ if (prototype !== null && prototype !== ObjectPrototype &&
149+ !ArrayIsArray(source)) {
150+ return clone;
151+ }
152+ const keys = ObjectKeys(source);
153+ for (let i = 0; i < keys.length; i++) {
154+ const key = keys[i];
155+ const descriptor = ObjectGetOwnPropertyDescriptor(source, key);
156+ if (!ObjectPrototypeHasOwnProperty(descriptor, 'value')) continue;
157+ const existing = ObjectGetOwnPropertyDescriptor(clone, key);
158+ const value = repairClone(descriptor.value, existing?.value, seen);
159+ if ((existing !== undefined && existing.value === value) ||
160+ existing?.configurable === false) {
161+ continue;
162+ }
163+ ObjectDefineProperty(clone, key, {
164+ __proto__: null,
165+ configurable: descriptor.configurable,
166+ enumerable: descriptor.enumerable,
167+ value,
168+ writable: descriptor.writable,
169+ });
170+ }
171+ if (ArrayIsArray(source)) {
172+ const descriptor = ObjectGetOwnPropertyDescriptor(source, 'length');
173+ ObjectSetPrototypeOf(descriptor, null);
174+ ObjectDefineProperty(clone, 'length', descriptor);
175+ }
176+ return clone;
177+ }
178+
179+ function cloneRecordData(data) {
180+ let clone;
181+ try {
182+ clone = structuredClone(data);
183+ } catch (error) {
184+ if (data.error === undefined) throw error;
185+ clone = structuredClone({ __proto__: null, ...data, error: undefined });
186+ clone.error = deserializeError(serializeError(data.error));
187+ }
188+ try {
189+ return repairClone(data, clone, new SafeMap());
190+ } catch {
191+ return clone;
192+ }
193+ }
12194
13195class BenchmarksStream extends Readable {
14- #buffer = [];
15- #canPush = true;
196+ #blocked = false;
197+ #drainWaiters = [];
198+ #hasReader = false;
16199
17200 constructor() {
18201 super({
@@ -22,13 +205,37 @@ class BenchmarksStream extends Readable {
22205 }
23206
24207 _read() {
25- const wasBlocked = !this.#canPush;
26- this.#canPush = true;
27- while (this.#buffer.length > 0) {
28- const record = ArrayPrototypeShift(this.#buffer);
29- if (!this.#tryPush(record)) return;
208+ if (this.#blocked) {
209+ this.#blocked = false;
210+ const waiters = this.#drainWaiters;
211+ this.#drainWaiters = [];
212+ for (let i = 0; i < waiters.length; i++) waiters[i].resolve();
213+ }
214+ }
215+
216+ read(size) {
217+ if (size !== 0) this.#hasReader = true;
218+ return super.read(size);
219+ }
220+
221+ _destroy(error, callback) {
222+ const failure = error ??
223+ new ERR_INVALID_STATE('benchmark stream is closed');
224+ const waiters = this.#drainWaiters;
225+ this.#drainWaiters = [];
226+ for (let i = 0; i < waiters.length; i++) waiters[i].reject(failure);
227+ callback(error);
228+ }
229+
230+ waitForDrain() {
231+ if (this.destroyed) {
232+ return PromiseReject(this.errored ??
233+ new ERR_INVALID_STATE('benchmark stream is closed'));
30234 }
31- if (wasBlocked) this.emit(kBenchmarksStreamDrain);
235+ if (!this.#blocked) return PromiseResolve();
236+ const waiter = PromiseWithResolvers();
237+ ArrayPrototypePush(this.#drainWaiters, waiter);
238+ return waiter.promise;
32239 }
33240
34241 start(data) {
@@ -56,21 +263,25 @@ class BenchmarksStream extends Readable {
56263 }
57264
58265 [kEmitMessage](type, data) {
59- this.emit(type, data);
60- return this.#tryPush({ type, data });
266+ const recordData = cloneRecordData(data);
267+ const record = { __proto__: null, type, data: recordData };
268+ if (this.listenerCount(type) > 0) {
269+ this.emit(type, cloneRecordData(recordData));
270+ }
271+ return this.#tryPush(record);
61272 }
62273
63274 #tryPush(record) {
64- if (this.#canPush) {
65- this.#canPush = this.push(record);
66- } else {
67- ArrayPrototypePush(this.#buffer, record);
275+ if (this.destroyed) return false;
276+ const canPush = this.push(record);
277+ if (record !== null && !canPush && this.#hasReader) {
278+ this.#blocked = true;
279+ return false;
68280 }
69- return this.#canPush ;
281+ return true ;
70282 }
71283}
72284
73285module.exports = {
74286 BenchmarksStream,
75- kBenchmarksStreamDrain,
76287};
0 commit comments