Skip to content

Commit 256df61

Browse files
jensjohaCommit Queue
authored andcommitted
[CFE] Expression compilation inside inner function in extension/extension type
The VM sends the name of the inner function which we can't look up so we need to get it from the offset. A future CL should probably stop passing the method name at all. There are still snafus possible here, although seemingly unrelated (see #61502). Change-Id: Ife3da622ca5d2f95be5024c8bbcfb700503f256d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/449540 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent bf6edc7 commit 256df61

12 files changed

+279
-18
lines changed

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,12 +917,37 @@ const String unnamedExtensionSentinel = '<unnamed extension>';
917917
/// extractQualifiedNameFromExtensionMethodName('_extension#3|set#bar'),
918918
/// '<unnamed extension>.bar')
919919
///
920-
String? extractQualifiedNameFromExtensionMethodName(String? methodName) {
920+
/// DartDocTest(
921+
/// extractQualifiedNameFromExtensionMethodName(
922+
/// '_extension#1|bar',
923+
/// keepUnnamedExtensionNamePrefix: true,
924+
/// ),
925+
/// '_extension#1.bar',
926+
/// )
927+
/// DartDocTest(
928+
/// extractQualifiedNameFromExtensionMethodName(
929+
/// '_extension#2|get#bar',
930+
/// keepUnnamedExtensionNamePrefix: true,
931+
/// ),
932+
/// '_extension#2.bar',
933+
/// )
934+
/// DartDocTest(
935+
/// extractQualifiedNameFromExtensionMethodName(
936+
/// '_extension#3|set#bar',
937+
/// keepUnnamedExtensionNamePrefix: true,
938+
/// ),
939+
/// '_extension#3.bar',
940+
/// )
941+
String? extractQualifiedNameFromExtensionMethodName(
942+
String? methodName, {
943+
bool keepUnnamedExtensionNamePrefix = false,
944+
}) {
921945
if (methodName == null) return null;
922946
int delimiterIndex = methodName.indexOf(NameScheme.extensionNameDelimiter);
923947
if (delimiterIndex == -1) return null;
924948
String extensionName = methodName.substring(0, delimiterIndex);
925-
if (extensionName.startsWith(NameScheme.unnamedExtensionNamePrefix)) {
949+
if (extensionName.startsWith(NameScheme.unnamedExtensionNamePrefix) &&
950+
!keepUnnamedExtensionNamePrefix) {
926951
extensionName = unnamedExtensionSentinel;
927952
}
928953
String memberName = methodName.substring(

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ import '../api_prototype/incremental_kernel_generator.dart'
7373
IncrementalKernelGenerator,
7474
isLegalIdentifier;
7575
import '../api_prototype/lowering_predicates.dart'
76-
show isExtensionThisName, syntheticThisName, hasUnnamedExtensionNamePrefix;
76+
show
77+
isExtensionThisName,
78+
syntheticThisName,
79+
hasUnnamedExtensionNamePrefix,
80+
extractQualifiedNameFromExtensionMethodName;
7781
import '../api_prototype/memory_file_system.dart' show MemoryFileSystem;
7882
import '../builder/builder.dart' show Builder;
7983
import '../builder/compilation_unit.dart'
@@ -1835,6 +1839,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
18351839
}
18361840
LibraryBuilder libraryBuilder = compilationUnit.libraryBuilder;
18371841
List<VariableDeclarationImpl> extraKnownVariables = [];
1842+
String? usedMethodName = methodName;
18381843
if (scriptUri != null && offset != TreeNode.noOffset) {
18391844
Uri? scriptUriAsUri = Uri.tryParse(scriptUri);
18401845
if (scriptUriAsUri != null) {
@@ -1866,6 +1871,15 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
18661871
offset,
18671872
);
18681873

1874+
if (foundScope.member != null &&
1875+
(foundScope.member!.isExtensionMember ||
1876+
foundScope.member!.isExtensionTypeMember)) {
1877+
usedMethodName = extractQualifiedNameFromExtensionMethodName(
1878+
foundScope.member!.name.text,
1879+
keepUnnamedExtensionNamePrefix: true,
1880+
);
1881+
}
1882+
18691883
Map<TypeParameter, TypeParameterType> substitutionMap = {};
18701884
Map<String, TypeParameter> typeDefinitionNamesMap = {};
18711885
for (TypeParameter typeDefinition in typeDefinitions) {
@@ -1959,11 +1973,11 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
19591973
Extension? extension;
19601974
ExtensionTypeDeclaration? extensionType;
19611975
String? extensionName;
1962-
if (methodName != null) {
1963-
int indexOfDot = methodName.indexOf(".");
1976+
if (usedMethodName != null) {
1977+
int indexOfDot = usedMethodName.indexOf(".");
19641978
if (indexOfDot >= 0) {
1965-
String beforeDot = methodName.substring(0, indexOfDot);
1966-
String afterDot = methodName.substring(indexOfDot + 1);
1979+
String beforeDot = usedMethodName.substring(0, indexOfDot);
1980+
String afterDot = usedMethodName.substring(indexOfDot + 1);
19671981
Builder? builder = libraryBuilder.libraryNameSpace
19681982
.lookup(beforeDot)
19691983
?.getable;

pkg/front_end/test/dartdoc_test_test.dart

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ void testTestExtraction() {
380380
],
381381
);
382382

383-
// Two valid tests. Four invalid ones.
383+
// Two valid tests. Five invalid ones.
384384
expect(
385385
extractTests("""
386386
// DartDocTest(1+1, 2)
@@ -389,6 +389,7 @@ void testTestExtraction() {
389389
// DartDocTest(2+40, 42]
390390
// DartDocTest(2+40, 42)
391391
// DartDocTest(2+40, 42+)
392+
// DartDocTest(2+40, 42, 42)
392393
"""),
393394
<impl.Test>[
394395
new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:1:16"),
@@ -425,6 +426,13 @@ darttest:/foo.dart:6:25: Expected an identifier, but got ')'.
425426
148,
426427
"darttest:/foo.dart:6:25",
427428
),
429+
new impl.TestParseError(
430+
"""darttest:/foo.dart:7:24: Expected ')' before this.
431+
// DartDocTest(2+40, 42, 42)
432+
^""",
433+
173,
434+
"darttest:/foo.dart:7:24",
435+
),
428436
],
429437
);
430438

@@ -519,6 +527,25 @@ darttest:/foo.dart:6:25: Expected an identifier, but got ')'.
519527
new impl.ThrowsTest("2~/0", "darttest:/foo.dart:7:26"),
520528
],
521529
);
530+
531+
// Expect with trailing comma.
532+
expect(
533+
extractTests("""
534+
// not a test comment
535+
void foo_bar_long_name() {}
536+
537+
// DartDocTestThrows(1~/0,)
538+
// DartDocTest(1+1, 2,)
539+
// DartDocTest(2+2, 4, )
540+
// DartDocTestThrows(2~/0, )"""),
541+
<impl.Test>[
542+
// For now the order is expect tests first.
543+
new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:5:20"),
544+
new impl.ExpectTest("2+2", "4", "darttest:/foo.dart:6:20"),
545+
new impl.ThrowsTest("1~/0", "darttest:/foo.dart:4:26"),
546+
new impl.ThrowsTest("2~/0", "darttest:/foo.dart:7:26"),
547+
],
548+
);
522549
}
523550

524551
void testCommentExtraction() {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 type.
14+
15+
sources: |
16+
import 'dart:developer';
17+
18+
extension type Foo(int value) {
19+
void foo() {
20+
void bar() {
21+
debugger();
22+
if (value == 42) {
23+
print("It's 42!");
24+
}
25+
}
26+
bar();
27+
}
28+
}
29+
30+
void main(List<String> args) {
31+
Foo foo = new Foo(args.length);
32+
foo.foo();
33+
}
34+
35+
definitions: ["#this"]
36+
definition_types: ["dart:core", "_Smi", "1", "0"]
37+
type_definitions: []
38+
type_bounds: []
39+
type_defaults: []
40+
method: "bar"
41+
static: true
42+
offset: 96
43+
scriptUri: main.dart
44+
expression: "value"
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(lowered #lib1::Foo% /* erasure=dart.core::int, declared=! */ #this) → dynamic
4+
return #this as{Unchecked} dart.core::int;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 expression inside an extension type.
14+
15+
sources: |
16+
import 'dart:developer';
17+
18+
extension type Foo(int value) {
19+
void foo() {
20+
() {
21+
debugger();
22+
if (value == 42) {
23+
print("It's 42!");
24+
}
25+
}();
26+
}
27+
}
28+
29+
void main(List<String> args) {
30+
Foo foo = new Foo(args.length);
31+
foo.foo();
32+
}
33+
34+
definitions: ["#this"]
35+
definition_types: ["dart:core", "_Smi", "1", "0"]
36+
type_definitions: []
37+
type_bounds: []
38+
type_defaults: []
39+
method: "<anonymous closure>"
40+
static: true
41+
offset: 88
42+
scriptUri: main.dart
43+
expression: "value"
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(lowered #lib1::Foo% /* erasure=dart.core::int, declared=! */ #this) → dynamic
4+
return #this as{Unchecked} dart.core::int;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
15+
sources: |
16+
import 'dart:developer';
17+
18+
extension on int {
19+
void foo() {
20+
void bar() {
21+
debugger();
22+
if (this == 42) {
23+
print("It's 42!");
24+
}
25+
}
26+
bar();
27+
}
28+
}
29+
30+
void main(List<String> args) {
31+
args.length.foo();
32+
}
33+
34+
definitions: ["#this"]
35+
definition_types: ["dart:core", "_Smi", "1", "0"]
36+
type_definitions: []
37+
type_bounds: []
38+
type_defaults: []
39+
method: "bar"
40+
static: true
41+
offset: 83
42+
scriptUri: main.dart
43+
expression: "this"
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(lowered dart.core::_Smi #this) → dynamic
4+
return #this;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 expression inside an extension.
14+
15+
sources: |
16+
import 'dart:developer';
17+
18+
extension on int {
19+
void foo() {
20+
() {
21+
debugger();
22+
if (this == 42) {
23+
print("It's 42!");
24+
}
25+
}();
26+
}
27+
}
28+
29+
void main(List<String> args) {
30+
args.length.foo();
31+
}
32+
33+
definitions: ["#this"]
34+
definition_types: ["dart:core", "_Smi", "1", "0"]
35+
type_definitions: []
36+
type_bounds: []
37+
type_defaults: []
38+
method: "<anonymous closure>"
39+
static: true
40+
offset: 75
41+
scriptUri: main.dart
42+
expression: "this"

0 commit comments

Comments
 (0)