From 24d9024c951407ff441eb6818f8f6eb2e858c557 Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Tue, 24 Mar 2026 09:03:44 +0200 Subject: [PATCH] Fix HTML comments in dynamic Function() and for-in initializer evaluation - Fix CreateDynamicFunction body/params wrapping to match spec (step 29/16): line feeds after params and around body text ensure HTML-like comments () are correctly handled - Implement AnnexB B.3.6: evaluate for-in loop variable initializer before enumeration (e.g., `for (var a = ++effects in {})`) - Remove 6 test262 exclusions (5 HTML comment + 1 for-in initializer) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Test262Harness.settings.json | 13 +--------- .../Function/FunctionInstance.Dynamic.cs | 25 +++---------------- .../Statements/JintForInForOfStatement.cs | 21 ++++++++++++++++ 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/Jint.Tests.Test262/Test262Harness.settings.json b/Jint.Tests.Test262/Test262Harness.settings.json index 82d81b92da..1bfca9aaa1 100644 --- a/Jint.Tests.Test262/Test262Harness.settings.json +++ b/Jint.Tests.Test262/Test262Harness.settings.json @@ -364,16 +364,9 @@ // === ANNEX B EXCLUSIONS === - // Acornima parser limitation: HTML-like comments () in dynamic Function() bodies/params - // Requires parser option for HTML comments which Acornima doesn't support in function constructor context - "annexB/built-ins/Function/createdynfn-html-close-comment-body.js", - "annexB/built-ins/Function/createdynfn-html-close-comment-params.js", - "annexB/built-ins/Function/createdynfn-html-open-comment-body.js", - "annexB/built-ins/Function/createdynfn-html-open-comment-params.js", - "annexB/built-ins/Function/createdynfn-no-line-terminator-html-close-comment-body.js", - // Acornima parser limitation: assignment target type validation for call expressions // Per B.3.7, non-strict mode should allow CallExpression as assignment target in certain cases + // Requires Acornima AllowCallExpressionAsLhs option (pending upstream PR) "annexB/language/expressions/assignmenttargettype/callexpression-as-for-in-lhs.js", "annexB/language/expressions/assignmenttargettype/callexpression-as-for-of-lhs.js", "annexB/language/expressions/assignmenttargettype/callexpression-in-compound-assignment.js", @@ -382,10 +375,6 @@ "annexB/language/expressions/assignmenttargettype/callexpression.js", "annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js", - // Acornima parser limitation: for-in initializer in non-strict mode - // Per B.3.6, `for (var x = expr in obj)` should be allowed in sloppy mode - "annexB/language/statements/for-in/nonstrict-initializer.js", - // Acornima parser/RegExp limitation: malformed named groups in non-unicode mode // Per B.1.2, non-unicode RegExp should accept some malformed named group syntax "annexB/built-ins/RegExp/named-groups/non-unicode-malformed.js" diff --git a/Jint/Native/Function/FunctionInstance.Dynamic.cs b/Jint/Native/Function/FunctionInstance.Dynamic.cs index cb0c252bbd..66597d5c4d 100644 --- a/Jint/Native/Function/FunctionInstance.Dynamic.cs +++ b/Jint/Native/Function/FunctionInstance.Dynamic.cs @@ -121,27 +121,10 @@ internal Function CreateDynamicFunction( break; } - if (p.Contains('/')) - { - // ensure comments don't screw up things - functionExpression += "\n" + p + "\n"; - } - else - { - functionExpression += p; - } - - functionExpression += ")"; - - if (body.Contains('/')) - { - // ensure comments don't screw up things - functionExpression += "{\n" + body + "\n}"; - } - else - { - functionExpression += "{" + body + "}"; - } + // Per spec (CreateDynamicFunction step 29), a line feed follows the parameters, + // and the body is wrapped with line feeds (step 16). This ensures HTML-like + // comments () are correctly handled as line comments. + functionExpression += p + "\n){\n" + body + "\n}"; } var parserOptions = _engine.GetActiveParserOptions(); diff --git a/Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs b/Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs index 212523ebbe..0c49f3eb09 100644 --- a/Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs @@ -31,6 +31,10 @@ internal sealed class JintForInForOfStatement : JintStatement private readonly LhsKind _lhsKind; private readonly DisposeHint _disposeHint; + // AnnexB B.3.6: for-in initializer expression (e.g., `for (var a = expr in obj)`) + private readonly JintExpression? _forInVarInitializer; + private readonly string? _forInVarName; + public JintForInForOfStatement(ForInStatement statement) : base(statement) { _leftNode = statement.Left; @@ -40,6 +44,14 @@ public JintForInForOfStatement(ForInStatement statement) : base(statement) InitializeLhs(out _lhsKind, out _disposeHint, out _tdzNames, out _destructuring, out _assignmentPattern, out _expr); _body = new ProbablyBlockStatement(_forBody); _right = JintExpression.Build(_rightExpression); + + // AnnexB B.3.6: for-in with initializer + if (_leftNode is VariableDeclaration { Kind: VariableDeclarationKind.Var } varDecl + && varDecl.Declarations[0] is { Init: not null, Id: Identifier id }) + { + _forInVarInitializer = JintExpression.Build(varDecl.Declarations[0].Init!); + _forInVarName = id.Name; + } } public JintForInForOfStatement(ForOfStatement statement) : base(statement) @@ -197,6 +209,15 @@ private bool HeadEvaluation(EvaluationContext context, [NotNullWhen(true)] out I } engine.UpdateLexicalEnvironment(tdz); + + // AnnexB B.3.6: evaluate for-in initializer before the right-hand expression + if (_forInVarInitializer is not null) + { + var lhs = engine.ResolveBinding(_forInVarName!); + var value = _forInVarInitializer.GetValue(context); + engine.PutValue(lhs, value); + } + var exprValue = _right.GetValue(context); engine.UpdateLexicalEnvironment(oldEnv);