This project has moved and is read-only. For the latest updates, please go here.

Returning syntax error location

Apr 28, 2015 at 12:00 AM
While working on a testing framework that executes JavaScript tests, I wanted to have ClearScript tell me the location of the syntax errors it found. It doesn't.

This is in a non-browser environment. I thought version 5.4.1 would fix it. It doesn't.

A colleague debugged the DLL and discovered that the GetDetails() method in the WindowsScriptEngine class (WindowsScriptEngine.Site.cs) will return either a stackTrace or an error location but not both.

The answer was to rework the try block to be:

try
                {
                    var errorLocation = GetErrorLocation(error);
                    if (!string.IsNullOrWhiteSpace(errorLocation))
                    {
                        message += "\n" + errorLocation;
                    }

                    var stackTrace = engine.GetStackTraceInternal();
                    if (!string.IsNullOrWhiteSpace(stackTrace))
                    {
                        message += "\n" + stackTrace;
                    }

                    return message;

                }
Now it returns both in one message.
Coordinator
Apr 28, 2015 at 2:12 AM
Hello!

If the stack trace can be retrieved successfully, there should be no need to also retrieve the location string, because the former is a superset of the latter. For example, here's a sample stack trace:
Function expected
    at baz (foo.js:2:28) -> 123()
    at bar (foo.js:5:28) -> baz()
    at foo (foo.js:8:28) -> bar()
    at JScript global code (foo.js:10:24) -> foo()
And here's the location string for the same error:
Function expected
    at (foo.js:2:28) -> 123();
As you can see, the location string is redundant if the stack trace is available. Note that the script engine can detect some errors (such as syntax errors) before it starts executing script code. In that case there's no stack trace, so it makes sense to use the location string.

Thanks!
Apr 28, 2015 at 6:19 PM

Well, in our case, it did not work that way!

We are using Node.js code to ‘require’ our scripts as in the following stack trace:

ScriptEngineException Error loading module /NativeModeApi.js: Syntax error

at (NativeModeApi.js:38:4) -> try {

at anonymous (anonymous code:3:12) -> __scriptGlobal.executeModule('/NativeModeApi.js',require,exports,module)

at buildModule (modules.js:429:6) -> exports = factory.call(null, module.require, module.exports, module)

at memoizeModule (modules.js:380:8) -> buildModule(cache_memo[id].module, factory)

at eventLoad (modules.js:246:4) -> memoizeModule(id, this, event.target.responseText, false)

at JScript anonymous function (modules.js:14:303) -> return e.apply(this instanceof c ? this : b || window, f.concat(a.call(arguments)))

at JScript anonymous function (modules.js:222:10) -> xhr.send(null)

at require (modules.js:275:8) -> module.load(id, null)

at JScript global code (Script Document [temp]:0:0) -> __scriptTestBase.scanScript(require('NativeModeApi'))

The top line points to the line in the test script at which I purposely introduced a syntax error.

Without the change we made as specified in my original post (i.e. the original 5.4.1 code), the top line, “at (NativeModeApi.js:38:4) -> try {“

Is not present.

Coordinator
Apr 28, 2015 at 7:07 PM
Hi again,

Is executeModule a .NET method? If so, does it re-enter the script engine internally?

Thanks!
Apr 28, 2015 at 7:24 PM

Yes, it is a .Net method and yes, it re-enters the script engine.

Apr 28, 2015 at 9:52 PM
Hi,

I just wanted to chime in here. I am the colleague that pblattnerjr mentions above.

We have exposed a .NET global object through ClearScript which contains a method which loads the contents of a referenced JavaScript module and calls the ScriptEngine.Execute method. We do this so that we can associate a document name with a dynamically loaded module. We used this to create a module loader similar to the node.js require syntax.

From our .NET application we call ScriptEngine.Execute to run JavaScript module, and associate a document name with that module. From that JavaScript module we call a .NET method which executes another JavaScript module. If that secondary JavaScript module contains a SyntaxError, the location is not reported because we have a JavaScript stack trace (due to the first JavaScript module).
Coordinator
Apr 29, 2015 at 3:37 AM
Hello,

Thanks for describing your scenario.

You're right, it does appear that a syntax error occurring in a nested invocation of the script engine can have a stack trace as well as a useful location string. ClearScript should detect this case and report both.

We'll fix this in the next point release.

Thank again!
Jul 14, 2016 at 1:09 PM
Edited Jul 14, 2016 at 3:21 PM
Hi,

I seem to have the same problem.
I adapted the basic module loader of node.js (native_module.js). Loading a module will call into a run method in the JavaScript module implementation. This in turn calls back into c# to finally execute the code on the engine.
Doing this however does not give us useful information like position in case of syntax errors in the module to be loaded:
SyntaxError: Unexpected token ILLEGAL
    at NativeModule.run (snp_native_module.js [temp]:140:41) -> exports.checkMeasurement = function (measurement) {§
    at NativeModule.run (snp_native_module.js [temp]:65:22)
The wrong token § is not in snp_native_module.js. But 140:41 in snp_native_module.js is the location where I call back to c# to execute the module.

Currently we use version 5.4.5.
Is this already fixed in 5.4.6? Or is there any workaround?
Coordinator
Jul 14, 2016 at 3:18 PM
Hi ZoneRunner,

Are you seeing this with JScript/VBScript or V8?

Thanks!
Jul 14, 2016 at 3:20 PM
We are using V8.
Coordinator
Jul 14, 2016 at 3:55 PM
Edited Jul 14, 2016 at 3:55 PM
Hi again,

Exceptions thrown from V8ScriptEngine, even when passing though multiple host-to-script transitions, should be linked to the root exception via InnerException. Try traversing the exception chain, or calling GetBaseException, to get to the one that corresponds to the syntax error. That should contain all the information that V8 provides.

Good luck!
Jul 16, 2016 at 9:20 PM
Edited Jul 16, 2016 at 9:25 PM
Hi,

well it seems the problem is not as easy to describe as I hoped so I have build a small example:
public class Program
{
    static void Main(string[] args)
    {
        try
        {
            var host = new ScriptHost();
            dynamic result = host.LoadModule();
            Console.WriteLine(result.content);
        }
        catch (Exception ex)
        {
            // (0)
            throw;
        }
    }
}

public class ScriptHost
{
    private V8ScriptEngine _engine = new V8ScriptEngine("MyEngine");
    private dynamic _moduleLoader;

    public ScriptHost()
    {
        var moduleLoaderSource = @"(function loadModule(host, name, source) { return host.Run(name, source); })";
        _moduleLoader = _engine.Evaluate("module_loader.js", moduleLoaderSource);
    }

    public object LoadModule()
    {
        var someModuleSource = @"({ content: 'There is a syntax error in this module.'§ })";
        return _moduleLoader(this, "some_module.js", someModuleSource);
    }

    public object Run(string name, string source)
    {
        try
        {
            return _engine.Evaluate(name, source);
        }
        catch (Exception ex)
        {
            // (1)
            throw;
        }
    }
}
Now let's look at the ErrorDetail property of the two exceptions (1) and (2):
(1)
"SyntaxError: Invalid or unexpected token
    at loadModule (module_loader.js [temp]:1:56) -> ({ content: 'There is a syntax error in this module.'§ })"
The stacktrace looks confusing here. While the given source code is indeed the syntax error, it claims that this line is in module_loader.js [temp]:1:56 which is wrong.
(2)
"Error: SyntaxError: Invalid or unexpected token
    at loadModule (module_loader.js [temp]:1:56) -> (function loadModule(host, name, source) { return host.Run(name, source); })"
Ok, now the given source code corresponds to the location. However, that only means that now both are wrong. Much less problematic but also strange: The new "Error:" prefix that was not part of the messsage at (1).

Ideally I would expect a stacktrace like this:
"SyntaxError: Invalid or unexpected token
    at (some_module.js [temp]:1:53) -> ({ content: 'There is a syntax error in this module.'§ })"
    at loadModule (module_loader.js [temp]:1:56)"
Coordinator
Jul 17, 2016 at 5:48 PM
Hi ZoneRunner,

Thanks for posting your sample code!

The detail text of exception 1 does appear to be formatted incorrectly. When V8 initially reports the syntax error, ClearScript is confused by the presence of a stack trace, since script code with syntax errors isn't executable. ClearScript's assumption is incorrect in cases involving nested invocations of the script engine.

The outer exception (exception 2) is correct, however. The syntax error passes through the host, where it's wrapped in a host exception, and as is the case with all host exceptions, it's passed back to the outer script frame as a JavaScript object of type Error, whose constructor adds the "Error: " prefix.

ClearScript's next point release will include a fix for the formatting of the inner exception's detail text. With the fix, the inner exception will look exactly as you suggest; its detail text will include the location of the syntax error as well as the stack traces of any outer script frames.

Thanks again!
Jul 19, 2016 at 7:02 AM
Great :)
Thanks.