JsEngine Class

An ECMAScript agent (https://tc39.es/ecma262/#sec-agents): it owns the realms, the job queue and the execution limits of the scripts it runs. An engine is used by one thread at a time.

public sealed class JsEngine : IDisposable
Implements

Constructors

NameDescription
JsEngine(JsEngineOptions?)Initializes a new instance of the JsEngine class.

JsEngine(JsEngineOptions?)

JsEngine.Async.cs

Initializes a new instance of the JsEngine class.

public JsEngine(JsEngineOptions? options = null)

Parameters

options JsEngineOptions?

Properties

NameDescription
CurrentRealmGets the realm of the running execution context (the current realm).
IsScriptRunningGets whether a script is running (the execution context stack is not empty).
OptionsGets the options of the engine: a copy of the options given to the constructor. The limits can be changed on the thread of the engine; the other options cannot (see JsEngineOptions).
RealmsGets the realms created by JsEngine.CreateRealm (the realms of the ShadowRealm objects are not included).

CurrentRealm

JsEngine.cs

Gets the realm of the running execution context (the current realm).

public JsRealm? CurrentRealm { get; }

Property value

JsRealm?

IsScriptRunning

JsEngine.Execution.cs

Gets whether a script is running (the execution context stack is not empty).

public bool IsScriptRunning { get; }

Property value

bool

Gets the options of the engine: a copy of the options given to the constructor. The limits can be changed on the thread of the engine; the other options cannot (see JsEngineOptions).

public JsEngineOptions Options { get; }

Property value

JsEngineOptions

Gets the realms created by JsEngine.CreateRealm (the realms of the ShadowRealm objects are not included).

public IReadOnlyList<JsRealm> Realms { get; }

Methods

NameDescription
Call(JsValue, JsValue, ReadOnlySpan<JsValue>)Call(F, V, argumentsList) (https://tc39.es/ecma262/#sec-call).
CaptureStackTrace()Captures the stack trace of the running code, in the format of the stack property of the errors (without the first line).
CheckInterrupt()Throws a JsTerminationException when the running script must stop: the execution is canceled (JsEngineOptions.CancellationToken), a limit of JsEngineOptions or of an execution scope (JsEngine.EnterExecutionScope) is exceeded, or the running entry was already terminated. The host calls it periodically while it runs code on behalf of the script (e.g. WebAssembly code); the engine checks the limits itself while JavaScript code and the built-in functions run.
Construct(JsValue, ReadOnlySpan<JsValue>, JsValue)Construct(F, argumentsList, newTarget) (https://tc39.es/ecma262/#sec-construct); newTarget is the constructor when it is undefined.
CreateRealm()Creates a realm with its intrinsics and an ordinary global object.
Dispose()Releases the resources of the engine: the work posted afterwards does not run (JsEngine.Post), and the pending tasks of JsEngine.ToTask are canceled.
EnqueueJob(Action, JsRealm?)Enqueues a job in the promise job queue (a microtask): it runs from JsEngine.RunJobs, after the jobs already in the queue.
Enter()Makes the engine the engine of the current thread until the returned scope is disposed. The methods of the objects (JsObject.GetProperty...) need it when they are not called by the engine (a callback).
EnterActiveScriptOrModule(JsScriptOrModule?)Pushes an execution context whose ScriptOrModule is scriptOrModule until the returned scope is disposed: it is the active script or module (JsEngine.GetActiveScriptOrModule) of the code it runs, until code that has its own script or module runs (e.g. HostCallJobCallback restores the active script captured by HostMakeJobCallback, so that the code compiled by an eval called by a promise job resolves its imports against it).
EnterExecutionScope(JsExecutionLimits?)Starts an execution scope: the calls made until the returned scope is disposed are one entry into JavaScript, and limits apply to them as a whole, in addition to the limits of JsEngineOptions (which apply to the scope as a whole as well: the calls in the scope do not restart them). It bounds a sequence of calls (e.g. a script and the jobs it enqueues: JsEngine.RunJobs runs each job in its own entry otherwise), or one call of an engine shared by several requests, without changing the options of the engine. The scope also makes the engine the engine of the current thread (JsEngine.Enter). A scope started while JavaScript runs (from a host function) adds its limits to the running entry until it is disposed.
EnterNestedJobQueue()Makes the jobs enqueued from now on go to a new queue, which JsEngine.RunJobs runs, until the returned scope is disposed (e.g. the microtasks of a nested event loop). The jobs left in the nested queue then go to the outer queue.
GetActiveScriptOrModule()GetActiveScriptOrModule(): the script or the module of the innermost running code that has one, or null.
GetCurrentLocation()Gets the position of the running JavaScript code (the call of the running .NET callback), or null when no script runs.
GetRegisteredSymbol(string)Gets the symbol of the global symbol registry of the engine whose key is key, creating it when needed: the symbol that Symbol.for(key) returns in all the realms of the engine. Another engine has its own registry, so the same key gives another symbol there.
IsArray(JsValue)IsArray(value) (https://tc39.es/ecma262/#sec-isarray): true for arrays and proxies of arrays; throws for a revoked proxy.
Post(Action, JsRealm?)Posts work to run on the thread of the engine. It can be called from any thread (e.g. the continuation of an I/O operation). The work runs from JsEngine.RunJobs as a task of the event loop: after the promise jobs already in the queue, in the order it was posted, and followed by the promise jobs it enqueues. A JsException thrown by the work goes to JsHost.ReportJobException; another exception comes out of JsEngine.RunJobs. A host whose JsHost.EnqueueGenericJob is overridden runs the work in its own event loop.
RunInRealm<T>(JsRealm, Func<T>)Runs action with realm as the current realm (the realm of the objects created by the built-ins and the API).
RunJobs()Runs the jobs until the queue is empty: the promise jobs first (in the order they were enqueued), then the generic jobs and the timeout jobs whose delay has elapsed, each followed by the promise jobs it enqueued. The timeout jobs whose delay has not elapsed stay in the queue.
RunUntilSettledAsync(JsValue, CancellationToken)Runs the event loop of the engine until the promise settles: runs the jobs (JsEngine.RunJobs), and waits for posted work and timeout jobs (JsEngine.WaitForJobsAsync) while the promise is pending. The engine is free between two runs of the jobs: the work of the host (e.g. the continuations of its tasks) reaches it with JsEngine.Post.
SameValue(JsValue, JsValue)SameValue(x, y) (https://tc39.es/ecma262/#sec-samevalue).
ToBigInt(JsValue)ToBigInt(value) (https://tc39.es/ecma262/#sec-tobigint).
ToBoolean(JsValue)ToBoolean(value) (https://tc39.es/ecma262/#sec-toboolean).
ToDotNet(JsValue)Converts a JavaScript value to a .NET value: undefined and null are null; a boolean, a number, a string and a BigInt are a Boolean, a Double, a String and a BigInteger; a Date is a DateTimeOffset (null for an invalid date); an array is a List<T> of the converted elements; another object is a Dictionary<TKey, TValue> of its own enumerable properties whose key is a string (read with [[Get]], so the getters run).
ToDotNet<T>(JsValue)Converts a JavaScript value to a .NET value of type T:undefined and null cannot be converted to a value type that is not nullable, except Boolean.
ToNumber(JsValue)ToNumber(value) (https://tc39.es/ecma262/#sec-tonumber); it may run code (valueOf).
ToObject(JsValue)ToObject(value) (https://tc39.es/ecma262/#sec-toobject): a TypeError for undefined and null.
ToPropertyKey(JsValue)ToPropertyKey(value) (https://tc39.es/ecma262/#sec-topropertykey); it may run code.
ToString(JsValue)ToString(value) (https://tc39.es/ecma262/#sec-tostring); it may run code, and throws a TypeError for a symbol.
ToTask(JsValue)Gets a task that completes when the promise settles: its result is the value of a fulfilled promise, and the JsException of the rejection reason for a rejected promise. A value that is not a promise is resolved as Promise.resolve does. The promise settles while the engine runs its jobs (JsEngine.RunJobs, or JsEngine.RunUntilSettledAsync), and the continuations of the task run on the thread pool, after the job. The task is canceled when the engine is disposed first.
WaitForJobsAsync(CancellationToken)Waits until a job is ready to run: a promise job, work posted by JsEngine.Post, a generic job, or a timeout job whose delay has elapsed. It completes at once when one is already queued. A host calls it between two calls to JsEngine.RunJobs, while the engine does not run, to implement its event loop.

Call(JsValue, JsValue, ReadOnlySpan<JsValue>)

JsEngine.Public.cs

Call(F, V, argumentsList) (https://tc39.es/ecma262/#sec-call).

public JsValue Call(JsValue function, JsValue thisValue, params scoped ReadOnlySpan<JsValue> arguments)

Parameters

function JsValue
thisValue JsValue
arguments ReadOnlySpan<JsValue>

Returns

JsValue

Exceptions

JsException
The value is not callable (a TypeError), or the function threw an exception.
InvalidOperationException
The function, the this value or an argument belongs to another engine.

CaptureStackTrace()

JsEngine.Public.cs

Captures the stack trace of the running code, in the format of the stack property of the errors (without the first line).

public string CaptureStackTrace()

Returns

string

CheckInterrupt()

JsEngine.Execution.cs

Throws a JsTerminationException when the running script must stop: the execution is canceled (JsEngineOptions.CancellationToken), a limit of JsEngineOptions or of an execution scope (JsEngine.EnterExecutionScope) is exceeded, or the running entry was already terminated. The host calls it periodically while it runs code on behalf of the script (e.g. WebAssembly code); the engine checks the limits itself while JavaScript code and the built-in functions run.

public void CheckInterrupt()

Exceptions

JsTerminationException
The script must stop.

Construct(JsValue, ReadOnlySpan<JsValue>, JsValue)

JsEngine.Public.cs

Construct(F, argumentsList, newTarget) (https://tc39.es/ecma262/#sec-construct); newTarget is the constructor when it is undefined.

public JsValue Construct(JsValue constructor, ReadOnlySpan<JsValue> arguments, JsValue newTarget = default)

Parameters

constructor JsValue
arguments ReadOnlySpan<JsValue>
newTarget JsValue

Returns

JsValue

Exceptions

JsException
The value is not a constructor (a TypeError), or the constructor threw an exception.
InvalidOperationException
The constructor, the new target or an argument belongs to another engine.

CreateRealm()

JsEngine.cs

Creates a realm with its intrinsics and an ordinary global object.

public JsRealm CreateRealm()

Returns

JsRealm

Dispose()

JsEngine.cs

Releases the resources of the engine: the work posted afterwards does not run (JsEngine.Post), and the pending tasks of JsEngine.ToTask are canceled.

public void Dispose()

EnqueueJob(Action, JsRealm?)

JsEngine.Public.cs

Enqueues a job in the promise job queue (a microtask): it runs from JsEngine.RunJobs, after the jobs already in the queue.

public void EnqueueJob(Action job, JsRealm? realm = null)

Parameters

job Action
The job.
realm JsRealm?
The realm that is current while the job runs, or null to keep the current realm.

Makes the engine the engine of the current thread until the returned scope is disposed. The methods of the objects (JsObject.GetProperty...) need it when they are not called by the engine (a callback).

public JsEngineScope Enter()

Returns

JsEngineScope

Exceptions

InvalidOperationException
Another thread is using the engine.

Remarks

An engine is used by one thread at a time: the thread that enters it owns it until it disposes its outermost scope. The owning thread can enter the engine again (nested scopes, callbacks); another thread gets an InvalidOperationException instead of corrupting the state of the engine.

EnterActiveScriptOrModule(JsScriptOrModule?)

JsEngine.Execution.cs

Pushes an execution context whose ScriptOrModule is scriptOrModule until the returned scope is disposed: it is the active script or module (JsEngine.GetActiveScriptOrModule) of the code it runs, until code that has its own script or module runs (e.g. HostCallJobCallback restores the active script captured by HostMakeJobCallback, so that the code compiled by an eval called by a promise job resolves its imports against it).

public JsScriptContextScope EnterActiveScriptOrModule(JsScriptOrModule? scriptOrModule)

Parameters

scriptOrModule JsScriptOrModule?
The script or module, or null for an execution context without script or module.

Returns

JsScriptContextScope

EnterExecutionScope(JsExecutionLimits?)

JsEngine.Execution.cs

Starts an execution scope: the calls made until the returned scope is disposed are one entry into JavaScript, and limits apply to them as a whole, in addition to the limits of JsEngineOptions (which apply to the scope as a whole as well: the calls in the scope do not restart them). It bounds a sequence of calls (e.g. a script and the jobs it enqueues: JsEngine.RunJobs runs each job in its own entry otherwise), or one call of an engine shared by several requests, without changing the options of the engine. The scope also makes the engine the engine of the current thread (JsEngine.Enter). A scope started while JavaScript runs (from a host function) adds its limits to the running entry until it is disposed.

public JsExecutionScope EnterExecutionScope(JsExecutionLimits? limits = null)

Parameters

limits JsExecutionLimits?
The limits of the scope, or null for the limits of the options only.

Returns

JsExecutionScope

Exceptions

JsTerminationException
The execution is canceled, or the running entry was terminated.

EnterNestedJobQueue()

JsEngine.Public.cs

Makes the jobs enqueued from now on go to a new queue, which JsEngine.RunJobs runs, until the returned scope is disposed (e.g. the microtasks of a nested event loop). The jobs left in the nested queue then go to the outer queue.

public IDisposable EnterNestedJobQueue()

GetActiveScriptOrModule()

JsEngine.Public.cs

GetActiveScriptOrModule(): the script or the module of the innermost running code that has one, or null.

public JsScriptOrModule? GetActiveScriptOrModule()

GetCurrentLocation()

JsEngine.Public.cs

Gets the position of the running JavaScript code (the call of the running .NET callback), or null when no script runs.

public JsSourceLocation? GetCurrentLocation()

GetRegisteredSymbol(string)

JsEngine.Symbols.cs

Gets the symbol of the global symbol registry of the engine whose key is key, creating it when needed: the symbol that Symbol.for(key) returns in all the realms of the engine. Another engine has its own registry, so the same key gives another symbol there.

public JsSymbol GetRegisteredSymbol(string key)

Parameters

key string

Returns

JsSymbol

Exceptions

InvalidOperationException
Another thread is using the engine.

IsArray(JsValue)

JsEngine.Public.cs

IsArray(value) (https://tc39.es/ecma262/#sec-isarray): true for arrays and proxies of arrays; throws for a revoked proxy.

public bool IsArray(JsValue value)

Parameters

value JsValue

Returns

bool

Post(Action, JsRealm?)

JsEngine.Async.cs

Posts work to run on the thread of the engine. It can be called from any thread (e.g. the continuation of an I/O operation). The work runs from JsEngine.RunJobs as a task of the event loop: after the promise jobs already in the queue, in the order it was posted, and followed by the promise jobs it enqueues. A JsException thrown by the work goes to JsHost.ReportJobException; another exception comes out of JsEngine.RunJobs. A host whose JsHost.EnqueueGenericJob is overridden runs the work in its own event loop.

public bool Post(Action action, JsRealm? realm = null)

Parameters

action Action
The work.
realm JsRealm?
The realm that is current while the work runs, or null to keep the current realm.

Returns

bool false when the engine is disposed: the work does not run.

RunInRealm<T>(JsRealm, Func<T>)

JsEngine.Public.cs

Runs action with realm as the current realm (the realm of the objects created by the built-ins and the API).

public T RunInRealm<T>(JsRealm realm, Func<T> action)

Type parameters

T

Parameters

realm JsRealm
action Func<T>

Returns

T

Runs the jobs until the queue is empty: the promise jobs first (in the order they were enqueued), then the generic jobs and the timeout jobs whose delay has elapsed, each followed by the promise jobs it enqueued. The timeout jobs whose delay has not elapsed stay in the queue.

public void RunJobs()

Exceptions

JsException
A job threw an exception, and the host does not handle the job errors (the other jobs stay in the queue).
JsTerminationException
A job was terminated, or the execution is canceled (the other jobs stay in the queue).

Remarks

Each job is an entry into JavaScript: the limits of JsEngineOptions apply to each job, not to the whole drain, so jobs that keep enqueuing jobs can run forever. Run the jobs in an execution scope (JsEngine.EnterExecutionScope) to bound the drain as a whole: the jobs are then part of the entry of the scope. The jobs do not run once the execution is canceled. Once the queue is empty, it raises JsEngine.UnhandledRejection for the promises still rejected without a handler.

RunUntilSettledAsync(JsValue, CancellationToken)

JsEngine.Async.cs

Runs the event loop of the engine until the promise settles: runs the jobs (JsEngine.RunJobs), and waits for posted work and timeout jobs (JsEngine.WaitForJobsAsync) while the promise is pending. The engine is free between two runs of the jobs: the work of the host (e.g. the continuations of its tasks) reaches it with JsEngine.Post.

net10.0

public Task<JsValue> RunUntilSettledAsync(JsValue promise, CancellationToken cancellationToken = default)

net11.0

public Task<JsValue> RunUntilSettledAsync(JsValue promise, CancellationToken cancellationToken = default)

Parameters

promise JsValue
cancellationToken CancellationToken

Returns

Task<JsValue> The value of the fulfilled promise.

Exceptions

JsException
The promise is rejected, or a job threw an exception that the host does not handle.
ObjectDisposedException
The engine is disposed.
OperationCanceledException
cancellationToken is canceled.

SameValue(JsValue, JsValue)

JsEngine.Public.cs

SameValue(x, y) (https://tc39.es/ecma262/#sec-samevalue).

public static bool SameValue(JsValue x, JsValue y)

Parameters

x JsValue
y JsValue

Returns

bool

ToBigInt(JsValue)

JsEngine.Public.cs

ToBigInt(value) (https://tc39.es/ecma262/#sec-tobigint).

public BigInteger ToBigInt(JsValue value)

Parameters

value JsValue

Returns

BigInteger

ToBoolean(JsValue)

JsEngine.Public.cs

ToBoolean(value) (https://tc39.es/ecma262/#sec-toboolean).

public static bool ToBoolean(JsValue value)

Parameters

value JsValue

Returns

bool

ToDotNet overloads

ToDotNet(JsValue)

JsMarshalling.cs

Converts a JavaScript value to a .NET value: undefined and null are null; a boolean, a number, a string and a BigInt are a Boolean, a Double, a String and a BigInteger; a Date is a DateTimeOffset (null for an invalid date); an array is a List<T> of the converted elements; another object is a Dictionary<TKey, TValue> of its own enumerable properties whose key is a string (read with [[Get]], so the getters run).

public object? ToDotNet(JsValue value)
Parameters
value JsValue
Returns

object?

Exceptions
NotSupportedException
The value is, or contains, a symbol or a function.
InvalidOperationException
The value contains a cycle.

ToDotNet<T>(JsValue)

JsMarshalling.cs

Converts a JavaScript value to a .NET value of type T:

undefined and null cannot be converted to a value type that is not nullable, except Boolean.

public T ToDotNet<T>(JsValue value)
Type parameters
T
Parameters
value JsValue
Returns

T

Exceptions
JsException
The value cannot be converted (a TypeError or a RangeError), or a conversion of JavaScript threw an exception.
NotSupportedException
T is not one of the supported types.

ToNumber(JsValue)

JsEngine.Public.cs

ToNumber(value) (https://tc39.es/ecma262/#sec-tonumber); it may run code (valueOf).

public double ToNumber(JsValue value)

Parameters

value JsValue

Returns

double

ToObject(JsValue)

JsEngine.Public.cs

ToObject(value) (https://tc39.es/ecma262/#sec-toobject): a TypeError for undefined and null.

public JsObject ToObject(JsValue value)

Parameters

value JsValue

Returns

JsObject

ToPropertyKey(JsValue)

JsEngine.Public.cs

ToPropertyKey(value) (https://tc39.es/ecma262/#sec-topropertykey); it may run code.

public JsPropertyKey ToPropertyKey(JsValue value)

Parameters

value JsValue

Returns

JsPropertyKey

ToString(JsValue)

JsEngine.Public.cs

ToString(value) (https://tc39.es/ecma262/#sec-tostring); it may run code, and throws a TypeError for a symbol.

public string ToString(JsValue value)

Parameters

value JsValue

Returns

string

ToTask(JsValue)

JsEngine.Async.cs

Gets a task that completes when the promise settles: its result is the value of a fulfilled promise, and the JsException of the rejection reason for a rejected promise. A value that is not a promise is resolved as Promise.resolve does. The promise settles while the engine runs its jobs (JsEngine.RunJobs, or JsEngine.RunUntilSettledAsync), and the continuations of the task run on the thread pool, after the job. The task is canceled when the engine is disposed first.

public Task<JsValue> ToTask(JsValue promise)

Parameters

promise JsValue

Returns

Task<JsValue>

Exceptions

InvalidOperationException
The promise belongs to another engine, or the engine has no realm to resolve a value that is not a promise.

WaitForJobsAsync(CancellationToken)

JsEngine.Async.cs

Waits until a job is ready to run: a promise job, work posted by JsEngine.Post, a generic job, or a timeout job whose delay has elapsed. It completes at once when one is already queued. A host calls it between two calls to JsEngine.RunJobs, while the engine does not run, to implement its event loop.

net10.0

public Task WaitForJobsAsync(CancellationToken cancellationToken = default)

net11.0

public Task WaitForJobsAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

Returns

Task

Exceptions

ObjectDisposedException
The engine is disposed.
OperationCanceledException
cancellationToken is canceled.

Events

NameDescription
RejectionHandledOccurs when a handler is attached to a promise that JsEngine.UnhandledRejection reported.
UnhandledRejectionOccurs when a promise is rejected and has no handler at the end of JsEngine.RunJobs (once the job queue is empty: a handler attached by a job of the same JsEngine.RunJobs call, or by the script before the call, prevents it). It is raised on the thread of the engine. A promise rejected by a script that runs without a call to JsEngine.RunJobs is reported by the next call.

RejectionHandled

Occurs when a handler is attached to a promise that JsEngine.UnhandledRejection reported.

public event EventHandler<JsPromiseRejectionEventArgs>? RejectionHandled

UnhandledRejection

Occurs when a promise is rejected and has no handler at the end of JsEngine.RunJobs (once the job queue is empty: a handler attached by a job of the same JsEngine.RunJobs call, or by the script before the call, prevents it). It is raised on the thread of the engine. A promise rejected by a script that runs without a call to JsEngine.RunJobs is reported by the next call.

public event EventHandler<JsPromiseRejectionEventArgs>? UnhandledRejection