Skip to content

Commit 828d9e4

Browse files
committed
.
1 parent 4e4d39f commit 828d9e4

8 files changed

+98
-24
lines changed

continue.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
- Object destructuring rest now walks [[OwnPropertyKeys]] with exclusions checked before GetOwnPropertyDescriptor, skips Get for non-enumerables, and uses the original object (including proxies) instead of cloning; the proxy rest destructuring get/gOPD/ownKeys-order tests are passing.
3131
- Async functions now resolve their completion through the global Promise constructor even when executed from nested scopes (class/field environments fall back to the root global object), so the async class element clusters involving same-line static async methods/private names are green again.
3232
- Parameter environments now hang off their function environments so default-parameter `super`/`this` lookups see the right bindings, and sloppy generator methods coerce `this` to the realm global object; the object method definition default-super/sloppy-this generators are passing.
33+
- Named generator function expressions now skip the internal const name binding when a function-name environment already exists, so sloppy reassignments of the generator name are ignored instead of throwing (covers the generator `reassign-fn-name` and `scope-name-var-open` tests).
3334

3435
## Next Iteration Plan
3536
1. When resuming broader work, run a narrow Language filter outside `ArgumentsObject`/`Array_fromAsync` to find the next hot cluster (avoid full 43k sweep).

playground/Program.cs

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,95 @@
11
using System;
22
using Asynkron.JsEngine;
3+
using Asynkron.JsEngine.Ast;
34

45
internal static class Program
56
{
67
private static async Task Main()
78
{
89
await using var engine = new JsEngine();
910

10-
// Test named function expression binding immutability
11-
var code = """
12-
var probeBody, setBody;
13-
14-
var func = function f() {
15-
probeBody = function() { return f; };
16-
setBody = function() { f = null; };
11+
var funcObj = await engine.Evaluate("""
12+
let ref = function * BindingIdentifier() {
13+
return BindingIdentifier;
1714
};
15+
ref;
16+
""");
1817

19-
func();
20-
21-
console.log('probeBody() === func:', probeBody() === func); // should be true
18+
Console.WriteLine(funcObj?.GetType().FullName);
19+
var isStrictField = funcObj?.GetType().GetField("_isLexicallyStrict",
20+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
21+
Console.WriteLine($"lexically strict: {isStrictField?.GetValue(funcObj)}");
2222

23-
setBody(); // Try to reassign f to null
23+
var closureField = funcObj?.GetType().GetField("_closure",
24+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
25+
var closure = closureField?.GetValue(funcObj);
26+
Console.WriteLine($"closure strict: {(closure as Asynkron.JsEngine.JsEnvironment)?.IsStrict}");
27+
if (closure is Asynkron.JsEngine.JsEnvironment env)
28+
{
29+
var valuesField = typeof(Asynkron.JsEngine.JsEnvironment)
30+
.GetField("_values", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
31+
if (valuesField?.GetValue(env) is System.Collections.IDictionary map)
32+
{
33+
foreach (System.Collections.DictionaryEntry kvp in map)
34+
{
35+
var keyName = kvp.Key?.GetType().GetProperty("Name")?.GetValue(kvp.Key) as string;
36+
if (keyName == "BindingIdentifier" && kvp.Value is not null)
37+
{
38+
var bindingType = kvp.Value.GetType();
39+
Console.WriteLine($"binding type: {bindingType.FullName}");
40+
var constProp = bindingType.GetProperty("IsConst");
41+
var immutableProp = bindingType.GetProperty("IsImmutableBinding");
42+
Console.WriteLine($"IsConst={constProp?.GetValue(kvp.Value)} IsImmutable={immutableProp?.GetValue(kvp.Value)}");
43+
}
44+
}
45+
}
46+
}
2447

25-
console.log('probeBody() === func after setBody():', probeBody() === func); // should STILL be true (immutable)
26-
27-
if (probeBody() !== func) {
28-
throw new Error('inner binding is NOT immutable (from body). Expected func, got: ' + probeBody());
29-
}
48+
var functionField = funcObj?.GetType().GetField("_function",
49+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
50+
var realmField = funcObj?.GetType().GetField("_realmState",
51+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
52+
var homeObjectField = funcObj?.GetType().GetField("_homeObject",
53+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
54+
var capturedField = funcObj?.GetType().GetField("_capturedPrivateNameScopes",
55+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
56+
var privateScopeField = funcObj?.GetType().GetField("_privateNameScope",
57+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
3058

31-
console.log('All tests passed!');
32-
""";
59+
var instanceType = Type.GetType("Asynkron.JsEngine.Ast.TypedAstEvaluator+TypedGeneratorInstance, Asynkron.JsEngine");
60+
if (instanceType is not null)
61+
{
62+
var ctor = instanceType.GetConstructors(System.Reflection.BindingFlags.Instance |
63+
System.Reflection.BindingFlags.NonPublic |
64+
System.Reflection.BindingFlags.Public)[0];
65+
var instance = ctor.Invoke(new[]
66+
{
67+
functionField!.GetValue(funcObj),
68+
closure,
69+
Array.Empty<object?>(),
70+
null,
71+
funcObj,
72+
realmField!.GetValue(funcObj),
73+
isStrictField!.GetValue(funcObj),
74+
homeObjectField!.GetValue(funcObj),
75+
privateScopeField!.GetValue(funcObj),
76+
capturedField!.GetValue(funcObj)!
77+
});
78+
var strictField = instanceType.GetField("_isStrict",
79+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
80+
Console.WriteLine($"reflected instance _isStrict: {strictField?.GetValue(instance)}");
3381

34-
await engine.Evaluate(code);
82+
var ensureEnv = instanceType.GetMethod("EnsureExecutionEnvironment",
83+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
84+
var execEnv = ensureEnv?.Invoke(instance, Array.Empty<object?>()) as JsEnvironment;
85+
var ensureCtx = instanceType.GetMethod("EnsureEvaluationContext",
86+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
87+
var ctx = ensureCtx?.Invoke(instance, Array.Empty<object?>()) as EvaluationContext;
88+
var getFuncScope = typeof(JsEnvironment).GetMethod("GetFunctionScope",
89+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
90+
var funcScope = getFuncScope?.Invoke(execEnv, null) as JsEnvironment;
91+
Console.WriteLine($"execEnv strict={execEnv?.IsStrict} funcScopeStrict={funcScope?.IsStrict}");
92+
Console.WriteLine($"context strict scope={ctx?.CurrentScope.IsStrict}");
93+
}
3594
}
3695
}

src/Asynkron.JsEngine/Ast/FunctionExpressionExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,9 @@ functionExpression.Name is { } functionName &&
374374
{
375375
true when functionExpression.IsAsync => new AsyncGeneratorFactory(functionExpression,
376376
closureEnvironment,
377-
context.RealmState, context.CurrentScope.IsStrict, isConstructorFunction),
377+
context.RealmState, context.CurrentScope.IsStrict, hasFunctionNameEnvironment, isConstructorFunction),
378378
true => new TypedGeneratorFactory(functionExpression, closureEnvironment, context.RealmState,
379-
context.CurrentScope.IsStrict, isConstructorFunction),
379+
context.CurrentScope.IsStrict, hasFunctionNameEnvironment, isConstructorFunction),
380380
_ => new TypedFunction(functionExpression, closureEnvironment, context.RealmState,
381381
context.CurrentScope.IsStrict, hasFunctionNameEnvironment, isConstructorFunction)
382382
};

src/Asynkron.JsEngine/Ast/TypedAstEvaluator.AsyncGeneratorFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private sealed class AsyncGeneratorFactory : IJsCallable, IJsObjectLike, IProper
1414
private readonly JsEnvironment _closure;
1515
private readonly FunctionExpression _function;
1616
private readonly bool _isLexicallyStrict;
17+
private readonly bool _hasFunctionNameEnvironment;
1718
private readonly JsObject _properties = new();
1819
private readonly RealmState _realmState;
1920
private bool _isConstructorEnabled;
@@ -26,6 +27,7 @@ public AsyncGeneratorFactory(
2627
JsEnvironment closure,
2728
RealmState realmState,
2829
bool isLexicallyStrict,
30+
bool hasFunctionNameEnvironment = false,
2931
bool isConstructorFunction = true)
3032
{
3133
if (!function.IsGenerator || !function.IsAsync)
@@ -37,6 +39,7 @@ public AsyncGeneratorFactory(
3739
_closure = closure;
3840
_realmState = realmState;
3941
_isLexicallyStrict = isLexicallyStrict;
42+
_hasFunctionNameEnvironment = hasFunctionNameEnvironment;
4043
_isConstructorEnabled = isConstructorFunction;
4144
InitializeProperties();
4245
}
@@ -103,6 +106,7 @@ public void EnsureHasName(string name, bool overwriteExisting = false)
103106
this,
104107
_realmState,
105108
_isLexicallyStrict,
109+
_hasFunctionNameEnvironment,
106110
_homeObject,
107111
_privateNameScope,
108112
_capturedPrivateNameScopes);

src/Asynkron.JsEngine/Ast/TypedAstEvaluator.AsyncGeneratorInstance.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ private sealed class AsyncGeneratorInstance(
1515
IJsCallable callable,
1616
RealmState realmState,
1717
bool isLexicallyStrict,
18+
bool hasFunctionNameEnvironment,
1819
IJsObjectLike? homeObject,
1920
PrivateNameScope? privateNameScope,
2021
ImmutableArray<PrivateNameScope> capturedPrivateNameScopes)
2122
{
2223
private readonly TypedGeneratorInstance _inner = new(function, closure, arguments, thisValue, callable,
23-
realmState, isLexicallyStrict, homeObject, privateNameScope, capturedPrivateNameScopes);
24+
realmState, isLexicallyStrict, hasFunctionNameEnvironment, homeObject, privateNameScope,
25+
capturedPrivateNameScopes);
2426

2527
// WAITING ON FULL ASYNC GENERATOR IR SUPPORT:
2628
// For now we reuse the sync generator IR plan and runtime to execute

src/Asynkron.JsEngine/Ast/TypedAstEvaluator.TypedGeneratorFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private sealed class TypedGeneratorFactory : IJsCallable, IJsObjectLike, IProper
1414
private readonly JsEnvironment _closure;
1515
private readonly FunctionExpression _function;
1616
private readonly bool _isLexicallyStrict;
17+
private readonly bool _hasFunctionNameEnvironment;
1718
private readonly Dictionary<string, object?> _privateSlots = new(StringComparer.Ordinal);
1819
private readonly JsObject _properties = new();
1920
private readonly RealmState _realmState;
@@ -28,6 +29,7 @@ public TypedGeneratorFactory(
2829
JsEnvironment closure,
2930
RealmState realmState,
3031
bool isLexicallyStrict,
32+
bool hasFunctionNameEnvironment = false,
3133
bool isConstructorFunction = true)
3234
{
3335
if (!function.IsGenerator)
@@ -39,6 +41,7 @@ public TypedGeneratorFactory(
3941
_closure = closure;
4042
_realmState = realmState;
4143
_isLexicallyStrict = isLexicallyStrict;
44+
_hasFunctionNameEnvironment = hasFunctionNameEnvironment;
4245
_isConstructorEnabled = isConstructorFunction;
4346
InitializeProperties();
4447
}
@@ -105,6 +108,7 @@ public void EnsureHasName(string name, bool overwriteExisting = false)
105108
this,
106109
_realmState,
107110
_isLexicallyStrict,
111+
_hasFunctionNameEnvironment,
108112
_homeObject,
109113
_privateNameScope,
110114
_capturedPrivateNameScopes);

src/Asynkron.JsEngine/Ast/TypedAstEvaluator.TypedGeneratorInstance.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ private sealed class TypedGeneratorInstance
1717
private readonly JsEnvironment _closure;
1818
private readonly FunctionExpression _function;
1919
private readonly GeneratorPlan? _plan;
20+
private readonly bool _hasFunctionNameEnvironment;
2021
private readonly bool _isStrict;
2122
private readonly ImmutableArray<PrivateNameScope> _capturedPrivateNameScopes;
2223
private readonly RealmState _realmState;
@@ -54,6 +55,7 @@ public TypedGeneratorInstance(
5455
IJsCallable callable,
5556
RealmState realmState,
5657
bool isLexicallyStrict,
58+
bool hasFunctionNameEnvironment,
5759
IJsObjectLike? homeObject,
5860
PrivateNameScope? privateNameScope,
5961
ImmutableArray<PrivateNameScope> capturedPrivateNameScopes)
@@ -64,6 +66,7 @@ public TypedGeneratorInstance(
6466
_thisValue = thisValue;
6567
_callable = callable;
6668
_realmState = realmState;
69+
_hasFunctionNameEnvironment = hasFunctionNameEnvironment;
6770
_homeObject = homeObject;
6871
_privateNameScope = privateNameScope;
6972
_capturedPrivateNameScopes = capturedPrivateNameScopes;
@@ -293,7 +296,7 @@ private JsEnvironment CreateExecutionEnvironment()
293296
functionEnvironment.Define(Symbol.Arguments, argumentsObject, isLexical: false);
294297
}
295298

296-
if (_function.Name is { } functionName)
299+
if (_function.Name is { } functionName && !_hasFunctionNameEnvironment)
297300
{
298301
parameterEnvironment.Define(functionName, _callable, isConst: true, isLexical: true, blocksFunctionScopeOverride: true);
299302
}

src/Asynkron.JsEngine/JsEnvironment.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,8 @@ private void WriteResolvedBindingValue(
740740
{
741741
// Immutable bindings (named function expression names) throw in strict mode,
742742
// but silently fail in non-strict mode
743-
if (isStrictContext)
743+
var bindingIsStrict = bindingEnvironment.IsStrict || bindingEnvironment.GetFunctionScope().IsStrict;
744+
if (bindingIsStrict || isStrictContext)
744745
{
745746
throw new ThrowSignal(StandardLibrary.CreateTypeError(
746747
$"Cannot reassign constant '{name.Name}'.", realm: realm));

0 commit comments

Comments
 (0)