microsoft/ClearScript

How to map obj[Symbol.dispose] to obj.Dispose()

DavidBal opened this issue · 3 comments

Hi ClearScript Team,

in the recent update of TypeScript the using statement was added.
https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/#using-declarations-and-explicit-resource-management

The JavaScript for this feature looks something like this:

const obj = new DispoableObject();

try{

}
finally
{
 obj[Symbol.dispose]();
}

Is there any way we can map this call to CSharp?

Thanks in advance.

Best regards,
David

Hi @DavidBal,

Host objects in ClearScript currently don't support Symbol.dispose; in fact, V8 defines no such symbol. However, a relevant proposal appears to be in flight, and TypeScript is evidently ahead of the curve.

Anyway, assuming that Symbol.dispose is available, you could do something like this:

dynamic setupFunc = engine.Evaluate(@"(
    function (host, IDisposable) {
        Object.defineProperty(Object.getPrototypeOf(host), Symbol.dispose, {
            get() {
                const disposable = host.asType(IDisposable, this);
                return disposable ? () => disposable.Dispose() : undefined;
            }
        });
    }
)");
setupFunc(new HostFunctions(), typeof(IDisposable).ToHostType(engine));

With this setup in place, most host objects will support Symbol.dispose. We say "most" because the code above adds Symbol.dispose only to the HostFunctions prototype, which is shared among all "normal" host objects. There are other prototypes specifically for delegates and dynamic objects, for which a similar technique can be used if necessary.

We'll keep an eye on the proposal and add full support if and when it's incorporated into the JavaScript standard.

Good luck!

Hi again,

Here's a more complete solution:

dynamic setupFunc = engine.Evaluate(@"(
    function (obj, host, IDisposable) {
        Object.defineProperty(Object.getPrototypeOf(obj), Symbol.dispose, {
            get() {
                const disposable = host.asType(IDisposable, this);
                return disposable ? () => disposable.Dispose() : undefined;
            }
        });
    }
)");
var host = new HostFunctions();
var disposable = typeof(IDisposable).ToHostType(engine);
setupFunc(host, host, disposable);
setupFunc(new Action(() => {}), host, disposable);
setupFunc(new PropertyBag(), host, disposable);

This should set up Symbol.dispose support for all host objects and types.

Cheers!

Hi,

thank you that works great.

Here is my full implementation, should someone else have the same problem:

dynamic setupDisposeFunc = scriptEngine.Evaluate("""
(
    function (obj, host, IDisposable) {
                            
        Symbol.dispose ??= Symbol("Symbol.dispose");
        
        Object.defineProperty(Object.getPrototypeOf(obj), Symbol.dispose, {
            get() {
                const disposable = host.asType(IDisposable, this);
                return disposable ? () => disposable.Dispose() : undefined;
            }
        });
    }
)
""");

dynamic setupAsyncDisposeFunc = scriptEngine.Evaluate("""
(
    function (obj, host, IAsyncDisposable) {
                            
        Symbol.asyncDispose ??= Symbol("Symbol.asyncDispose");
        
        Object.defineProperty(Object.getPrototypeOf(obj), Symbol.asyncDispose, {
            get() {
                const disposable = host.asType(IAsyncDisposable, this);
                return disposable ? () => disposable.DisposeAsync() : undefined;
            }
        });
    }
)
""");

HostFunctions host = new HostFunctions();
object disposable = typeof(IDisposable).ToHostType(scriptEngine);
object asyncDisposable = typeof(IAsyncDisposable).ToHostType(scriptEngine);

setupDisposeFunc(host, host, disposable);
setupDisposeFunc(new Action(() => { }), host, disposable);
setupDisposeFunc(new PropertyBag(), host, disposable);

setupAsyncDisposeFunc(host, host, asyncDisposable);
setupAsyncDisposeFunc(new Action(() => { }), host, asyncDisposable);
setupAsyncDisposeFunc(new PropertyBag(), host, asyncDisposable);

Best regards,
David