Skip to content

Commit 92fa127

Browse files
jensjohaCommit Queue
authored andcommitted
[CFE] Expression compilation: #this doesn't have to be the first definition
In expression compilation, to support evaluating "this" in extensions and extension types the VM sends us a "#this" parameter. The code assumed this was given as the first variable in the list of definitions, but that has turned out to not be the case and not something the VM has ever promised. This CL allows the #this to be at any place. Bug: #61502 TEST=Manual Change-Id: I8868a2e05704cda57f2835e9f261ade12566bf7c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/451760 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Jens Johansen <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent de4e077 commit 92fa127

5 files changed

+103
-46
lines changed

pkg/front_end/lib/src/api_prototype/expression_compilation_tools.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Map<String, DartType>? createDefinitionsWithTypes(
4444
Map<String, DartType> completeDefinitions = {};
4545
for (int i = 0; i < definitions.length; i++) {
4646
String name = definitions[i];
47-
if (isLegalIdentifier(name) || (i == 0 && isExtensionThisName(name))) {
47+
if (isLegalIdentifier(name) || isExtensionThisName(name)) {
4848
ParsedType type = definitionTypesParsed[i];
4949
DartType dartType = type.createDartType(libraryIndex);
5050
completeDefinitions[name] = dartType;

pkg/front_end/lib/src/base/incremental_compiler.dart

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ import 'package:kernel/kernel.dart'
3232
DartType,
3333
DynamicType,
3434
Expression,
35-
Extension,
3635
ExtensionType,
37-
ExtensionTypeDeclaration,
3836
FunctionNode,
3937
InterfaceType,
4038
Library,
@@ -1959,19 +1957,21 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
19591957

19601958
_ticker.logMs("Loaded library $libraryUri");
19611959

1960+
int? offsetToUse;
19621961
Class? cls;
19631962
if (className != null) {
19641963
Builder? scopeMember = libraryBuilder.libraryNameSpace
19651964
.lookup(className)
19661965
?.getable;
19671966
if (scopeMember is ClassBuilder) {
19681967
cls = scopeMember.cls;
1968+
offsetToUse = cls.fileOffset;
19691969
} else {
19701970
return null;
19711971
}
19721972
}
1973-
Extension? extension;
1974-
ExtensionTypeDeclaration? extensionType;
1973+
1974+
bool isExtensionOrExtensionType = false;
19751975
String? extensionName;
19761976
if (usedMethodName != null) {
19771977
int indexOfDot = usedMethodName.indexOf(".");
@@ -1997,15 +1997,17 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
19971997
}
19981998
extensionName = beforeDot;
19991999
if (builder is ExtensionBuilder) {
2000-
extension = builder.extension;
2000+
isExtensionOrExtensionType = true;
2001+
offsetToUse = builder.fileOffset;
20012002
Builder? subBuilder = builder.lookupLocalMember(afterDot)?.getable;
20022003
if (subBuilder is MemberBuilder) {
20032004
if (subBuilder.isExtensionInstanceMember) {
20042005
isStatic = false;
20052006
}
20062007
}
20072008
} else if (builder is ExtensionTypeDeclarationBuilder) {
2008-
extensionType = builder.extensionTypeDeclaration;
2009+
isExtensionOrExtensionType = true;
2010+
offsetToUse = builder.fileOffset;
20092011
Builder? subBuilder = builder.lookupLocalMember(afterDot)?.getable;
20102012
if (subBuilder is MemberBuilder) {
20112013
if (subBuilder.isExtensionTypeInstanceMember) {
@@ -2042,25 +2044,25 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
20422044
return null;
20432045
}
20442046
}
2045-
int index = 0;
20462047
for (String name in usedDefinitions.keys) {
2047-
index++;
2048-
if (!(isLegalIdentifier(name) ||
2049-
((extension != null || extensionType != null) &&
2050-
!isStatic &&
2051-
index == 1 &&
2052-
isExtensionThisName(name)))) {
2053-
lastGoodKernelTarget.loader.addProblem(
2054-
codeIncrementalCompilerIllegalParameter.withArgumentsOld(name),
2055-
// TODO: pass variable declarations instead of
2056-
// parameter names for proper location detection.
2057-
// https://github.com/dart-lang/sdk/issues/44158
2058-
-1,
2059-
-1,
2060-
libraryUri,
2061-
);
2062-
return null;
2048+
if (isLegalIdentifier(name)) continue;
2049+
if (isExtensionThisName(name) &&
2050+
!isStatic &&
2051+
isExtensionOrExtensionType) {
2052+
// Accept #this for extensions and extension types.
2053+
continue;
20632054
}
2055+
2056+
lastGoodKernelTarget.loader.addProblem(
2057+
codeIncrementalCompilerIllegalParameter.withArgumentsOld(name),
2058+
// TODO: pass variable declarations instead of
2059+
// parameter names for proper location detection.
2060+
// https://github.com/dart-lang/sdk/issues/44158
2061+
-1,
2062+
-1,
2063+
libraryUri,
2064+
);
2065+
return null;
20642066
}
20652067

20662068
// Setup scope first in two-step process:
@@ -2167,35 +2169,33 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
21672169
MemoryFileSystem fs = hfs.memory;
21682170
fs.entityForUri(debugExprUri).writeAsStringSync(expression);
21692171

2172+
VariableDeclaration? extensionThis;
2173+
21702174
// TODO: pass variable declarations instead of
21712175
// parameter names for proper location detection.
21722176
// https://github.com/dart-lang/sdk/issues/44158
21732177
FunctionNode parameters = new FunctionNode(
21742178
null,
21752179
typeParameters: typeDefinitions,
2176-
positionalParameters: usedDefinitions.entries
2177-
.map<VariableDeclaration>(
2178-
(MapEntry<String, DartType> def) =>
2179-
new VariableDeclarationImpl(def.key, type: def.value)
2180-
..fileOffset =
2181-
cls?.fileOffset ??
2182-
extension?.fileOffset ??
2183-
extensionType?.fileOffset ??
2184-
libraryBuilder.library.fileOffset,
2185-
)
2186-
.toList(),
2180+
positionalParameters: usedDefinitions.entries.map<VariableDeclaration>((
2181+
MapEntry<String, DartType> def,
2182+
) {
2183+
VariableDeclarationImpl variable = new VariableDeclarationImpl(
2184+
def.key,
2185+
type: def.value,
2186+
)..fileOffset = offsetToUse ?? libraryBuilder.library.fileOffset;
2187+
2188+
if (isExtensionOrExtensionType &&
2189+
!isStatic &&
2190+
isExtensionThisName(def.key) &&
2191+
extensionThis == null) {
2192+
// The `#this` variable is special.
2193+
extensionThis = variable..isLowered = true;
2194+
}
2195+
return variable;
2196+
}).toList(),
21872197
);
21882198

2189-
VariableDeclaration? extensionThis;
2190-
if ((extension != null || extensionType != null) &&
2191-
!isStatic &&
2192-
parameters.positionalParameters.isNotEmpty) {
2193-
// We expect the first parameter to be called #this and be special.
2194-
if (isExtensionThisName(parameters.positionalParameters.first.name)) {
2195-
extensionThis = parameters.positionalParameters.first;
2196-
extensionThis.isLowered = true;
2197-
}
2198-
}
21992199
lastGoodKernelTarget.buildSyntheticLibrariesUntilBuildScopes([
22002200
debugLibrary,
22012201
]);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
# for details. All rights reserved. Use of this source code is governed by a
3+
# BSD-style license that can be found in the LICENSE file.
4+
5+
# Definition, offset, method etc extracted by starting the VM with
6+
# `-DDFE_VERBOSE=true`, e.g.
7+
# ```
8+
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
9+
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
10+
# ```
11+
# and then issuing the expression compilation.
12+
#
13+
# Paused inside a function declaration inside an extension.
14+
# For this code the VM sends it in a weird order.
15+
16+
sources: |
17+
import 'dart:developer';
18+
19+
extension on String {
20+
List<String> get foo {
21+
List<String> result = [];
22+
int offset = indexOf(':') + 1;
23+
void bar() {
24+
debugger();
25+
while (offset < length) {
26+
if (codeUnitAt(offset) != 32) {
27+
return;
28+
}
29+
offset++;
30+
}
31+
}
32+
33+
bar();
34+
result.add("$offset");
35+
offset++;
36+
return result;
37+
}
38+
}
39+
40+
void main(List<String> args) {
41+
print(args[0].foo);
42+
}
43+
44+
definitions: ["offset", "#this"]
45+
definition_types: ["dart:core", "_Smi", "1", "0", "dart:core", "_OneByteString", "1", "0"]
46+
type_definitions: []
47+
type_bounds: []
48+
type_defaults: []
49+
method: "bar"
50+
static: true
51+
offset: 161
52+
scriptUri: main.dart
53+
expression: "offset + this.length"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Errors: {
2+
}
3+
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_Smi offset, lowered dart.core::_OneByteString #this) → dynamic
4+
return offset.{dart.core::_IntegerImplementation::+}(#this.{dart.core::_StringBase::length}{dart.core::int}){(dart.core::num) → dart.core::num};

pkg/vm/lib/incremental_compiler.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class IncrementalCompiler {
262262
// Revert to old behaviour of setting everything to dynamic.
263263
for (int i = 0; i < definitions.length; i++) {
264264
String name = definitions[i];
265-
if (isLegalIdentifier(name) || (i == 0 && isExtensionThisName(name))) {
265+
if (isLegalIdentifier(name) || isExtensionThisName(name)) {
266266
completeDefinitions[name] = new DynamicType();
267267
}
268268
}

0 commit comments

Comments
 (0)