Building from Source
Relevant source files
The following files were used as context for generating this wiki page:
Jurassic is an implementation of the ECMAScript language that compiles JavaScript into .NET bytecode (CIL). Building the project from source involves understanding its multi-project structure, a key C# code generation step, and the core runtime compilation pipeline. The project is designed to be integrated into .NET programs and is deployed as a single .NET assembly.
The build process is not a simple compilation of C# code. It includes a custom tool that generates C# source code files (.g.cs) based on custom attributes in the main library. This generated code is then compiled along with the rest of the Jurassic library. The solution also includes projects for unit testing, standards-compliance testing, and performance benchmarking.
Sources: README.md:5-10, README.md:15
Project Structure
The solution is composed of several projects, each with a distinct role. The main Jurassic library is the primary output, while other projects support its development, testing, and validation.
| Project Name | Description | Source Files -
| Jurassic | The core JavaScript engine library. This is the main distributable assembly. | Jurassic/Jurassic.csproj -
| Attribute Code Generation | A console tool that uses Roslyn to scan the Jurassic library's source and generate .g.cs files for marshalling between .NET and JS. | Attribute Code Generation/Program.cs -
| Unit Tests | A test project containing various unit tests, including execution of real-world JavaScript libraries like showdown.js and coffee-script.js. | Unit Tests/Real-world/RealWorldTests.cs -
| Test Suite Runner | An application to run external JavaScript test suites, such as the ECMA-262 test suite, to ensure standards compliance. | Test Suite Runner/Test Suite Files/harness/helper.js -
| Benchmarker | A console application for running performance benchmarks, primarily using the SunSpider test suite. | Benchmarker/Program.cs -
This diagram illustrates the dependencies between the projects in the solution.
Build Process Overview
The build process for Jurassic consists of two main compilation stages:
- C# Code Generation (Pre-build): A dedicated tool,
Attribute Code Generation, parses the C# source code of theJurassiclibrary. It looks for specific custom attributes (e.g.,[JSInternalFunction]) and generates helper C# files (.g.cs). These files contain stub methods that handle the type conversions and boilerplate for exposing .NET methods to the JavaScript runtime. - JavaScript to CIL Compilation (Runtime): When
ScriptEngine.Execute()orScriptEngine.Evaluate()is called, the Jurassic runtime compiles the provided JavaScript code into .NET Common Intermediate Language (CIL). This is a just-in-time (JIT) compilation, not an interpretation, which is key to its performance.
The following diagram shows the high-level flow of these two stages.
C# Attribute-Based Code Generation
A critical part of the build process is the automatic generation of stub methods. This is handled by the Attribute Code Generation project. This console application uses the Roslyn C# compiler platform to analyze the source code of the Jurassic library.
Sources: Attribute Code Generation/Program.cs:7-13
Process Flow
- The tool enumerates all
.csfiles in theJurassicproject directory. - For each file, it builds a syntax tree using
CSharpSyntaxTree.ParseText. - A
ClassMembersCollector(aCSharpSyntaxWalker) visits nodes in the syntax tree to find methods and properties decorated with specificJSattributes. - If any such members are found, it generates a corresponding
.g.csfile (e.g.,GlobalObject.g.csforGlobalObject.cs). - This generated file contains a partial class with private static
__STUB__or__GETTER__methods. These stubs handle argument parsing, type conversion, and calling the original .NET method.
This diagram shows the logic within the Attribute Code Generation tool.
Key Attributes
The code generation tool looks for the following attributes to create the JavaScript-to-.NET bindings.
| Attribute | Purpose | Example File |
|---|---|---|
[JSInternalFunction] | Exposes a .NET method as a JavaScript function. | Jurassic/Library/GlobalObject.cs:70 |
[JSCallFunction] | Marks the method that implements the [[Call]] internal method for a function object. | Attribute Code Generation/Program.cs:175 |
[JSConstructorFunction] | Marks the method that implements the [[Construct]] internal method for a constructor. | Unit Tests/Core/DocumentationSamples.cs:115 |
[JSProperty] | Exposes a .NET property as a JavaScript property with a getter (and optionally a setter). | Jurassic/Library/RegExp/RegExpInstance.g.cs:16-20 |
[JSField] | Exposes a .NET field as a JavaScript property. | Attribute Code Generation/Program.cs:187 |
Sources: Attribute Code Generation/Program.cs:171-189
Generated Code Example
For the decodeURI function in GlobalObject.cs, the following stub is generated in GlobalObject.g.cs. This stub handles the logic for different numbers of arguments passed from JavaScript and performs the necessary type conversion before calling the actual C# implementation.
// Source: Jurassic/Library/GlobalObject.g.cs:33-42
private static object __STUB__DecodeURI(ScriptEngine engine, object thisObj, object[] args)
{
switch (args.Length)
{
case 0:
return DecodeURI(engine, "undefined");
default:
return DecodeURI(engine, TypeConverter.ToString(args[0]));
}
}
This generated code simplifies the core library logic by offloading the argument handling and type marshalling to these auto-generated stubs.
JavaScript to CIL Runtime Compilation
The core of Jurassic's functionality is its ability to compile JavaScript to .NET CIL at runtime. This process is initiated by the ScriptEngine class.
Sources: Jurassic/Core/ScriptEngine.cs:814-817
Compilation Pipeline
The ScriptEngine.Evaluate() and Execute() methods orchestrate the compilation.
- Source Input: A
ScriptSource(e.g.,StringScriptSource,FileScriptSource) provides the JavaScript code. - Method Generation: A
MethodGenerator(specificallyGlobalOrEvalMethodGeneratorfor global code) is created. - Parsing: The
MethodGenerator.Parse()method is called, which uses aParserto transform the source code into an Abstract Syntax Tree (AST). The root of this tree is aStatement(often aBlockStatement). - Optimization: The
MethodGenerator.Optimize()method is called to perform optimizations on the AST. - Code Generation:
MethodGenerator.GenerateCode()is called. This method uses anILGeneratorto traverse the AST and emit .NET CIL into aDynamicMethod. - Execution: The generated
DynamicMethodis compiled into a delegate and executed.
This process is visualized in the following diagram.
Key Compilation Classes
| Class | Role - |
|---|---|
ScriptEngine | The main entry point for executing JavaScript. It manages the global state and orchestrates the compilation and execution process. -" |
ScriptSource | An abstract base class representing a source of script code, such as a string or a file. - |
MethodGenerator | An abstract class that represents a unit of compilation (e.g., global code, a function). It manages parsing, optimization, and IL generation. - |
l OptimizationInfo | A class that holds information for code generation optimizations, such as strict mode status, source code location, and loop/return targets. - |
BlockStatement | Represents a block of statements in the AST, which corresponds to a lexical scope. It manages the generation of code for the statements it contains. - |
Sources: Jurassic/Core/ScriptEngine.cs, Jurassic/Core/ScriptSource/ScriptSource.cs, Jurassic/Compiler/MethodGenerator/MethodGenerator.cs, Jurassic/Compiler/Emit/OptimizationInfo.cs, Jurassic/Compiler/Statements/BlockStatement.cs
Conclusion
Building Jurassic from source is a two-fold process. First, a custom C# code generation tool uses Roslyn to create binding logic that connects the .NET and JavaScript worlds. This generated code, along with the handwritten library code, is compiled into the Jurassic.dll assembly. Second, at runtime, this library uses a sophisticated pipeline to compile JavaScript source code directly into .NET CIL for high-performance execution. This dual-compilation architecture is fundamental to Jurassic's design, enabling both rich .NET interoperability and efficient script execution.
Sources: Attribute Code Generation/Program.cs, Jurassic/Core/ScriptEngine.cs