Skip to content

Commit 207b563

Browse files
authored
Admit binary expression named property reads (#3021)
1 parent 1bcab06 commit 207b563

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

src/Asynkron.JsEngine/Execution/UnifiedBytecode/UnifiedBytecodeProductionEligibility.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,11 @@ private static bool TryFindExpressionDecline(
10581058
break;
10591059
}
10601060

1061+
if (TryIsFirstBoundaryBinaryNamedPropertyReadCandidate(program, identifierConstants, activationSlots))
1062+
{
1063+
break;
1064+
}
1065+
10611066
if (TryIsFirstBoundaryOptionalNamedChainCandidate(program, identifierConstants, activationSlots))
10621067
{
10631068
break;
@@ -1856,6 +1861,38 @@ private static bool TryIsFirstBoundaryObjectLiteralNamedPropertyReadCandidate(
18561861
return true;
18571862
}
18581863

1864+
private static bool TryIsFirstBoundaryBinaryNamedPropertyReadCandidate(
1865+
ExpressionProgram program,
1866+
ReadOnlySpan<IdentifierOperand> identifierConstants,
1867+
ActivationSlotShape activationSlots)
1868+
{
1869+
if (!TryMeasureSimpleBinaryOperandSpan(
1870+
program,
1871+
startIndex: 0,
1872+
identifierConstants,
1873+
activationSlots,
1874+
out var binarySpanLength) ||
1875+
binarySpanLength >= program.OperationCount)
1876+
{
1877+
return false;
1878+
}
1879+
1880+
var stringConstants = program.StringConstants.AsSpan();
1881+
for (var index = binarySpanLength; index < program.OperationCount; index++)
1882+
{
1883+
var operation = program.GetOperation(index);
1884+
if (operation.Kind != ExpressionOpKind.GetNamedProperty ||
1885+
operation.IsOptional ||
1886+
operation.ShortCircuitOnNullishTarget ||
1887+
operation.GetString(stringConstants).IsPrivateName())
1888+
{
1889+
return false;
1890+
}
1891+
}
1892+
1893+
return true;
1894+
}
1895+
18591896
/// <summary>
18601897
/// Returns <c>true</c> when the <see cref="ExpressionOpKind.GetNamedProperty"/> operation at
18611898
/// <paramref name="operationIndex"/> is the last op before a logical short-circuit jump

tests/Asynkron.JsEngine.Tests/UnifiedBytecodeProductionEligibilityTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2478,7 +2478,7 @@ function readBinaryTarget(a, b) {
24782478
}
24792479
""",
24802480
"readBinaryTarget",
2481-
(int)UnifiedBytecodeProductionDeclineCode.PropertyReadBoundaryOutOfScope)]
2481+
(int)UnifiedBytecodeProductionDeclineCode.None)]
24822482
[InlineData(
24832483
"""
24842484
function readComputedObjectLiteralKey(box) {

tests/Asynkron.JsEngine.Tests/UnifiedBytecodeProductionInvocationTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3722,6 +3722,25 @@ static record => record.Message.Contains(
37223722
StringComparison.Ordinal));
37233723
}
37243724

3725+
[Fact(Timeout = 5000)]
3726+
public async Task BinaryExpressionNamedPropertyRead_UsesUnifiedBytecodeProductionFastPath()
3727+
{
3728+
await using var engine = CreateEngine();
3729+
var result = await engine.Evaluate("""
3730+
function readBinaryTarget(left, right) {
3731+
return (left + right).length;
3732+
}
3733+
3734+
readBinaryTarget("xy", "z");
3735+
""");
3736+
3737+
Assert.Equal(3d, result);
3738+
Assert.Contains(CurrentLogger!.Collector.Snapshot(),
3739+
static record => record.Message.Contains(
3740+
"unified-bytecode-production-fast-path func=readBinaryTarget argc=2",
3741+
StringComparison.Ordinal));
3742+
}
3743+
37253744
[Fact(Timeout = 5000)]
37263745
public async Task TryCatch_GetterThrow_IsCaughtOnProductionFastPath()
37273746
{

0 commit comments

Comments
 (0)