Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
DavidBal opened this issue Aug 29, 2023 · 3 comments
Open

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

DavidBal opened this issue Aug 29, 2023 · 3 comments
Assignees

Comments

@DavidBal
Copy link

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

@ClearScriptLib ClearScriptLib self-assigned this Aug 29, 2023
@ClearScriptLib
Copy link
Collaborator

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!

@ClearScriptLib
Copy link
Collaborator

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!

@DavidBal
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants