MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

migrate node:stream stub to proper module

add fs stream module hooks
migrate tty.c to streams
fix wasm crash

+3082 -623
+106
examples/spec/diagnostics_channel.js
··· 1 + import { test, testThrows, testDeep, summary } from './helpers.js'; 2 + import diagnosticsChannel from 'node:diagnostics_channel'; 3 + 4 + console.log('diagnostics_channel\n'); 5 + 6 + function collectEvents(tracing) { 7 + const events = []; 8 + 9 + for (const name of ['start', 'end', 'asyncStart', 'asyncEnd', 'error']) { 10 + tracing[name].subscribe(message => { 11 + events.push({ name, message: { ...message } }); 12 + }); 13 + } 14 + 15 + return events; 16 + } 17 + 18 + test('exports channel', typeof diagnosticsChannel.channel, 'function'); 19 + test('exports tracingChannel', typeof diagnosticsChannel.tracingChannel, 'function'); 20 + 21 + const symbolName = Symbol('diagnostics'); 22 + test( 23 + 'channel caches string names', 24 + diagnosticsChannel.channel('spec:diagnostics:string') === diagnosticsChannel.channel('spec:diagnostics:string'), 25 + true 26 + ); 27 + test('channel accepts symbol names', diagnosticsChannel.channel(symbolName).name, symbolName); 28 + 29 + testThrows('channel rejects invalid names', () => diagnosticsChannel.channel(123)); 30 + testThrows('subscribe rejects non-functions', () => diagnosticsChannel.channel('spec:diagnostics:invalid-subscription').subscribe(123)); 31 + 32 + const storeChannel = diagnosticsChannel.channel('spec:diagnostics:store'); 33 + const store = { 34 + run(_value, fn) { 35 + return fn(); 36 + } 37 + }; 38 + test('hasSubscribers starts false', storeChannel.hasSubscribers, false); 39 + storeChannel.bindStore(store); 40 + test('bindStore contributes to hasSubscribers', storeChannel.hasSubscribers, true); 41 + storeChannel.unbindStore(store); 42 + test('unbindStore clears hasSubscribers', storeChannel.hasSubscribers, false); 43 + 44 + const snapshotChannel = diagnosticsChannel.channel('spec:diagnostics:snapshot'); 45 + const snapshotCalls = []; 46 + const lateSubscriber = () => snapshotCalls.push('late'); 47 + snapshotChannel.subscribe(() => { 48 + snapshotCalls.push('first'); 49 + snapshotChannel.subscribe(lateSubscriber); 50 + }); 51 + snapshotChannel.subscribe(() => snapshotCalls.push('second')); 52 + snapshotChannel.publish({}); 53 + snapshotChannel.publish({}); 54 + testDeep('publish snapshots subscribers', snapshotCalls, ['first', 'second', 'first', 'second', 'late']); 55 + 56 + const syncTracing = diagnosticsChannel.tracingChannel('spec:diagnostics:sync'); 57 + const syncEvents = collectEvents(syncTracing); 58 + test( 59 + 'traceSync returns result', 60 + syncTracing.traceSync(() => 42, { phase: 'sync' }), 61 + 42 62 + ); 63 + testDeep('traceSync publishes start/end', syncEvents, [ 64 + { name: 'start', message: { phase: 'sync' } }, 65 + { name: 'end', message: { phase: 'sync', result: 42 } } 66 + ]); 67 + 68 + const promiseTracing = diagnosticsChannel.tracingChannel('spec:diagnostics:promise'); 69 + const promiseEvents = collectEvents(promiseTracing); 70 + test('tracePromise resolves result', await promiseTracing.tracePromise(() => Promise.resolve(7), { phase: 'promise' }), 7); 71 + testDeep('tracePromise publishes end before async events', promiseEvents, [ 72 + { name: 'start', message: { phase: 'promise' } }, 73 + { name: 'end', message: { phase: 'promise' } }, 74 + { name: 'asyncStart', message: { phase: 'promise', result: 7 } }, 75 + { name: 'asyncEnd', message: { phase: 'promise', result: 7 } } 76 + ]); 77 + 78 + const callbackTracing = diagnosticsChannel.tracingChannel('spec:diagnostics:callback'); 79 + const callbackEvents = collectEvents(callbackTracing); 80 + let callbackError = ''; 81 + const callbackReturn = callbackTracing.traceCallback( 82 + callback => { 83 + try { 84 + callback(null, 9); 85 + } catch (err) { 86 + callbackError = err.message; 87 + } 88 + return 'outer-result'; 89 + }, 90 + -1, 91 + { phase: 'callback' }, 92 + undefined, 93 + () => { 94 + throw new Error('callback boom'); 95 + } 96 + ); 97 + test('traceCallback returns outer result', callbackReturn, 'outer-result'); 98 + test('traceCallback uses the last argument by default', callbackError, 'callback boom'); 99 + testDeep('traceCallback still publishes asyncEnd when callback throws', callbackEvents, [ 100 + { name: 'start', message: { phase: 'callback' } }, 101 + { name: 'asyncStart', message: { phase: 'callback', result: 9 } }, 102 + { name: 'asyncEnd', message: { phase: 'callback', result: 9 } }, 103 + { name: 'end', message: { phase: 'callback', result: 9 } } 104 + ]); 105 + 106 + summary();
+2 -1
include/common.h
··· 117 117 BRAND_WASM_TABLE, 118 118 BRAND_WASM_TAG, 119 119 BRAND_WASM_EXCEPTION, 120 - BRAND_DATE 120 + BRAND_DATE, 121 + BRAND_MODULE_NAMESPACE 121 122 } object_brand_id_t; 122 123 123 124 static inline void *mantissa_chk(void *p, const char *func) {
+2
include/modules/io.h
··· 29 29 #define C_RED C("\x1b[31m") 30 30 31 31 void init_console_module(void); 32 + ant_value_t console_library(ant_t *js); 33 + 32 34 void print_value_colored(const char *str, FILE *stream); 33 35 void print_repl_value(ant_t *js, ant_value_t val, FILE *stream); 34 36 bool print_uncaught_throw(ant_t *js);
+23
include/modules/stream.h
··· 1 + #ifndef ANT_STREAM_MODULE_H 2 + #define ANT_STREAM_MODULE_H 3 + 4 + #include "types.h" 5 + 6 + void stream_init_constructors(ant_t *js); 7 + 8 + ant_value_t stream_library(ant_t *js); 9 + ant_value_t stream_promises_library(ant_t *js); 10 + 11 + ant_value_t stream_readable_constructor(ant_t *js); 12 + ant_value_t stream_writable_constructor(ant_t *js); 13 + ant_value_t stream_readable_prototype(ant_t *js); 14 + ant_value_t stream_writable_prototype(ant_t *js); 15 + 16 + ant_value_t stream_construct_readable(ant_t *js, ant_value_t base_proto, ant_value_t options); 17 + ant_value_t stream_construct_writable(ant_t *js, ant_value_t base_proto, ant_value_t options); 18 + ant_value_t stream_readable_push(ant_t *js, ant_value_t stream_obj, ant_value_t chunk, ant_value_t encoding); 19 + 20 + void stream_init_readable_object(ant_t *js, ant_value_t obj, ant_value_t options); 21 + void stream_init_writable_object(ant_t *js, ant_value_t obj, ant_value_t options); 22 + 23 + #endif
+1
include/modules/timer.h
··· 3 3 4 4 #include "types.h" 5 5 6 + ant_value_t timers_library(ant_t *js); 6 7 ant_value_t timers_promises_library(ant_t *js); 7 8 8 9 void init_timer_module(void);
+1
include/modules/util.h
··· 4 4 #include "types.h" 5 5 6 6 ant_value_t util_library(ant_t *js); 7 + ant_value_t util_types_library(ant_t *js); 7 8 8 9 #endif
+242
src/builtins/node/diagnostics_channel.mjs
··· 1 + const registry = new Map(); 2 + 3 + const TRACING_EVENTS = ['start', 'end', 'asyncStart', 'asyncEnd', 'error']; 4 + 5 + function createInvalidArgType(name, expected) { 6 + const error = new TypeError(`The "${name}" argument must be of type ${expected}.`); 7 + error.code = 'ERR_INVALID_ARG_TYPE'; 8 + return error; 9 + } 10 + 11 + function validateFunction(value, name) { 12 + if (typeof value !== 'function') throw createInvalidArgType(name, 'function'); 13 + } 14 + 15 + function validateChannelName(name) { 16 + if (typeof name === 'string' || typeof name === 'symbol') return; 17 + throw createInvalidArgType('channel', 'string or symbol'); 18 + } 19 + 20 + function normalizeIndex(length, index) { 21 + if (index < 0) return Math.max(length + index, 0); 22 + return index; 23 + } 24 + 25 + function createTracingChannels(name) { 26 + const prefix = `tracing:${name}`; 27 + return { 28 + start: channel(`${prefix}:start`), 29 + end: channel(`${prefix}:end`), 30 + asyncStart: channel(`${prefix}:asyncStart`), 31 + asyncEnd: channel(`${prefix}:asyncEnd`), 32 + error: channel(`${prefix}:error`) 33 + }; 34 + } 35 + 36 + export class Channel { 37 + constructor(name) { 38 + this.name = name; 39 + this._subscribers = []; 40 + this._stores = undefined; 41 + } 42 + 43 + get hasSubscribers() { 44 + return this._subscribers.length > 0 || !!(this._stores && this._stores.size > 0); 45 + } 46 + 47 + subscribe(fn) { 48 + validateFunction(fn, 'subscription'); 49 + this._subscribers.push(fn); 50 + } 51 + 52 + unsubscribe(fn) { 53 + const index = this._subscribers.indexOf(fn); 54 + if (index === -1) return false; 55 + this._subscribers.splice(index, 1); 56 + return true; 57 + } 58 + 59 + publish(message) { 60 + const subscribers = this._subscribers.slice(); 61 + for (const fn of subscribers) fn(message, this.name); 62 + } 63 + 64 + bindStore(store, transform) { 65 + if (!this._stores) this._stores = new Map(); 66 + this._stores.set(store, transform); 67 + } 68 + 69 + unbindStore(store) { 70 + if (!this._stores) return false; 71 + return this._stores.delete(store); 72 + } 73 + 74 + runStores(context, fn, thisArg, ...args) { 75 + if (!this._stores || this._stores.size === 0) { 76 + this.publish(context); 77 + return fn.apply(thisArg, args); 78 + } 79 + 80 + const entries = Array.from(this._stores.entries()); 81 + const run = index => { 82 + if (index === entries.length) { 83 + this.publish(context); 84 + return fn.apply(thisArg, args); 85 + } 86 + 87 + const [store, transform] = entries[index]; 88 + return store.run(transform ? transform(context) : context, () => run(index + 1)); 89 + }; 90 + 91 + return run(0); 92 + } 93 + } 94 + 95 + export class TracingChannel { 96 + constructor(channels) { 97 + for (const event of TRACING_EVENTS) this[event] = channels[event]; 98 + } 99 + 100 + get hasSubscribers() { 101 + return TRACING_EVENTS.some(event => this[event].hasSubscribers); 102 + } 103 + 104 + subscribe(subscribers) { 105 + for (const event of TRACING_EVENTS) if (subscribers[event] !== undefined) this[event].subscribe(subscribers[event]); 106 + } 107 + 108 + unsubscribe(subscribers) { 109 + let ok = true; 110 + 111 + for (const event of TRACING_EVENTS) { 112 + if (subscribers[event] !== undefined && !this[event].unsubscribe(subscribers[event])) ok = false; 113 + } 114 + 115 + return ok; 116 + } 117 + 118 + traceSync(fn, context = {}, thisArg, ...args) { 119 + if (!this.hasSubscribers) return fn.apply(thisArg, args); 120 + 121 + const { start, end, error } = this; 122 + 123 + return start.runStores(context, () => { 124 + try { 125 + const result = fn.apply(thisArg, args); 126 + context.result = result; 127 + end.publish(context); 128 + return result; 129 + } catch (err) { 130 + context.error = err; 131 + error.publish(context); 132 + end.publish(context); 133 + throw err; 134 + } 135 + }); 136 + } 137 + 138 + tracePromise(fn, context = {}, thisArg, ...args) { 139 + if (!this.hasSubscribers) return fn.apply(thisArg, args); 140 + const { start, end, asyncStart, asyncEnd, error } = this; 141 + 142 + const resolve = result => { 143 + context.result = result; 144 + asyncStart.publish(context); 145 + asyncEnd.publish(context); 146 + return result; 147 + }; 148 + 149 + const reject = err => { 150 + context.error = err; 151 + error.publish(context); 152 + asyncStart.publish(context); 153 + asyncEnd.publish(context); 154 + return Promise.reject(err); 155 + }; 156 + 157 + return start.runStores(context, () => { 158 + try { 159 + let promise = fn.apply(thisArg, args); 160 + if (!(promise instanceof Promise)) promise = Promise.resolve(promise); 161 + const wrapped = promise.then(resolve, reject); 162 + end.publish(context); 163 + return wrapped; 164 + } catch (err) { 165 + context.error = err; 166 + error.publish(context); 167 + end.publish(context); 168 + throw err; 169 + } 170 + }); 171 + } 172 + 173 + traceCallback(fn, position = -1, context = {}, thisArg, ...args) { 174 + if (!this.hasSubscribers) return fn.apply(thisArg, args); 175 + 176 + const { start, end, asyncStart, asyncEnd, error } = this; 177 + const callbackIndex = normalizeIndex(args.length, position); 178 + const callback = args[callbackIndex]; 179 + 180 + validateFunction(callback, 'callback'); 181 + 182 + args.splice(callbackIndex, 1, function wrappedCallback(err, result) { 183 + if (err) { 184 + context.error = err; 185 + error.publish(context); 186 + } else context.result = result; 187 + 188 + return asyncStart.runStores(context, () => { 189 + try { 190 + return callback.apply(this, arguments); 191 + } finally { 192 + asyncEnd.publish(context); 193 + } 194 + }); 195 + }); 196 + 197 + return start.runStores(context, () => { 198 + try { 199 + const result = fn.apply(thisArg, args); 200 + end.publish(context); 201 + return result; 202 + } catch (err) { 203 + context.error = err; 204 + error.publish(context); 205 + end.publish(context); 206 + throw err; 207 + } 208 + }); 209 + } 210 + } 211 + 212 + export function channel(name) { 213 + validateChannelName(name); 214 + 215 + let ch = registry.get(name); 216 + if (!ch) { 217 + ch = new Channel(name); 218 + registry.set(name, ch); 219 + } 220 + 221 + return ch; 222 + } 223 + 224 + export function subscribe(name, fn) { 225 + channel(name).subscribe(fn); 226 + } 227 + 228 + export function unsubscribe(name, fn) { 229 + return channel(name).unsubscribe(fn); 230 + } 231 + 232 + export function hasSubscribers(name) { 233 + const ch = registry.get(name); 234 + return ch ? ch.hasSubscribers : false; 235 + } 236 + 237 + export function tracingChannel(nameOrChannels) { 238 + if (typeof nameOrChannels === 'string') nameOrChannels = createTracingChannels(nameOrChannels); 239 + return new TracingChannel(nameOrChannels); 240 + } 241 + 242 + export default { channel, subscribe, unsubscribe, hasSubscribers, tracingChannel, Channel, TracingChannel };
-563
src/builtins/node/stream.cjs
··· 1 - 'use strict'; 2 - 3 - var EventEmitter = require('node:events').EventEmitter; 4 - var BufferCtor = typeof Buffer !== 'undefined' ? Buffer : require('node:buffer').Buffer; 5 - 6 - function noop() {} 7 - 8 - function nextTick(fn) { 9 - if (typeof process !== 'undefined' && process && typeof process.nextTick === 'function') { 10 - var args = Array.prototype.slice.call(arguments, 1); 11 - process.nextTick(function () { 12 - fn.apply(undefined, args); 13 - }); 14 - return; 15 - } 16 - 17 - Promise.resolve().then(function () { 18 - fn(); 19 - }); 20 - } 21 - 22 - function once(fn) { 23 - var called = false; 24 - return function () { 25 - if (called) return; 26 - called = true; 27 - return fn.apply(this, arguments); 28 - }; 29 - } 30 - 31 - function inherits(target, source) { 32 - Object.getOwnPropertyNames(source).forEach(function (name) { 33 - if (name === 'constructor') return; 34 - if (Object.prototype.hasOwnProperty.call(target, name)) return; 35 - Object.defineProperty(target, name, Object.getOwnPropertyDescriptor(source, name)); 36 - }); 37 - } 38 - 39 - function normalizeChunk(chunk, objectMode, encoding) { 40 - if (objectMode || chunk == null || BufferCtor.isBuffer(chunk) || chunk instanceof Uint8Array) return chunk; 41 - if (typeof chunk === 'string') return BufferCtor.from(chunk, encoding || 'utf8'); 42 - return BufferCtor.from(String(chunk), encoding || 'utf8'); 43 - } 44 - 45 - function toAsyncIterable(source) { 46 - if (source && typeof source[Symbol.asyncIterator] === 'function') return source; 47 - if (source && typeof source[Symbol.iterator] === 'function') { 48 - return { 49 - async *[Symbol.asyncIterator]() { 50 - for (var value of source) yield value; 51 - } 52 - }; 53 - } 54 - 55 - if (source && typeof source.getReader === 'function') { 56 - return { 57 - async *[Symbol.asyncIterator]() { 58 - var reader = source.getReader(); 59 - try { 60 - while (true) { 61 - var result = await reader.read(); 62 - if (!result || result.done) break; 63 - yield result.value; 64 - } 65 - } finally { 66 - if (reader.releaseLock) reader.releaseLock(); 67 - } 68 - } 69 - }; 70 - } 71 - 72 - return { 73 - async *[Symbol.asyncIterator]() { 74 - if (source !== undefined) yield source; 75 - } 76 - }; 77 - } 78 - 79 - function Stream(options) { 80 - if (!(this instanceof Stream)) return new Stream(options); 81 - this.readable = true; 82 - this.writable = true; 83 - this.destroyed = false; 84 - this._paused = false; 85 - this._pipes = []; 86 - this._streamOptions = options || {}; 87 - } 88 - 89 - Stream.prototype = Object.create(EventEmitter.prototype); 90 - Stream.prototype.constructor = Stream; 91 - 92 - Stream.prototype.pipe = function (dest, options) { 93 - var source = this; 94 - var end = !options || options.end !== false; 95 - 96 - function onData(chunk) { 97 - if (!dest || typeof dest.write !== 'function') return; 98 - var ok = dest.write(chunk); 99 - if (ok === false && typeof source.pause === 'function') source.pause(); 100 - } 101 - 102 - function onDrain() { 103 - if (typeof source.resume === 'function') source.resume(); 104 - } 105 - 106 - function onEnd() { 107 - cleanup(); 108 - if (end && dest && typeof dest.end === 'function') dest.end(); 109 - } 110 - 111 - function onClose() { 112 - cleanup(); 113 - } 114 - 115 - function onError(err) { 116 - cleanup(); 117 - if (dest && typeof dest.emit === 'function' && dest.listenerCount('error') > 0) dest.emit('error', err); 118 - } 119 - 120 - function cleanup() { 121 - source.removeListener('data', onData); 122 - source.removeListener('end', onEnd); 123 - source.removeListener('close', onClose); 124 - source.removeListener('error', onError); 125 - if (dest && typeof dest.removeListener === 'function') dest.removeListener('drain', onDrain); 126 - source._pipes = source._pipes.filter(function (entry) { 127 - return entry.dest !== dest; 128 - }); 129 - } 130 - 131 - this._pipes.push({ dest: dest, cleanup: cleanup }); 132 - this.on('data', onData); 133 - this.once('end', onEnd); 134 - this.once('close', onClose); 135 - this.on('error', onError); 136 - if (dest && typeof dest.on === 'function') dest.on('drain', onDrain); 137 - if (dest && typeof dest.emit === 'function') dest.emit('pipe', this); 138 - if (typeof this.resume === 'function') this.resume(); 139 - return dest; 140 - }; 141 - 142 - Stream.prototype.unpipe = function (dest) { 143 - var pipes = this._pipes.slice(); 144 - for (var i = 0; i < pipes.length; i++) { 145 - if (!dest || pipes[i].dest === dest) pipes[i].cleanup(); 146 - } 147 - return this; 148 - }; 149 - 150 - Stream.prototype.pause = function () { 151 - this._paused = true; 152 - this.emit('pause'); 153 - return this; 154 - }; 155 - 156 - Stream.prototype.resume = function () { 157 - this._paused = false; 158 - this.emit('resume'); 159 - return this; 160 - }; 161 - 162 - Stream.prototype.isPaused = function () { 163 - return !!this._paused; 164 - }; 165 - 166 - Stream.prototype.destroy = function (err) { 167 - if (this.destroyed) return this; 168 - 169 - var self = this; 170 - this.destroyed = true; 171 - 172 - function done(destroyErr) { 173 - if (destroyErr) self.emit('error', destroyErr); 174 - self.emit('close'); 175 - } 176 - 177 - if (typeof this._destroy === 'function') { 178 - this._destroy(err || null, once(done)); 179 - } else done(err || null); 180 - 181 - return this; 182 - }; 183 - 184 - function Readable(options) { 185 - Stream.call(this, options); 186 - options = options || {}; 187 - this.readable = true; 188 - this.writable = false; 189 - this.readableEnded = false; 190 - this._readableState = { 191 - objectMode: !!options.objectMode, 192 - ended: false, 193 - endEmitted: false, 194 - flowing: false, 195 - reading: false, 196 - highWaterMark: options.highWaterMark || 16384, 197 - buffer: [] 198 - }; 199 - 200 - if (typeof options.read === 'function') this._read = options.read; 201 - } 202 - 203 - Readable.prototype = Object.create(Stream.prototype); 204 - Readable.prototype.constructor = Readable; 205 - 206 - Readable.prototype._read = noop; 207 - 208 - Readable.prototype._flushReadable = function () { 209 - while (this._readableState.flowing && this._readableState.buffer.length > 0) { 210 - var chunk = this._readableState.buffer.shift(); 211 - this.emit('data', chunk); 212 - } 213 - 214 - if (this._readableState.ended && this._readableState.buffer.length === 0 && !this._readableState.endEmitted) { 215 - this._readableState.endEmitted = true; 216 - this.readableEnded = true; 217 - this.emit('end'); 218 - this.emit('close'); 219 - } 220 - }; 221 - 222 - Readable.prototype._maybeRead = function () { 223 - if (this.destroyed || this._readableState.reading || this._readableState.ended) return; 224 - if (this._readableState.buffer.length > 0) return; 225 - 226 - this._readableState.reading = true; 227 - try { 228 - this._read(this._readableState.highWaterMark); 229 - } finally { 230 - this._readableState.reading = false; 231 - } 232 - }; 233 - 234 - Readable.prototype.push = function (chunk, encoding) { 235 - if (this.destroyed) return false; 236 - 237 - if (chunk === null) { 238 - this._readableState.ended = true; 239 - this._flushReadable(); 240 - return false; 241 - } 242 - 243 - this._readableState.buffer.push(normalizeChunk(chunk, this._readableState.objectMode, encoding)); 244 - if (this._readableState.flowing) this._flushReadable(); 245 - return this._readableState.flowing; 246 - }; 247 - 248 - Readable.prototype.read = function () { 249 - if (this._readableState.buffer.length === 0) this._maybeRead(); 250 - if (this._readableState.buffer.length === 0) return null; 251 - 252 - var chunk = this._readableState.buffer.shift(); 253 - if (this._readableState.flowing) this._flushReadable(); 254 - return chunk; 255 - }; 256 - 257 - Readable.prototype.on = function (event, listener) { 258 - var result = Stream.prototype.on.call(this, event, listener); 259 - if (event === 'data') this.resume(); 260 - return result; 261 - }; 262 - 263 - Readable.prototype.resume = function () { 264 - this._readableState.flowing = true; 265 - Stream.prototype.resume.call(this); 266 - this._maybeRead(); 267 - this._flushReadable(); 268 - return this; 269 - }; 270 - 271 - Readable.prototype.pause = function () { 272 - this._readableState.flowing = false; 273 - return Stream.prototype.pause.call(this); 274 - }; 275 - 276 - Readable.from = function (source, options) { 277 - var readable = new Readable(options); 278 - 279 - nextTick(function () { 280 - (async function () { 281 - try { 282 - for await (var chunk of toAsyncIterable(source)) { 283 - if (readable.destroyed) return; 284 - readable.push(chunk); 285 - } 286 - readable.push(null); 287 - } catch (err) { 288 - readable.destroy(err); 289 - } 290 - })(); 291 - }); 292 - 293 - return readable; 294 - }; 295 - 296 - Readable.fromWeb = function (source, options) { 297 - return Readable.from(source, options); 298 - }; 299 - 300 - function Writable(options) { 301 - Stream.call(this, options); 302 - options = options || {}; 303 - this.readable = false; 304 - this.writable = true; 305 - this.writableEnded = false; 306 - this.writableFinished = false; 307 - this._writableState = { 308 - objectMode: !!options.objectMode || !!options.writableObjectMode, 309 - finished: false, 310 - ended: false 311 - }; 312 - 313 - if (typeof options.write === 'function') this._write = options.write; 314 - if (typeof options.final === 'function') this._final = options.final; 315 - if (typeof options.destroy === 'function') this._destroy = options.destroy; 316 - } 317 - 318 - Writable.prototype = Object.create(Stream.prototype); 319 - Writable.prototype.constructor = Writable; 320 - 321 - Writable.prototype._write = function (_chunk, _encoding, callback) { 322 - callback(); 323 - }; 324 - 325 - Writable.prototype._final = function (callback) { 326 - callback(); 327 - }; 328 - 329 - Writable.prototype.write = function (chunk, encoding, callback) { 330 - if (typeof encoding === 'function') { 331 - callback = encoding; 332 - encoding = undefined; 333 - } 334 - 335 - if (this.writableEnded || this.destroyed) { 336 - var writeErr = new Error('write after end'); 337 - if (typeof callback === 'function') callback(writeErr); 338 - else this.emit('error', writeErr); 339 - return false; 340 - } 341 - 342 - var self = this; 343 - var done = once(function (err) { 344 - if (err) { 345 - self.destroy(err); 346 - if (typeof callback === 'function') callback(err); 347 - return; 348 - } 349 - if (typeof callback === 'function') callback(); 350 - self.emit('drain'); 351 - }); 352 - 353 - try { 354 - this._write(normalizeChunk(chunk, this._writableState.objectMode, encoding), encoding || 'utf8', done); 355 - } catch (err) { 356 - done(err); 357 - return false; 358 - } 359 - 360 - return !this.destroyed; 361 - }; 362 - 363 - Writable.prototype.end = function (chunk, encoding, callback) { 364 - if (typeof chunk === 'function') { 365 - callback = chunk; 366 - chunk = undefined; 367 - encoding = undefined; 368 - } else if (typeof encoding === 'function') { 369 - callback = encoding; 370 - encoding = undefined; 371 - } 372 - 373 - if (chunk !== undefined && chunk !== null) this.write(chunk, encoding); 374 - if (this.writableEnded) { 375 - if (typeof callback === 'function') callback(); 376 - return this; 377 - } 378 - 379 - var self = this; 380 - this.writableEnded = true; 381 - this._writableState.ended = true; 382 - 383 - this._final( 384 - once(function (err) { 385 - if (err) { 386 - self.destroy(err); 387 - if (typeof callback === 'function') callback(err); 388 - return; 389 - } 390 - 391 - self.writableFinished = true; 392 - self._writableState.finished = true; 393 - self.emit('finish'); 394 - if (typeof callback === 'function') callback(); 395 - if (!self.readable) self.emit('close'); 396 - }) 397 - ); 398 - 399 - return this; 400 - }; 401 - 402 - Writable.prototype.cork = noop; 403 - Writable.prototype.uncork = noop; 404 - 405 - function Duplex(options) { 406 - Readable.call(this, options); 407 - Writable.call(this, options); 408 - options = options || {}; 409 - this.readable = true; 410 - this.writable = true; 411 - this.allowHalfOpen = options.allowHalfOpen !== false; 412 - } 413 - 414 - Duplex.prototype = Object.create(Readable.prototype); 415 - Duplex.prototype.constructor = Duplex; 416 - inherits(Duplex.prototype, Writable.prototype); 417 - 418 - function Transform(options) { 419 - Duplex.call(this, options); 420 - options = options || {}; 421 - if (typeof options.transform === 'function') this._transform = options.transform; 422 - if (typeof options.flush === 'function') this._flush = options.flush; 423 - } 424 - 425 - Transform.prototype = Object.create(Duplex.prototype); 426 - Transform.prototype.constructor = Transform; 427 - 428 - Transform.prototype._transform = function (chunk, _encoding, callback) { 429 - callback(null, chunk); 430 - }; 431 - 432 - Transform.prototype._write = function (chunk, encoding, callback) { 433 - var self = this; 434 - this._transform(chunk, encoding, function (err, data) { 435 - if (err) { 436 - callback(err); 437 - return; 438 - } 439 - if (data !== undefined && data !== null) self.push(data); 440 - callback(); 441 - }); 442 - }; 443 - 444 - Transform.prototype._final = function (callback) { 445 - var self = this; 446 - if (typeof this._flush === 'function') { 447 - this._flush(function (err, data) { 448 - if (err) { 449 - callback(err); 450 - return; 451 - } 452 - if (data !== undefined && data !== null) self.push(data); 453 - self.push(null); 454 - callback(); 455 - }); 456 - return; 457 - } 458 - 459 - this.push(null); 460 - callback(); 461 - }; 462 - 463 - function PassThrough(options) { 464 - Transform.call(this, options); 465 - } 466 - 467 - PassThrough.prototype = Object.create(Transform.prototype); 468 - PassThrough.prototype.constructor = PassThrough; 469 - 470 - PassThrough.prototype._transform = function (chunk, _encoding, callback) { 471 - callback(null, chunk); 472 - }; 473 - 474 - function finished(stream, callback) { 475 - callback = once(typeof callback === 'function' ? callback : noop); 476 - 477 - function onFinish() { 478 - cleanup(); 479 - callback(); 480 - } 481 - 482 - function onError(err) { 483 - cleanup(); 484 - callback(err); 485 - } 486 - 487 - function cleanup() { 488 - stream.removeListener('end', onFinish); 489 - stream.removeListener('finish', onFinish); 490 - stream.removeListener('close', onFinish); 491 - stream.removeListener('error', onError); 492 - } 493 - 494 - stream.on('end', onFinish); 495 - stream.on('finish', onFinish); 496 - stream.on('close', onFinish); 497 - stream.on('error', onError); 498 - return stream; 499 - } 500 - 501 - function pipeline() { 502 - var args = Array.prototype.slice.call(arguments); 503 - var callback = typeof args[args.length - 1] === 'function' ? args.pop() : noop; 504 - 505 - if (args.length < 2) { 506 - nextTick(callback); 507 - return args[0]; 508 - } 509 - 510 - var done = once(callback); 511 - for (var i = 0; i < args.length - 1; i++) { 512 - finished(args[i], function (err) { 513 - if (err) done(err); 514 - }); 515 - args[i].pipe(args[i + 1]); 516 - } 517 - 518 - finished(args[args.length - 1], done); 519 - return args[args.length - 1]; 520 - } 521 - 522 - var promises = { 523 - pipeline: function () { 524 - var args = Array.prototype.slice.call(arguments); 525 - return new Promise(function (resolve, reject) { 526 - args.push(function (err) { 527 - if (err) reject(err); 528 - else resolve(); 529 - }); 530 - pipeline.apply(undefined, args); 531 - }); 532 - }, 533 - finished: function (stream) { 534 - return new Promise(function (resolve, reject) { 535 - finished(stream, function (err) { 536 - if (err) reject(err); 537 - else resolve(); 538 - }); 539 - }); 540 - } 541 - }; 542 - 543 - Stream.Stream = Stream; 544 - Stream.Readable = Readable; 545 - Stream.Writable = Writable; 546 - Stream.Duplex = Duplex; 547 - Stream.Transform = Transform; 548 - Stream.PassThrough = PassThrough; 549 - Stream.pipeline = pipeline; 550 - Stream.finished = finished; 551 - Stream.promises = promises; 552 - 553 - module.exports = Stream; 554 - module.exports.default = Stream; 555 - module.exports.Stream = Stream; 556 - module.exports.Readable = Readable; 557 - module.exports.Writable = Writable; 558 - module.exports.Duplex = Duplex; 559 - module.exports.Transform = Transform; 560 - module.exports.PassThrough = PassThrough; 561 - module.exports.pipeline = pipeline; 562 - module.exports.finished = finished; 563 - module.exports.promises = promises;
-1
src/builtins/node/stub.txt
··· 4 4 - https.mjs 5 5 - http2.mjs 6 6 - tls.mjs 7 - - stream.cjs
+8 -2
src/esm/loader.c
··· 167 167 return vtype(default_val) != T_UNDEF ? default_val : ns; 168 168 } 169 169 170 + static ant_value_t esm_make_namespace_object(ant_t *js) { 171 + ant_value_t ns = js_mkobj(js); 172 + js_set_slot(ns, SLOT_BRAND, js_mknum(BRAND_MODULE_NAMESPACE)); 173 + return ns; 174 + } 175 + 170 176 static ant_value_t esm_complete_value_module(esm_module_t *mod, ant_value_t value) { 171 177 if (is_err(value)) { 172 178 mod->is_loading = false; ··· 1020 1026 return esm_complete_value_module(mod, img_val); 1021 1027 } 1022 1028 case ESM_MODULE_KIND_NATIVE: { 1023 - ant_value_t ns = js_mkobj(js); 1029 + ant_value_t ns = esm_make_namespace_object(js); 1024 1030 mod->namespace_obj = ns; 1025 1031 1026 1032 ant_value_t native_exports = napi_load_native_module(js, mod->resolved_path, ns); ··· 1098 1104 }} 1099 1105 1100 1106 char *js_code = content; 1101 - ant_value_t ns = js_mkobj(js); 1107 + ant_value_t ns = esm_make_namespace_object(js); 1102 1108 mod->namespace_obj = ns; 1103 1109 1104 1110 const char *prev_filename = js->filename;
+6
src/main.c
··· 81 81 #include "modules/globals.h" 82 82 #include "modules/wasm.h" 83 83 #include "modules/string_decoder.h" 84 + #include "modules/stream.h" 84 85 #include "modules/structured-clone.h" 85 86 #include "modules/v8.h" 86 87 #include "modules/worker_threads.h" ··· 631 632 ant_register_library(internal_http_metadata_library, "ant:internal/http_metadata", NULL); 632 633 633 634 ant_standard_library("util", util_library); 635 + ant_standard_library("util/types", util_types_library); 636 + ant_standard_library("console", console_library); 634 637 ant_standard_library("net", net_library); 635 638 ant_standard_library("dns", dns_library); 636 639 ant_standard_library("assert", assert_library); ··· 652 655 ant_standard_library("v8", v8_library); 653 656 ant_standard_library("zlib", zlib_library); 654 657 ant_standard_library("string_decoder", string_decoder_library); 658 + ant_standard_library("stream", stream_library); 659 + ant_standard_library("timers", timers_library); 655 660 656 661 ant_standard_library("fs/promises", fs_promises_library); 657 662 ant_standard_library("timers/promises", timers_promises_library); 658 663 ant_standard_library("readline/promises", readline_promises_library); 664 + ant_standard_library("stream/promises", stream_promises_library); 659 665 660 666 ant_value_t snapshot_result = ant_load_snapshot(js); 661 667 if (vtype(snapshot_result) == T_ERR) {
+410 -8
src/modules/fs.c
··· 30 30 #include "modules/date.h" 31 31 #include "modules/buffer.h" 32 32 #include "modules/events.h" 33 + #include "modules/stream.h" 33 34 #include "modules/symbol.h" 34 35 35 36 #define fs_err_code(js, code, op, path) ({ \ ··· 134 135 ant_value_t listener; 135 136 } fs_watchfile_options_t; 136 137 137 - static ant_value_t g_dirent_proto = 0; 138 - static ant_value_t g_fswatcher_proto = 0; 139 - static ant_value_t g_fswatcher_ctor = 0; 138 + typedef struct { 139 + mode_t mode; 140 + double size, uid, gid; 141 + double atime_ms, mtime_ms, ctime_ms, birthtime_ms; 142 + } fs_stat_fields_t; 143 + 144 + static ant_value_t g_dirent_proto = 0; 145 + static ant_value_t g_fswatcher_proto = 0; 146 + static ant_value_t g_fswatcher_ctor = 0; 147 + static ant_value_t g_readstream_proto = 0; 148 + static ant_value_t g_readstream_ctor = 0; 149 + static ant_value_t g_writestream_proto = 0; 150 + static ant_value_t g_writestream_ctor = 0; 140 151 141 152 static fs_watcher_t *active_watchers = NULL; 142 153 static UT_array *pending_requests = NULL; ··· 187 198 return result; 188 199 } 189 200 190 - typedef struct { 191 - mode_t mode; 192 - double size, uid, gid; 193 - double atime_ms, mtime_ms, ctime_ms, birthtime_ms; 194 - } fs_stat_fields_t; 201 + static int parse_open_flags(ant_t *js, ant_value_t arg); 202 + static ant_value_t fs_coerce_path(ant_t *js, ant_value_t arg); 203 + 204 + static ant_value_t fs_stream_error(ant_t *js, ant_value_t stream_obj, const char *op, int uv_code) { 205 + ant_value_t props = js_mkobj(js); 206 + ant_value_t path_val = js_get(js, stream_obj, "path"); 207 + const char *code = uv_err_name(uv_code); 208 + 209 + if (code) js_set(js, props, "code", js_mkstr(js, code, strlen(code))); 210 + js_set(js, props, "errno", js_mknum((double)uv_code)); 211 + if (vtype(path_val) == T_STR) js_set(js, props, "path", path_val); 212 + return js_mkerr_props(js, JS_ERR_TYPE, props, "%s failed: %s", op, uv_strerror(uv_code)); 213 + } 214 + 215 + static ant_value_t fs_stream_push_chunk(ant_t *js, ant_value_t stream_obj, ant_value_t chunk) { 216 + return stream_readable_push(js, stream_obj, chunk, js_mkundef()); 217 + } 218 + 219 + static ant_value_t fs_stream_callback(ant_t *js, ant_value_t callback, ant_value_t value) { 220 + if (!is_callable(callback)) return js_mkundef(); 221 + return fs_call_value(js, callback, js_mkundef(), &value, 1); 222 + } 223 + 224 + static int fs_stream_close_fd_sync(ant_t *js, ant_value_t stream_obj) { 225 + ant_value_t fd_val = js_get(js, stream_obj, "fd"); 226 + uv_fs_t req; 227 + int result = 0; 228 + 229 + if (vtype(fd_val) != T_NUM) { 230 + js_set(js, stream_obj, "pending", js_false); 231 + js_set(js, stream_obj, "closed", js_true); 232 + return 0; 233 + } 234 + 235 + result = uv_fs_close(uv_default_loop(), &req, (uv_file)js_getnum(fd_val), NULL); 236 + uv_fs_req_cleanup(&req); 237 + 238 + js_set(js, stream_obj, "fd", js_mknull()); 239 + js_set(js, stream_obj, "pending", js_false); 240 + js_set(js, stream_obj, "closed", js_true); 241 + 242 + return result; 243 + } 244 + 245 + static int fs_stream_open_fd_sync(ant_t *js, ant_value_t stream_obj) { 246 + ant_value_t fd_val = js_get(js, stream_obj, "fd"); 247 + ant_value_t path_val = js_get(js, stream_obj, "path"); 248 + ant_value_t mode_val = js_get(js, stream_obj, "mode"); 249 + ant_value_t flags_val = js_get_slot(stream_obj, SLOT_FS_FLAGS); 250 + 251 + size_t path_len = 0; 252 + const char *path = NULL; 253 + char *path_copy = NULL; 254 + uv_fs_t req; 255 + 256 + int flags = 0; 257 + int mode = 0666; 258 + int result = 0; 259 + 260 + if (vtype(fd_val) == T_NUM) return (int)js_getnum(fd_val); 261 + if (vtype(path_val) != T_STR) return UV_EINVAL; 262 + 263 + path = js_getstr(js, path_val, &path_len); 264 + if (!path) return UV_EINVAL; 265 + 266 + path_copy = strndup(path, path_len); 267 + if (!path_copy) return UV_ENOMEM; 268 + 269 + flags = (vtype(flags_val) == T_NUM) ? (int)js_getnum(flags_val) : O_RDONLY; 270 + if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); 271 + 272 + result = uv_fs_open(uv_default_loop(), &req, path_copy, flags, mode, NULL); 273 + uv_fs_req_cleanup(&req); 274 + free(path_copy); 275 + 276 + if (result < 0) return result; 277 + 278 + js_set(js, stream_obj, "fd", js_mknum((double)result)); 279 + js_set(js, stream_obj, "pending", js_false); 280 + js_set(js, stream_obj, "closed", js_false); 281 + 282 + ant_value_t open_arg = js_mknum((double)result); 283 + eventemitter_emit_args(js, stream_obj, "open", &open_arg, 1); 284 + eventemitter_emit_args(js, stream_obj, "ready", NULL, 0); 285 + 286 + return result; 287 + } 288 + 289 + static ant_value_t fs_stream_destroy(ant_t *js, ant_value_t *args, int nargs) { 290 + ant_value_t stream_obj = js_getthis(js); 291 + ant_value_t err = nargs > 0 ? args[0] : js_mknull(); 292 + ant_value_t callback = nargs > 1 ? args[1] : js_mkundef(); 293 + int result = fs_stream_close_fd_sync(js, stream_obj); 294 + 295 + if (result < 0 && (is_null(err) || is_undefined(err))) 296 + err = fs_stream_error(js, stream_obj, "close", result); 297 + if (is_undefined(err)) err = js_mknull(); 298 + 299 + return fs_stream_callback(js, callback, err); 300 + } 301 + 302 + static ant_value_t fs_stream_close(ant_t *js, ant_value_t *args, int nargs) { 303 + ant_value_t stream_obj = js_getthis(js); 304 + 305 + if (nargs > 0 && is_callable(args[0])) { 306 + eventemitter_add_listener(js, stream_obj, "close", args[0], true); 307 + if (js_truthy(js, js_get(js, stream_obj, "closed"))) 308 + fs_call_value(js, args[0], js_mkundef(), NULL, 0); 309 + } 310 + 311 + if (!js_truthy(js, js_get(js, stream_obj, "destroyed"))) { 312 + ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 313 + if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, NULL, 0); 314 + } 315 + 316 + return stream_obj; 317 + } 318 + 319 + static ant_value_t fs_readstream__read(ant_t *js, ant_value_t *args, int nargs) { 320 + ant_value_t stream_obj = js_getthis(js); 321 + ant_value_t pos_val = js_get(js, stream_obj, "pos"); 322 + ant_value_t end_val = js_get(js, stream_obj, "end"); 323 + ant_value_t bytes_read_val = js_get(js, stream_obj, "bytesRead"); 324 + 325 + int fd = fs_stream_open_fd_sync(js, stream_obj); 326 + int64_t pos = (vtype(pos_val) == T_NUM) ? (int64_t)js_getnum(pos_val) : 0; 327 + int64_t end = (vtype(end_val) == T_NUM) ? (int64_t)js_getnum(end_val) : -1; 328 + 329 + size_t want = 16384; 330 + bool reached_eof = false; 331 + 332 + if (fd < 0) { 333 + ant_value_t err = fs_stream_error(js, stream_obj, "open", fd); 334 + ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 335 + if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); 336 + return js_mkundef(); 337 + } 338 + 339 + if (nargs > 0 && vtype(args[0]) == T_NUM && js_getnum(args[0]) > 0) 340 + want = (size_t)js_getnum(args[0]); 341 + 342 + if (end >= 0) { 343 + if (pos > end) { 344 + if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 345 + return fs_stream_push_chunk(js, stream_obj, js_mknull()); 346 + } 347 + if ((int64_t)want > (end - pos + 1)) want = (size_t)(end - pos + 1); 348 + } 349 + 350 + ArrayBufferData *ab = create_array_buffer_data(want); 351 + if (!ab) { 352 + ant_value_t err = js_mkerr(js, "Failed to allocate ReadStream buffer"); 353 + ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 354 + if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); 355 + return js_mkundef(); 356 + } 357 + 358 + uv_fs_t req; 359 + uv_buf_t buf = uv_buf_init((char *)ab->data, (unsigned int)want); 360 + int result = uv_fs_read(uv_default_loop(), &req, fd, &buf, 1, pos, NULL); 361 + uv_fs_req_cleanup(&req); 362 + 363 + if (result < 0) { 364 + ant_value_t err = fs_stream_error(js, stream_obj, "read", result); 365 + ant_value_t destroy_fn = js_getprop_fallback(js, stream_obj, "destroy"); 366 + free_array_buffer_data(ab); 367 + if (is_callable(destroy_fn)) fs_call_value(js, destroy_fn, stream_obj, &err, 1); 368 + return js_mkundef(); 369 + } 370 + 371 + if (result == 0) { 372 + free_array_buffer_data(ab); 373 + if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) 374 + (void)fs_stream_close_fd_sync(js, stream_obj); 375 + return fs_stream_push_chunk(js, stream_obj, js_mknull()); 376 + } 377 + 378 + ant_value_t chunk = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, (size_t)result, "Buffer"); 379 + if (vtype(chunk) == T_ERR) { 380 + free_array_buffer_data(ab); 381 + return chunk; 382 + } 383 + 384 + if (result < (int)want) reached_eof = true; 385 + if (end >= 0 && (pos + result - 1) >= end) reached_eof = true; 386 + 387 + js_set(js, stream_obj, "pos", js_mknum((double)(pos + result))); 388 + js_set(js, stream_obj, "bytesRead", js_mknum( 389 + (vtype(bytes_read_val) == T_NUM 390 + ? js_getnum(bytes_read_val) : 0.0) + (double)result 391 + )); 392 + 393 + fs_stream_push_chunk(js, stream_obj, chunk); 394 + 395 + if (reached_eof) { 396 + if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 397 + return fs_stream_push_chunk(js, stream_obj, js_mknull()); 398 + } 399 + 400 + return js_mkundef(); 401 + } 402 + 403 + static ant_value_t fs_writestream__write(ant_t *js, ant_value_t *args, int nargs) { 404 + ant_value_t stream_obj = js_getthis(js); 405 + ant_value_t callback = nargs > 2 ? args[2] : js_mkundef(); 406 + ant_value_t pos_val = js_get(js, stream_obj, "pos"); 407 + ant_value_t bytes_written_val = js_get(js, stream_obj, "bytesWritten"); 408 + 409 + const uint8_t *bytes = NULL; 410 + size_t len = 0; 411 + 412 + int fd = fs_stream_open_fd_sync(js, stream_obj); 413 + int64_t pos = (vtype(pos_val) == T_NUM) ? (int64_t)js_getnum(pos_val) : -1; 414 + size_t offset = 0; 415 + 416 + if (fd < 0) return fs_stream_callback(js, callback, fs_stream_error(js, stream_obj, "open", fd)); 417 + 418 + if (vtype(args[0]) == T_STR) { 419 + bytes = (const uint8_t *)js_getstr(js, args[0], &len); 420 + } else if (!buffer_source_get_bytes(js, args[0], &bytes, &len)) { 421 + return fs_stream_callback(js, callback, js_mkerr(js, "WriteStream chunk must be a string or ArrayBufferView")); 422 + } 423 + 424 + while (offset < len) { 425 + uv_fs_t req; 426 + uv_buf_t buf = uv_buf_init((char *)(bytes + offset), (unsigned int)(len - offset)); 427 + int result = uv_fs_write(uv_default_loop(), &req, fd, &buf, 1, pos, NULL); 428 + uv_fs_req_cleanup(&req); 429 + 430 + if (result < 0) { 431 + if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 432 + return fs_stream_callback(js, callback, fs_stream_error(js, stream_obj, "write", result)); 433 + } 434 + if (result == 0) { 435 + if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) fs_stream_close_fd_sync(js, stream_obj); 436 + return fs_stream_callback(js, callback, js_mkerr(js, "write failed: short write")); 437 + } 438 + 439 + offset += (size_t)result; 440 + if (pos >= 0) pos += result; 441 + } 442 + 443 + if (pos >= 0) js_set(js, stream_obj, "pos", js_mknum((double)pos)); 444 + js_set(js, stream_obj, "bytesWritten", js_mknum( 445 + (vtype(bytes_written_val) == T_NUM 446 + ? js_getnum(bytes_written_val) : 0.0) + (double)offset 447 + )); 448 + 449 + return fs_stream_callback(js, callback, js_mknull()); 450 + } 451 + 452 + static ant_value_t fs_writestream__final(ant_t *js, ant_value_t *args, int nargs) { 453 + ant_value_t stream_obj = js_getthis(js); 454 + ant_value_t callback = nargs > 0 ? args[0] : js_mkundef(); 455 + ant_value_t value = js_mknull(); 456 + 457 + if (js_truthy(js, js_get(js, stream_obj, "autoClose"))) { 458 + int result = fs_stream_close_fd_sync(js, stream_obj); 459 + if (result < 0) value = fs_stream_error(js, stream_obj, "close", result); 460 + } 461 + 462 + return fs_stream_callback(js, callback, value); 463 + } 464 + 465 + static ant_value_t fs_create_readstream_impl(ant_t *js, ant_value_t path_arg, ant_value_t options_arg, ant_value_t proto) { 466 + ant_value_t path_val = fs_coerce_path(js, path_arg); 467 + ant_value_t options = is_object_type(options_arg) ? options_arg : js_mkobj(js); 468 + ant_value_t stream_options = js_mkobj(js); 469 + 470 + ant_value_t hwm = js_get(js, options, "highWaterMark"); 471 + ant_value_t flags_raw = js_get(js, options, "flags"); 472 + ant_value_t fd_val = js_get(js, options, "fd"); 473 + ant_value_t start_val = js_get(js, options, "start"); 474 + ant_value_t end_val = js_get(js, options, "end"); 475 + ant_value_t mode_val = js_get(js, options, "mode"); 476 + 477 + ant_value_t auto_close_val = js_get(js, options, "autoClose"); 478 + ant_value_t emit_close_val = js_get(js, options, "emitClose"); 479 + ant_value_t stream_obj = 0; 480 + 481 + int flags = parse_open_flags(js, is_undefined(flags_raw) ? js_mkstr(js, "r", 1) : flags_raw); 482 + if (vtype(path_val) != T_STR) return js_mkerr(js, "ReadStream path must be a string"); 483 + if (vtype(hwm) == T_NUM && js_getnum(hwm) > 0) js_set(js, stream_options, "highWaterMark", hwm); 484 + 485 + stream_obj = stream_construct_readable(js, proto, stream_options); 486 + if (is_err(stream_obj)) return stream_obj; 487 + 488 + js_set(js, stream_obj, "_read", js_mkfun(fs_readstream__read)); 489 + js_set(js, stream_obj, "_destroy", js_mkfun(fs_stream_destroy)); 490 + js_set(js, stream_obj, "path", path_val); 491 + js_set(js, stream_obj, "flags", is_undefined(flags_raw) ? js_mkstr(js, "r", 1) : flags_raw); 492 + js_set(js, stream_obj, "mode", vtype(mode_val) == T_NUM ? mode_val : js_mknum(0666)); 493 + js_set(js, stream_obj, "fd", vtype(fd_val) == T_NUM ? fd_val : js_mkundef()); 494 + js_set(js, stream_obj, "pending", js_bool(vtype(fd_val) != T_NUM)); 495 + js_set(js, stream_obj, "closed", js_false); 496 + js_set(js, stream_obj, "autoClose", is_undefined(auto_close_val) ? js_true : js_bool(js_truthy(js, auto_close_val))); 497 + js_set(js, stream_obj, "emitClose", is_undefined(emit_close_val) ? js_true : js_bool(js_truthy(js, emit_close_val))); 498 + js_set(js, stream_obj, "bytesRead", js_mknum(0)); 499 + js_set(js, stream_obj, "start", vtype(start_val) == T_NUM ? start_val : js_mkundef()); 500 + js_set(js, stream_obj, "end", vtype(end_val) == T_NUM ? end_val : js_mkundef()); 501 + js_set(js, stream_obj, "pos", js_mknum(vtype(start_val) == T_NUM ? js_getnum(start_val) : 0.0)); 502 + js_set_slot(stream_obj, SLOT_FS_FLAGS, js_mknum((double)flags)); 503 + 504 + return stream_obj; 505 + } 506 + 507 + static ant_value_t fs_create_writestream_impl(ant_t *js, ant_value_t path_arg, ant_value_t options_arg, ant_value_t proto) { 508 + ant_value_t path_val = fs_coerce_path(js, path_arg); 509 + ant_value_t options = is_object_type(options_arg) ? options_arg : js_mkobj(js); 510 + ant_value_t stream_options = js_mkobj(js); 511 + 512 + ant_value_t flags_raw = js_get(js, options, "flags"); 513 + ant_value_t fd_val = js_get(js, options, "fd"); 514 + ant_value_t start_val = js_get(js, options, "start"); 515 + ant_value_t mode_val = js_get(js, options, "mode"); 516 + ant_value_t auto_close_val = js_get(js, options, "autoClose"); 517 + ant_value_t emit_close_val = js_get(js, options, "emitClose"); 518 + ant_value_t hwm = js_get(js, options, "highWaterMark"); 519 + ant_value_t stream_obj = 0; 520 + 521 + int flags = parse_open_flags(js, is_undefined(flags_raw) ? js_mkstr(js, "w", 1) : flags_raw); 522 + double start_pos = (vtype(start_val) == T_NUM) ? js_getnum(start_val) : ((flags & O_APPEND) ? -1.0 : 0.0); 523 + 524 + if (vtype(path_val) != T_STR) return js_mkerr(js, "WriteStream path must be a string"); 525 + if (vtype(hwm) == T_NUM && js_getnum(hwm) > 0) js_set(js, stream_options, "highWaterMark", hwm); 526 + 527 + stream_obj = stream_construct_writable(js, proto, stream_options); 528 + if (is_err(stream_obj)) return stream_obj; 529 + 530 + js_set(js, stream_obj, "_write", js_mkfun(fs_writestream__write)); 531 + js_set(js, stream_obj, "_final", js_mkfun(fs_writestream__final)); 532 + js_set(js, stream_obj, "_destroy", js_mkfun(fs_stream_destroy)); 533 + js_set(js, stream_obj, "path", path_val); 534 + js_set(js, stream_obj, "flags", is_undefined(flags_raw) ? js_mkstr(js, "w", 1) : flags_raw); 535 + js_set(js, stream_obj, "mode", vtype(mode_val) == T_NUM ? mode_val : js_mknum(0666)); 536 + js_set(js, stream_obj, "fd", vtype(fd_val) == T_NUM ? fd_val : js_mkundef()); 537 + js_set(js, stream_obj, "pending", js_bool(vtype(fd_val) != T_NUM)); 538 + js_set(js, stream_obj, "closed", js_false); 539 + js_set(js, stream_obj, "autoClose", is_undefined(auto_close_val) ? js_true : js_bool(js_truthy(js, auto_close_val))); 540 + js_set(js, stream_obj, "emitClose", is_undefined(emit_close_val) ? js_true : js_bool(js_truthy(js, emit_close_val))); 541 + js_set(js, stream_obj, "bytesWritten", js_mknum(0)); 542 + js_set(js, stream_obj, "start", vtype(start_val) == T_NUM ? start_val : js_mkundef()); 543 + js_set(js, stream_obj, "pos", js_mknum(start_pos)); 544 + js_set_slot(stream_obj, SLOT_FS_FLAGS, js_mknum((double)flags)); 545 + 546 + return stream_obj; 547 + } 548 + 549 + static ant_value_t js_readstream_ctor(ant_t *js, ant_value_t *args, int nargs) { 550 + if (nargs < 1) return js_mkerr(js, "ReadStream() requires a path argument"); 551 + return fs_create_readstream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_readstream_proto); 552 + } 553 + 554 + static ant_value_t js_writestream_ctor(ant_t *js, ant_value_t *args, int nargs) { 555 + if (nargs < 1) return js_mkerr(js, "WriteStream() requires a path argument"); 556 + return fs_create_writestream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_writestream_proto); 557 + } 558 + 559 + static ant_value_t builtin_fs_createReadStream(ant_t *js, ant_value_t *args, int nargs) { 560 + if (nargs < 1) return js_mkerr(js, "createReadStream() requires a path argument"); 561 + return fs_create_readstream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_readstream_proto); 562 + } 563 + 564 + static ant_value_t builtin_fs_createWriteStream(ant_t *js, ant_value_t *args, int nargs) { 565 + if (nargs < 1) return js_mkerr(js, "createWriteStream() requires a path argument"); 566 + return fs_create_writestream_impl(js, args[0], nargs > 1 ? args[1] : js_mkundef(), g_writestream_proto); 567 + } 568 + 569 + static void fs_init_stream_constructors(ant_t *js) { 570 + if (g_readstream_ctor && g_writestream_ctor) return; 571 + 572 + stream_init_constructors(js); 573 + 574 + g_readstream_proto = js_mkobj(js); 575 + js_set_proto_init(g_readstream_proto, stream_readable_prototype(js)); 576 + js_set(js, g_readstream_proto, "close", js_mkfun(fs_stream_close)); 577 + js_set_sym(js, g_readstream_proto, get_toStringTag_sym(), js_mkstr(js, "ReadStream", 10)); 578 + g_readstream_ctor = js_make_ctor(js, js_readstream_ctor, g_readstream_proto, "ReadStream", 10); 579 + js_set_proto_init(g_readstream_ctor, stream_readable_constructor(js)); 580 + 581 + g_writestream_proto = js_mkobj(js); 582 + js_set_proto_init(g_writestream_proto, stream_writable_prototype(js)); 583 + js_set(js, g_writestream_proto, "close", js_mkfun(fs_stream_close)); 584 + js_set_sym(js, g_writestream_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); 585 + g_writestream_ctor = js_make_ctor(js, js_writestream_ctor, g_writestream_proto, "WriteStream", 11); 586 + js_set_proto_init(g_writestream_ctor, stream_writable_constructor(js)); 587 + } 195 588 196 589 static ant_value_t fs_make_date(ant_t *js, double ms) { 197 590 ant_value_t obj = js_mkobj(js); ··· 3394 3787 3395 3788 fs_set_callback_compatible_methods(js, lib); 3396 3789 fs_init_watch_constructors(js); 3790 + fs_init_stream_constructors(js); 3397 3791 3398 3792 js_set(js, lib, "read", js_mkfun(builtin_fs_read_fd)); 3399 3793 js_set(js, lib, "readFileSync", js_mkfun(builtin_fs_readFileSync)); 3400 3794 js_set(js, lib, "readSync", js_mkfun(builtin_fs_readSync)); 3401 3795 js_set(js, lib, "stream", js_mkfun(builtin_fs_readBytes)); 3796 + js_set(js, lib, "createReadStream", js_mkfun(builtin_fs_createReadStream)); 3797 + js_set(js, lib, "createWriteStream", js_mkfun(builtin_fs_createWriteStream)); 3402 3798 js_set(js, lib, "openSync", js_mkfun(builtin_fs_openSync)); 3403 3799 js_set(js, lib, "closeSync", js_mkfun(builtin_fs_closeSync)); 3404 3800 js_set(js, lib, "writeFileSync", js_mkfun(builtin_fs_writeFileSync)); ··· 3424 3820 js_set(js, lib, "watchFile", js_mkfun(builtin_fs_watchFile)); 3425 3821 js_set(js, lib, "unwatchFile", js_mkfun(builtin_fs_unwatchFile)); 3426 3822 js_set(js, lib, "FSWatcher", g_fswatcher_ctor); 3823 + js_set(js, lib, "ReadStream", g_readstream_ctor); 3824 + js_set(js, lib, "WriteStream", g_writestream_ctor); 3427 3825 js_set(js, realpath_sync, "native", realpath_sync); 3428 3826 3429 3827 js_set_getter_desc( ··· 3458 3856 3459 3857 if (g_fswatcher_proto) mark(js, g_fswatcher_proto); 3460 3858 if (g_fswatcher_ctor) mark(js, g_fswatcher_ctor); 3859 + if (g_readstream_proto) mark(js, g_readstream_proto); 3860 + if (g_readstream_ctor) mark(js, g_readstream_ctor); 3861 + if (g_writestream_proto) mark(js, g_writestream_proto); 3862 + if (g_writestream_ctor) mark(js, g_writestream_ctor); 3461 3863 if (!pending_requests) return; 3462 3864 3463 3865 unsigned int len = utarray_len(pending_requests);
+7 -2
src/modules/io.c
··· 799 799 return js_mkundef(); 800 800 } 801 801 802 - void init_console_module() { 803 - ant_t *js = rt->js; 802 + ant_value_t console_library(ant_t *js) { 804 803 ant_value_t console_obj = js_mkobj(js); 805 804 806 805 js_set(js, console_obj, "log", js_mkfun(js_console_log)); ··· 816 815 js_set(js, console_obj, "inspect", js_mkfun(js_console_inspect)); 817 816 818 817 js_set_sym(js, console_obj, get_toStringTag_sym(), js_mkstr(js, "console", 7)); 818 + return console_obj; 819 + } 820 + 821 + void init_console_module() { 822 + ant_t *js = rt->js; 823 + ant_value_t console_obj = console_library(js); 819 824 js_set(js, js_glob(js), "console", console_obj); 820 825 }
+2 -2
src/modules/readline.c
··· 516 516 break; 517 517 case 127: case 8: handle_backspace(iface); break; 518 518 case 4: 519 - if (iface->line_len == 0) { emit_event(js, iface, "close", NULL, 0); iface->closed = true; } 519 + if (iface->line_len == 0) rl_close_interface(js, iface); 520 520 else handle_delete(iface); 521 521 break; 522 522 case 1: iface->line_pos = 0; refresh_line(iface); break; ··· 535 535 if (!iface || iface->closed || iface->paused) goto cleanup; 536 536 537 537 if (nread < 0) { 538 - if (nread == UV_EOF) { emit_event(js, iface, "close", NULL, 0); iface->closed = true; } 538 + if (nread == UV_EOF) rl_close_interface(js, iface); 539 539 goto cleanup; 540 540 } 541 541
+1599
src/modules/stream.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + 4 + #include "ant.h" 5 + #include "ptr.h" 6 + #include "internal.h" 7 + #include "silver/engine.h" 8 + #include "esm/loader.h" 9 + 10 + #include "gc/roots.h" 11 + 12 + #include "modules/assert.h" 13 + #include "modules/events.h" 14 + #include "modules/stream.h" 15 + #include "modules/symbol.h" 16 + 17 + enum { STREAM_NATIVE_TAG = 0x5354524Du }; // STRM 18 + 19 + static ant_value_t g_stream_proto = 0; 20 + static ant_value_t g_stream_ctor = 0; 21 + 22 + static ant_value_t g_readable_proto = 0; 23 + static ant_value_t g_readable_ctor = 0; 24 + 25 + static ant_value_t g_writable_proto = 0; 26 + static ant_value_t g_writable_ctor = 0; 27 + 28 + static ant_value_t g_duplex_proto = 0; 29 + static ant_value_t g_duplex_ctor = 0; 30 + 31 + static ant_value_t g_transform_proto = 0; 32 + static ant_value_t g_transform_ctor = 0; 33 + 34 + static ant_value_t g_passthrough_proto = 0; 35 + static ant_value_t g_passthrough_ctor = 0; 36 + 37 + static ant_value_t stream_noop(ant_t *js, ant_value_t *args, int nargs) { 38 + return js_mkundef(); 39 + } 40 + 41 + static bool stream_is_instance(ant_value_t value) { 42 + return is_object_type(value) && js_check_native_tag(value, STREAM_NATIVE_TAG); 43 + } 44 + 45 + static ant_value_t stream_require_this(ant_t *js, ant_value_t value, const char *label) { 46 + if (!stream_is_instance(value)) 47 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid %s", label); 48 + return value; 49 + } 50 + 51 + static ant_value_t stream_truthy_or_object(ant_t *js, ant_value_t value) { 52 + return js_truthy(js, value) ? value : js_mkobj(js); 53 + } 54 + 55 + static bool stream_key_is_cstr(ant_t *js, ant_value_t value, const char *expected) { 56 + size_t len = 0; 57 + const char *s = NULL; 58 + if (vtype(value) != T_STR) return false; 59 + s = js_getstr(js, value, &len); 60 + return s && len == strlen(expected) && memcmp(s, expected, len) == 0; 61 + } 62 + 63 + static ant_value_t stream_event_key(ant_t *js, ant_value_t value) { 64 + uint8_t t = vtype(value); 65 + if (t == T_STR || t == T_SYMBOL) return value; 66 + return js_mkerr(js, "event must be a string or Symbol"); 67 + } 68 + 69 + static ant_value_t stream_call( 70 + ant_t *js, 71 + ant_value_t fn, 72 + ant_value_t this_val, 73 + ant_value_t *args, 74 + int nargs, 75 + bool is_ctor 76 + ) { 77 + if (!is_callable(fn)) return js_mkundef(); 78 + return sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, is_ctor); 79 + } 80 + 81 + static ant_value_t stream_call_prop( 82 + ant_t *js, 83 + ant_value_t target, 84 + const char *name, 85 + ant_value_t *args, 86 + int nargs 87 + ) { 88 + ant_value_t fn = js_getprop_fallback(js, target, name); 89 + if (is_err(fn) || !is_callable(fn)) return js_mkundef(); 90 + return stream_call(js, fn, target, args, nargs, false); 91 + } 92 + 93 + static void stream_call_callback(ant_t *js, ant_value_t fn, ant_value_t *args, int nargs) { 94 + if (!is_callable(fn)) return; 95 + stream_call(js, fn, js_mkundef(), args, nargs, false); 96 + } 97 + 98 + static void stream_schedule_microtask(ant_t *js, ant_cfunc_t fn, ant_value_t data) { 99 + ant_value_t promise = js_mkpromise(js); 100 + ant_value_t cb = js_heavy_mkfun(js, fn, data); 101 + ant_value_t then_result = 0; 102 + 103 + js_resolve_promise(js, promise, js_mkundef()); 104 + then_result = js_promise_then(js, promise, cb, js_mkundef()); 105 + promise_mark_handled(then_result); 106 + } 107 + 108 + static ant_value_t stream_buffer_ctor(ant_t *js) { 109 + ant_value_t ns = js_esm_import_sync_cstr(js, "buffer", 6); 110 + if (is_err(ns)) return ns; 111 + return js_get(js, ns, "Buffer"); 112 + } 113 + 114 + static ant_value_t stream_make_buffer(ant_t *js, ant_value_t value, ant_value_t encoding) { 115 + ant_value_t buffer_ctor = stream_buffer_ctor(js); 116 + ant_value_t from_fn = 0; 117 + ant_value_t args[2]; 118 + 119 + if (is_err(buffer_ctor)) return buffer_ctor; 120 + from_fn = js_get(js, buffer_ctor, "from"); 121 + if (is_err(from_fn) || !is_callable(from_fn)) 122 + return js_mkerr(js, "Buffer.from is not available"); 123 + 124 + args[0] = value; 125 + args[1] = encoding; 126 + return stream_call(js, from_fn, buffer_ctor, args, 2, false); 127 + } 128 + 129 + static ant_value_t stream_normalize_chunk( 130 + ant_t *js, 131 + ant_value_t chunk, 132 + bool object_mode, 133 + ant_value_t encoding 134 + ) { 135 + ant_value_t str_val = 0; 136 + 137 + if (object_mode || is_null(chunk) || is_undefined(chunk) || vtype(chunk) == T_TYPEDARRAY) 138 + return chunk; 139 + 140 + if (vtype(chunk) == T_STR) return stream_make_buffer(js, chunk, encoding); 141 + 142 + str_val = js_tostring_val(js, chunk); 143 + if (is_err(str_val)) return str_val; 144 + return stream_make_buffer(js, str_val, encoding); 145 + } 146 + 147 + static ant_value_t stream_readable_state(ant_t *js, ant_value_t stream_obj) { 148 + return js_get(js, stream_obj, "_readableState"); 149 + } 150 + 151 + static ant_value_t stream_writable_state(ant_t *js, ant_value_t stream_obj) { 152 + return js_get(js, stream_obj, "_writableState"); 153 + } 154 + 155 + static ant_value_t stream_pipes(ant_t *js, ant_value_t stream_obj) { 156 + return js_get(js, stream_obj, "_pipes"); 157 + } 158 + 159 + static ant_value_t stream_readable_buffer(ant_t *js, ant_value_t stream_obj) { 160 + ant_value_t state = stream_readable_state(js, stream_obj); 161 + if (!is_object_type(state)) return js_mkundef(); 162 + return js_get(js, state, "buffer"); 163 + } 164 + 165 + static ant_offset_t stream_readable_buffer_head(ant_t *js, ant_value_t stream_obj) { 166 + ant_value_t state = stream_readable_state(js, stream_obj); 167 + ant_value_t head = is_object_type(state) ? js_get(js, state, "bufferHead") : js_mkundef(); 168 + return vtype(head) == T_NUM ? (ant_offset_t)js_getnum(head) : 0; 169 + } 170 + 171 + static void stream_set_readable_buffer_head(ant_t *js, ant_value_t stream_obj, ant_offset_t head) { 172 + ant_value_t state = stream_readable_state(js, stream_obj); 173 + if (is_object_type(state)) js_set(js, state, "bufferHead", js_mknum((double)head)); 174 + } 175 + 176 + static ant_offset_t stream_readable_buffer_len(ant_t *js, ant_value_t stream_obj) { 177 + ant_value_t buffer = stream_readable_buffer(js, stream_obj); 178 + ant_offset_t head = stream_readable_buffer_head(js, stream_obj); 179 + ant_offset_t len = vtype(buffer) == T_ARR ? js_arr_len(js, buffer) : 0; 180 + return len > head ? len - head : 0; 181 + } 182 + 183 + static void stream_compact_readable_buffer(ant_t *js, ant_value_t stream_obj) { 184 + ant_value_t state = stream_readable_state(js, stream_obj); 185 + ant_value_t buffer = stream_readable_buffer(js, stream_obj); 186 + ant_offset_t head = stream_readable_buffer_head(js, stream_obj); 187 + ant_offset_t len = vtype(buffer) == T_ARR ? js_arr_len(js, buffer) : 0; 188 + ant_value_t compact = 0; 189 + 190 + if (!is_object_type(state) || vtype(buffer) != T_ARR) return; 191 + if (head == 0) return; 192 + 193 + if (head >= len) { 194 + compact = js_mkarr(js); 195 + js_set(js, state, "buffer", compact); 196 + js_set(js, state, "bufferHead", js_mknum(0)); 197 + return; 198 + } 199 + 200 + if (head <= 32 && head * 2 < len) return; 201 + compact = js_mkarr(js); 202 + for (ant_offset_t i = head; i < len; i++) js_arr_push(js, compact, js_arr_get(js, buffer, i)); 203 + 204 + js_set(js, state, "buffer", compact); 205 + js_set(js, state, "bufferHead", js_mknum(0)); 206 + } 207 + 208 + static void stream_buffer_push(ant_t *js, ant_value_t stream_obj, ant_value_t value) { 209 + ant_value_t state = stream_readable_state(js, stream_obj); 210 + ant_value_t buffer = stream_readable_buffer(js, stream_obj); 211 + 212 + if (!is_object_type(state) || vtype(buffer) != T_ARR) return; 213 + js_arr_push(js, buffer, value); 214 + } 215 + 216 + static ant_value_t stream_buffer_shift(ant_t *js, ant_value_t stream_obj) { 217 + ant_value_t buffer = stream_readable_buffer(js, stream_obj); 218 + ant_offset_t head = stream_readable_buffer_head(js, stream_obj); 219 + ant_offset_t len = vtype(buffer) == T_ARR ? js_arr_len(js, buffer) : 0; 220 + ant_value_t value = js_mkundef(); 221 + 222 + if (vtype(buffer) != T_ARR || head >= len) return js_mkundef(); 223 + value = js_arr_get(js, buffer, head); 224 + stream_set_readable_buffer_head(js, stream_obj, head + 1); 225 + stream_compact_readable_buffer(js, stream_obj); 226 + return value; 227 + } 228 + 229 + static bool stream_listener_count_positive(ant_t *js, ant_value_t target, const char *event_name) { 230 + ant_value_t args[1]; 231 + ant_value_t result = 0; 232 + 233 + args[0] = js_mkstr(js, event_name, strlen(event_name)); 234 + result = stream_call_prop(js, target, "listenerCount", args, 1); 235 + return vtype(result) == T_NUM && js_getnum(result) > 0; 236 + } 237 + 238 + static void stream_remove_listener( 239 + ant_t *js, 240 + ant_value_t target, 241 + const char *event_name, 242 + ant_value_t listener 243 + ) { 244 + ant_value_t args[2]; 245 + args[0] = js_mkstr(js, event_name, strlen(event_name)); 246 + args[1] = listener; 247 + stream_call_prop(js, target, "removeListener", args, 2); 248 + } 249 + 250 + static ant_value_t stream_get_option(ant_t *js, ant_value_t options, const char *name) { 251 + if (!is_object_type(options)) return js_mkundef(); 252 + return js_get(js, options, name); 253 + } 254 + 255 + static ant_value_t stream_make_base_object(ant_t *js, ant_value_t proto) { 256 + ant_value_t obj = js_mkobj(js); 257 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 258 + js_set_native_tag(obj, STREAM_NATIVE_TAG); 259 + return obj; 260 + } 261 + 262 + static void stream_init_base(ant_t *js, ant_value_t obj, ant_value_t raw_options) { 263 + ant_value_t pipes = js_mkarr(js); 264 + js_set(js, obj, "readable", js_true); 265 + js_set(js, obj, "writable", js_true); 266 + js_set(js, obj, "destroyed", js_false); 267 + js_set(js, obj, "_paused", js_false); 268 + js_set(js, obj, "_pipes", pipes); 269 + js_set(js, obj, "_streamOptions", stream_truthy_or_object(js, raw_options)); 270 + } 271 + 272 + static void stream_init_readable(ant_t *js, ant_value_t obj, ant_value_t raw_options) { 273 + ant_value_t options = is_object_type(raw_options) ? raw_options : js_mkobj(js); 274 + ant_value_t state = js_mkobj(js); 275 + ant_value_t read_fn = stream_get_option(js, options, "read"); 276 + ant_value_t hwm = stream_get_option(js, options, "highWaterMark"); 277 + double high_water_mark = (vtype(hwm) == T_NUM && js_getnum(hwm) > 0) ? js_getnum(hwm) : 16384.0; 278 + 279 + stream_init_base(js, obj, raw_options); 280 + js_set(js, obj, "readable", js_true); 281 + js_set(js, obj, "writable", js_false); 282 + js_set(js, obj, "readableEnded", js_false); 283 + 284 + js_set(js, state, "objectMode", js_bool(js_truthy(js, stream_get_option(js, options, "objectMode")))); 285 + js_set(js, state, "ended", js_false); 286 + js_set(js, state, "endEmitted", js_false); 287 + js_set(js, state, "flowing", js_false); 288 + js_set(js, state, "reading", js_false); 289 + js_set(js, state, "highWaterMark", js_mknum(high_water_mark)); 290 + js_set(js, state, "buffer", js_mkarr(js)); 291 + js_set(js, state, "bufferHead", js_mknum(0)); 292 + js_set(js, obj, "_readableState", state); 293 + 294 + if (is_callable(read_fn)) js_set(js, obj, "_read", read_fn); 295 + } 296 + 297 + static void stream_init_writable(ant_t *js, ant_value_t obj, ant_value_t raw_options) { 298 + ant_value_t options = is_object_type(raw_options) ? raw_options : js_mkobj(js); 299 + ant_value_t state = js_mkobj(js); 300 + bool object_mode = js_truthy(js, stream_get_option(js, options, "objectMode")) 301 + || js_truthy(js, stream_get_option(js, options, "writableObjectMode")); 302 + ant_value_t write_fn = stream_get_option(js, options, "write"); 303 + ant_value_t final_fn = stream_get_option(js, options, "final"); 304 + ant_value_t destroy_fn = stream_get_option(js, options, "destroy"); 305 + 306 + stream_init_base(js, obj, raw_options); 307 + js_set(js, obj, "readable", js_false); 308 + js_set(js, obj, "writable", js_true); 309 + js_set(js, obj, "writableEnded", js_false); 310 + js_set(js, obj, "writableFinished", js_false); 311 + 312 + js_set(js, state, "objectMode", js_bool(object_mode)); 313 + js_set(js, state, "finished", js_false); 314 + js_set(js, state, "ended", js_false); 315 + js_set(js, obj, "_writableState", state); 316 + 317 + if (is_callable(write_fn)) js_set(js, obj, "_write", write_fn); 318 + if (is_callable(final_fn)) js_set(js, obj, "_final", final_fn); 319 + if (is_callable(destroy_fn)) js_set(js, obj, "_destroy", destroy_fn); 320 + } 321 + 322 + static ant_value_t stream_construct( 323 + ant_t *js, 324 + ant_value_t base_proto, 325 + ant_value_t raw_options, 326 + void (*init_fn)(ant_t *, ant_value_t, ant_value_t) 327 + ) { 328 + ant_value_t proto = js_instance_proto_from_new_target(js, base_proto); 329 + ant_value_t obj = stream_make_base_object(js, is_object_type(proto) ? proto : base_proto); 330 + init_fn(js, obj, raw_options); 331 + return obj; 332 + } 333 + 334 + static ant_value_t stream_emit_named(ant_t *js, ant_value_t stream_obj, const char *event_name) { 335 + return js_bool(eventemitter_emit_args(js, stream_obj, event_name, NULL, 0)); 336 + } 337 + 338 + static void stream_emit_error(ant_t *js, ant_value_t stream_obj, ant_value_t error) { 339 + ant_value_t args[1]; 340 + args[0] = error; 341 + eventemitter_emit_args(js, stream_obj, "error", args, 1); 342 + } 343 + 344 + static ant_value_t stream_readable_maybe_read(ant_t *js, ant_value_t stream_obj); 345 + static ant_value_t stream_readable_flush(ant_t *js, ant_value_t stream_obj); 346 + static ant_value_t stream_readable_push_value(ant_t *js, ant_value_t stream_obj, ant_value_t chunk, ant_value_t encoding); 347 + 348 + static ant_value_t js_stream_pause(ant_t *js, ant_value_t *args, int nargs) { 349 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "stream"); 350 + if (is_err(stream_obj)) return stream_obj; 351 + 352 + js_set(js, stream_obj, "_paused", js_true); 353 + stream_emit_named(js, stream_obj, "pause"); 354 + return stream_obj; 355 + } 356 + 357 + static ant_value_t js_stream_resume(ant_t *js, ant_value_t *args, int nargs) { 358 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "stream"); 359 + if (is_err(stream_obj)) return stream_obj; 360 + 361 + js_set(js, stream_obj, "_paused", js_false); 362 + stream_emit_named(js, stream_obj, "resume"); 363 + return stream_obj; 364 + } 365 + 366 + static ant_value_t js_stream_is_paused(ant_t *js, ant_value_t *args, int nargs) { 367 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "stream"); 368 + ant_value_t paused = 0; 369 + if (is_err(stream_obj)) return stream_obj; 370 + paused = js_get(js, stream_obj, "_paused"); 371 + return js_bool(js_truthy(js, paused)); 372 + } 373 + 374 + static void stream_pipe_remove_state(ant_t *js, ant_value_t source, ant_value_t state_obj) { 375 + ant_value_t pipes = stream_pipes(js, source); 376 + ant_offset_t len = vtype(pipes) == T_ARR ? js_arr_len(js, pipes) : 0; 377 + ant_value_t next = js_mkarr(js); 378 + 379 + for (ant_offset_t i = 0; i < len; i++) { 380 + ant_value_t item = js_arr_get(js, pipes, i); 381 + if (item != state_obj) js_arr_push(js, next, item); 382 + } 383 + 384 + js_set(js, source, "_pipes", next); 385 + } 386 + 387 + static void stream_pipe_cleanup(ant_t *js, ant_value_t state_obj) { 388 + ant_value_t cleaned = js_get(js, state_obj, "cleaned"); 389 + ant_value_t source = js_get(js, state_obj, "source"); 390 + ant_value_t dest = js_get(js, state_obj, "dest"); 391 + ant_value_t on_data = js_get(js, state_obj, "onData"); 392 + ant_value_t on_drain = js_get(js, state_obj, "onDrain"); 393 + ant_value_t on_end = js_get(js, state_obj, "onEnd"); 394 + ant_value_t on_close = js_get(js, state_obj, "onClose"); 395 + ant_value_t on_error = js_get(js, state_obj, "onError"); 396 + 397 + if (js_truthy(js, cleaned)) return; 398 + js_set(js, state_obj, "cleaned", js_true); 399 + 400 + if (stream_is_instance(source)) { 401 + stream_remove_listener(js, source, "data", on_data); 402 + stream_remove_listener(js, source, "end", on_end); 403 + stream_remove_listener(js, source, "close", on_close); 404 + stream_remove_listener(js, source, "error", on_error); 405 + stream_pipe_remove_state(js, source, state_obj); 406 + } 407 + 408 + if (is_object_type(dest)) 409 + stream_remove_listener(js, dest, "drain", on_drain); 410 + } 411 + 412 + static ant_value_t stream_pipe_on_data(ant_t *js, ant_value_t *args, int nargs) { 413 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 414 + ant_value_t source = js_get(js, state_obj, "source"); 415 + ant_value_t dest = js_get(js, state_obj, "dest"); 416 + ant_value_t result = js_mkundef(); 417 + 418 + if (!is_object_type(dest)) return js_mkundef(); 419 + result = stream_call_prop(js, dest, "write", nargs > 0 ? &args[0] : NULL, nargs > 0 ? 1 : 0); 420 + if (is_err(result)) return result; 421 + 422 + if (result == js_false) stream_call_prop(js, source, "pause", NULL, 0); 423 + return js_mkundef(); 424 + } 425 + 426 + static ant_value_t stream_pipe_on_drain(ant_t *js, ant_value_t *args, int nargs) { 427 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 428 + ant_value_t source = js_get(js, state_obj, "source"); 429 + stream_call_prop(js, source, "resume", NULL, 0); 430 + return js_mkundef(); 431 + } 432 + 433 + static ant_value_t stream_pipe_on_end(ant_t *js, ant_value_t *args, int nargs) { 434 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 435 + ant_value_t dest = js_get(js, state_obj, "dest"); 436 + bool end_dest = js_truthy(js, js_get(js, state_obj, "end")); 437 + stream_pipe_cleanup(js, state_obj); 438 + if (end_dest) stream_call_prop(js, dest, "end", NULL, 0); 439 + return js_mkundef(); 440 + } 441 + 442 + static ant_value_t stream_pipe_on_close(ant_t *js, ant_value_t *args, int nargs) { 443 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 444 + stream_pipe_cleanup(js, state_obj); 445 + return js_mkundef(); 446 + } 447 + 448 + static ant_value_t stream_pipe_on_error(ant_t *js, ant_value_t *args, int nargs) { 449 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 450 + ant_value_t dest = js_get(js, state_obj, "dest"); 451 + stream_pipe_cleanup(js, state_obj); 452 + if (is_object_type(dest) && stream_listener_count_positive(js, dest, "error") && nargs > 0) 453 + eventemitter_emit_args(js, dest, "error", &args[0], 1); 454 + return js_mkundef(); 455 + } 456 + 457 + static ant_value_t js_stream_pipe(ant_t *js, ant_value_t *args, int nargs) { 458 + ant_value_t source = stream_require_this(js, js_getthis(js), "stream"); 459 + ant_value_t options = nargs > 1 ? args[1] : js_mkundef(); 460 + ant_value_t state_obj = 0; 461 + ant_value_t readable_state = 0; 462 + bool end_dest = true; 463 + 464 + if (is_err(source)) return source; 465 + if (nargs < 1 || !is_object_type(args[0])) return js_mkerr(js, "pipe requires a destination stream"); 466 + if (is_object_type(options)) { 467 + ant_value_t end_val = js_get(js, options, "end"); 468 + if (!is_undefined(end_val)) end_dest = end_val != js_false; 469 + } 470 + 471 + state_obj = js_mkobj(js); 472 + js_set(js, state_obj, "source", source); 473 + js_set(js, state_obj, "dest", args[0]); 474 + js_set(js, state_obj, "end", js_bool(end_dest)); 475 + js_set(js, state_obj, "cleaned", js_false); 476 + js_set(js, state_obj, "onData", js_heavy_mkfun(js, stream_pipe_on_data, state_obj)); 477 + js_set(js, state_obj, "onDrain", js_heavy_mkfun(js, stream_pipe_on_drain, state_obj)); 478 + js_set(js, state_obj, "onEnd", js_heavy_mkfun(js, stream_pipe_on_end, state_obj)); 479 + js_set(js, state_obj, "onClose", js_heavy_mkfun(js, stream_pipe_on_close, state_obj)); 480 + js_set(js, state_obj, "onError", js_heavy_mkfun(js, stream_pipe_on_error, state_obj)); 481 + 482 + js_arr_push(js, stream_pipes(js, source), state_obj); 483 + eventemitter_add_listener(js, source, "data", js_get(js, state_obj, "onData"), false); 484 + eventemitter_add_listener(js, source, "end", js_get(js, state_obj, "onEnd"), true); 485 + eventemitter_add_listener(js, source, "close", js_get(js, state_obj, "onClose"), true); 486 + eventemitter_add_listener(js, source, "error", js_get(js, state_obj, "onError"), false); 487 + eventemitter_add_listener(js, args[0], "drain", js_get(js, state_obj, "onDrain"), false); 488 + eventemitter_emit_args(js, args[0], "pipe", &source, 1); 489 + readable_state = stream_readable_state(js, source); 490 + if (is_object_type(readable_state)) js_set(js, readable_state, "flowing", js_true); 491 + stream_call_prop(js, source, "resume", NULL, 0); 492 + 493 + return args[0]; 494 + } 495 + 496 + static ant_value_t js_stream_unpipe(ant_t *js, ant_value_t *args, int nargs) { 497 + ant_value_t source = stream_require_this(js, js_getthis(js), "stream"); 498 + ant_value_t pipes = 0; 499 + ant_value_t matches = 0; 500 + ant_offset_t len = 0; 501 + ant_value_t dest = nargs > 0 ? args[0] : js_mkundef(); 502 + 503 + if (is_err(source)) return source; 504 + pipes = stream_pipes(js, source); 505 + if (vtype(pipes) != T_ARR) return source; 506 + 507 + matches = js_mkarr(js); 508 + len = js_arr_len(js, pipes); 509 + for (ant_offset_t i = 0; i < len; i++) { 510 + ant_value_t state_obj = js_arr_get(js, pipes, i); 511 + ant_value_t entry_dest = js_get(js, state_obj, "dest"); 512 + if (!is_object_type(dest) || entry_dest == dest) js_arr_push(js, matches, state_obj); 513 + } 514 + 515 + len = js_arr_len(js, matches); 516 + for (ant_offset_t i = 0; i < len; i++) stream_pipe_cleanup(js, js_arr_get(js, matches, i)); 517 + return source; 518 + } 519 + 520 + static ant_value_t stream_destroy_done(ant_t *js, ant_value_t *args, int nargs) { 521 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 522 + ant_value_t stream_obj = js_get(js, state_obj, "stream"); 523 + ant_value_t destroyed_err = (nargs > 0) ? args[0] : js_mkundef(); 524 + if (!is_null(destroyed_err) && !is_undefined(destroyed_err)) stream_emit_error(js, stream_obj, destroyed_err); 525 + stream_emit_named(js, stream_obj, "close"); 526 + return js_mkundef(); 527 + } 528 + 529 + static ant_value_t stream_once_call(ant_t *js, ant_value_t *args, int nargs) { 530 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 531 + ant_value_t fn = js_get(js, state_obj, "fn"); 532 + ant_value_t this_val = js_get(js, state_obj, "thisVal"); 533 + ant_value_t called = js_get(js, state_obj, "called"); 534 + 535 + if (js_truthy(js, called)) return js_mkundef(); 536 + js_set(js, state_obj, "called", js_true); 537 + return stream_call(js, fn, this_val, args, nargs, false); 538 + } 539 + 540 + static ant_value_t stream_make_once(ant_t *js, ant_value_t fn, ant_value_t this_val) { 541 + ant_value_t state_obj = js_mkobj(js); 542 + js_set(js, state_obj, "fn", fn); 543 + js_set(js, state_obj, "thisVal", this_val); 544 + js_set(js, state_obj, "called", js_false); 545 + return js_heavy_mkfun(js, stream_once_call, state_obj); 546 + } 547 + 548 + static ant_value_t js_stream_destroy(ant_t *js, ant_value_t *args, int nargs) { 549 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "stream"); 550 + ant_value_t destroy_fn = 0; 551 + ant_value_t done_state = 0; 552 + ant_value_t done = 0; 553 + ant_value_t destroy_args[2]; 554 + ant_value_t error = nargs > 0 ? args[0] : js_mkundef(); 555 + ant_value_t result = 0; 556 + 557 + if (is_err(stream_obj)) return stream_obj; 558 + if (js_truthy(js, js_get(js, stream_obj, "destroyed"))) return stream_obj; 559 + 560 + js_set(js, stream_obj, "destroyed", js_true); 561 + 562 + done_state = js_mkobj(js); 563 + js_set(js, done_state, "stream", stream_obj); 564 + done = stream_make_once(js, js_heavy_mkfun(js, stream_destroy_done, done_state), js_mkundef()); 565 + destroy_fn = js_get(js, stream_obj, "_destroy"); 566 + 567 + if (is_callable(destroy_fn)) { 568 + destroy_args[0] = is_undefined(error) ? js_mknull() : error; 569 + destroy_args[1] = done; 570 + result = stream_call(js, destroy_fn, stream_obj, destroy_args, 2, false); 571 + return is_err(result) ? result : stream_obj; 572 + } 573 + 574 + destroy_args[0] = is_undefined(error) ? js_mknull() : error; 575 + stream_call_callback(js, done, destroy_args, 1); 576 + return stream_obj; 577 + } 578 + 579 + static ant_value_t js_readable__read(ant_t *js, ant_value_t *args, int nargs) { 580 + return js_mkundef(); 581 + } 582 + 583 + static ant_value_t stream_readable_start_flowing(ant_t *js, ant_value_t *args, int nargs) { 584 + ant_value_t stream_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 585 + ant_value_t state = stream_readable_state(js, stream_obj); 586 + 587 + if (!is_object_type(state)) return js_mkundef(); 588 + if (!js_truthy(js, js_get(js, state, "flowing"))) return js_mkundef(); 589 + 590 + { 591 + ant_value_t saved_this = js->this_val; 592 + js->this_val = stream_obj; 593 + js_stream_resume(js, NULL, 0); 594 + js->this_val = saved_this; 595 + } 596 + stream_readable_maybe_read(js, stream_obj); 597 + stream_readable_flush(js, stream_obj); 598 + return js_mkundef(); 599 + } 600 + 601 + static ant_value_t stream_readable_flush(ant_t *js, ant_value_t stream_obj) { 602 + ant_value_t state = stream_readable_state(js, stream_obj); 603 + 604 + if (!is_object_type(state)) return js_mkundef(); 605 + 606 + while (js_truthy(js, js_get(js, state, "flowing")) && stream_readable_buffer_len(js, stream_obj) > 0) { 607 + ant_value_t chunk = stream_buffer_shift(js, stream_obj); 608 + eventemitter_emit_args(js, stream_obj, "data", &chunk, 1); 609 + } 610 + 611 + if ( 612 + js_truthy(js, js_get(js, state, "ended")) && 613 + stream_readable_buffer_len(js, stream_obj) == 0 && 614 + !js_truthy(js, js_get(js, state, "endEmitted")) 615 + ) { 616 + js_set(js, state, "endEmitted", js_true); 617 + js_set(js, stream_obj, "readableEnded", js_true); 618 + stream_emit_named(js, stream_obj, "end"); 619 + stream_emit_named(js, stream_obj, "close"); 620 + } 621 + 622 + return js_mkundef(); 623 + } 624 + 625 + static ant_value_t stream_readable_maybe_read(ant_t *js, ant_value_t stream_obj) { 626 + ant_value_t state = stream_readable_state(js, stream_obj); 627 + ant_value_t read_fn = 0; 628 + ant_value_t args[1]; 629 + 630 + if (!is_object_type(state)) return js_mkundef(); 631 + if (js_truthy(js, js_get(js, stream_obj, "destroyed"))) return js_mkundef(); 632 + if (js_truthy(js, js_get(js, state, "reading"))) return js_mkundef(); 633 + if (js_truthy(js, js_get(js, state, "ended"))) return js_mkundef(); 634 + if (stream_readable_buffer_len(js, stream_obj) > 0) return js_mkundef(); 635 + 636 + read_fn = js_get(js, stream_obj, "_read"); 637 + js_set(js, state, "reading", js_true); 638 + args[0] = js_get(js, state, "highWaterMark"); 639 + if (is_callable(read_fn)) stream_call(js, read_fn, stream_obj, args, 1, false); 640 + js_set(js, state, "reading", js_false); 641 + return js_mkundef(); 642 + } 643 + 644 + static ant_value_t stream_readable_push_value( 645 + ant_t *js, 646 + ant_value_t stream_obj, 647 + ant_value_t chunk, 648 + ant_value_t encoding 649 + ) { 650 + ant_value_t state = stream_readable_state(js, stream_obj); 651 + ant_value_t normalized = 0; 652 + 653 + if (!is_object_type(state)) return js_false; 654 + if (js_truthy(js, js_get(js, stream_obj, "destroyed"))) return js_false; 655 + 656 + if (is_null(chunk)) { 657 + js_set(js, state, "ended", js_true); 658 + stream_readable_flush(js, stream_obj); 659 + return js_false; 660 + } 661 + 662 + normalized = stream_normalize_chunk( 663 + js, 664 + chunk, 665 + js_truthy(js, js_get(js, state, "objectMode")), 666 + is_undefined(encoding) ? js_mkstr(js, "utf8", 4) : encoding 667 + ); 668 + if (is_err(normalized)) return normalized; 669 + 670 + stream_buffer_push(js, stream_obj, normalized); 671 + if (js_truthy(js, js_get(js, state, "flowing"))) stream_readable_flush(js, stream_obj); 672 + return js_bool(js_truthy(js, js_get(js, state, "flowing"))); 673 + } 674 + 675 + static ant_value_t js_readable_push(ant_t *js, ant_value_t *args, int nargs) { 676 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Readable"); 677 + ant_value_t chunk = nargs > 0 ? args[0] : js_mkundef(); 678 + ant_value_t encoding = nargs > 1 ? args[1] : js_mkundef(); 679 + if (is_err(stream_obj)) return stream_obj; 680 + return stream_readable_push_value(js, stream_obj, chunk, encoding); 681 + } 682 + 683 + static ant_value_t js_readable_read(ant_t *js, ant_value_t *args, int nargs) { 684 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Readable"); 685 + ant_value_t state = 0; 686 + ant_value_t chunk = 0; 687 + 688 + if (is_err(stream_obj)) return stream_obj; 689 + state = stream_readable_state(js, stream_obj); 690 + if (!is_object_type(state)) return js_mknull(); 691 + 692 + if (stream_readable_buffer_len(js, stream_obj) == 0) stream_readable_maybe_read(js, stream_obj); 693 + if (stream_readable_buffer_len(js, stream_obj) == 0) return js_mknull(); 694 + 695 + chunk = stream_buffer_shift(js, stream_obj); 696 + if (js_truthy(js, js_get(js, state, "flowing"))) stream_readable_flush(js, stream_obj); 697 + return chunk; 698 + } 699 + 700 + static ant_value_t js_readable_on(ant_t *js, ant_value_t *args, int nargs) { 701 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Readable"); 702 + ant_value_t key = 0; 703 + ant_value_t state = 0; 704 + 705 + if (is_err(stream_obj)) return stream_obj; 706 + if (nargs < 2) return js_mkerr(js, "on requires 2 arguments (event, listener)"); 707 + key = stream_event_key(js, args[0]); 708 + if (is_err(key)) return key; 709 + if (!eventemitter_add_listener_val(js, stream_obj, key, args[1], false)) 710 + return js_mkerr(js, "listener must be a function"); 711 + 712 + if (stream_key_is_cstr(js, key, "data")) { 713 + state = stream_readable_state(js, stream_obj); 714 + if (is_object_type(state)) js_set(js, state, "flowing", js_true); 715 + stream_schedule_microtask(js, stream_readable_start_flowing, stream_obj); 716 + } 717 + 718 + return stream_obj; 719 + } 720 + 721 + static ant_value_t js_readable_resume(ant_t *js, ant_value_t *args, int nargs) { 722 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Readable"); 723 + ant_value_t state = 0; 724 + if (is_err(stream_obj)) return stream_obj; 725 + 726 + state = stream_readable_state(js, stream_obj); 727 + if (is_object_type(state)) js_set(js, state, "flowing", js_true); 728 + js_stream_resume(js, NULL, 0); 729 + stream_readable_maybe_read(js, stream_obj); 730 + stream_readable_flush(js, stream_obj); 731 + return stream_obj; 732 + } 733 + 734 + static ant_value_t js_readable_pause(ant_t *js, ant_value_t *args, int nargs) { 735 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Readable"); 736 + ant_value_t state = 0; 737 + if (is_err(stream_obj)) return stream_obj; 738 + 739 + state = stream_readable_state(js, stream_obj); 740 + if (is_object_type(state)) js_set(js, state, "flowing", js_false); 741 + return js_stream_pause(js, args, nargs); 742 + } 743 + 744 + static ant_value_t js_writable__write(ant_t *js, ant_value_t *args, int nargs) { 745 + ant_value_t callback = nargs > 2 ? args[2] : js_mkundef(); 746 + stream_call_callback(js, callback, NULL, 0); 747 + return js_mkundef(); 748 + } 749 + 750 + static ant_value_t js_writable__final(ant_t *js, ant_value_t *args, int nargs) { 751 + ant_value_t callback = nargs > 0 ? args[0] : js_mkundef(); 752 + stream_call_callback(js, callback, NULL, 0); 753 + return js_mkundef(); 754 + } 755 + 756 + static ant_value_t stream_writable_write_done(ant_t *js, ant_value_t *args, int nargs) { 757 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 758 + ant_value_t stream_obj = js_get(js, state_obj, "stream"); 759 + ant_value_t callback = js_get(js, state_obj, "callback"); 760 + ant_value_t err = nargs > 0 ? args[0] : js_mkundef(); 761 + 762 + if (!is_undefined(err) && !is_null(err)) { 763 + ant_value_t destroy_args[1] = { err }; 764 + js_set(js, state_obj, "done", js_true); 765 + { 766 + ant_value_t saved_this = js->this_val; 767 + js->this_val = stream_obj; 768 + js_stream_destroy(js, destroy_args, 1); 769 + js->this_val = saved_this; 770 + } 771 + if (is_callable(callback)) stream_call_callback(js, callback, &err, 1); 772 + return js_mkundef(); 773 + } 774 + 775 + if (is_callable(callback)) stream_call_callback(js, callback, NULL, 0); 776 + stream_emit_named(js, stream_obj, "drain"); 777 + return js_mkundef(); 778 + } 779 + 780 + static ant_value_t js_writable_write(ant_t *js, ant_value_t *args, int nargs) { 781 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Writable"); 782 + ant_value_t state = 0; 783 + ant_value_t callback = js_mkundef(); 784 + ant_value_t encoding = js_mkstr(js, "utf8", 4); 785 + ant_value_t normalized = 0; 786 + ant_value_t write_fn = 0; 787 + ant_value_t done_state = 0; 788 + ant_value_t done = 0; 789 + ant_value_t write_args[3]; 790 + 791 + if (is_err(stream_obj)) return stream_obj; 792 + state = stream_writable_state(js, stream_obj); 793 + if (!is_object_type(state)) return js_false; 794 + 795 + if (nargs > 1 && is_callable(args[1])) callback = args[1]; 796 + else if (nargs > 1 && !is_undefined(args[1])) encoding = args[1]; 797 + if (nargs > 2 && is_callable(args[2])) callback = args[2]; 798 + 799 + if (js_truthy(js, js_get(js, stream_obj, "writableEnded")) || js_truthy(js, js_get(js, stream_obj, "destroyed"))) { 800 + ant_value_t err = js_mkerr(js, "write after end"); 801 + if (is_callable(callback)) stream_call_callback(js, callback, &err, 1); 802 + else stream_emit_error(js, stream_obj, err); 803 + return js_false; 804 + } 805 + 806 + normalized = stream_normalize_chunk( 807 + js, 808 + nargs > 0 ? args[0] : js_mkundef(), 809 + js_truthy(js, js_get(js, state, "objectMode")), 810 + encoding 811 + ); 812 + if (is_err(normalized)) return normalized; 813 + 814 + done_state = js_mkobj(js); 815 + js_set(js, done_state, "stream", stream_obj); 816 + js_set(js, done_state, "callback", callback); 817 + js_set(js, done_state, "done", js_false); 818 + done = stream_make_once(js, js_heavy_mkfun(js, stream_writable_write_done, done_state), js_mkundef()); 819 + write_fn = js_get(js, stream_obj, "_write"); 820 + 821 + write_args[0] = normalized; 822 + write_args[1] = encoding; 823 + write_args[2] = done; 824 + if (is_callable(write_fn)) { 825 + ant_value_t result = stream_call(js, write_fn, stream_obj, write_args, 3, false); 826 + if (is_err(result)) { 827 + ant_value_t err_args[1] = { result }; 828 + stream_call_callback(js, done, err_args, 1); 829 + return js_false; 830 + } 831 + } 832 + 833 + return js_bool(!js_truthy(js, js_get(js, stream_obj, "destroyed"))); 834 + } 835 + 836 + static ant_value_t stream_writable_end_done(ant_t *js, ant_value_t *args, int nargs) { 837 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 838 + ant_value_t stream_obj = js_get(js, state_obj, "stream"); 839 + ant_value_t callback = js_get(js, state_obj, "callback"); 840 + ant_value_t err = nargs > 0 ? args[0] : js_mkundef(); 841 + 842 + if (!is_undefined(err) && !is_null(err)) { 843 + ant_value_t destroy_args[1] = { err }; 844 + ant_value_t saved_this = js->this_val; 845 + js->this_val = stream_obj; 846 + js_stream_destroy(js, destroy_args, 1); 847 + js->this_val = saved_this; 848 + if (is_callable(callback)) stream_call_callback(js, callback, &err, 1); 849 + return js_mkundef(); 850 + } 851 + 852 + js_set(js, stream_obj, "writableFinished", js_true); 853 + js_set(js, stream_writable_state(js, stream_obj), "finished", js_true); 854 + stream_emit_named(js, stream_obj, "finish"); 855 + if (is_callable(callback)) stream_call_callback(js, callback, NULL, 0); 856 + if (!js_truthy(js, js_get(js, stream_obj, "readable"))) stream_emit_named(js, stream_obj, "close"); 857 + return js_mkundef(); 858 + } 859 + 860 + static ant_value_t js_writable_end(ant_t *js, ant_value_t *args, int nargs) { 861 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Writable"); 862 + ant_value_t callback = js_mkundef(); 863 + ant_value_t chunk = js_mkundef(); 864 + ant_value_t encoding = js_mkundef(); 865 + ant_value_t final_fn = 0; 866 + ant_value_t final_args[1]; 867 + ant_value_t done_state = 0; 868 + ant_value_t done = 0; 869 + 870 + if (is_err(stream_obj)) return stream_obj; 871 + 872 + if (nargs > 0 && is_callable(args[0])) callback = args[0]; 873 + else { 874 + if (nargs > 0) chunk = args[0]; 875 + if (nargs > 1 && is_callable(args[1])) callback = args[1]; 876 + else if (nargs > 1) encoding = args[1]; 877 + if (nargs > 2 && is_callable(args[2])) callback = args[2]; 878 + } 879 + 880 + if (!is_undefined(chunk) && !is_null(chunk)) { 881 + ant_value_t write_args[3]; 882 + ant_value_t saved_this = js->this_val; 883 + write_args[0] = chunk; 884 + write_args[1] = encoding; 885 + write_args[2] = js_mkundef(); 886 + js->this_val = stream_obj; 887 + js_writable_write(js, write_args, 3); 888 + js->this_val = saved_this; 889 + } 890 + 891 + if (js_truthy(js, js_get(js, stream_obj, "writableEnded"))) { 892 + if (is_callable(callback)) stream_call_callback(js, callback, NULL, 0); 893 + return stream_obj; 894 + } 895 + 896 + js_set(js, stream_obj, "writableEnded", js_true); 897 + js_set(js, stream_writable_state(js, stream_obj), "ended", js_true); 898 + 899 + done_state = js_mkobj(js); 900 + js_set(js, done_state, "stream", stream_obj); 901 + js_set(js, done_state, "callback", callback); 902 + done = stream_make_once(js, js_heavy_mkfun(js, stream_writable_end_done, done_state), js_mkundef()); 903 + 904 + final_fn = js_get(js, stream_obj, "_final"); 905 + final_args[0] = done; 906 + if (is_callable(final_fn)) stream_call(js, final_fn, stream_obj, final_args, 1, false); 907 + else stream_call_callback(js, done, NULL, 0); 908 + 909 + return stream_obj; 910 + } 911 + 912 + static ant_value_t js_transform__transform(ant_t *js, ant_value_t *args, int nargs) { 913 + ant_value_t callback = nargs > 2 ? args[2] : js_mkundef(); 914 + ant_value_t cb_args[2]; 915 + cb_args[0] = js_mknull(); 916 + cb_args[1] = nargs > 0 ? args[0] : js_mkundef(); 917 + stream_call_callback(js, callback, cb_args, 2); 918 + return js_mkundef(); 919 + } 920 + 921 + static ant_value_t stream_transform_write_callback(ant_t *js, ant_value_t *args, int nargs) { 922 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 923 + ant_value_t stream_obj = js_get(js, state_obj, "stream"); 924 + ant_value_t outer_callback = js_get(js, state_obj, "callback"); 925 + 926 + if (nargs > 0 && !is_null(args[0]) && !is_undefined(args[0])) { 927 + stream_call_callback(js, outer_callback, &args[0], 1); 928 + return js_mkundef(); 929 + } 930 + 931 + if (nargs > 1 && !is_null(args[1]) && !is_undefined(args[1])) 932 + stream_readable_push_value(js, stream_obj, args[1], js_mkundef()); 933 + 934 + stream_call_callback(js, outer_callback, NULL, 0); 935 + return js_mkundef(); 936 + } 937 + 938 + static ant_value_t js_transform__write(ant_t *js, ant_value_t *args, int nargs) { 939 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Transform"); 940 + ant_value_t transform_fn = 0; 941 + ant_value_t cb_state = 0; 942 + ant_value_t cb = 0; 943 + ant_value_t call_args[3]; 944 + 945 + if (is_err(stream_obj)) return stream_obj; 946 + transform_fn = js_get(js, stream_obj, "_transform"); 947 + 948 + cb_state = js_mkobj(js); 949 + js_set(js, cb_state, "stream", stream_obj); 950 + js_set(js, cb_state, "callback", nargs > 2 ? args[2] : js_mkundef()); 951 + cb = js_heavy_mkfun(js, stream_transform_write_callback, cb_state); 952 + 953 + call_args[0] = nargs > 0 ? args[0] : js_mkundef(); 954 + call_args[1] = nargs > 1 ? args[1] : js_mkstr(js, "utf8", 4); 955 + call_args[2] = cb; 956 + 957 + return stream_call(js, transform_fn, stream_obj, call_args, 3, false); 958 + } 959 + 960 + static ant_value_t stream_transform_final_callback(ant_t *js, ant_value_t *args, int nargs) { 961 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 962 + ant_value_t stream_obj = js_get(js, state_obj, "stream"); 963 + ant_value_t callback = js_get(js, state_obj, "callback"); 964 + 965 + if (nargs > 0 && !is_null(args[0]) && !is_undefined(args[0])) { 966 + stream_call_callback(js, callback, &args[0], 1); 967 + return js_mkundef(); 968 + } 969 + 970 + if (nargs > 1 && !is_null(args[1]) && !is_undefined(args[1])) 971 + stream_readable_push_value(js, stream_obj, args[1], js_mkundef()); 972 + stream_readable_push_value(js, stream_obj, js_mknull(), js_mkundef()); 973 + stream_call_callback(js, callback, NULL, 0); 974 + return js_mkundef(); 975 + } 976 + 977 + static ant_value_t js_transform__final(ant_t *js, ant_value_t *args, int nargs) { 978 + ant_value_t stream_obj = stream_require_this(js, js_getthis(js), "Transform"); 979 + ant_value_t flush_fn = 0; 980 + ant_value_t cb_state = 0; 981 + ant_value_t cb = 0; 982 + ant_value_t call_args[1]; 983 + 984 + if (is_err(stream_obj)) return stream_obj; 985 + flush_fn = js_get(js, stream_obj, "_flush"); 986 + if (!is_callable(flush_fn)) { 987 + stream_readable_push_value(js, stream_obj, js_mknull(), js_mkundef()); 988 + stream_call_callback(js, nargs > 0 ? args[0] : js_mkundef(), NULL, 0); 989 + return js_mkundef(); 990 + } 991 + 992 + cb_state = js_mkobj(js); 993 + js_set(js, cb_state, "stream", stream_obj); 994 + js_set(js, cb_state, "callback", nargs > 0 ? args[0] : js_mkundef()); 995 + cb = js_heavy_mkfun(js, stream_transform_final_callback, cb_state); 996 + call_args[0] = cb; 997 + return stream_call(js, flush_fn, stream_obj, call_args, 1, false); 998 + } 999 + 1000 + static ant_value_t js_passthrough__transform(ant_t *js, ant_value_t *args, int nargs) { 1001 + return js_transform__transform(js, args, nargs); 1002 + } 1003 + 1004 + static ant_value_t stream_finished_cleanup(ant_t *js, ant_value_t state_obj) { 1005 + ant_value_t stream_obj = js_get(js, state_obj, "stream"); 1006 + if (stream_is_instance(stream_obj) || is_object_type(stream_obj)) { 1007 + stream_remove_listener(js, stream_obj, "end", js_get(js, state_obj, "onFinish")); 1008 + stream_remove_listener(js, stream_obj, "finish", js_get(js, state_obj, "onFinish")); 1009 + stream_remove_listener(js, stream_obj, "close", js_get(js, state_obj, "onFinish")); 1010 + stream_remove_listener(js, stream_obj, "error", js_get(js, state_obj, "onError")); 1011 + } 1012 + return js_mkundef(); 1013 + } 1014 + 1015 + static ant_value_t stream_finished_fire(ant_t *js, ant_value_t state_obj, ant_value_t error) { 1016 + ant_value_t called = js_get(js, state_obj, "called"); 1017 + ant_value_t callback = js_get(js, state_obj, "callback"); 1018 + ant_value_t cb_args[1]; 1019 + 1020 + if (js_truthy(js, called)) return js_mkundef(); 1021 + js_set(js, state_obj, "called", js_true); 1022 + stream_finished_cleanup(js, state_obj); 1023 + 1024 + if (is_undefined(error)) stream_call_callback(js, callback, NULL, 0); 1025 + else { 1026 + cb_args[0] = error; 1027 + stream_call_callback(js, callback, cb_args, 1); 1028 + } 1029 + return js_mkundef(); 1030 + } 1031 + 1032 + static ant_value_t stream_finished_on_finish(ant_t *js, ant_value_t *args, int nargs) { 1033 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1034 + return stream_finished_fire(js, state_obj, js_mkundef()); 1035 + } 1036 + 1037 + static ant_value_t stream_finished_on_error(ant_t *js, ant_value_t *args, int nargs) { 1038 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1039 + ant_value_t error = nargs > 0 ? args[0] : js_mkundef(); 1040 + return stream_finished_fire(js, state_obj, error); 1041 + } 1042 + 1043 + static ant_value_t stream_finished_register(ant_t *js, ant_value_t stream_obj, ant_value_t callback) { 1044 + ant_value_t state_obj = js_mkobj(js); 1045 + ant_value_t on_finish = 0; 1046 + ant_value_t on_error = 0; 1047 + 1048 + if (!is_callable(callback)) callback = js_mkfun(stream_noop); 1049 + 1050 + js_set(js, state_obj, "stream", stream_obj); 1051 + js_set(js, state_obj, "callback", callback); 1052 + js_set(js, state_obj, "called", js_false); 1053 + on_finish = js_heavy_mkfun(js, stream_finished_on_finish, state_obj); 1054 + on_error = js_heavy_mkfun(js, stream_finished_on_error, state_obj); 1055 + js_set(js, state_obj, "onFinish", on_finish); 1056 + js_set(js, state_obj, "onError", on_error); 1057 + 1058 + eventemitter_add_listener(js, stream_obj, "end", on_finish, false); 1059 + eventemitter_add_listener(js, stream_obj, "finish", on_finish, false); 1060 + eventemitter_add_listener(js, stream_obj, "close", on_finish, false); 1061 + eventemitter_add_listener(js, stream_obj, "error", on_error, false); 1062 + return stream_obj; 1063 + } 1064 + 1065 + static ant_value_t js_stream_finished(ant_t *js, ant_value_t *args, int nargs) { 1066 + ant_value_t callback = nargs > 1 ? args[1] : js_mkundef(); 1067 + if (nargs < 1 || !is_object_type(args[0])) return js_mkerr(js, "finished requires a stream"); 1068 + return stream_finished_register(js, args[0], callback); 1069 + } 1070 + 1071 + static ant_value_t stream_pipeline_done(ant_t *js, ant_value_t *args, int nargs) { 1072 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1073 + ant_value_t callback = js_get(js, state_obj, "callback"); 1074 + ant_value_t called = js_get(js, state_obj, "called"); 1075 + ant_value_t cb_args[1]; 1076 + 1077 + if (js_truthy(js, called)) return js_mkundef(); 1078 + js_set(js, state_obj, "called", js_true); 1079 + 1080 + if (nargs > 0 && !is_undefined(args[0])) { 1081 + cb_args[0] = args[0]; 1082 + stream_call_callback(js, callback, cb_args, 1); 1083 + } else stream_call_callback(js, callback, NULL, 0); 1084 + return js_mkundef(); 1085 + } 1086 + 1087 + static ant_value_t stream_pipeline_error(ant_t *js, ant_value_t *args, int nargs) { 1088 + ant_value_t done = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1089 + if (nargs > 0 && !is_undefined(args[0])) stream_call_callback(js, done, &args[0], 1); 1090 + return js_mkundef(); 1091 + } 1092 + 1093 + static ant_value_t stream_pipeline_schedule_done(ant_t *js, ant_value_t *args, int nargs) { 1094 + ant_value_t done = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1095 + stream_call_callback(js, done, NULL, 0); 1096 + return js_mkundef(); 1097 + } 1098 + 1099 + static ant_value_t js_stream_pipeline(ant_t *js, ant_value_t *args, int nargs) { 1100 + int stream_count = nargs; 1101 + ant_value_t callback = js_mkundef(); 1102 + ant_value_t done_state = 0; 1103 + ant_value_t done = 0; 1104 + 1105 + if (nargs > 0 && is_callable(args[nargs - 1])) { 1106 + callback = args[nargs - 1]; 1107 + stream_count--; 1108 + } 1109 + 1110 + if (!is_callable(callback)) callback = js_mkfun(stream_noop); 1111 + if (stream_count <= 0) return js_mkundef(); 1112 + 1113 + done_state = js_mkobj(js); 1114 + js_set(js, done_state, "callback", callback); 1115 + js_set(js, done_state, "called", js_false); 1116 + done = js_heavy_mkfun(js, stream_pipeline_done, done_state); 1117 + 1118 + if (stream_count < 2) { 1119 + stream_schedule_microtask(js, stream_pipeline_schedule_done, done); 1120 + return args[0]; 1121 + } 1122 + 1123 + for (int i = 0; i < stream_count - 1; i++) { 1124 + ant_value_t error_cb = js_heavy_mkfun(js, stream_pipeline_error, done); 1125 + ant_value_t finished_args[2]; 1126 + 1127 + finished_args[0] = args[i]; 1128 + finished_args[1] = error_cb; 1129 + js_stream_finished(js, finished_args, 2); 1130 + 1131 + ant_value_t pipe_args[2]; 1132 + pipe_args[0] = args[i + 1]; 1133 + pipe_args[1] = js_mkundef(); 1134 + stream_call_prop(js, args[i], "pipe", pipe_args, 2); 1135 + } 1136 + 1137 + { 1138 + ant_value_t finished_args[2]; 1139 + finished_args[0] = args[stream_count - 1]; 1140 + finished_args[1] = done; 1141 + js_stream_finished(js, finished_args, 2); 1142 + } 1143 + 1144 + return args[stream_count - 1]; 1145 + } 1146 + 1147 + static ant_value_t stream_promise_callback(ant_t *js, ant_value_t *args, int nargs) { 1148 + ant_value_t promise = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1149 + if (nargs > 0 && !is_undefined(args[0])) js_reject_promise(js, promise, args[0]); 1150 + else js_resolve_promise(js, promise, js_mkundef()); 1151 + return js_mkundef(); 1152 + } 1153 + 1154 + static ant_value_t js_stream_promises_finished(ant_t *js, ant_value_t *args, int nargs) { 1155 + ant_value_t promise = js_mkpromise(js); 1156 + ant_value_t finished_args[2]; 1157 + if (nargs < 1 || !is_object_type(args[0])) { 1158 + js_reject_promise(js, promise, js_mkerr(js, "finished requires a stream")); 1159 + return promise; 1160 + } 1161 + finished_args[0] = args[0]; 1162 + finished_args[1] = js_heavy_mkfun(js, stream_promise_callback, promise); 1163 + js_stream_finished(js, finished_args, 2); 1164 + return promise; 1165 + } 1166 + 1167 + static ant_value_t js_stream_promises_pipeline(ant_t *js, ant_value_t *args, int nargs) { 1168 + ant_value_t promise = js_mkpromise(js); 1169 + ant_value_t *call_args = NULL; 1170 + 1171 + if (nargs <= 0) { 1172 + js_resolve_promise(js, promise, js_mkundef()); 1173 + return promise; 1174 + } 1175 + 1176 + call_args = malloc((size_t)(nargs + 1) * sizeof(*call_args)); 1177 + if (!call_args) { 1178 + js_reject_promise(js, promise, js_mkerr(js, "out of memory")); 1179 + return promise; 1180 + } 1181 + 1182 + for (int i = 0; i < nargs; i++) call_args[i] = args[i]; 1183 + call_args[nargs] = js_heavy_mkfun(js, stream_promise_callback, promise); 1184 + js_stream_pipeline(js, call_args, nargs + 1); 1185 + free(call_args); 1186 + return promise; 1187 + } 1188 + 1189 + static void stream_release_reader(ant_t *js, ant_value_t state_obj) { 1190 + ant_value_t reader = js_get(js, state_obj, "reader"); 1191 + if (!is_object_type(reader)) return; 1192 + stream_call_prop(js, reader, "releaseLock", NULL, 0); 1193 + } 1194 + 1195 + static ant_value_t stream_readable_from_step(ant_t *js, ant_value_t *args, int nargs); 1196 + 1197 + static void stream_readable_from_schedule(ant_t *js, ant_value_t state_obj) { 1198 + stream_schedule_microtask(js, stream_readable_from_step, state_obj); 1199 + } 1200 + 1201 + static ant_value_t stream_readable_from_fail(ant_t *js, ant_value_t state_obj, ant_value_t error) { 1202 + ant_value_t readable = js_get(js, state_obj, "readable"); 1203 + stream_release_reader(js, state_obj); 1204 + if (stream_is_instance(readable)) { 1205 + ant_value_t destroy_args[1] = { error }; 1206 + ant_value_t saved_this = js->this_val; 1207 + js->this_val = readable; 1208 + js_stream_destroy(js, destroy_args, 1); 1209 + js->this_val = saved_this; 1210 + } 1211 + return js_mkundef(); 1212 + } 1213 + 1214 + static ant_value_t stream_readable_from_handle_result(ant_t *js, ant_value_t *args, int nargs) { 1215 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1216 + ant_value_t readable = js_get(js, state_obj, "readable"); 1217 + ant_value_t result = nargs > 0 ? args[0] : js_mkundef(); 1218 + ant_value_t done = 0; 1219 + ant_value_t value = 0; 1220 + 1221 + if (js_truthy(js, js_get(js, readable, "destroyed"))) { 1222 + stream_release_reader(js, state_obj); 1223 + return js_mkundef(); 1224 + } 1225 + 1226 + if (!is_object_type(result)) return stream_readable_from_fail(js, state_obj, js_mkerr(js, "iterator step must be an object")); 1227 + done = js_get(js, result, "done"); 1228 + value = js_get(js, result, "value"); 1229 + if (js_truthy(js, done)) { 1230 + stream_release_reader(js, state_obj); 1231 + stream_readable_push_value(js, readable, js_mknull(), js_mkundef()); 1232 + return js_mkundef(); 1233 + } 1234 + 1235 + stream_readable_push_value(js, readable, value, js_mkundef()); 1236 + if (!js_truthy(js, js_get(js, readable, "destroyed"))) stream_readable_from_schedule(js, state_obj); 1237 + return js_mkundef(); 1238 + } 1239 + 1240 + static ant_value_t stream_readable_from_reject(ant_t *js, ant_value_t *args, int nargs) { 1241 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1242 + ant_value_t error = nargs > 0 ? args[0] : js_mkerr(js, "stream iteration failed"); 1243 + return stream_readable_from_fail(js, state_obj, error); 1244 + } 1245 + 1246 + static ant_value_t stream_readable_from_step(ant_t *js, ant_value_t *args, int nargs) { 1247 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1248 + ant_value_t readable = js_get(js, state_obj, "readable"); 1249 + ant_value_t mode = js_get(js, state_obj, "mode"); 1250 + ant_value_t iterator = js_get(js, state_obj, "iterator"); 1251 + 1252 + ant_value_t next_result = js_mkundef(); 1253 + ant_value_t on_resolve = js_heavy_mkfun(js, stream_readable_from_handle_result, state_obj); 1254 + ant_value_t on_reject = js_heavy_mkfun(js, stream_readable_from_reject, state_obj); 1255 + ant_value_t then_result = 0; 1256 + 1257 + if (js_truthy(js, js_get(js, readable, "destroyed"))) { 1258 + stream_release_reader(js, state_obj); 1259 + return js_mkundef(); 1260 + } 1261 + 1262 + if (stream_key_is_cstr(js, mode, "reader")) next_result = stream_call_prop(js, iterator, "read", NULL, 0); 1263 + else next_result = stream_call_prop(js, iterator, "next", NULL, 0); 1264 + 1265 + if (is_err(next_result)) return stream_readable_from_fail(js, state_obj, next_result); 1266 + if (vtype(next_result) == T_PROMISE) { 1267 + then_result = js_promise_then(js, next_result, on_resolve, on_reject); 1268 + promise_mark_handled(then_result); 1269 + return js_mkundef(); 1270 + } 1271 + 1272 + ant_value_t one_arg[1] = { next_result }; 1273 + return stream_readable_from_handle_result(js, one_arg, 1); 1274 + } 1275 + 1276 + static ant_value_t stream_readable_from_start(ant_t *js, ant_value_t *args, int nargs) { 1277 + ant_value_t state_obj = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1278 + ant_value_t readable = js_get(js, state_obj, "readable"); 1279 + ant_value_t source = js_get(js, state_obj, "source"); 1280 + 1281 + ant_value_t async_iter_fn = 0; 1282 + ant_value_t reader_fn = 0; 1283 + js_iter_t it; 1284 + 1285 + if (js_truthy(js, js_get(js, readable, "destroyed"))) return js_mkundef(); 1286 + 1287 + async_iter_fn = is_object_type(source) ? js_get_sym(js, source, get_asyncIterator_sym()) : js_mkundef(); 1288 + if (is_callable(async_iter_fn)) { 1289 + ant_value_t iterator = stream_call(js, async_iter_fn, source, NULL, 0, false); 1290 + if (is_err(iterator)) return stream_readable_from_fail(js, state_obj, iterator); 1291 + js_set(js, state_obj, "iterator", iterator); 1292 + js_set(js, state_obj, "mode", js_mkstr(js, "async", 5)); 1293 + stream_readable_from_schedule(js, state_obj); 1294 + return js_mkundef(); 1295 + } 1296 + 1297 + if (js_iter_open(js, source, &it)) { 1298 + ant_value_t value = 0; 1299 + while (js_iter_next(js, &it, &value)) { 1300 + if (js_truthy(js, js_get(js, readable, "destroyed"))) break; 1301 + stream_readable_push_value(js, readable, value, js_mkundef()); 1302 + } 1303 + js_iter_close(js, &it); 1304 + if (!js_truthy(js, js_get(js, readable, "destroyed"))) 1305 + stream_readable_push_value(js, readable, js_mknull(), js_mkundef()); 1306 + return js_mkundef(); 1307 + } 1308 + 1309 + reader_fn = is_object_type(source) ? js_get(js, source, "getReader") : js_mkundef(); 1310 + if (is_callable(reader_fn)) { 1311 + ant_value_t reader = stream_call(js, reader_fn, source, NULL, 0, false); 1312 + if (is_err(reader)) return stream_readable_from_fail(js, state_obj, reader); 1313 + js_set(js, state_obj, "reader", reader); 1314 + js_set(js, state_obj, "iterator", reader); 1315 + js_set(js, state_obj, "mode", js_mkstr(js, "reader", 6)); 1316 + stream_readable_from_schedule(js, state_obj); 1317 + return js_mkundef(); 1318 + } 1319 + 1320 + if (!is_undefined(source)) stream_readable_push_value(js, readable, source, js_mkundef()); 1321 + stream_readable_push_value(js, readable, js_mknull(), js_mkundef()); 1322 + 1323 + return js_mkundef(); 1324 + } 1325 + 1326 + static ant_value_t js_readable_from(ant_t *js, ant_value_t *args, int nargs) { 1327 + ant_value_t ctor_args[1]; 1328 + ant_value_t readable = 0; 1329 + ant_value_t state_obj = 0; 1330 + 1331 + ctor_args[0] = nargs > 1 ? args[1] : js_mkundef(); 1332 + readable = stream_construct(js, g_readable_proto, ctor_args[0], stream_init_readable); 1333 + if (is_err(readable)) return readable; 1334 + 1335 + state_obj = js_mkobj(js); 1336 + js_set(js, state_obj, "readable", readable); 1337 + js_set(js, state_obj, "source", nargs > 0 ? args[0] : js_mkundef()); 1338 + js_set(js, state_obj, "iterator", js_mkundef()); 1339 + js_set(js, state_obj, "reader", js_mkundef()); 1340 + js_set(js, state_obj, "mode", js_mkundef()); 1341 + stream_schedule_microtask(js, stream_readable_from_start, state_obj); 1342 + 1343 + return readable; 1344 + } 1345 + 1346 + static ant_value_t js_readable_from_web(ant_t *js, ant_value_t *args, int nargs) { 1347 + return js_readable_from(js, args, nargs); 1348 + } 1349 + 1350 + static ant_value_t js_stream_ctor(ant_t *js, ant_value_t *args, int nargs) { 1351 + return stream_construct(js, g_stream_proto, nargs > 0 ? args[0] : js_mkundef(), stream_init_base); 1352 + } 1353 + 1354 + static ant_value_t js_readable_ctor(ant_t *js, ant_value_t *args, int nargs) { 1355 + return stream_construct(js, g_readable_proto, nargs > 0 ? args[0] : js_mkundef(), stream_init_readable); 1356 + } 1357 + 1358 + static ant_value_t js_writable_ctor(ant_t *js, ant_value_t *args, int nargs) { 1359 + return stream_construct(js, g_writable_proto, nargs > 0 ? args[0] : js_mkundef(), stream_init_writable); 1360 + } 1361 + 1362 + static ant_value_t js_duplex_ctor(ant_t *js, ant_value_t *args, int nargs) { 1363 + ant_value_t proto = js_instance_proto_from_new_target(js, g_duplex_proto); 1364 + ant_value_t obj = stream_make_base_object(js, is_object_type(proto) ? proto : g_duplex_proto); 1365 + ant_value_t options = nargs > 0 ? args[0] : js_mkundef(); 1366 + ant_value_t options_obj = is_object_type(options) ? options : js_mkobj(js); 1367 + 1368 + stream_init_readable(js, obj, options); 1369 + stream_init_writable(js, obj, options); 1370 + 1371 + js_set(js, obj, "readable", js_true); 1372 + js_set(js, obj, "writable", js_true); 1373 + js_set(js, obj, "allowHalfOpen", js_bool(stream_get_option(js, options_obj, "allowHalfOpen") != js_false)); 1374 + 1375 + return obj; 1376 + } 1377 + 1378 + static ant_value_t js_transform_ctor(ant_t *js, ant_value_t *args, int nargs) { 1379 + ant_value_t proto = js_instance_proto_from_new_target(js, g_transform_proto); 1380 + ant_value_t obj = stream_make_base_object(js, is_object_type(proto) ? proto : g_transform_proto); 1381 + ant_value_t options = nargs > 0 ? args[0] : js_mkundef(); 1382 + ant_value_t options_obj = is_object_type(options) ? options : js_mkobj(js); 1383 + 1384 + ant_value_t transform_fn = 0; 1385 + ant_value_t flush_fn = 0; 1386 + 1387 + stream_init_readable(js, obj, options); 1388 + stream_init_writable(js, obj, options); 1389 + 1390 + js_set(js, obj, "readable", js_true); 1391 + js_set(js, obj, "writable", js_true); 1392 + js_set(js, obj, "allowHalfOpen", js_bool(stream_get_option(js, options_obj, "allowHalfOpen") != js_false)); 1393 + 1394 + transform_fn = stream_get_option(js, options_obj, "transform"); 1395 + flush_fn = stream_get_option(js, options_obj, "flush"); 1396 + 1397 + if (is_callable(transform_fn)) js_set(js, obj, "_transform", transform_fn); 1398 + if (is_callable(flush_fn)) js_set(js, obj, "_flush", flush_fn); 1399 + 1400 + return obj; 1401 + } 1402 + 1403 + static ant_value_t js_passthrough_ctor(ant_t *js, ant_value_t *args, int nargs) { 1404 + ant_value_t proto = js_instance_proto_from_new_target(js, g_passthrough_proto); 1405 + ant_value_t obj = stream_make_base_object(js, is_object_type(proto) ? proto : g_passthrough_proto); 1406 + ant_value_t options = nargs > 0 ? args[0] : js_mkundef(); 1407 + ant_value_t options_obj = is_object_type(options) ? options : js_mkobj(js); 1408 + 1409 + stream_init_readable(js, obj, options); 1410 + stream_init_writable(js, obj, options); 1411 + 1412 + js_set(js, obj, "readable", js_true); 1413 + js_set(js, obj, "writable", js_true); 1414 + js_set(js, obj, "allowHalfOpen", js_bool(stream_get_option(js, options_obj, "allowHalfOpen") != js_false)); 1415 + 1416 + return obj; 1417 + } 1418 + 1419 + void stream_init_constructors(ant_t *js) { 1420 + ant_value_t events = 0; 1421 + ant_value_t ee_ctor = 0; 1422 + ant_value_t ee_proto = 0; 1423 + 1424 + if (g_stream_ctor) return; 1425 + 1426 + events = events_library(js); 1427 + ee_ctor = js_get(js, events, "EventEmitter"); 1428 + ee_proto = js_get(js, ee_ctor, "prototype"); 1429 + 1430 + g_stream_proto = js_mkobj(js); 1431 + js_set_proto_init(g_stream_proto, ee_proto); 1432 + js_set(js, g_stream_proto, "pipe", js_mkfun(js_stream_pipe)); 1433 + js_set(js, g_stream_proto, "unpipe", js_mkfun(js_stream_unpipe)); 1434 + js_set(js, g_stream_proto, "pause", js_mkfun(js_stream_pause)); 1435 + js_set(js, g_stream_proto, "resume", js_mkfun(js_stream_resume)); 1436 + js_set(js, g_stream_proto, "isPaused", js_mkfun(js_stream_is_paused)); 1437 + js_set(js, g_stream_proto, "destroy", js_mkfun(js_stream_destroy)); 1438 + js_set_sym(js, g_stream_proto, get_toStringTag_sym(), js_mkstr(js, "Stream", 6)); 1439 + g_stream_ctor = js_make_ctor(js, js_stream_ctor, g_stream_proto, "Stream", 6); 1440 + 1441 + g_readable_proto = js_mkobj(js); 1442 + js_set_proto_init(g_readable_proto, g_stream_proto); 1443 + js_set(js, g_readable_proto, "_read", js_mkfun(js_readable__read)); 1444 + js_set(js, g_readable_proto, "push", js_mkfun(js_readable_push)); 1445 + js_set(js, g_readable_proto, "read", js_mkfun(js_readable_read)); 1446 + js_set(js, g_readable_proto, "on", js_mkfun(js_readable_on)); 1447 + js_set(js, g_readable_proto, "resume", js_mkfun(js_readable_resume)); 1448 + js_set(js, g_readable_proto, "pause", js_mkfun(js_readable_pause)); 1449 + js_set_sym(js, g_readable_proto, get_toStringTag_sym(), js_mkstr(js, "Readable", 8)); 1450 + g_readable_ctor = js_make_ctor(js, js_readable_ctor, g_readable_proto, "Readable", 8); 1451 + js_set(js, g_readable_ctor, "from", js_mkfun(js_readable_from)); 1452 + js_set(js, g_readable_ctor, "fromWeb", js_mkfun(js_readable_from_web)); 1453 + 1454 + g_writable_proto = js_mkobj(js); 1455 + js_set_proto_init(g_writable_proto, g_stream_proto); 1456 + js_set(js, g_writable_proto, "_write", js_mkfun(js_writable__write)); 1457 + js_set(js, g_writable_proto, "_final", js_mkfun(js_writable__final)); 1458 + js_set(js, g_writable_proto, "write", js_mkfun(js_writable_write)); 1459 + js_set(js, g_writable_proto, "end", js_mkfun(js_writable_end)); 1460 + js_set(js, g_writable_proto, "cork", js_mkfun(stream_noop)); 1461 + js_set(js, g_writable_proto, "uncork", js_mkfun(stream_noop)); 1462 + js_set_sym(js, g_writable_proto, get_toStringTag_sym(), js_mkstr(js, "Writable", 8)); 1463 + g_writable_ctor = js_make_ctor(js, js_writable_ctor, g_writable_proto, "Writable", 8); 1464 + 1465 + g_duplex_proto = js_mkobj(js); 1466 + js_set_proto_init(g_duplex_proto, g_readable_proto); 1467 + js_set(js, g_duplex_proto, "_write", js_mkfun(js_writable__write)); 1468 + js_set(js, g_duplex_proto, "_final", js_mkfun(js_writable__final)); 1469 + js_set(js, g_duplex_proto, "write", js_mkfun(js_writable_write)); 1470 + js_set(js, g_duplex_proto, "end", js_mkfun(js_writable_end)); 1471 + js_set(js, g_duplex_proto, "cork", js_mkfun(stream_noop)); 1472 + js_set(js, g_duplex_proto, "uncork", js_mkfun(stream_noop)); 1473 + js_set_sym(js, g_duplex_proto, get_toStringTag_sym(), js_mkstr(js, "Duplex", 6)); 1474 + g_duplex_ctor = js_make_ctor(js, js_duplex_ctor, g_duplex_proto, "Duplex", 6); 1475 + 1476 + g_transform_proto = js_mkobj(js); 1477 + js_set_proto_init(g_transform_proto, g_duplex_proto); 1478 + js_set(js, g_transform_proto, "_transform", js_mkfun(js_transform__transform)); 1479 + js_set(js, g_transform_proto, "_write", js_mkfun(js_transform__write)); 1480 + js_set(js, g_transform_proto, "_final", js_mkfun(js_transform__final)); 1481 + js_set_sym(js, g_transform_proto, get_toStringTag_sym(), js_mkstr(js, "Transform", 9)); 1482 + g_transform_ctor = js_make_ctor(js, js_transform_ctor, g_transform_proto, "Transform", 9); 1483 + 1484 + g_passthrough_proto = js_mkobj(js); 1485 + js_set_proto_init(g_passthrough_proto, g_transform_proto); 1486 + js_set(js, g_passthrough_proto, "_transform", js_mkfun(js_passthrough__transform)); 1487 + js_set_sym(js, g_passthrough_proto, get_toStringTag_sym(), js_mkstr(js, "PassThrough", 11)); 1488 + g_passthrough_ctor = js_make_ctor(js, js_passthrough_ctor, g_passthrough_proto, "PassThrough", 11); 1489 + 1490 + gc_register_root(&g_stream_proto); 1491 + gc_register_root(&g_stream_ctor); 1492 + gc_register_root(&g_readable_proto); 1493 + gc_register_root(&g_readable_ctor); 1494 + gc_register_root(&g_writable_proto); 1495 + gc_register_root(&g_writable_ctor); 1496 + gc_register_root(&g_duplex_proto); 1497 + gc_register_root(&g_duplex_ctor); 1498 + gc_register_root(&g_transform_proto); 1499 + gc_register_root(&g_transform_ctor); 1500 + gc_register_root(&g_passthrough_proto); 1501 + gc_register_root(&g_passthrough_ctor); 1502 + } 1503 + 1504 + ant_value_t stream_readable_constructor(ant_t *js) { 1505 + stream_init_constructors(js); 1506 + return g_readable_ctor; 1507 + } 1508 + 1509 + ant_value_t stream_writable_constructor(ant_t *js) { 1510 + stream_init_constructors(js); 1511 + return g_writable_ctor; 1512 + } 1513 + 1514 + ant_value_t stream_readable_prototype(ant_t *js) { 1515 + stream_init_constructors(js); 1516 + return g_readable_proto; 1517 + } 1518 + 1519 + ant_value_t stream_writable_prototype(ant_t *js) { 1520 + stream_init_constructors(js); 1521 + return g_writable_proto; 1522 + } 1523 + 1524 + ant_value_t stream_construct_readable(ant_t *js, ant_value_t base_proto, ant_value_t options) { 1525 + stream_init_constructors(js); 1526 + return stream_construct(js, base_proto, options, stream_init_readable); 1527 + } 1528 + 1529 + ant_value_t stream_construct_writable(ant_t *js, ant_value_t base_proto, ant_value_t options) { 1530 + stream_init_constructors(js); 1531 + return stream_construct(js, base_proto, options, stream_init_writable); 1532 + } 1533 + 1534 + void stream_init_readable_object(ant_t *js, ant_value_t obj, ant_value_t options) { 1535 + stream_init_constructors(js); 1536 + if (!is_object_type(obj)) return; 1537 + js_set_native_tag(obj, STREAM_NATIVE_TAG); 1538 + stream_init_readable(js, obj, options); 1539 + } 1540 + 1541 + void stream_init_writable_object(ant_t *js, ant_value_t obj, ant_value_t options) { 1542 + stream_init_constructors(js); 1543 + if (!is_object_type(obj)) return; 1544 + js_set_native_tag(obj, STREAM_NATIVE_TAG); 1545 + stream_init_writable(js, obj, options); 1546 + } 1547 + 1548 + ant_value_t stream_readable_push(ant_t *js, ant_value_t stream_obj, ant_value_t chunk, ant_value_t encoding) { 1549 + stream_init_constructors(js); 1550 + return stream_readable_push_value(js, stream_obj, chunk, encoding); 1551 + } 1552 + 1553 + ant_value_t stream_library(ant_t *js) { 1554 + ant_value_t lib = js_mkobj(js); 1555 + ant_value_t promises = js_mkobj(js); 1556 + stream_init_constructors(js); 1557 + 1558 + js_set(js, promises, "pipeline", js_mkfun(js_stream_promises_pipeline)); 1559 + js_set(js, promises, "finished", js_mkfun(js_stream_promises_finished)); 1560 + 1561 + js_set_module_default(js, lib, g_stream_ctor, "Stream"); 1562 + js_set(js, lib, "Readable", g_readable_ctor); 1563 + js_set(js, lib, "Writable", g_writable_ctor); 1564 + js_set(js, lib, "Duplex", g_duplex_ctor); 1565 + js_set(js, lib, "Transform", g_transform_ctor); 1566 + js_set(js, lib, "PassThrough", g_passthrough_ctor); 1567 + js_set(js, lib, "pipeline", js_mkfun(js_stream_pipeline)); 1568 + js_set(js, lib, "finished", js_mkfun(js_stream_finished)); 1569 + js_set(js, lib, "promises", promises); 1570 + 1571 + js_set(js, g_stream_ctor, "Readable", g_readable_ctor); 1572 + js_set(js, g_stream_ctor, "Writable", g_writable_ctor); 1573 + js_set(js, g_stream_ctor, "Duplex", g_duplex_ctor); 1574 + js_set(js, g_stream_ctor, "Transform", g_transform_ctor); 1575 + js_set(js, g_stream_ctor, "PassThrough", g_passthrough_ctor); 1576 + js_set(js, g_stream_ctor, "pipeline", js_get(js, lib, "pipeline")); 1577 + js_set(js, g_stream_ctor, "finished", js_get(js, lib, "finished")); 1578 + js_set(js, g_stream_ctor, "promises", promises); 1579 + 1580 + js_set(js, promises, "default", promises); 1581 + js_set_slot_wb(js, promises, SLOT_DEFAULT, promises); 1582 + js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "stream", 6)); 1583 + 1584 + return lib; 1585 + } 1586 + 1587 + ant_value_t stream_promises_library(ant_t *js) { 1588 + ant_value_t stream_ns = js_esm_import_sync_cstr(js, "stream", 6); 1589 + ant_value_t promises = 0; 1590 + 1591 + if (is_err(stream_ns)) return stream_ns; 1592 + promises = js_get(js, stream_ns, "promises"); 1593 + 1594 + if (!is_object_type(promises)) return js_mkerr(js, "stream.promises is not available"); 1595 + js_set(js, promises, "default", promises); 1596 + js_set_slot_wb(js, promises, SLOT_DEFAULT, promises); 1597 + 1598 + return promises; 1599 + }
+25 -15
src/modules/timer.c
··· 687 687 return timer_state.microtasks != NULL ? 1 : 0; 688 688 } 689 689 690 + static void timers_define_common(ant_t *js, ant_value_t obj) { 691 + js_set(js, obj, "setTimeout", js_mkfun(js_set_timeout)); 692 + js_set(js, obj, "clearTimeout", js_mkfun(js_clear_timeout)); 693 + js_set(js, obj, "setInterval", js_mkfun(js_set_interval)); 694 + js_set(js, obj, "clearInterval", js_mkfun(js_clear_timeout)); 695 + js_set(js, obj, "setImmediate", js_mkfun(js_set_immediate)); 696 + js_set(js, obj, "clearImmediate", js_mkfun(js_clear_immediate)); 697 + js_set(js, obj, "queueMicrotask", js_mkfun(js_queue_microtask)); 698 + } 699 + 700 + void init_timer_module() { 701 + ant_t *js = rt->js; 702 + timer_state.js = js; 703 + timers_define_common(js, js_glob(js)); 704 + } 705 + 706 + ant_value_t timers_library(ant_t *js) { 707 + ant_value_t lib = js_mkobj(js); 708 + 709 + timers_define_common(js, lib); 710 + js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "timers", 6)); 711 + 712 + return lib; 713 + } 714 + 690 715 // TODO: mostly stubbed 691 716 ant_value_t timers_promises_library(ant_t *js) { 692 717 ant_value_t lib = js_mkobj(js); ··· 701 726 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "timers/promises", 16)); 702 727 703 728 return lib; 704 - } 705 - 706 - void init_timer_module() { 707 - ant_t *js = rt->js; 708 - 709 - timer_state.js = js; 710 - ant_value_t global = js_glob(js); 711 - 712 - js_set(js, global, "setTimeout", js_mkfun(js_set_timeout)); 713 - js_set(js, global, "clearTimeout", js_mkfun(js_clear_timeout)); 714 - js_set(js, global, "setInterval", js_mkfun(js_set_interval)); 715 - js_set(js, global, "clearInterval", js_mkfun(js_clear_timeout)); 716 - js_set(js, global, "setImmediate", js_mkfun(js_set_immediate)); 717 - js_set(js, global, "clearImmediate", js_mkfun(js_clear_immediate)); 718 - js_set(js, global, "queueMicrotask", js_mkfun(js_queue_microtask)); 719 729 } 720 730 721 731 void gc_mark_timers(ant_t *js, gc_mark_fn mark) {
+47 -27
src/modules/tty.c
··· 27 27 #endif 28 28 29 29 #include "ant.h" 30 + #include "gc/roots.h" 30 31 #include "descriptors.h" 31 32 #include "errors.h" 32 33 #include "internal.h" ··· 34 35 #include "tty_ctrl.h" 35 36 #include "silver/engine.h" 36 37 38 + #include "modules/stream.h" 37 39 #include "modules/symbol.h" 38 40 #include "modules/tty.h" 41 + 42 + static ant_value_t g_tty_readstream_proto = 0; 43 + static ant_value_t g_tty_readstream_ctor = 0; 44 + static ant_value_t g_tty_writestream_proto = 0; 45 + static ant_value_t g_tty_writestream_ctor = 0; 39 46 40 47 static bool parse_fd(ant_value_t value, int *fd_out) { 41 48 int fd = 0; ··· 588 595 } 589 596 } 590 597 591 - ant_value_t ctor = js_getcurrentfunc(js); 592 - ant_value_t proto = js_get(js, ctor, "prototype"); 593 - ant_value_t obj = js_mkobj(js); 594 - if (is_special_object(proto)) js_set_proto_init(obj, proto); 598 + ant_value_t obj = stream_construct_readable(js, g_tty_readstream_proto, js_mkundef()); 599 + if (is_err(obj)) return obj; 595 600 596 601 ensure_stream_common_props(js, obj, fd); 597 602 js_set(js, obj, "isRaw", js_false); ··· 617 622 } 618 623 } 619 624 620 - ant_value_t ctor = js_getcurrentfunc(js); 621 - ant_value_t proto = js_get(js, ctor, "prototype"); 622 - ant_value_t obj = js_mkobj(js); 623 - if (is_special_object(proto)) js_set_proto_init(obj, proto); 625 + ant_value_t obj = stream_construct_writable(js, g_tty_writestream_proto, js_mkundef()); 626 + if (is_err(obj)) return obj; 624 627 625 628 ensure_stream_common_props(js, obj, fd); 626 629 return obj; ··· 648 651 js_set_sym(js, proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); 649 652 } 650 653 654 + static void tty_init_stream_constructors(ant_t *js) { 655 + if (g_tty_readstream_ctor && g_tty_writestream_ctor) return; 656 + stream_init_constructors(js); 657 + 658 + g_tty_readstream_proto = js_mkobj(js); 659 + js_set_proto_init(g_tty_readstream_proto, stream_readable_prototype(js)); 660 + setup_readstream_proto(js, g_tty_readstream_proto); 661 + 662 + g_tty_readstream_ctor = js_make_ctor(js, tty_read_stream_constructor, g_tty_readstream_proto, "ReadStream", 10); 663 + js_set_proto_init(g_tty_readstream_ctor, stream_readable_constructor(js)); 664 + 665 + gc_register_root(&g_tty_readstream_proto); 666 + gc_register_root(&g_tty_readstream_ctor); 667 + 668 + g_tty_writestream_proto = js_mkobj(js); 669 + js_set_proto_init(g_tty_writestream_proto, stream_writable_prototype(js)); 670 + setup_writestream_proto(js, g_tty_writestream_proto); 671 + 672 + g_tty_writestream_ctor = js_make_ctor(js, tty_write_stream_constructor, g_tty_writestream_proto, "WriteStream", 11); 673 + js_set_proto_init(g_tty_writestream_ctor, stream_writable_constructor(js)); 674 + 675 + gc_register_root(&g_tty_writestream_proto); 676 + gc_register_root(&g_tty_writestream_ctor); 677 + } 678 + 651 679 void init_tty_module(void) { 652 680 ant_t *js = rt->js; 653 681 if (!js) return; 682 + tty_init_stream_constructors(js); 654 683 655 684 ant_value_t process_obj = js_get(js, js_glob(js), "process"); 656 685 if (!is_special_object(process_obj)) return; ··· 666 695 if (is_callable(native_set_raw)) { 667 696 js_set(js, stdin_proto, "__antNativeSetRawMode", native_set_raw); 668 697 } 698 + js_set_proto_init(stdin_proto, stream_readable_prototype(js)); 669 699 setup_readstream_proto(js, stdin_proto); 670 700 } 701 + stream_init_readable_object(js, stdin_obj, js_mkundef()); 671 702 } 672 703 673 704 ant_value_t stdout_obj = js_get(js, process_obj, "stdout"); ··· 677 708 js_set_getter_desc(js, stdout_obj, "columns", 7, js_mkfun(tty_write_stream_columns_getter), JS_DESC_E | JS_DESC_C); 678 709 679 710 ant_value_t stdout_proto = js_get_proto(js, stdout_obj); 711 + if (is_special_object(stdout_proto)) js_set_proto_init(stdout_proto, stream_writable_prototype(js)); 680 712 setup_writestream_proto(js, stdout_proto); 713 + stream_init_writable_object(js, stdout_obj, js_mkundef()); 681 714 } 682 715 683 716 ant_value_t stderr_obj = js_get(js, process_obj, "stderr"); ··· 685 718 ensure_stream_common_props(js, stderr_obj, ANT_STDERR_FD); 686 719 js_set_getter_desc(js, stderr_obj, "rows", 4, js_mkfun(tty_write_stream_rows_getter), JS_DESC_E | JS_DESC_C); 687 720 js_set_getter_desc(js, stderr_obj, "columns", 7, js_mkfun(tty_write_stream_columns_getter), JS_DESC_E | JS_DESC_C); 688 - 721 + 689 722 ant_value_t stderr_proto = js_get_proto(js, stderr_obj); 723 + if (is_special_object(stderr_proto)) js_set_proto_init(stderr_proto, stream_writable_prototype(js)); 690 724 setup_writestream_proto(js, stderr_proto); 725 + stream_init_writable_object(js, stderr_obj, js_mkundef()); 691 726 } 692 727 } 693 728 694 729 ant_value_t tty_library(ant_t *js) { 695 730 ant_value_t lib = js_mkobj(js); 696 - 697 - ant_value_t read_ctor_obj = js_mkobj(js); 698 - js_set_slot(read_ctor_obj, SLOT_CFUNC, js_mkfun(tty_read_stream_constructor)); 699 - ant_value_t read_proto = js_mkobj(js); 700 - setup_readstream_proto(js, read_proto); 701 - js_set(js, read_ctor_obj, "prototype", read_proto); 702 - ant_value_t read_ctor = js_obj_to_func(read_ctor_obj); 703 - js_set(js, read_proto, "constructor", read_ctor); 704 - 705 - ant_value_t write_ctor_obj = js_mkobj(js); 706 - js_set_slot(write_ctor_obj, SLOT_CFUNC, js_mkfun(tty_write_stream_constructor)); 707 - ant_value_t write_proto = js_mkobj(js); 708 - setup_writestream_proto(js, write_proto); 709 - js_set(js, write_ctor_obj, "prototype", write_proto); 710 - ant_value_t write_ctor = js_obj_to_func(write_ctor_obj); 711 - js_set(js, write_proto, "constructor", write_ctor); 731 + tty_init_stream_constructors(js); 712 732 713 733 js_set(js, lib, "isatty", js_mkfun(tty_isatty)); 714 - js_set(js, lib, "ReadStream", read_ctor); 715 - js_set(js, lib, "WriteStream", write_ctor); 734 + js_set(js, lib, "ReadStream", g_tty_readstream_ctor); 735 + js_set(js, lib, "WriteStream", g_tty_writestream_ctor); 716 736 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "tty", 3)); 717 737 718 738 return lib;
+305 -1
src/modules/util.c
··· 11 11 12 12 #include "ant.h" 13 13 #include "internal.h" 14 + #include "esm/library.h" 14 15 #include "silver/engine.h" 15 16 17 + #include "modules/buffer.h" 18 + #include "modules/date.h" 16 19 #include "modules/json.h" 17 20 #include "modules/symbol.h" 18 21 #include "modules/util.h" 22 + #include "modules/collections.h" 19 23 20 24 typedef struct { 21 25 char *buf; ··· 210 214 return out; 211 215 } 212 216 217 + static bool util_has_proto_in_chain(ant_t *js, ant_value_t value, ant_value_t proto) { 218 + if (!is_special_object(proto)) return false; 219 + 220 + ant_value_t current = value; 221 + while (is_special_object(current)) { 222 + current = js_get_proto(js, current); 223 + if (current == proto) return true; 224 + } 225 + 226 + return false; 227 + } 228 + 229 + static bool util_is_typed_array_value(ant_value_t value, TypedArrayData **out) { 230 + ant_value_t slot; 231 + 232 + if (vtype(value) == T_TYPEDARRAY) { 233 + if (out) *out = (TypedArrayData *)js_gettypedarray(value); 234 + return true; 235 + } 236 + 237 + if (!is_object_type(value)) return false; 238 + slot = js_get_slot(value, SLOT_BUFFER); 239 + if (vtype(slot) != T_TYPEDARRAY) return false; 240 + 241 + if (out) *out = (TypedArrayData *)js_gettypedarray(slot); 242 + return true; 243 + } 244 + 245 + static ArrayBufferData *util_get_arraybuffer_data(ant_value_t value) { 246 + ant_value_t slot; 247 + 248 + if (!is_object_type(value) || buffer_is_dataview(value)) return NULL; 249 + slot = js_get_slot(value, SLOT_BUFFER); 250 + if (vtype(slot) != T_NUM) return NULL; 251 + 252 + return (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 253 + } 254 + 255 + static bool util_is_boxed_primitive(ant_value_t value, uint8_t *type_out) { 256 + ant_value_t primitive; 257 + 258 + if (!is_object_type(value)) return false; 259 + primitive = js_get_slot(value, SLOT_PRIMITIVE); 260 + if (vtype(primitive) == T_UNDEF) return false; 261 + 262 + if (type_out) *type_out = vtype(primitive); 263 + return true; 264 + } 265 + 266 + static bool util_has_to_string_tag(ant_t *js, ant_value_t value, const char *tag, size_t tag_len) { 267 + ant_value_t to_string_tag; 268 + size_t actual_len = 0; 269 + const char *actual; 270 + 271 + if (!is_object_type(value)) return false; 272 + 273 + to_string_tag = js_get_sym(js, value, get_toStringTag_sym()); 274 + if (vtype(to_string_tag) != T_STR) return false; 275 + 276 + actual = js_getstr(js, to_string_tag, &actual_len); 277 + return actual != NULL && actual_len == tag_len && memcmp(actual, tag, tag_len) == 0; 278 + } 279 + 280 + static bool util_is_arguments_object_value(ant_t *js, ant_value_t value) { 281 + ant_value_t callee = js_mkundef(); 282 + 283 + if (vtype(value) != T_ARR) return false; 284 + if (vtype(js_get_slot(value, SLOT_STRICT_ARGS)) != T_UNDEF) return true; 285 + if (!util_has_to_string_tag(js, value, "Arguments", 9)) return false; 286 + 287 + return js_try_get_own_data_prop(js, value, "callee", 6, &callee); 288 + } 289 + 290 + static ant_value_t util_types_is_any_array_buffer(ant_t *js, ant_value_t *args, int nargs) { 291 + ArrayBufferData *buffer = (nargs > 0) ? util_get_arraybuffer_data(args[0]) : NULL; 292 + return js_bool(buffer != NULL); 293 + } 294 + 295 + static ant_value_t util_types_is_array_buffer(ant_t *js, ant_value_t *args, int nargs) { 296 + ArrayBufferData *buffer = (nargs > 0) ? util_get_arraybuffer_data(args[0]) : NULL; 297 + return js_bool(buffer != NULL && !buffer->is_shared); 298 + } 299 + 300 + static ant_value_t util_types_is_shared_array_buffer(ant_t *js, ant_value_t *args, int nargs) { 301 + ArrayBufferData *buffer = (nargs > 0) ? util_get_arraybuffer_data(args[0]) : NULL; 302 + return js_bool(buffer != NULL && buffer->is_shared); 303 + } 304 + 305 + static ant_value_t util_types_is_array_buffer_view(ant_t *js, ant_value_t *args, int nargs) { 306 + if (nargs < 1) return js_false; 307 + return js_bool(buffer_is_dataview(args[0]) || util_is_typed_array_value(args[0], NULL)); 308 + } 309 + 310 + static ant_value_t util_types_is_data_view(ant_t *js, ant_value_t *args, int nargs) { 311 + if (nargs < 1) return js_false; 312 + return js_bool(buffer_is_dataview(args[0])); 313 + } 314 + 315 + static ant_value_t util_types_is_typed_array(ant_t *js, ant_value_t *args, int nargs) { 316 + if (nargs < 1) return js_false; 317 + return js_bool(util_is_typed_array_value(args[0], NULL)); 318 + } 319 + 320 + static ant_value_t util_types_is_float16_array(ant_t *js, ant_value_t *args, int nargs) { 321 + TypedArrayData *typed_array = NULL; 322 + if (nargs < 1 || !util_is_typed_array_value(args[0], &typed_array)) return js_false; 323 + return js_bool(typed_array != NULL && typed_array->type == TYPED_ARRAY_FLOAT16); 324 + } 325 + 326 + #define DEFINE_TYPED_ARRAY_CHECK(fn_name, typed_array_kind) \ 327 + static ant_value_t fn_name(ant_t *js, ant_value_t *args, int nargs) { \ 328 + TypedArrayData *typed_array = NULL; \ 329 + if (nargs < 1 || !util_is_typed_array_value(args[0], &typed_array)) return js_false; \ 330 + return js_bool(typed_array != NULL && typed_array->type == typed_array_kind); \ 331 + } 332 + 333 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_int8_array, TYPED_ARRAY_INT8) 334 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint8_array, TYPED_ARRAY_UINT8) 335 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint8_clamped_array, TYPED_ARRAY_UINT8_CLAMPED) 336 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_int16_array, TYPED_ARRAY_INT16) 337 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint16_array, TYPED_ARRAY_UINT16) 338 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_int32_array, TYPED_ARRAY_INT32) 339 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_uint32_array, TYPED_ARRAY_UINT32) 340 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_float32_array, TYPED_ARRAY_FLOAT32) 341 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_float64_array, TYPED_ARRAY_FLOAT64) 342 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_bigint64_array, TYPED_ARRAY_BIGINT64) 343 + DEFINE_TYPED_ARRAY_CHECK(util_types_is_biguint64_array, TYPED_ARRAY_BIGUINT64) 344 + 345 + static ant_value_t util_types_is_promise(ant_t *js, ant_value_t *args, int nargs) { 346 + if (nargs < 1) return js_false; 347 + return js_bool(vtype(args[0]) == T_PROMISE); 348 + } 349 + 350 + static ant_value_t util_types_is_proxy(ant_t *js, ant_value_t *args, int nargs) { 351 + if (nargs < 1 || !is_object_type(args[0])) return js_false; 352 + return js_bool(is_proxy(args[0])); 353 + } 354 + 355 + static ant_value_t util_types_is_regexp(ant_t *js, ant_value_t *args, int nargs) { 356 + ant_value_t regexp_proto; 357 + if (nargs < 1 || !is_object_type(args[0])) return js_false; 358 + regexp_proto = js_get_ctor_proto(js, "RegExp", 6); 359 + return js_bool(util_has_proto_in_chain(js, args[0], regexp_proto)); 360 + } 361 + 362 + static ant_value_t util_types_is_date(ant_t *js, ant_value_t *args, int nargs) { 363 + if (nargs < 1) return js_false; 364 + return js_bool(is_date_instance(args[0])); 365 + } 366 + 367 + static ant_value_t util_types_is_map(ant_t *js, ant_value_t *args, int nargs) { 368 + if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false; 369 + return js_bool(js_obj_ptr(args[0])->type_tag == T_MAP); 370 + } 371 + 372 + static ant_value_t util_types_is_set(ant_t *js, ant_value_t *args, int nargs) { 373 + if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false; 374 + return js_bool(js_obj_ptr(args[0])->type_tag == T_SET); 375 + } 376 + 377 + static ant_value_t util_types_is_weak_map(ant_t *js, ant_value_t *args, int nargs) { 378 + if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false; 379 + return js_bool(js_obj_ptr(args[0])->type_tag == T_WEAKMAP); 380 + } 381 + 382 + static ant_value_t util_types_is_weak_set(ant_t *js, ant_value_t *args, int nargs) { 383 + if (nargs < 1 || vtype(args[0]) != T_OBJ) return js_false; 384 + return js_bool(js_obj_ptr(args[0])->type_tag == T_WEAKSET); 385 + } 386 + 387 + static ant_value_t util_types_is_async_function(ant_t *js, ant_value_t *args, int nargs) { 388 + ant_value_t func_obj; 389 + if (nargs < 1 || vtype(args[0]) != T_FUNC) return js_false; 390 + func_obj = js_func_obj(args[0]); 391 + return js_bool(js_get_slot(func_obj, SLOT_ASYNC) == js_true); 392 + } 393 + 394 + static ant_value_t util_types_is_generator_function(ant_t *js, ant_value_t *args, int nargs) { 395 + sv_closure_t *closure; 396 + 397 + if (nargs < 1 || vtype(args[0]) != T_FUNC) return js_false; 398 + 399 + closure = js_func_closure(args[0]); 400 + return js_bool(closure != NULL && closure->func != NULL && closure->func->is_generator); 401 + } 402 + 403 + static ant_value_t util_types_is_generator_object(ant_t *js, ant_value_t *args, int nargs) { 404 + if (nargs < 1) return js_false; 405 + return js_bool(vtype(args[0]) == T_GENERATOR); 406 + } 407 + 408 + static ant_value_t util_types_is_arguments_object(ant_t *js, ant_value_t *args, int nargs) { 409 + if (nargs < 1) return js_false; 410 + return js_bool(util_is_arguments_object_value(js, args[0])); 411 + } 412 + 413 + static ant_value_t util_types_is_native_error(ant_t *js, ant_value_t *args, int nargs) { 414 + if (nargs < 1 || !is_object_type(args[0])) return js_false; 415 + return js_bool(js_get_slot(args[0], SLOT_ERROR_BRAND) == js_true); 416 + } 417 + 418 + static ant_value_t util_types_is_boxed_primitive(ant_t *js, ant_value_t *args, int nargs) { 419 + return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], NULL)); 420 + } 421 + 422 + static ant_value_t util_types_is_boolean_object(ant_t *js, ant_value_t *args, int nargs) { 423 + uint8_t type = T_UNDEF; 424 + return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_BOOL); 425 + } 426 + 427 + static ant_value_t util_types_is_number_object(ant_t *js, ant_value_t *args, int nargs) { 428 + uint8_t type = T_UNDEF; 429 + return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_NUM); 430 + } 431 + 432 + static ant_value_t util_types_is_string_object(ant_t *js, ant_value_t *args, int nargs) { 433 + uint8_t type = T_UNDEF; 434 + return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_STR); 435 + } 436 + 437 + static ant_value_t util_types_is_symbol_object(ant_t *js, ant_value_t *args, int nargs) { 438 + uint8_t type = T_UNDEF; 439 + return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_SYMBOL); 440 + } 441 + 442 + static ant_value_t util_types_is_bigint_object(ant_t *js, ant_value_t *args, int nargs) { 443 + uint8_t type = T_UNDEF; 444 + return js_bool(nargs > 0 && util_is_boxed_primitive(args[0], &type) && type == T_BIGINT); 445 + } 446 + 447 + static ant_value_t util_types_is_map_iterator(ant_t *js, ant_value_t *args, int nargs) { 448 + if (nargs < 1 || !is_object_type(args[0])) return js_false; 449 + return js_bool(util_has_proto_in_chain(js, args[0], g_map_iter_proto)); 450 + } 451 + 452 + static ant_value_t util_types_is_set_iterator(ant_t *js, ant_value_t *args, int nargs) { 453 + if (nargs < 1 || !is_object_type(args[0])) return js_false; 454 + return js_bool(util_has_proto_in_chain(js, args[0], g_set_iter_proto)); 455 + } 456 + 457 + static ant_value_t util_types_is_module_namespace_object(ant_t *js, ant_value_t *args, int nargs) { 458 + if (nargs < 1) return js_false; 459 + return js_bool(js_check_brand(args[0], BRAND_MODULE_NAMESPACE)); 460 + } 461 + 462 + ant_value_t util_types_library(ant_t *js) { 463 + ant_value_t types = js_mkobj(js); 464 + 465 + js_set(js, types, "isAnyArrayBuffer", js_mkfun(util_types_is_any_array_buffer)); 466 + js_set(js, types, "isArrayBuffer", js_mkfun(util_types_is_array_buffer)); 467 + js_set(js, types, "isArgumentsObject", js_mkfun(util_types_is_arguments_object)); 468 + js_set(js, types, "isArrayBufferView", js_mkfun(util_types_is_array_buffer_view)); 469 + js_set(js, types, "isAsyncFunction", js_mkfun(util_types_is_async_function)); 470 + js_set(js, types, "isBigInt64Array", js_mkfun(util_types_is_bigint64_array)); 471 + js_set(js, types, "isBigIntObject", js_mkfun(util_types_is_bigint_object)); 472 + js_set(js, types, "isBigUint64Array", js_mkfun(util_types_is_biguint64_array)); 473 + js_set(js, types, "isBooleanObject", js_mkfun(util_types_is_boolean_object)); 474 + js_set(js, types, "isBoxedPrimitive", js_mkfun(util_types_is_boxed_primitive)); 475 + js_set(js, types, "isDataView", js_mkfun(util_types_is_data_view)); 476 + js_set(js, types, "isDate", js_mkfun(util_types_is_date)); 477 + js_set(js, types, "isFloat16Array", js_mkfun(util_types_is_float16_array)); 478 + js_set(js, types, "isFloat32Array", js_mkfun(util_types_is_float32_array)); 479 + js_set(js, types, "isFloat64Array", js_mkfun(util_types_is_float64_array)); 480 + js_set(js, types, "isGeneratorFunction", js_mkfun(util_types_is_generator_function)); 481 + js_set(js, types, "isGeneratorObject", js_mkfun(util_types_is_generator_object)); 482 + js_set(js, types, "isInt8Array", js_mkfun(util_types_is_int8_array)); 483 + js_set(js, types, "isInt16Array", js_mkfun(util_types_is_int16_array)); 484 + js_set(js, types, "isInt32Array", js_mkfun(util_types_is_int32_array)); 485 + js_set(js, types, "isMap", js_mkfun(util_types_is_map)); 486 + js_set(js, types, "isMapIterator", js_mkfun(util_types_is_map_iterator)); 487 + js_set(js, types, "isModuleNamespaceObject", js_mkfun(util_types_is_module_namespace_object)); 488 + js_set(js, types, "isNativeError", js_mkfun(util_types_is_native_error)); 489 + js_set(js, types, "isNumberObject", js_mkfun(util_types_is_number_object)); 490 + js_set(js, types, "isPromise", js_mkfun(util_types_is_promise)); 491 + js_set(js, types, "isProxy", js_mkfun(util_types_is_proxy)); 492 + js_set(js, types, "isRegExp", js_mkfun(util_types_is_regexp)); 493 + js_set(js, types, "isSet", js_mkfun(util_types_is_set)); 494 + js_set(js, types, "isSetIterator", js_mkfun(util_types_is_set_iterator)); 495 + js_set(js, types, "isSharedArrayBuffer", js_mkfun(util_types_is_shared_array_buffer)); 496 + js_set(js, types, "isStringObject", js_mkfun(util_types_is_string_object)); 497 + js_set(js, types, "isSymbolObject", js_mkfun(util_types_is_symbol_object)); 498 + js_set(js, types, "isTypedArray", js_mkfun(util_types_is_typed_array)); 499 + js_set(js, types, "isUint8Array", js_mkfun(util_types_is_uint8_array)); 500 + js_set(js, types, "isUint8ClampedArray", js_mkfun(util_types_is_uint8_clamped_array)); 501 + js_set(js, types, "isUint16Array", js_mkfun(util_types_is_uint16_array)); 502 + js_set(js, types, "isUint32Array", js_mkfun(util_types_is_uint32_array)); 503 + js_set(js, types, "isWeakMap", js_mkfun(util_types_is_weak_map)); 504 + js_set(js, types, "isWeakSet", js_mkfun(util_types_is_weak_set)); 505 + 506 + return types; 507 + } 508 + 509 + static ant_value_t util_get_types_object(ant_t *js) { 510 + bool loaded = false; 511 + ant_value_t types = js_esm_load_registered_library(js, "util/types", 10, &loaded); 512 + return loaded ? types : util_types_library(js); 513 + } 514 + 213 515 static ant_value_t util_debuglog_call(ant_params_t) { 214 516 return js_mkundef(); 215 517 } ··· 645 947 return js_mkundef(); 646 948 } 647 949 648 - ant_value_t util_library(ant_t *js) { 950 + ant_value_t util_library(ant_t *js) { 649 951 ant_value_t lib = js_mkobj(js); 952 + ant_value_t types = util_get_types_object(js); 650 953 651 954 js_set(js, lib, "format", js_mkfun(util_format)); 652 955 js_set(js, lib, "formatWithOptions", js_mkfun(util_format_with_options)); ··· 658 961 js_set(js, lib, "promisify", js_mkfun(util_promisify)); 659 962 js_set(js, lib, "stripVTControlCharacters", js_mkfun(util_strip_vt_control_characters)); 660 963 js_set(js, lib, "styleText", js_mkfun(util_style_text)); 964 + js_set(js, lib, "types", types); 661 965 662 966 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "util", 4)); 663 967 return lib;
+12
src/modules/wasi.c
··· 35 35 wasm_function_inst_t func; 36 36 } wasi_func_env_t; 37 37 38 + static inline bool wasi_is_proc_exit_exception(const char *exception) { 39 + return exception != NULL && strstr(exception, "wasi proc exit") != NULL; 40 + } 41 + 42 + static ant_value_t wasi_handle_proc_exit(wasm_module_inst_t inst) { 43 + uint32_t exit_code = wasm_runtime_get_wasi_exit_code(inst); 44 + wasm_runtime_clear_exception(inst); 45 + if (exit_code != 0) exit((int)exit_code); 46 + return js_mkundef(); 47 + } 48 + 38 49 static ant_value_t wasi_exported_func_call(ant_t *js, ant_value_t *args, int nargs) { 39 50 if (!js_check_native_tag(js->current_func, WASI_FUNC_TAG)) 40 51 return js_mkerr(js, "Invalid WASI function"); ··· 53 64 54 65 if (!wasm_runtime_call_wasm(env->exec_env, env->func, param_count, wasm_argv)) { 55 66 const char *exception = wasm_runtime_get_exception(env->inst); 67 + if (wasi_is_proc_exit_exception(exception)) return wasi_handle_proc_exit(env->inst); 56 68 return js_mkerr(js, "%s", exception ? exception : "WASI function call failed"); 57 69 } 58 70
+84 -1
src/types/modules/fs.d.ts
··· 1 1 declare module 'fs' { 2 + import type { Readable, Writable } from 'stream'; 3 + 2 4 interface Stats { 3 5 size: number; 4 6 mode: number; ··· 31 33 interval?: number; 32 34 } 33 35 36 + interface ReadStream extends Readable { 37 + readonly path: string; 38 + readonly bytesRead: number; 39 + readonly pending: boolean; 40 + readonly closed: boolean; 41 + readonly fd?: number | null; 42 + close(callback?: () => void): this; 43 + } 44 + 45 + interface WriteStream extends Writable { 46 + readonly path: string; 47 + readonly bytesWritten: number; 48 + readonly pending: boolean; 49 + readonly closed: boolean; 50 + readonly fd?: number | null; 51 + close(callback?: () => void): this; 52 + } 53 + 34 54 const constants: { 35 55 F_OK: number; 36 56 R_OK: number; ··· 50 70 function readFile(path: string): Promise<Uint8Array>; 51 71 function readFileSync(path: string, encoding: Encoding | { encoding: Encoding }): string; 52 72 function readFileSync(path: string): Uint8Array; 53 - function read(fd: number, buffer: ArrayBufferView, offset?: number, length?: number, position?: number | null, callback?: (err: Error | null, bytesRead: number, buffer: ArrayBufferView) => void): Promise<number>; 73 + function read( 74 + fd: number, 75 + buffer: ArrayBufferView, 76 + offset?: number, 77 + length?: number, 78 + position?: number | null, 79 + callback?: (err: Error | null, bytesRead: number, buffer: ArrayBufferView) => void 80 + ): Promise<number>; 54 81 function readSync(fd: number, buffer: ArrayBufferView, offset?: number, length?: number, position?: number | null): number; 55 82 function stream(path: string): Promise<string>; 83 + function createReadStream( 84 + path: string, 85 + options?: { 86 + fd?: number; 87 + flags?: string | number; 88 + mode?: number; 89 + start?: number; 90 + end?: number; 91 + autoClose?: boolean; 92 + emitClose?: boolean; 93 + highWaterMark?: number; 94 + } 95 + ): ReadStream; 96 + function createWriteStream( 97 + path: string, 98 + options?: { 99 + fd?: number; 100 + flags?: string | number; 101 + mode?: number; 102 + start?: number; 103 + autoClose?: boolean; 104 + emitClose?: boolean; 105 + highWaterMark?: number; 106 + } 107 + ): WriteStream; 56 108 function open(path: string, flags?: string, mode?: number): Promise<number>; 57 109 function openSync(path: string, flags?: string, mode?: number): number; 58 110 function close(fd: number): Promise<void>; ··· 100 152 function unwatchFile(path: string, listener?: (curr: Stats, prev: Stats) => void): void; 101 153 const FSWatcher: { 102 154 prototype: FSWatcher; 155 + }; 156 + const ReadStream: { 157 + prototype: ReadStream; 158 + new ( 159 + path: string, 160 + options?: { 161 + fd?: number; 162 + flags?: string | number; 163 + mode?: number; 164 + start?: number; 165 + end?: number; 166 + autoClose?: boolean; 167 + emitClose?: boolean; 168 + highWaterMark?: number; 169 + } 170 + ): ReadStream; 171 + }; 172 + const WriteStream: { 173 + prototype: WriteStream; 174 + new ( 175 + path: string, 176 + options?: { 177 + fd?: number; 178 + flags?: string | number; 179 + mode?: number; 180 + start?: number; 181 + autoClose?: boolean; 182 + emitClose?: boolean; 183 + highWaterMark?: number; 184 + } 185 + ): WriteStream; 103 186 }; 104 187 } 105 188
+77
src/types/modules/stream.d.ts
··· 1 + declare module 'stream' { 2 + class Stream { 3 + pipe<T extends Writable>(destination: T, options?: { end?: boolean }): T; 4 + unpipe(destination?: Writable): this; 5 + pause(): this; 6 + resume(): this; 7 + isPaused(): boolean; 8 + destroy(error?: Error): this; 9 + on(event: string, listener: (...args: unknown[]) => void): this; 10 + once(event: string, listener: (...args: unknown[]) => void): this; 11 + off(event: string, listener: (...args: unknown[]) => void): this; 12 + removeListener(event: string, listener: (...args: unknown[]) => void): this; 13 + removeAllListeners(event?: string): this; 14 + } 15 + 16 + class Readable extends Stream { 17 + constructor(options?: Record<string, unknown>); 18 + _read(size?: number): void; 19 + read(size?: number): Uint8Array | string | null; 20 + push(chunk: Uint8Array | string | null): boolean; 21 + on(event: 'data', listener: (chunk: Uint8Array | string) => void): this; 22 + on(event: 'end' | 'close' | 'readable', listener: () => void): this; 23 + on(event: 'error', listener: (error: Error) => void): this; 24 + on(event: string, listener: (...args: unknown[]) => void): this; 25 + static from(source: unknown, options?: Record<string, unknown>): Readable; 26 + static fromWeb(source: unknown, options?: Record<string, unknown>): Readable; 27 + } 28 + 29 + class Writable extends Stream { 30 + constructor(options?: Record<string, unknown>); 31 + _write(chunk: Uint8Array | string, encoding: string, callback: (error?: Error | null) => void): void; 32 + write(chunk: Uint8Array | string, encoding?: string, callback?: (error?: Error | null) => void): boolean; 33 + end(chunk?: Uint8Array | string, encoding?: string, callback?: (error?: Error | null) => void): this; 34 + cork(): void; 35 + uncork(): void; 36 + on(event: 'drain' | 'finish' | 'close', listener: () => void): this; 37 + on(event: 'error', listener: (error: Error) => void): this; 38 + on(event: string, listener: (...args: unknown[]) => void): this; 39 + } 40 + 41 + class Duplex extends Readable { 42 + constructor(options?: Record<string, unknown>); 43 + write(chunk: Uint8Array | string, encoding?: string, callback?: (error?: Error | null) => void): boolean; 44 + end(chunk?: Uint8Array | string, encoding?: string, callback?: (error?: Error | null) => void): this; 45 + cork(): void; 46 + uncork(): void; 47 + } 48 + 49 + class Transform extends Duplex { 50 + constructor(options?: Record<string, unknown>); 51 + } 52 + 53 + class PassThrough extends Transform { 54 + constructor(options?: Record<string, unknown>); 55 + } 56 + 57 + function pipeline(...streams: Array<Stream | ((error?: Error | null) => void)>): Stream; 58 + function finished(stream: Stream, callback?: (error?: Error | null) => void): Stream; 59 + 60 + const promises: { 61 + pipeline(...streams: Stream[]): Promise<void>; 62 + finished(stream: Stream): Promise<void>; 63 + }; 64 + 65 + export default Stream; 66 + export { Stream, Readable, Writable, Duplex, Transform, PassThrough, pipeline, finished, promises }; 67 + } 68 + 69 + declare module 'ant:stream' { 70 + export * from 'stream'; 71 + export { default } from 'stream'; 72 + } 73 + 74 + declare module 'node:stream' { 75 + export * from 'stream'; 76 + export { default } from 'stream'; 77 + }
+17
src/types/modules/timers.d.ts
··· 1 + declare module 'timers' { 2 + export const setTimeout: typeof globalThis.setTimeout; 3 + export const clearTimeout: typeof globalThis.clearTimeout; 4 + export const setInterval: typeof globalThis.setInterval; 5 + export const clearInterval: typeof globalThis.clearInterval; 6 + export const setImmediate: typeof globalThis.setImmediate; 7 + export const clearImmediate: typeof globalThis.clearImmediate; 8 + export const queueMicrotask: typeof globalThis.queueMicrotask; 9 + } 10 + 11 + declare module 'ant:timers' { 12 + export * from 'timers'; 13 + } 14 + 15 + declare module 'node:timers' { 16 + export * from 'timers'; 17 + }
+53
src/types/modules/util.d.ts
··· 1 + declare namespace utilTypes { 2 + function isAnyArrayBuffer(value: unknown): boolean; 3 + function isArrayBuffer(value: unknown): boolean; 4 + function isArgumentsObject(value: unknown): boolean; 5 + function isArrayBufferView(value: unknown): boolean; 6 + function isAsyncFunction(value: unknown): boolean; 7 + function isBigInt64Array(value: unknown): boolean; 8 + function isBigIntObject(value: unknown): boolean; 9 + function isBigUint64Array(value: unknown): boolean; 10 + function isBooleanObject(value: unknown): boolean; 11 + function isBoxedPrimitive(value: unknown): boolean; 12 + function isDataView(value: unknown): boolean; 13 + function isDate(value: unknown): boolean; 14 + function isFloat16Array(value: unknown): boolean; 15 + function isFloat32Array(value: unknown): boolean; 16 + function isFloat64Array(value: unknown): boolean; 17 + function isGeneratorFunction(value: unknown): boolean; 18 + function isGeneratorObject(value: unknown): boolean; 19 + function isInt8Array(value: unknown): boolean; 20 + function isInt16Array(value: unknown): boolean; 21 + function isInt32Array(value: unknown): boolean; 22 + function isMap(value: unknown): boolean; 23 + function isMapIterator(value: unknown): boolean; 24 + function isModuleNamespaceObject(value: unknown): boolean; 25 + function isNativeError(value: unknown): boolean; 26 + function isNumberObject(value: unknown): boolean; 27 + function isPromise(value: unknown): boolean; 28 + function isProxy(value: unknown): boolean; 29 + function isRegExp(value: unknown): boolean; 30 + function isSet(value: unknown): boolean; 31 + function isSetIterator(value: unknown): boolean; 32 + function isSharedArrayBuffer(value: unknown): boolean; 33 + function isStringObject(value: unknown): boolean; 34 + function isSymbolObject(value: unknown): boolean; 35 + function isTypedArray(value: unknown): boolean; 36 + function isUint8Array(value: unknown): boolean; 37 + function isUint8ClampedArray(value: unknown): boolean; 38 + function isUint16Array(value: unknown): boolean; 39 + function isUint32Array(value: unknown): boolean; 40 + function isWeakMap(value: unknown): boolean; 41 + function isWeakSet(value: unknown): boolean; 42 + } 43 + 1 44 declare module 'util' { 2 45 type StyleTextFormat = string | string[]; 3 46 ··· 14 57 function promisify(fn: (...args: unknown[]) => unknown): (...args: unknown[]) => Promise<unknown>; 15 58 function stripVTControlCharacters(str: string): string; 16 59 function styleText(format: StyleTextFormat, text: string, options?: StyleTextOptions): string; 60 + 61 + const types: typeof utilTypes; 17 62 } 18 63 19 64 declare module 'ant:util' { ··· 23 68 declare module 'node:util' { 24 69 export * from 'util'; 25 70 } 71 + 72 + declare module 'util/types' { 73 + export = utilTypes; 74 + } 75 + 76 + declare module 'node:util/types' { 77 + export = utilTypes; 78 + }
+53
tests/test_fs_streams.cjs
··· 1 + const fs = require('node:fs'); 2 + const sourcePath = 'tests/.fs_stream_source.txt'; 3 + const copyPath = 'tests/.fs_stream_copy.txt'; 4 + const content = 'hello from fs streams\nline two'; 5 + 6 + try { 7 + fs.unlinkSync(sourcePath); 8 + } catch {} 9 + try { 10 + fs.unlinkSync(copyPath); 11 + } catch {} 12 + 13 + function fail(error) { 14 + console.error(error); 15 + process.exit(1); 16 + } 17 + 18 + const writer = fs.createWriteStream(sourcePath); 19 + if (!(writer instanceof fs.WriteStream)) throw new Error('createWriteStream() did not return fs.WriteStream'); 20 + writer.on('error', fail); 21 + writer.on('finish', () => { 22 + const written = fs.readFileSync(sourcePath, 'utf8'); 23 + if (written !== content) fail(new Error(`unexpected write content: ${written}`)); 24 + 25 + const reader = fs.createReadStream(sourcePath); 26 + if (!(reader instanceof fs.ReadStream)) fail(new Error('createReadStream() did not return fs.ReadStream')); 27 + 28 + let readBack = ''; 29 + reader.on('error', fail); 30 + reader.on('data', chunk => { 31 + readBack += chunk.toString(); 32 + }); 33 + reader.on('end', () => { 34 + if (readBack !== content) fail(new Error(`unexpected read content: ${readBack}`)); 35 + 36 + const pipedReader = fs.createReadStream(sourcePath); 37 + const pipedWriter = fs.createWriteStream(copyPath); 38 + pipedReader.on('error', fail); 39 + pipedWriter.on('error', fail); 40 + pipedWriter.on('finish', () => { 41 + const copied = fs.readFileSync(copyPath, 'utf8'); 42 + if (copied !== content) fail(new Error(`unexpected piped content: ${copied}`)); 43 + 44 + fs.unlinkSync(sourcePath); 45 + fs.unlinkSync(copyPath); 46 + console.log('fs stream test passed'); 47 + }); 48 + pipedReader.pipe(pipedWriter); 49 + }); 50 + }); 51 + 52 + writer.write('hello '); 53 + writer.end('from fs streams\nline two');