diff --git a/src/modules/events.c b/src/modules/events.c index 9e54c83..0ed13a0 100644 --- a/src/modules/events.c +++ b/src/modules/events.c @@ -1232,8 +1232,15 @@ ant_value_t events_library(ant_t *js) { ant_value_t eventemitter_prototype(ant_t *js) { if (g_eventemitter_proto) return g_eventemitter_proto; + ant_value_t object_proto = js->sym.object_proto; + ant_value_t function_proto = js_get_slot(js_glob(js), SLOT_FUNC_PROTO); + if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8); + ant_value_t eventemitter_ctor = js_mkobj(js); ant_value_t eventemitter_proto = js_mkobj(js); + + if (is_object_type(object_proto)) js_set_proto_init(eventemitter_proto, object_proto); + if (is_object_type(function_proto)) js_set_proto_init(eventemitter_ctor, function_proto); js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); js_set(js, eventemitter_proto, "addListener", js_mkfun(js_eventemitter_on)); @@ -1310,14 +1317,20 @@ void init_events_module(void) { ant_value_t pre_fn = js_make_ctor(js, js_promiserejectionevent_ctor, g_promiserejectionevent_proto, "PromiseRejectionEvent", 21); js_set(js, global, "PromiseRejectionEvent", pre_fn); + ant_value_t object_proto = js->sym.object_proto; + ant_value_t function_proto = js_get_slot(global, SLOT_FUNC_PROTO); + if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8); + ant_value_t eventtarget_proto = js_mkobj(js); g_eventtarget_proto = eventtarget_proto; + if (is_object_type(object_proto)) js_set_proto_init(eventtarget_proto, object_proto); js_set(js, eventtarget_proto, "addEventListener", js_mkfun(js_add_event_listener_method)); js_set(js, eventtarget_proto, "removeEventListener", js_mkfun(js_remove_event_listener_method)); js_set(js, eventtarget_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method)); js_set_sym(js, eventtarget_proto, get_toStringTag_sym(), js_mkstr(js, "EventTarget", 11)); ant_value_t eventtarget_ctor = js_mkobj(js); + if (is_object_type(function_proto)) js_set_proto_init(eventtarget_ctor, function_proto); js_set_slot(eventtarget_ctor, SLOT_CFUNC, js_mkfun(js_eventtarget_ctor)); js_mkprop_fast(js, eventtarget_ctor, "prototype", 9, eventtarget_proto); js_mkprop_fast(js, eventtarget_ctor, "name", 4, ANT_STRING("EventTarget")); diff --git a/tests/bench_object_spread.cjs b/tests/bench_object_spread.cjs new file mode 100644 index 0000000..3ddcaaf --- /dev/null +++ b/tests/bench_object_spread.cjs @@ -0,0 +1,74 @@ +const now = () => + typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now(); + +function bench(name, fn, iters = 1) { + fn(); + const t0 = now(); + for (let i = 0; i < iters; i++) fn(); + const dt = now() - t0; + const per = (dt / iters).toFixed(6); + console.log(`${name}: ${dt.toFixed(2)} ms total, ${per} ms/iter (${iters} iters)`); +} + +const visibleSymbol = Symbol('visible'); +const getterSymbol = Symbol('getter'); + +function makePlainSource() { + return { + a: 1, + b: 2, + c: 3, + d: 4, + e: 5, + f: 6, + }; +} + +function makeMixedSource() { + return { + a: 1, + b: 2, + c: 3, + d: 4, + [visibleSymbol]: 5, + }; +} + +function makeGetterSource() { + const obj = { + a: 1, + b: 2, + c: 3, + d: 4, + }; + + Object.defineProperty(obj, getterSymbol, { + enumerable: true, + get() { + return 42; + }, + }); + + return obj; +} + +function spreadPlain() { + const src = makePlainSource(); + return { ...src }; +} + +function spreadMixedSymbol() { + const src = makeMixedSource(); + return { ...src }; +} + +function spreadSymbolGetter() { + const src = makeGetterSource(); + return { ...src }; +} + +console.log('=== Object Spread Benchmark ===\n'); + +bench('spread plain object', spreadPlain, 200_000); +bench('spread enumerable symbol', spreadMixedSymbol, 200_000); +bench('spread enumerable symbol getter', spreadSymbolGetter, 100_000); diff --git a/tests/test_events_prototype_chain.cjs b/tests/test_events_prototype_chain.cjs new file mode 100644 index 0000000..933326b --- /dev/null +++ b/tests/test_events_prototype_chain.cjs @@ -0,0 +1,39 @@ +const { EventEmitter } = require('events'); + +function fail(message) { + console.log(`FAIL: ${message}`); + process.exit(1); +} + +if (Object.getPrototypeOf(EventEmitter.prototype) !== Object.prototype) { + fail('EventEmitter.prototype should inherit from Object.prototype'); +} + +function Program() {} + +if (typeof Program.prototype.__defineGetter__ !== 'function') { + fail('fresh function prototypes should inherit legacy accessors'); +} + +Program.prototype.__proto__ = EventEmitter.prototype; + +if (typeof Program.prototype.__defineGetter__ !== 'function') { + fail('repointed prototypes should still inherit legacy accessors through EventEmitter.prototype'); +} + +Program.prototype.__defineGetter__('terminal', function () { + return this._terminal; +}); + +const program = new Program(); +program._terminal = 'xterm-256color'; + +if (program.terminal !== 'xterm-256color') { + fail('getter installed after EventEmitter prototype reassignment should work'); +} + +if (typeof EventTarget === 'function' && Object.getPrototypeOf(EventTarget.prototype) !== Object.prototype) { + fail('EventTarget.prototype should inherit from Object.prototype'); +} + +console.log('PASS');