Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions Jint.Tests/Runtime/DestructuringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,54 @@ public void EmptyRest()
{
_engine.Execute("function test({ ...props }){}; test({});");
}

[Fact]
public void VarDestructuringInForOfShouldHoistInStrictMode()
{
// Nested destructuring var names must be hoisted to function scope.
// Previously only Identifier bindings were collected; patterns like [[x]] were skipped,
// causing ReferenceError in strict mode.
var result = _engine.Evaluate("""
'use strict';
(function() {
var results = [];
for (var [[x, y, z] = [4, 5, 6]] of [[]]) {
results.push(x, y, z);
}
return results.join(',');
})()
""");

Assert.Equal("4,5,6", result.AsString());
}

[Fact]
public void VarObjectDestructuringInForOfShouldHoistInStrictMode()
{
var result = _engine.Evaluate("""
'use strict';
(function() {
for (var { a, b } of [{ a: 1, b: 2 }]) {}
return a + ',' + b;
})()
""");

Assert.Equal("1,2", result.AsString());
}

[Fact]
public void VarDestructuringInForAwaitOfShouldHoistInStrictMode()
{
var engine = new Engine();
var result = engine.Evaluate("""
'use strict';
(async function() {
for await (var [[x] = [1]] of [[]]) {}
return x;
})()
""");

result = result.UnwrapIfPromise();
Assert.Equal(1, result.AsInteger());
}
}
16 changes: 5 additions & 11 deletions Jint/HoistingScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal sealed class HoistingScope
internal readonly List<Key>? _varNames;

internal readonly List<Declaration>? _lexicalDeclarations;
internal readonly List<string>? _lexicalNames;
internal readonly List<Key>? _lexicalNames;

/// <summary>
/// B.3.2/B.3.3: Block-level function declarations that need AnnexB var hoisting in sloppy mode.
Expand All @@ -22,7 +22,7 @@ private HoistingScope(
List<Key>? varNames,
List<VariableDeclaration>? variableDeclarations,
List<Declaration>? lexicalDeclarations,
List<string>? lexicalNames,
List<Key>? lexicalNames,
List<FunctionDeclaration>? annexBFunctionDeclarations = null)
{
_functionDeclarations = functionDeclarations;
Expand Down Expand Up @@ -171,7 +171,7 @@ private sealed class ScriptWalker

private readonly bool _collectLexicalNames;
internal List<Declaration>? _lexicalDeclarations;
internal List<string>? _lexicalNames;
internal List<Key>? _lexicalNames;

/// <summary>
/// B.3.2/B.3.3: Function declarations inside blocks/switch cases in sloppy mode.
Expand Down Expand Up @@ -271,10 +271,7 @@ private void VisitCore(Node node, Node? parent, HashSet<string>? enclosingLexica
ref readonly var nodeList = ref variableDeclaration.Declarations;
foreach (var declaration in nodeList)
{
if (declaration.Id is Identifier identifier)
{
_varNames.Add(identifier.Name);
}
declaration.Id.GetBoundNames(_varNames);
}
}
}
Expand All @@ -289,10 +286,7 @@ private void VisitCore(Node node, Node? parent, HashSet<string>? enclosingLexica
ref readonly var nodeList = ref variableDeclaration.Declarations;
foreach (var declaration in nodeList)
{
if (declaration.Id is Identifier identifier)
{
_lexicalNames.Add(identifier.Name);
}
declaration.Id.GetBoundNames(_lexicalNames);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Jint/Runtime/Interpreter/JintFunctionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ internal static State BuildState(IFunction function)
}
else if (!state.HasParameterExpressions)
{
if (state.FunctionNames.Contains(KnownKeys.Arguments) || lexicalNames?.Contains(KnownKeys.Arguments.Name) == true)
if (state.FunctionNames.Contains(KnownKeys.Arguments) || lexicalNames?.Contains(KnownKeys.Arguments) == true)
{
state.ArgumentsObjectNeeded = false;
}
Expand Down Expand Up @@ -468,7 +468,7 @@ internal static State BuildState(IFunction function)
continue;
}

if (lexicalNames?.Contains(fn.Name) == true)
if (lexicalNames?.Contains(fn) == true)
{
continue;
}
Expand Down