Error Handling
Relevant source files
The following files were used as context for generating this wiki page:
The Jurassic engine provides a robust error handling mechanism that bridges the gap between .NET exceptions and JavaScript errors. It distinguishes between compile-time (syntax) errors and runtime errors, wrapping JavaScript Error objects within .NET Exception classes. This allows .NET code hosting the engine to catch and inspect errors originating from script execution, including detailed information like stack traces, line numbers, and source file paths.
The system is designed to be compliant with the ECMAScript standard, supporting the full prototype chain for built-in Error types and providing detailed stack trace information that is populated when an error is thrown.
.NET Exception Classes
Jurassic uses two primary .NET exception classes to represent errors. These classes are the main interface for C# code to catch and handle issues from the script engine.
JavaScriptException
This is the primary wrapper for all runtime errors that occur during script execution. It can be thrown when a JavaScript throw statement is executed, or when the engine encounters a runtime violation, such as a TypeError or ReferenceError.
The following diagram shows the structure of the JavaScriptException class.
Diagram showing JavaScriptException inheriting from the base .NET Exception class.
Key Properties
| Property | Type | Description |
|---|---|---|
ErrorObject | object | The underlying JavaScript object that was thrown. This is often an ErrorInstance. |
ErrorType | ErrorType? | The enumerated type of the error (e.g., TypeError, RangeError). |
ErrorMessage | string | The error message, excluding the error type name. |
LineNumber | int | The line number in the source file where the error occurred. |
SourcePath | string | The path or URL of the source file. |
FunctionName | string | The name of the function where the exception occurred. |
Engine | ScriptEngine | A reference to the script engine associated with the error. |
Sources: Jurassic/Core/JavaScriptException.cs:9-160
JavaScriptException has multiple constructors to facilitate creating exceptions from both .NET code and from within the engine when a JavaScript error is thrown. When a JavaScriptException is created from a JavaScript ErrorInstance, it automatically populates its properties and sets the stack trace on the original JavaScript error object.
Sources: Jurassic/Core/JavaScriptException.cs:17-121
SyntaxErrorException
This exception is thrown by the compiler when the JavaScript source code contains a syntax error. It is distinct from JavaScriptException as it occurs during the parsing phase, before any code is executed.
Key Properties
| Property | Type | Description |
|---|---|---|
LineNumber | int | The line number in the source file where the syntax error was detected. |
SourcePath | string | The path or URL of the source file. |
FunctionName | string | The name of the function being parsed, if applicable. |
Sources: Jurassic/Compiler/SyntaxErrorException.cs:9-49
JavaScript Error Objects
On the JavaScript side, errors are represented by objects that inherit from Error.prototype.
ErrorInstance
ErrorInstance is the base .NET class for all JavaScript error objects. It provides the core properties and methods defined by the ECMAScript standard for errors.
Key Properties
| Property | Type | Description |
|---|---|---|
Name | string | The name for the type of error (e.g., "Error", "TypeError"). |
Message | string | A human-readable description of the error. |
Stack | string | The stack trace. This is populated when the error is thrown, not when it is created. |
Sources: Jurassic/Library/Error/ErrorInstance.cs:78-98
The toString() method of an ErrorInstance returns a string formatted as "Name: Message".
Sources: Jurassic/Library/Error/ErrorInstance.cs:111-129
Prototype Chain
Jurassic correctly implements the prototype chain for all standard error types. For example, a RangeError instance inherits from RangeError.prototype, which in turn inherits from Error.prototype, and finally Object.prototype.
This diagram illustrates the prototype chain for a new RangeError('test') object.
Diagram showing the JavaScript error object prototype chain.
Sources: Unit Tests/Library/ErrorTests.cs:33-71
Stack Trace Generation
A key feature of Jurassic's error handling is the generation of detailed stack traces. The stack property of an ErrorInstance is undefined when the error is first created. It is only populated when a throw statement is executed and the corresponding JavaScriptException is caught by the engine.
The process is as follows:
- JavaScript code throws an
Errorobject. - The Jurassic engine catches this and wraps it in a
.NET JavaScriptException. - During the
JavaScriptException's construction, it callsSetStackTrace()on the originalErrorInstance. SetStackTrace()callsEngine.FormatStackTrace(), which builds the complete stack string.- The formatted string is then assigned to the
stackproperty of theErrorInstance.
The following sequence diagram illustrates this flow.
Diagram showing the sequence of events for populating an error's stack trace.
Sources: Jurassic/Core/JavaScriptException.cs:36-42, Jurassic/Library/Error/ErrorInstance.cs:100-105, Jurassic/Core/ScriptEngine.cs:1130-1153, Unit Tests/Library/ErrorTests.cs:74-167
Error Creation and Propagation
Errors can be created and thrown from both JavaScript and .NET code, and the engine itself can generate errors for invalid operations.
The general flow for a JavaScript-initiated error that is caught in .NET is shown below.
Flowchart showing how a JavaScript throw propagates to a .NET catch block.
Sources: Jurassic/Core/ScriptEngine.cs:989-992, Jurassic/Core/JavaScriptException.cs:17-43
Creating Errors in .NET
You can create a JavaScriptException directly in C# code to simulate a JavaScript error. This is useful for custom host functions that need to report errors back to the script.
// Create a new JavaScriptException instance.
throw new JavaScriptException(ErrorType.TypeError, "An operation was not supported.");
// Create an exception with line and source information.
throw new JavaScriptException(ErrorType.RangeError, "Index out of bounds", 5, "myFile.js");
Sources: Jurassic/Core/JavaScriptException.cs:45-121
Implicit Engine Errors
The Jurassic engine will implicitly create and throw errors for standard JavaScript violations. For example:
- Calling
decodeURIwith a malformed URI string will throw aURIError. - Attempting to call a method on
nullorundefinedwill throw aTypeError.
// Example from GlobalObject.cs that throws a URIError
private static string Decode(ScriptEngine engine, string input, bool[] reservedSet)
{
// ...
if (utf8Byte < 0)
throw new JavaScriptException(ErrorType.URIError, "URI malformed");
// ...
}
Sources: Jurassic/Library/GlobalObject.cs:310-312, Unit Tests/Library/GlobalTests.cs:107-117
Standard Error Types
Jurassic supports the standard built-in error types. These are defined in the ErrorType enum.
ErrorType Enum Member | Corresponding JavaScript Error |
|---|---|
Error | Error |
RangeError | RangeError |
TypeError | TypeError |
SyntaxError | SyntaxError |
URIError | URIError |
EvalError | EvalError |
ReferenceError | ReferenceError |
The ScriptEngine initializes a constructor for each of these error types (e.g., engine.Error, engine.TypeError).
Sources: Jurassic/Core/JavaScriptException.cs:38, Jurassic/Core/ScriptEngine.cs:118-125
Conclusion
Jurassic's error handling system effectively models the ECMAScript error specification while providing a clear and powerful bridge to the .NET exception handling world. By wrapping JavaScript errors in JavaScriptException and providing detailed context like line numbers and stack traces, it enables developers to robustly debug and manage errors that cross the boundary between managed C# code and interpreted JavaScript. The distinction between SyntaxErrorException for compile-time issues and JavaScriptException for runtime issues allows for precise error management strategies.