Executing scripts
Use Execute for statements and declarations, and Evaluate when the resulting JavaScript value matters.
var engine = new Engine();
engine.Execute("function square(x) { return x * x; }", "math.js");
var value = engine.Evaluate("square(6)", "request.js");
Console.WriteLine(value.AsNumber()); // 36The optional source name appears in JavaScript stack traces and debugger locations.
Invoke functions
Invoke accepts CLR arguments and converts them. Call accepts values already represented as JsValue.
engine.Execute("function add(a, b) { return a + b; }");
var fromClr = engine.Invoke("add", 2, 3);
var add = engine.GetValue("add");
var fromJsValues = engine.Call(add, 4, 5);A string passed to Invoke names one global property; it is not JavaScript source or a dotted path. Read a nested function first:
engine.Execute("var api = { add: (a, b) => a + b };");
var add = engine.GetValue(engine.GetValue("api"), "add");
var result = engine.Invoke(add, 1, 2);Top-level var and function declarations create global-object properties. let, const, and class create global lexical bindings, so retrieve those with Evaluate("name"), not GetValue("name").
Prepare repeated code
Parse once when the same program runs repeatedly:
var prepared = Engine.PrepareScript(
"input.map(x => x * 2)",
source: "transform.js",
strict: true);
var result = engine.SetValue("input", new[] { 1, 2, 3 })
.Evaluate(in prepared);A Prepared<Script> is reusable and thread-safe and may be shared across engines. The JsValue result is not: objects belong to the engine and realm that created them. Convert output before crossing that boundary; see Working with values.
Each top-level Execute, Evaluate, Invoke, or Call is a separate run and resets ordinary execution budgets. See Execution constraints when one host operation makes several calls.
Evaluate an expression against a context object
To evaluate an expression against a context object without defining globals, compile it once as a function that takes the context as a parameter, then call it with each context:
var engine = new Engine();
var expression = "ctx.price * ctx.quantity";
// Compile once. Evaluating a function expression defines no globals.
var total = engine.Evaluate($"(ctx) => ({expression})");
// Invoke converts a CLR argument; Call takes a JsValue, such as one a host function received.
var fromClr = engine.Invoke(total, new { price = 4, quantity = 3 }); // 12
var fromScript = total.Call(engine.Evaluate("({ price: 5, quantity: 2 })")); // 10For bare names (price * quantity instead of ctx.price * ctx.quantity), wrap the expression in with. A name the context lacks falls through to the globals, and with is a syntax error in strict code, so this works only when Options.Strict is off, the default:
var total = engine.Evaluate("(function (scope) { with (scope) { return (price * quantity); } })");
var result = engine.Invoke(total, new { price = 4, quantity = 3 }); // 12When each run creates a new engine, prepare the function once in a static field. Each engine evaluates it into its own function, because a JsValue must never be shared across engines:
// Parsed once and shared by every engine.
private static readonly Prepared<Script> Total =
Engine.PrepareScript("(ctx) => (ctx.price * ctx.quantity)");
public static JsValue EvaluateTotal(Engine engine, JsValue ctx)
{
// The function belongs to this engine; cache it per engine, never in a static.
var total = engine.Evaluate(in Total);
return total.Call(ctx);
}A host function cannot see the local variables of the script that called it; only eval captures the caller's scope. Pass the context explicitly, as above.