Runtime Environment
Relevant source files
The following files were used as context for generating this wiki page:
- Jurassic/Compiler/ExecutionContext/RuntimeScope.cs
- Jurassic/Library/GlobalObject.cs
- Jurassic/Core/ScriptEngine.cs
- Jurassic/Compiler/Emit/OptimizationInfo.cs
- Jurassic/Compiler/MethodGenerator/MethodGenerator.cs
- [Unit Tests/Core/DocumentationSamples.cs](https://github.com/zhgabor/jurassic/blob/HEAD/Unit Tests/Core/DocumentationSamples.cs)
- Jurassic/Library/GlobalObject.g.cs
The Jurassic runtime environment is the ecosystem responsible for compiling and executing JavaScript code. Unlike an interpreter, Jurassic is a compiler that transforms JavaScript into .NET Common Intermediate Language (CIL) for high performance. The environment is orchestrated by the ScriptEngine, which manages the entire lifecycle of script execution, from parsing to execution.
The core components of the runtime environment are the ScriptEngine, the GlobalObject, and the RuntimeScope. The ScriptEngine serves as the primary interface for developers, initializing all built-in JavaScript objects and handling execution requests. The GlobalObject represents the global namespace, and RuntimeScope manages the complexities of lexical scoping for variables. Together, these components create a complete, standards-compliant implementation of the ECMAScript runtime.
Sources: README.md:9-12, Jurassic/Core/ScriptEngine.cs
ScriptEngine: The Core Component
The ScriptEngine is the central class in the Jurassic library and the main entry point for executing JavaScript. It encapsulates the state of the JavaScript environment, including all built-in objects, the global scope, and configuration settings.
Sources: Jurassic/Core/ScriptEngine.cs
Initialization
When a ScriptEngine instance is created, it performs a comprehensive setup process to establish the JavaScript environment. This includes:
- Creating the base of the prototype chain (
Object.prototypeandFunction.prototype). - Instantiating all built-in constructor functions (e.g.,
Object,Array,String,Number,RegExp,Promise, and allErrortypes). - Creating special singleton objects like
MathandJSON. - Initializing the
GlobalObject, which will hold all the global variables and functions. - Populating the
GlobalObjectwith references to all the built-in constructors and functions, making them accessible in the global scope.
Sources: Jurassic/Core/ScriptEngine.cs:81-207
The following diagram illustrates the high-level initialization flow within the ScriptEngine constructor.
Sources: Jurassic/Core/ScriptEngine.cs:81-207
Execution Lifecycle
Script execution is managed through methods like Evaluate() and Execute(). These methods initiate a multi-stage process that transforms JavaScript source code into executable .NET code.
- Source Input: The process starts with a
ScriptSource, which can be a string or a file. - Method Generation: A
GlobalOrEvalMethodGeneratoris created to manage the compilation for the given source. - Parsing: The source code is parsed into an Abstract Syntax Tree (AST).
- Optimization: The AST is traversed and optimized.
- Code Generation: The
MethodGeneratoruses anILGeneratorto emit .NET CIL based on the AST. This process creates aGeneratedMethod, which is a .NET delegate. - Execution: The compiled delegate is invoked, running the JavaScript logic.
The diagram below shows the sequence for a call to ScriptEngine.Evaluate().
Key ScriptEngine API
The ScriptEngine provides a simple API for interacting with the JavaScript environment.
| Method/Property | Description | Source |
|---|---|---|
Evaluate(string code) | Executes JavaScript code and returns the value of the last expression. | Jurassic/Core/ScriptEngine.cs:780-784 |
Execute(string code) | Executes JavaScript code without returning a value. | Jurassic/Core/ScriptEngine.cs:882-911 |
SetGlobalValue(string name, object value) | Sets the value of a variable in the global scope. | Jurassic/Core/ScriptEngine.cs:1018-1076 |
GetGlobalValue(string name) | Retrieves the value of a variable from the global scope. | Jurassic/Core/ScriptEngine.cs:966-972 |
CallGlobalFunction(string name, ...) | Calls a function in the global scope with the given arguments. | Jurassic/Core/ScriptEngine.cs:1082-1094 |
Global (Property) | Gets the built-in global object. | Jurassic/Core/ScriptEngine.cs:411-415 |
ForceStrictMode (Property) | Forces all executed code to run in strict mode. | Jurassic/Core/ScriptEngine.cs:222-226 |
CompatibilityMode (Property) | Sets the engine to emulate ECMAScript 3 or 5 behavior. | Jurassic/Core/ScriptEngine.cs:228-242 |
A common use case is setting and retrieving values from the script environment.
// 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
GlobalObject: The Global Namespace
The GlobalObject is an ObjectInstance that represents the global scope in JavaScript. It contains all the built-in properties and functions defined by the ECMAScript standard. It is accessible as this in the global context.
Sources: Jurassic/Library/GlobalObject.cs
Standard Properties and Functions
The GlobalObject is initialized with standard properties and functions.
| Property/Function | Description | Source |
|---|---|---|
Infinity, NaN, undefined | Global constants. Their writability depends on the CompatibilityMode. | Jurassic/Library/GlobalObject.cs:29-35 |
eval() | Evaluates a string of JavaScript code. Its behavior (global vs. local scope) depends on whether it's a direct or indirect call. | Jurassic/Library/GlobalObject.cs:168-193 |
parseInt(), parseFloat() | Functions for parsing strings into numbers. These are delegated from NumberConstructor. | Jurassic/Core/ScriptEngine.cs:205-206 |
isNaN(), isFinite() | Functions to check if a value is NaN or finite. | Jurassic/Library/GlobalObject.cs:195-217 |
decodeURI(), encodeURI() | Functions for encoding and decoding Uniform Resource Identifiers (URIs). | Jurassic/Library/GlobalObject.cs:57-129 |
decodeURIComponent(), encodeURIComponent() | Functions for encoding and decoding URI components. | Jurassic/Library/GlobalObject.cs:69-141 |
The properties are defined in GetDeclarativeProperties and GetGlobalProperties.
Sources: Jurassic/Library/GlobalObject.cs:24-37, Jurassic/Library/GlobalObject.g.cs:13-26
RuntimeScope: Managing Lexical Scopes
RuntimeScope is a fundamental class for managing variable visibility and lifetime according to JavaScript's lexical scoping rules. It correctly handles var, let, and const declarations across global, function, and block scopes.
*Sources: Jurassic/Compiler/ExecutionContext/RuntimeScope.cs *
Architecture
A RuntimeScope forms a singly-linked list, creating a "scope chain". When a variable is accessed, the chain is traversed from the current scope upwards to its parent until the variable is found.
Key properties of a RuntimeScope:
Parent: A reference to the enclosing (parent) scope. The global scope has anullparent.ScopeType: An enum indicating the type of scope (e.g.,Global,Function,Block,With).ScopeObject: AnObjectInstancethat holds variables for certain scope types, notably theGlobalObjectfor the global scope and a target object for awithstatement.values: A dictionary holding locally-defined variables (let,const, and function-scopedvar).
The following diagram shows the class structure of RuntimeScope.
Variable Resolution
Variable lookup (GetValue) and assignment (SetValue) are the core functions of RuntimeScope.
-
GetValue(string variableName, ...): This method searches for a variable by name.- It first checks the local
valuesdictionary of the current scope. - If not found, it checks the
ScopeObject(if one exists). Forwithscopes, it also respects[Symbol.unscopables]. - If still not found, it recursively calls
GetValueon itsParentscope. - If the top of the chain is reached without finding the variable, a
ReferenceErroris thrown.
- It first checks the local
-
SetValue(string variableName, object value, ...): This method works similarly, traversing the scope chain to find where the variable is declared and then updating its value.- If a variable is declared with
const, attempting to set it will throw aTypeError. - In strict mode, assigning to an undeclared variable will throw a
ReferenceError. In non-strict mode, it will create a new property on the global object.
- If a variable is declared with
The flowchart below illustrates the logic for GetValue.
Compilation Context (OptimizationInfo)
While not strictly part of the post-compilation runtime, the OptimizationInfo class is a critical state container used during compilation to shape the resulting runtime code. It is passed throughout the code generation process to provide context to the ILGenerator.
Key responsibilities include:
- Tracking whether the compiler is in
StrictMode. - Holding a reference to the
ScriptSourceand the currentSourceCodeSpanfor debugging and error reporting. - Managing
ILLabeltargets for control flow statements likereturn,break, andcontinue. This is handled via a stack (breakOrContinueStack). - Providing cached
ILLocalVariableinstances for shared resources like regular expressions or property name strings, which optimizes the generated CIL.
| Property | Description | Source |
|---|---|---|
StrictMode | Indicates if strict mode is enabled. | Jurassic/Compiler/Emit/OptimizationInfo.cs:22-25 |
Source | The source code being compiled. | Jurassic/Compiler/Emit/OptimizationInfo.cs:33-36 |
ReturnTarget | The ILLabel to jump to for a return statement. | Jurassic/Compiler/Emit/OptimizationInfo.cs:253-257 |
InsideLoop | A boolean indicating if code generation is inside a loop. | Jurassic/Compiler/Emit/OptimizationInfo.cs:274-278 |
*Sources: Jurassic/Compiler/Emit/OptimizationInfo.cs *
Summary
The Jurassic runtime environment is a sophisticated system designed for the high-performance execution of JavaScript. It is centered around the ScriptEngine, which manages state and orchestrates the compilation of JavaScript into .NET CIL. The GlobalObject provides the standard global namespace, while the RuntimeScope class elegantly handles JavaScript's complex lexical scoping rules. This architecture ensures that Jurassic can execute JavaScript code not only quickly but also in a way that is compliant with the ECMAScript specification.