microsoft / ClearScript

A library for adding scripting to .NET applications. Supports V8 (Windows, Linux, macOS) and JScript/VBScript (Windows).
https://microsoft.github.io/ClearScript/
MIT License
1.77k stars 148 forks source link

Exception in async code #599

Closed flat-eric147 closed 1 month ago

flat-eric147 commented 2 months ago

Hi,

how would ClearScript catch an exception when an error occurs in an async code segment? Take for example the following code:

` var request = new XMLHttpRequest() request.onreadystatechange = function () { if (request.readyState == 4 && request.status == 200) { try { var json = JSON.parse(request.responseText)

  // do something with the repsonse.... now do something stupid:
  let zero = null
  zero.stupid = 42
} catch (ex) {
  // handle stupid error
}

} } request.open("GET", "https://jsonplaceholder.typicode.com/users", true) request.setRequestHeader("Content-Type", "application/json") request.send() `

So if I hadn't placed the try/catch block the error would go undetected. Is there anything I can do in ClearScript to get an error message?

ClearScriptLib commented 2 months ago

Hi @flat-eric147,

It's difficult to provide a concise answer without knowing what XMLHttpRequest is. It isn't a JavaScript built-in, so it could be a .NET type, a COM type, or perhaps something else. Can you say anything about it? How are you exposing it to the script engine?

Thanks!

flat-eric147 commented 2 months ago

Hi, apologies, I totally forgot that I added a binding to XMLHttpRequest, so yes then it's a .NET type. I am exposing it through AddCOMType("XMLHttpRequest", "MSXML2.XMLHTTP"); So I guess that means I have no chance :)

ClearScriptLib commented 1 month ago

Hi @flat-eric147,

One possibility is to modify the onreadystatechange property so that the assigned callback is always invoked within a try...catch statement. Unfortunately, you're using a COM type, so extending it or modifying its implementation is probably not an option. One thing you can still do, however, is wrap it, and JavaScript's Proxy provides a nice mechanism for that.

First, let's import the COM type without exposing it to the script engine. We can use ExtendedHostFunctions.comType for that, although it's a bit "off-label":

var xmlHttpRequest = new ExtendedHostFunctions().comType("MSXML2.XMLHTTP");

Next, let's define a function that builds a proxy for that type. The proxy will intercept construction and return a proxy to the constructed instance that adds error handling to the onreadystatechange callback:

dynamic makeProxy = engine.Evaluate(@"
    impl => new Proxy(impl, {
        construct(target, args) {
            let callback;
            return new Proxy(new target(...args), {
                get(target, name, receiver) {
                    if (name === 'onreadystatechange') {
                        return callback;
                    }
                    return Reflect.get(target, name, receiver);
                },
                set(target, name, value, receiver) {
                    if (name === 'onreadystatechange') {
                        callback = value;
                        value = function () {
                            try {
                                callback();
                            } catch (exception) {
                                // -=> HANDLE EXCEPTION HERE <=-
                            }
                        }
                    }
                    return Reflect.set(target, name, value, receiver);
                }
            });
        }
    })
");

Finally, we'll call our function to build the proxy and expose it to the script engine as XMLHttpRequest:

engine.Script.XMLHttpRequest = makeProxy(xmlHttpRequest);

Note that you can do whatever you want inside that catch clause – log the exception, invoke another host method, etc. Please let us know if this solution works for you.

Thanks!

flat-eric147 commented 1 month ago

oh boy, this makes me almost cry, it's a thing of great beauty. It works nicely! I'm glad I asked, I would have never worked that out on my own. Thank you!!!!

ClearScriptLib commented 1 month ago

Thanks for letting us know! Please reopen this issue if you have additional questions or comments. Thanks again!