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
4 changes: 2 additions & 2 deletions docs/rules/unified-bytecode-prototypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ all-or-nothing until a separate routing issue proves production readiness.
10b. Treat `arguments` as an arguments-object dependency only after binding
resolution proves it is not an ordinary activation slot. A parameter named
`arguments` or a lexical body binding named `arguments` is regular slot
traffic for production unified bytecode reads and `typeof`; the real implicit
arguments object, arguments call targets, and writes/updates/deletes of the
traffic for production unified bytecode reads, `typeof`, and updates; the real implicit
arguments object, arguments call targets, and writes/deletes of the
implicit arguments binding still decline before VM execution. Keep the
invocation descriptor, expression selector, and compiler in agreement so a
name-only `operation.IsArguments` check cannot block shadowed bindings or
Expand Down
4 changes: 2 additions & 2 deletions docs/unified-bytecode-expansion-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ must still obey the no-mixed-execution rule.
| `AsyncLikeFunction` | `Evaluate` activation gate and awaited with-object plan decline | Existing async / awaited IR route | Resumable async/generator lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_AsyncLikeActivation_DeclinesBeforePlanInspection"` |
| `GeneratorFunction` | `Evaluate` activation gate for ordinary sync production routing | Existing generator IR route | Resumable async/generator lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_GeneratorActivation_DeclinesBeforePlanInspection"` |
| `CapturedOrDynamicActivation` | Activation descriptor `HasCapturedOrDynamicActivation`, including captured function scope or unresolved dynamic activation outside the with-backed lane | Existing sync IR / environment route | Dynamic activation lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_ActivationDependencies_DeclineBeforeCompile"` |
| `ArgumentsObjectDependency` | Activation descriptor and expression scan for real `arguments` object access or arguments call targets; parameter/lexical `arguments` reads and `typeof` now route as activation slots | Existing sync IR / arguments-object route | Arguments object lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_ArgumentsAccess_DeclinesWithArgumentsDependency"` |
| `ArgumentsObjectDependency` | Activation descriptor and expression scan for real `arguments` object access or arguments call targets; parameter/lexical `arguments` reads, `typeof`, and updates now route as activation slots | Existing sync IR / arguments-object route | Arguments object lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_ArgumentsAccess_DeclinesWithArgumentsDependency"` |
| `ArrowLexicalThisDependency` | Activation descriptor gate for arrow lexical `this` / `new.target` ownership before ordinary sync routing | Existing arrow invocation route | Arrow route lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_OrdinarySyncActivationDescriptorBlockers_DeclineBeforeCompile"` |
| `ClassConstructorActivation` | Activation descriptor gate for class constructor activation outside the admitted explicit derived-constructor `super(...)` route without `this` body reads/writes | Constructor route | Constructor boundary lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionConstructCallTests&FullyQualifiedName~SuperConstruct"` |
| `CallDependency` | Direct eval outside the one-argument non-spread eval-identifier boundary, out-of-boundary call-target preparation, complex call arguments, and descriptor-level non-parameter callee calls | Existing sync IR call route | Wider call invocation lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_CallWithComplexTemplateLiteralSubstitution_DeclinesCallDependency"` |
Expand Down Expand Up @@ -302,7 +302,7 @@ not always have a `UnifiedBytecodeProductionDeclineCode`.
| `pre-gate:IsDefaultDerivedConstructor` | Default derived class constructor setup | Constructor route | Constructor boundary lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionInvocationTests"` |
| `pre-gate:hasParameterExpressions` | Default/rest/destructured parameter expressions | Existing parameter environment route | Parameter semantics lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionInvocationTests"` |
| `pre-gate:hasOnlySimpleIdentifierParameters` | Non-simple parameter lists | Existing parameter environment route | Parameter semantics lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionInvocationTests"` |
| `pre-gate:usesArguments` | Functions that read the real implicit `arguments` object; shadowing parameter/lexical `arguments` reads and `typeof` are activation-slot traffic | Existing arguments-object route | Arguments object lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_ArgumentsAccess_DeclinesWithArgumentsDependency"` |
| `pre-gate:usesArguments` | Functions that read the real implicit `arguments` object; shadowing parameter/lexical `arguments` reads, `typeof`, and updates are activation-slot traffic | Existing arguments-object route | Arguments object lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_ArgumentsAccess_DeclinesWithArgumentsDependency"` |
| `pre-gate:needsArgumentsBinding` | Sloppy mapped arguments binding when an implicit arguments object is needed and not admitted by with-backed dynamic-name routing | Existing arguments-object route | Arguments object lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionInvocationTests"` |
| `pre-gate:allowIdentifierCache` | Identifier cache disabled outside the with-backed dynamic-name lane | Existing environment lookup route | Dynamic-name lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionEligibilityTests&FullyQualifiedName~Evaluate_DynamicIdentifierLookup_DeclinesWithDynamicLookupDependency"` |
| `pre-gate:lexicalThisEnvironment` | Lexical `this` environment captured by arrow / class-derived shapes | Existing lexical-this route | This-binding lane | `rtk dotnet test tests/Asynkron.JsEngine.Tests --filter "FullyQualifiedName~UnifiedBytecodeProductionInvocationTests"` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4128,12 +4128,6 @@ private static bool TryAppendExpressionProgramOps(
break;

case ExpressionOpKind.UpdateIdentifier:
if (operation.IsArguments)
{
reason = "arguments update is not supported.";
return false;
}

var updateIdentifier = operation.GetIdentifier(expressionProgram.IdentifierConstants.AsSpan());
if (TryResolveActivationSlot(updateIdentifier, slotLayout, out var updateSlot))
{
Expand All @@ -4143,6 +4137,12 @@ private static bool TryAppendExpressionProgramOps(
break;
}

if (operation.IsArguments)
{
reason = "arguments update is not supported.";
return false;
}

if (!allowsDynamicIdentifiers)
{
reason =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1234,18 +1234,15 @@ private static bool TryFindExpressionDecline(
return true;

case ExpressionOpKind.UpdateIdentifier:
if (!operation.IsArguments)
var updateIdentifier = operation.GetIdentifier(identifierConstants);
if (TryResolveActivationSlot(updateIdentifier, activationSlots))
{
var updateIdentifier = operation.GetIdentifier(identifierConstants);
if (TryResolveActivationSlot(updateIdentifier, activationSlots))
{
break;
}
break;
}

if (allowsDynamicIdentifiers)
{
break;
}
if (!operation.IsArguments && allowsDynamicIdentifiers)
{
break;
}

declineCode = UnifiedBytecodeProductionDeclineCode.PropertyUpdateDependency;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2694,6 +2694,43 @@ function readArguments() {
Assert.Equal(UnifiedBytecodeProductionDeclineCode.ArgumentsObjectDependency, result.Code);
}

[Fact]
public void Evaluate_UpdateParameterNamedArguments_AcceptsAsActivationSlot()
{
var plan = GetFunctionPlan("""
function bump(arguments) {
arguments++;
return arguments;
}
""",
"bump");

var result = UnifiedBytecodeProductionEligibility.Evaluate(
plan,
new UnifiedBytecodeProductionActivationDescriptor());

Assert.True(result.IsEligible);
}

[Fact]
public void Evaluate_UpdateImplicitArgumentsObject_DeclinesWithDynamicLookupDependency()
{
var plan = GetFunctionPlan("""
function bump() {
arguments++;
return 1;
}
""",
"bump");

var result = UnifiedBytecodeProductionEligibility.Evaluate(
plan,
new UnifiedBytecodeProductionActivationDescriptor());

Assert.False(result.IsEligible);
Assert.Equal(UnifiedBytecodeProductionDeclineCode.DynamicLookupDependency, result.Code);
}

[Fact]
public void Evaluate_DynamicIdentifierLookup_DeclinesWithDynamicLookupDependency()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5029,6 +5029,67 @@ record => record.Message.Contains(
StringComparison.Ordinal));
}

[Fact(Timeout = 5000)]
public async Task UpdateParameterNamedArguments_UsesUnifiedBytecodeProductionFastPath()
{
await using var engine = CreateEngine();
var result = await engine.Evaluate("""
function bump(arguments) {
arguments++;
return arguments;
}

bump(41);
""");

Assert.Equal(42d, result);
Assert.Contains(CurrentLogger!.Collector.Snapshot(),
record => record.Message.Contains(
"unified-bytecode-production-fast-path func=bump argc=1",
StringComparison.Ordinal));
}

[Fact(Timeout = 5000)]
public async Task UpdateLexicalArgumentsBinding_UsesUnifiedBytecodeProductionFastPath()
{
await using var engine = CreateEngine();
var result = await engine.Evaluate("""
function bump() {
let arguments = 41;
++arguments;
return arguments;
}

bump();
""");

Assert.Equal(42d, result);
Assert.Contains(CurrentLogger!.Collector.Snapshot(),
record => record.Message.Contains(
"unified-bytecode-production-fast-path func=bump argc=0",
StringComparison.Ordinal));
}

[Fact(Timeout = 5000)]
public async Task UpdateImplicitArgumentsObject_DeclinesAndFallsBack()
{
await using var engine = CreateEngine();
var result = await engine.Evaluate("""
function bump() {
arguments++;
return 1;
}

bump(41);
""");

Assert.Equal(1d, result);
Assert.DoesNotContain(CurrentLogger!.Collector.Snapshot(),
record => record.Message.Contains(
"unified-bytecode-production-fast-path func=bump",
StringComparison.Ordinal));
}

[Theory(Timeout = 5000)]
[InlineData(
"""
Expand Down
Loading