From 0f730d45b6f9681ba577b9b494eb969c4311e4a4 Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Mon, 6 Jul 2026 23:44:44 +0300 Subject: [PATCH] Build the yield argument handler once, not per evaluation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JintYieldExpression called JintExpression.Build(expression.Argument) inside EvaluateInternal — on every single yield evaluation. Build only returns a cached handler when the AST node''s UserData was pre-populated (prepared scripts), so for ordinary execution every `yield ` reconstructed the argument''s whole handler subtree per iteration: for `yield { a: i, b: i+1 }` that is a JintObjectExpression + ObjectProperty[] + ExpressionCache + JintIdentifierExpression and JintBinaryExpression handlers — every time the generator produced a value. The handler also lost all its per-node inline caches each rebuild. The argument handler is now built once in the constructor like every other expression handler. Generator-producing-object-literals workload (fresh engine per iteration): 132.2 -> 52.9 MB/iter (-60%), 98.4 -> 67.9 ms/iter (-31%). Co-Authored-By: Claude Fable 5 --- .../Runtime/Interpreter/Expressions/JintYieldExpression.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Jint/Runtime/Interpreter/Expressions/JintYieldExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintYieldExpression.cs index af11a15721..0765cd8af4 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintYieldExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintYieldExpression.cs @@ -10,8 +10,11 @@ namespace Jint.Runtime.Interpreter.Expressions; internal sealed class JintYieldExpression : JintExpression { + private readonly JintExpression? _argument; + public JintYieldExpression(YieldExpression expression) : base(expression) { + _argument = expression.Argument is not null ? Build(expression.Argument) : null; } protected override object EvaluateInternal(EvaluationContext context) @@ -171,9 +174,9 @@ protected override object EvaluateInternal(EvaluationContext context) // Normal yield: evaluate argument and yield the value JsValue value; - if (expression.Argument is not null) + if (_argument is not null) { - value = Build(expression.Argument).GetValue(context); + value = _argument.GetValue(context); // If the argument evaluation suspended the generator (nested yield), propagate // the suspension up without yielding again - the inner yield already suspended