From 21e056ec430790b36dfe23001def1acf5046da7a Mon Sep 17 00:00:00 2001 From: Stefan Zerkalica Date: Mon, 4 Nov 2024 15:57:34 +0300 Subject: [PATCH 1/6] $mol_wire_sync: idempotent new operator support --- wire/sync/sync.test.ts | 73 ++++++++++++++++++++++++++++++++++++++++++ wire/sync/sync.ts | 54 +++++++++++++++++++++++-------- wire/task/task.ts | 69 +++++++++++++++++++++------------------ 3 files changed, 152 insertions(+), 44 deletions(-) diff --git a/wire/sync/sync.test.ts b/wire/sync/sync.test.ts index 7197e8b949c..c7f67da20bd 100644 --- a/wire/sync/sync.test.ts +++ b/wire/sync/sync.test.ts @@ -15,6 +15,79 @@ namespace $ { type Check = $mol_type_assert, string> }, + + async 'test method from host'( $ ) { + let count = 0 + class A { + static a() { + return $mol_wire_sync(this).b() + } + + static b() { return Promise.resolve(++count) } + } + + + $mol_assert_equal(await $mol_wire_async(A).a(), 1, count) + + }, + + async 'test function'( $ ) { + let count = 0 + class A { + static a() { + return $mol_wire_sync(this.b)() + } + + static b() { return Promise.resolve(++count) } + } + + + $mol_assert_equal(await $mol_wire_async(A).a(), 1, count) + + }, + + async 'test construct from scope'( $ ) { + class A { + static instances = [] as A[] + + static a() { + const a = new ($mol_wire_sync(scope).A)() + this.instances.push( a ) + $mol_wire_sync(this).b() + } + + static b() { return Promise.resolve() } + } + const scope = { A } + + await $mol_wire_async(A).a() + $mol_assert_equal(A.instances.length, 2) + $mol_assert_equal(A.instances[0] instanceof A) + + $mol_assert_equal(A.instances[0], A.instances[1]) + + }, + + async 'test construct itself'( $ ) { + class A { + static instances = [] as A[] + + static a() { + const a = new ($mol_wire_sync_make(A))() + this.instances.push( a ) + $mol_wire_sync(this).b() + } + + static b() { return Promise.resolve() } + } + + await $mol_wire_async(A).a() + $mol_assert_equal(A.instances.length, 2) + $mol_assert_equal(A.instances[0] instanceof A) + + $mol_assert_equal(A.instances[0], A.instances[1]) + + } }) } diff --git a/wire/sync/sync.ts b/wire/sync/sync.ts index 90a5a328eb2..751900e6daa 100644 --- a/wire/sync/sync.ts +++ b/wire/sync/sync.ts @@ -1,5 +1,20 @@ namespace $ { - + const constructors = new WeakMap() + + function $mol_wire_sync_factory( + val: new (...args: Args) => Result + ) { + let make = constructors.get(val) as null | ((...args: Args) => Result) + + if (! make) { + make = $mol_func_name_from((...args: Args) => new val(...args), val) + + constructors.set(val, make) + } + + return make + } + /** * Convert asynchronous (promise-based) API to synchronous by wrapping function and method calls in a fiber. * @see https://mol.hyoo.ru/#!section=docs/=1fcpsq_1wh0h2 @@ -9,26 +24,39 @@ namespace $ { get( obj, field ) { - const val = (obj as any)[ field ] + let val = (obj as any)[ field ] if( typeof val !== 'function' ) return val - - const temp = $mol_wire_task.getter( val ) - return function $mol_wire_sync( this: Host, ... args: any[] ) { - const fiber = temp( obj, args ) - return fiber.sync() - } - + + return new Proxy( val, { + apply( target, self, args ) { + return $mol_wire_task.get(target, obj, args ).sync() + }, + construct(target, args) { + const constr = $mol_wire_sync_factory(target) + return $mol_wire_task.get(constr , obj, args ).sync() as object + }, + + }) }, - + + construct(obj, args) { + const constr = $mol_wire_sync_factory(obj as new ( ... args: unknown[] )=> unknown) + return $mol_wire_task.get( constr , obj, args ).sync() as object + }, + apply( obj, self, args ) { - const temp = $mol_wire_task.getter( obj as ( ... args: any[] )=> any ) - const fiber = temp( self, args ) - return fiber.sync() + return $mol_wire_task.get(obj as ( ... args: any[] )=> any, self, args).sync() }, } ) as unknown as ObjectOrFunctionResultAwaited } + export function $mol_wire_sync_make< Constructor extends (new (...args: any[]) => unknown) > ( + obj: Constructor + ) { + return $mol_wire_sync(obj) as typeof obj + } + type FunctionResultAwaited = Some extends (...args: infer Args) => infer Res ? (...args: Args) => Awaited : Some diff --git a/wire/task/task.ts b/wire/task/task.ts index aef3f348b65..01fa4ac3961 100644 --- a/wire/task/task.ts +++ b/wire/task/task.ts @@ -6,48 +6,55 @@ namespace $ { Args extends readonly unknown[], Result, > extends $mol_wire_fiber< Host, Args, Result > { - - static getter< + + static get< Host, Args extends readonly unknown[], Result, >( task: ( this : Host , ... args : Args )=> Result, - ): ( host: Host, args: Args )=> $mol_wire_task< Host, Args, Result > { + host: Host, + args: Args, + ) { + const sub = $mol_wire_auto() + const existen = sub?.track_next() as $mol_wire_task< Host, Args, Result > | undefined - return function $mol_wire_task_get( host: Host, args: Args ) { - - const sub = $mol_wire_auto() - const existen = sub?.track_next() as $mol_wire_task< Host, Args, Result > | undefined - - reuse: if( existen ) { - - if( !existen.temp ) break reuse - - if( existen.host !== host ) break reuse - if( existen.task !== task ) break reuse - if( !$mol_compare_deep( existen.args, args ) ) break reuse - - return existen - } + reuse: if( existen ) { - const key = ( (host as any)?.[ Symbol.toStringTag ] ?? host ) + ( '.' + task.name + '<#>' ) - const next = new $mol_wire_task( key, task, host, args ) + if( !existen.temp ) break reuse - // Disabled because non-idempotency is required for try-catch - if( existen?.temp ) { - $$.$mol_log3_warn({ - place: '$mol_wire_task', - message: `Non idempotency`, - existen, - next, - hint: 'Ignore it', - }) - } + if( existen.host !== host ) break reuse + if( existen.task !== task ) break reuse + if( !$mol_compare_deep( existen.args, args ) ) break reuse - return next + return existen + } + + const key = ( (host as any)?.[ Symbol.toStringTag ] ?? host ) + ( '.' + task.name + '<#>' ) + const next = new $mol_wire_task( key, task, host, args ) + + // Disabled because non-idempotency is required for try-catch + if( existen?.temp ) { + $$.$mol_log3_warn({ + place: '$mol_wire_task', + message: `Non idempotency`, + existen, + next, + hint: 'Ignore it', + }) } + return next + } + + static getter< + Host, + Args extends readonly unknown[], + Result, + >( + task: ( this : Host , ... args : Args )=> Result, + ) { + return (host: Host, args: Args) => this.get(task, host, args) } get temp() { From 5277bb07431903ea1e3d4edb89e22929a6ef7fb3 Mon Sep 17 00:00:00 2001 From: Stefan Zerkalica Date: Mon, 4 Nov 2024 17:05:52 +0300 Subject: [PATCH 2/6] $mol_wire_sync fix restartable task getter --- wire/sync/sync.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/wire/sync/sync.ts b/wire/sync/sync.ts index 751900e6daa..3231405cc71 100644 --- a/wire/sync/sync.ts +++ b/wire/sync/sync.ts @@ -26,26 +26,28 @@ namespace $ { let val = (obj as any)[ field ] if( typeof val !== 'function' ) return val - + const temp = $mol_wire_task.getter(val) + return new Proxy( val, { apply( target, self, args ) { - return $mol_wire_task.get(target, obj, args ).sync() + return temp( obj, args ).sync() }, construct(target, args) { - const constr = $mol_wire_sync_factory(target) - return $mol_wire_task.get(constr , obj, args ).sync() as object + const temp = $mol_wire_task.getter($mol_wire_sync_factory(target)) + return temp( obj, args ).sync() as object }, }) }, construct(obj, args) { - const constr = $mol_wire_sync_factory(obj as new ( ... args: unknown[] )=> unknown) - return $mol_wire_task.get( constr , obj, args ).sync() as object + const temp = $mol_wire_task.getter($mol_wire_sync_factory(obj as new ( ... args: unknown[] )=> unknown)) + return temp( obj, args ).sync() as object }, apply( obj, self, args ) { - return $mol_wire_task.get(obj as ( ... args: any[] )=> any, self, args).sync() + const temp = $mol_wire_task.getter(obj as ( ... args: any[] )=> any) + return temp(self, args).sync() }, } ) as unknown as ObjectOrFunctionResultAwaited From dea4df54fc22334766ac393cc32f8a70c84f89e0 Mon Sep 17 00:00:00 2001 From: Stefan Zerkalica Date: Mon, 4 Nov 2024 18:53:33 +0300 Subject: [PATCH 3/6] $mol_wire_task restore jit optimized --- wire/task/task.ts | 69 +++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/wire/task/task.ts b/wire/task/task.ts index 01fa4ac3961..aef3f348b65 100644 --- a/wire/task/task.ts +++ b/wire/task/task.ts @@ -6,55 +6,48 @@ namespace $ { Args extends readonly unknown[], Result, > extends $mol_wire_fiber< Host, Args, Result > { - - static get< + + static getter< Host, Args extends readonly unknown[], Result, >( task: ( this : Host , ... args : Args )=> Result, - host: Host, - args: Args, - ) { - const sub = $mol_wire_auto() - const existen = sub?.track_next() as $mol_wire_task< Host, Args, Result > | undefined + ): ( host: Host, args: Args )=> $mol_wire_task< Host, Args, Result > { - reuse: if( existen ) { + return function $mol_wire_task_get( host: Host, args: Args ) { - if( !existen.temp ) break reuse + const sub = $mol_wire_auto() + const existen = sub?.track_next() as $mol_wire_task< Host, Args, Result > | undefined - if( existen.host !== host ) break reuse - if( existen.task !== task ) break reuse - if( !$mol_compare_deep( existen.args, args ) ) break reuse + reuse: if( existen ) { + + if( !existen.temp ) break reuse + + if( existen.host !== host ) break reuse + if( existen.task !== task ) break reuse + if( !$mol_compare_deep( existen.args, args ) ) break reuse + + return existen + } - return existen - } - - const key = ( (host as any)?.[ Symbol.toStringTag ] ?? host ) + ( '.' + task.name + '<#>' ) - const next = new $mol_wire_task( key, task, host, args ) - - // Disabled because non-idempotency is required for try-catch - if( existen?.temp ) { - $$.$mol_log3_warn({ - place: '$mol_wire_task', - message: `Non idempotency`, - existen, - next, - hint: 'Ignore it', - }) + const key = ( (host as any)?.[ Symbol.toStringTag ] ?? host ) + ( '.' + task.name + '<#>' ) + const next = new $mol_wire_task( key, task, host, args ) + + // Disabled because non-idempotency is required for try-catch + if( existen?.temp ) { + $$.$mol_log3_warn({ + place: '$mol_wire_task', + message: `Non idempotency`, + existen, + next, + hint: 'Ignore it', + }) + } + + return next } - return next - } - - static getter< - Host, - Args extends readonly unknown[], - Result, - >( - task: ( this : Host , ... args : Args )=> Result, - ) { - return (host: Host, args: Args) => this.get(task, host, args) } get temp() { From 3f94bae0ee97ebb44679994be1b76a6dfd336f7c Mon Sep 17 00:00:00 2001 From: Stefan Zerkalica Date: Mon, 4 Nov 2024 21:02:35 +0300 Subject: [PATCH 4/6] $mol_wire_sync review fixes --- wire/sync/sync.test.ts | 4 ++-- wire/sync/sync.ts | 29 +++++++++++++---------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/wire/sync/sync.test.ts b/wire/sync/sync.test.ts index c7f67da20bd..0f419ed4edd 100644 --- a/wire/sync/sync.test.ts +++ b/wire/sync/sync.test.ts @@ -12,7 +12,7 @@ namespace $ { return $mol_wire_sync(this).a() } } - + type Check = $mol_type_assert, string> }, @@ -73,7 +73,7 @@ namespace $ { static instances = [] as A[] static a() { - const a = new ($mol_wire_sync_make(A))() + const a = new ($mol_wire_sync(A))() this.instances.push( a ) $mol_wire_sync(this).b() } diff --git a/wire/sync/sync.ts b/wire/sync/sync.ts index 3231405cc71..4baf4a717ee 100644 --- a/wire/sync/sync.ts +++ b/wire/sync/sync.ts @@ -1,16 +1,15 @@ namespace $ { - const constructors = new WeakMap() + const factories = new WeakMap() - function $mol_wire_sync_factory( + function factory( val: new (...args: Args) => Result ) { - let make = constructors.get(val) as null | ((...args: Args) => Result) + let make = factories.get(val) as null | ((...args: Args) => Result) - if (! make) { - make = $mol_func_name_from((...args: Args) => new val(...args), val) + if ( make ) return make - constructors.set(val, make) - } + make = $mol_func_name_from((...args: Args) => new val(...args), val) + factories.set(val, make) return make } @@ -33,7 +32,7 @@ namespace $ { return temp( obj, args ).sync() }, construct(target, args) { - const temp = $mol_wire_task.getter($mol_wire_sync_factory(target)) + const temp = $mol_wire_task.getter(factory(target)) return temp( obj, args ).sync() as object }, @@ -41,7 +40,7 @@ namespace $ { }, construct(obj, args) { - const temp = $mol_wire_task.getter($mol_wire_sync_factory(obj as new ( ... args: unknown[] )=> unknown)) + const temp = $mol_wire_task.getter(factory(obj as new ( ... args: unknown[] )=> unknown)) return temp( obj, args ).sync() as object }, @@ -53,22 +52,20 @@ namespace $ { } ) as unknown as ObjectOrFunctionResultAwaited } - export function $mol_wire_sync_make< Constructor extends (new (...args: any[]) => unknown) > ( - obj: Constructor - ) { - return $mol_wire_sync(obj) as typeof obj - } - type FunctionResultAwaited = Some extends (...args: infer Args) => infer Res ? (...args: Args) => Awaited : Some + type ConstructorResultAwaited = Some extends new (...args: infer Args) => infer Res + ? new (...args: Args) => Res + : {} + type MethodsResultAwaited = { [K in keyof Host]: FunctionResultAwaited } type ObjectOrFunctionResultAwaited = ( Some extends (...args: any) => unknown ? FunctionResultAwaited : {} - ) & ( Some extends Object ? MethodsResultAwaited : Some ) + ) & ( Some extends Object ? MethodsResultAwaited & ConstructorResultAwaited : Some ) } From e0825a891ae6805d2c589046285c7fdfec5c82b8 Mon Sep 17 00:00:00 2001 From: Stefan Zerkalica Date: Mon, 4 Nov 2024 21:19:58 +0300 Subject: [PATCH 5/6] $mol_wire_sync fix wire sync type --- wire/sync/sync.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wire/sync/sync.ts b/wire/sync/sync.ts index 4baf4a717ee..ae1ad9177c9 100644 --- a/wire/sync/sync.ts +++ b/wire/sync/sync.ts @@ -18,7 +18,7 @@ namespace $ { * Convert asynchronous (promise-based) API to synchronous by wrapping function and method calls in a fiber. * @see https://mol.hyoo.ru/#!section=docs/=1fcpsq_1wh0h2 */ - export function $mol_wire_sync< Host extends object >( obj: Host ) { + export function $mol_wire_sync< Host extends object & (new ( ... args: unknown[] )=> unknown) >( obj: Host ) { return new Proxy( obj, { get( obj, field ) { @@ -40,7 +40,7 @@ namespace $ { }, construct(obj, args) { - const temp = $mol_wire_task.getter(factory(obj as new ( ... args: unknown[] )=> unknown)) + const temp = $mol_wire_task.getter(factory(obj)) return temp( obj, args ).sync() as object }, From 771fa6996238ce6c799c83d5c170cf01b21840a9 Mon Sep 17 00:00:00 2001 From: Stefan Zerkalica Date: Mon, 4 Nov 2024 21:49:51 +0300 Subject: [PATCH 6/6] $mol_wire_sync removed bad test --- wire/sync/sync.test.ts | 22 ---------------------- wire/sync/sync.ts | 20 +++++++------------- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/wire/sync/sync.test.ts b/wire/sync/sync.test.ts index 0f419ed4edd..2cdefadb02d 100644 --- a/wire/sync/sync.test.ts +++ b/wire/sync/sync.test.ts @@ -46,28 +46,6 @@ namespace $ { }, - async 'test construct from scope'( $ ) { - class A { - static instances = [] as A[] - - static a() { - const a = new ($mol_wire_sync(scope).A)() - this.instances.push( a ) - $mol_wire_sync(this).b() - } - - static b() { return Promise.resolve() } - } - const scope = { A } - - await $mol_wire_async(A).a() - $mol_assert_equal(A.instances.length, 2) - $mol_assert_equal(A.instances[0] instanceof A) - - $mol_assert_equal(A.instances[0], A.instances[1]) - - }, - async 'test construct itself'( $ ) { class A { static instances = [] as A[] diff --git a/wire/sync/sync.ts b/wire/sync/sync.ts index ae1ad9177c9..550a7ff5384 100644 --- a/wire/sync/sync.ts +++ b/wire/sync/sync.ts @@ -18,7 +18,7 @@ namespace $ { * Convert asynchronous (promise-based) API to synchronous by wrapping function and method calls in a fiber. * @see https://mol.hyoo.ru/#!section=docs/=1fcpsq_1wh0h2 */ - export function $mol_wire_sync< Host extends object & (new ( ... args: unknown[] )=> unknown) >( obj: Host ) { + export function $mol_wire_sync< Host extends object >( obj: Host ) { return new Proxy( obj, { get( obj, field ) { @@ -26,21 +26,15 @@ namespace $ { let val = (obj as any)[ field ] if( typeof val !== 'function' ) return val const temp = $mol_wire_task.getter(val) - - return new Proxy( val, { - apply( target, self, args ) { - return temp( obj, args ).sync() - }, - construct(target, args) { - const temp = $mol_wire_task.getter(factory(target)) - return temp( obj, args ).sync() as object - }, - - }) + + return function $mol_wire_sync( this: Host, ... args: unknown[] ) { + const fiber = temp( obj, args ) + return fiber.sync() + } }, construct(obj, args) { - const temp = $mol_wire_task.getter(factory(obj)) + const temp = $mol_wire_task.getter(factory(obj as (new ( ... args: unknown[] )=> unknown))) return temp( obj, args ).sync() as object },