Getting Started
Relevant source files
The following files were used as context for generating this wiki page:
- Jurassic/Core/ScriptEngine.cs
- REPL/Program.cs
- README.md
- [Unit Tests/Core/DocumentationSamples.cs](https://github.com/zhgabor/jurassic/blob/HEAD/Unit Tests/Core/DocumentationSamples.cs)
- [Unit Tests/Real-world/RealWorldTests.cs](https://github.com/zhgabor/jurassic/blob/HEAD/Unit Tests/Real-world/RealWorldTests.cs)
- Jurassic/Compiler/MethodGenerator/MethodGenerator.cs
- Jurassic/Library/GlobalObject.cs
- Attribute Code Generation/Program.cs
Jurassic is a compiler that transforms JavaScript into .NET bytecode (CIL), allowing for the execution of JavaScript code within a .NET application. It is not an interpreter. The primary entry point for all scripting functionality is the ScriptEngine class, which provides the environment for compiling and running JavaScript.
This guide covers the basic steps to integrate and use the Jurassic engine, including initialization, script execution, and interoperability between .NET and JavaScript. The engine aims for high performance and standards compliance with ECMAScript 5, with ongoing progress towards ECMAScript 6.
Sources: README.md:8-12, Jurassic/Core/ScriptEngine.cs:12-15
Initializing the Script Engine
The first step is to create an instance of the ScriptEngine class. The constructor sets up the entire JavaScript runtime environment, including all built-in objects and functions.
Sources: Jurassic/Core/ScriptEngine.cs:91-94, Unit Tests/Core/DocumentationSamples.cs:16-18
Engine Constructor Flow
When a new ScriptEngine() is instantiated, it performs the following setup sequence:
- Creates the base of the prototype chain (
Object.prototype). - Creates the core function prototype (
Function.prototype). - Initializes all built-in constructor functions (e.g.,
Object,Function,Array,String,Number,RegExp,Date,Errortypes, and ES6 types likeMap,Set,Promise,Symbol). - Populates the
GlobalObjectwith these built-in constructors and other global properties likeMath,JSON,Infinity,NaN, andundefined. - Global functions like
parseInt,parseFloat,encodeURI, etc., are also attached to the global object.
This diagram illustrates the initialization process within the ScriptEngine constructor.
Executing JavaScript
The ScriptEngine provides several methods to execute JavaScript code. The most common are Evaluate() and Execute().
Sources: Jurassic/Core/ScriptEngine.cs:788-990
Evaluate vs. Execute
Evaluate(): Executes a script and returns the value of the last executed expression. It has a generic overloadEvaluate<T>()to convert the result to a specific .NET type.Execute(): Executes a script without returning a value. This is suitable for scripts that perform actions but do not produce a result, such as setting up functions or modifying state.ExecuteFile(): A convenience method to execute a script directly from a file path.
Sources: Jurassic/Core/ScriptEngine.cs:788-806, Jurassic/Core/ScriptEngine.cs:902-936
The following examples demonstrate basic usage:
// From Unit Tests/Core/DocumentationSamples.cs
var engine = new ScriptEngine();
// Using Evaluate() to get a result
Assert.AreEqual(52, engine.Evaluate("5 * 10 + 2"));
// Using Evaluate<T>() to get a typed result
Assert.AreEqual(3, engine.Evaluate<int>("1.5 + 2.4"));
// Using Execute() for a script file
engine.ExecuteFile(@"../../../Core/Sample Files/execute1.js");
Sources: Unit Tests/Core/DocumentationSamples.cs:16-30
The Compilation and Execution Pipeline
Jurassic compiles JavaScript source into CIL before execution. This process involves several stages managed internally by the engine.
This flowchart shows the steps from providing a script source to getting a result.
.NET Interoperability
A key feature of Jurassic is the ability to facilitate communication between the .NET host application and the JavaScript runtime.
Exposing .NET to JavaScript
You can expose .NET variables, objects, and functions to the JavaScript global scope.
Setting Global Variables
Use SetGlobalValue() to create or modify global JavaScript variables from .NET. The value can be a primitive, a null, or an ObjectInstance.
// From Unit Tests/Core/DocumentationSamples.cs
var engine = new ScriptEngine();
engine.SetGlobalValue("interop", 15);
engine.ExecuteFile(@"../../../Core/Sample Files/globals1.js");
Assert.AreEqual(20, engine.GetGlobalValue<int>("interop"));
Sources: Unit Tests/Core/DocumentationSamples.cs:33-38, Jurassic/Core/ScriptEngine.cs:1071-1129
Exposing .NET Functions
Use SetGlobalFunction() to expose a .NET Delegate (like Func<> or Action<>) as a global JavaScript function.
// From Unit Tests/Core/DocumentationSamples.cs
var engine = new ScriptEngine();
engine.SetGlobalFunction("test", new Func<int, int, int>((a, b) => a + b));
Assert.AreEqual(11, engine.Evaluate<int>("test(5, 6)"));
Sources: Unit Tests/Core/DocumentationSamples.cs:47-52, Jurassic/Core/ScriptEngine.cs:1162-1171
Exposing .NET Classes
To expose a .NET class, you can wrap it in a class that inherits from Jurassic.Library.ObjectInstance. Functions can be exposed using the [JSFunction] attribute, and constructors with [JSConstructorFunction]. The Attribute Code Generation project automatically generates the necessary stub code for these attributes.
This class diagram shows the structure for exposing a custom Math2 object.
Calling JavaScript from .NET
You can call a global JavaScript function from your .NET code using CallGlobalFunction().
// From Unit Tests/Core/DocumentationSamples.cs
var engine = new ScriptEngine();
engine.Evaluate("function test(a, b) { return a + b }");
Assert.AreEqual(11, engine.CallGlobalFunction<int>("test", 5, 6));
Sources: Unit Tests/Core/DocumentationSamples.cs:55-59, Jurassic/Core/ScriptEngine.cs:1132-1160
This sequence diagram illustrates the two-way communication flow.
Engine Configuration
The ScriptEngine has several properties to control its behavior.
| Property | Description | Default |
|---|---|---|
ForceStrictMode | Forces all executed code to run in ECMAScript 5 strict mode, even without a "use strict"; directive. | false |
CompatibilityMode | Can be set to ECMAScript3 or ECMAScript5 (default). Affects behavior like the writability of Infinity, NaN, and undefined. | ECMAScript5 |
EnableILAnalysis | If true, disassembles the generated IL and stores it in the DisassembledIL property of the corresponding GeneratedMethod. | false |
RecursionDepthLimit | Sets a limit on the recursion depth for user-defined functions to prevent stack overflow. A value of 0 means no limit. | 0 |
Sources: Jurassic/Core/ScriptEngine.cs:256-302
Example: A Simple REPL
The REPL project demonstrates a basic Read-Eval-Print-Loop, showcasing how to continuously execute user input with a persistent ScriptEngine instance.
// Simplified from REPL/Program.cs
var engine = new ScriptEngine();
// Set up a Firebug-style console.
engine.SetGlobalValue("console", new Jurassic.Library.FirebugConsole(engine));
while (true)
{
Console.Write("] ");
string input = Console.ReadLine();
if (input == "quit")
break;
try
{
var result = engine.Evaluate(input);
if (result != Undefined.Value)
Console.WriteLine(result);
}
catch (JavaScriptException ex)
{
Console.WriteLine(ex.Message);
}
}
Sources: REPL/Program.cs, Jurassic/Library/FirebugConsole/FirebugConsole.g.cs