Skip to content

Commit 48d8225

Browse files
jensjohacommit-bot@chromium.org
authored andcommitted
[VM] [CFE]: Expression compilation inside extension method
This CL handles expression compilation inside extension methods better. It is now possible to evaluate "this" and other methods defined in the extension. #46757 TEST=service tests added. Change-Id: I3c71eb23117e26b01961f32103f4046f0b537976 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212286 Commit-Queue: Jens Johansen <[email protected]> Reviewed-by: Ben Konyi <[email protected]> Reviewed-by: Anna Gringauze <[email protected]> Reviewed-by: Johnni Winther <[email protected]>
1 parent d80cff6 commit 48d8225

27 files changed

+321
-58
lines changed

.dart_tool/package_config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"constraint, update this by running tools/generate_package_config.dart."
1212
],
1313
"configVersion": 2,
14-
"generated": "2021-09-08T14:55:24.803714",
14+
"generated": "2021-09-10T10:06:41.987732",
1515
"generator": "tools/generate_package_config.dart",
1616
"packages": [
1717
{
@@ -494,7 +494,7 @@
494494
"name": "observatory_2",
495495
"rootUri": "../runtime/observatory_2",
496496
"packageUri": "lib/",
497-
"languageVersion": "2.2"
497+
"languageVersion": "2.6"
498498
},
499499
{
500500
"name": "observatory_test_package",

pkg/dev_compiler/lib/src/kernel/expression_compiler.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,8 +440,8 @@ class ExpressionCompiler {
440440
scope.typeParameters,
441441
debugProcedureName,
442442
scope.library.importUri,
443-
scope.cls?.name,
444-
scope.isStatic);
443+
className: scope.cls?.name,
444+
isStatic: scope.isStatic);
445445

446446
_log('Compiled expression to kernel');
447447

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,9 @@ abstract class IncrementalKernelGenerator {
136136
List<TypeParameter> typeDefinitions,
137137
String syntheticProcedureName,
138138
Uri libraryUri,
139-
[String? className,
140-
bool isStatic = false]);
139+
{String? className,
140+
String? methodName,
141+
bool isStatic = false});
141142

142143
/// Sets experimental features.
143144
///

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,8 +660,8 @@ String extractLocalNameFromLateLoweredSetter(String name) {
660660
///
661661
/// where '#this' is the synthetic "extension this" parameter.
662662
bool isExtensionThis(VariableDeclaration node) {
663-
assert(
664-
node.isLowered || node.name == null || !isExtensionThisName(node.name));
663+
assert(node.isLowered || node.name == null || !isExtensionThisName(node.name),
664+
"$node has name ${node.name} and node.isLowered = ${node.isLowered}");
665665
return node.isLowered && isExtensionThisName(node.name);
666666
}
667667

pkg/front_end/lib/src/api_unstable/vm.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export '../api_prototype/incremental_kernel_generator.dart'
2929
export '../api_prototype/kernel_generator.dart'
3030
show kernelForModule, kernelForProgram;
3131

32+
export '../api_prototype/lowering_predicates.dart' show isExtensionThisName;
33+
3234
export '../api_prototype/memory_file_system.dart' show MemoryFileSystem;
3335

3436
export '../api_prototype/standard_file_system.dart' show StandardFileSystem;

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

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
1111

1212
import 'package:front_end/src/api_prototype/experimental_flags.dart';
1313
import 'package:front_end/src/api_prototype/front_end.dart';
14+
15+
import 'package:front_end/src/api_prototype/lowering_predicates.dart'
16+
show isExtensionThisName;
17+
1418
import 'package:front_end/src/base/nnbd_mode.dart';
19+
20+
import 'package:front_end/src/fasta/builder/member_builder.dart'
21+
show MemberBuilder;
22+
1523
import 'package:front_end/src/fasta/fasta_codes.dart';
1624
import 'package:front_end/src/fasta/source/source_loader.dart';
1725
import 'package:kernel/binary/ast_from_binary.dart'
@@ -34,6 +42,7 @@ import 'package:kernel/kernel.dart'
3442
Component,
3543
DartType,
3644
Expression,
45+
Extension,
3746
FunctionNode,
3847
Library,
3948
LibraryDependency,
@@ -48,7 +57,8 @@ import 'package:kernel/kernel.dart'
4857
Source,
4958
Supertype,
5059
TreeNode,
51-
TypeParameter;
60+
TypeParameter,
61+
VariableDeclaration;
5262

5363
import 'package:kernel/canonical_name.dart'
5464
show CanonicalNameError, CanonicalNameSdkError;
@@ -71,6 +81,8 @@ import 'builder/builder.dart' show Builder;
7181

7282
import 'builder/class_builder.dart' show ClassBuilder;
7383

84+
import 'builder/extension_builder.dart' show ExtensionBuilder;
85+
7486
import 'builder/field_builder.dart' show FieldBuilder;
7587

7688
import 'builder/library_builder.dart' show LibraryBuilder;
@@ -1907,8 +1919,9 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
19071919
List<TypeParameter> typeDefinitions,
19081920
String syntheticProcedureName,
19091921
Uri libraryUri,
1910-
[String? className,
1911-
bool isStatic = false]) async {
1922+
{String? className,
1923+
String? methodName,
1924+
bool isStatic = false}) async {
19121925
assert(dillLoadedData != null && userCode != null);
19131926

19141927
return await context.runInContext((_) async {
@@ -1923,6 +1936,26 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
19231936
cls = classBuilder?.cls;
19241937
if (cls == null) return null;
19251938
}
1939+
Extension? extension;
1940+
String? extensionName;
1941+
if (methodName != null) {
1942+
int indexOfDot = methodName.indexOf(".");
1943+
if (indexOfDot >= 0) {
1944+
String beforeDot = methodName.substring(0, indexOfDot);
1945+
String afterDot = methodName.substring(indexOfDot + 1);
1946+
Builder? builder = libraryBuilder.scopeBuilder[beforeDot];
1947+
extensionName = beforeDot;
1948+
if (builder is ExtensionBuilder) {
1949+
extension = builder.extension;
1950+
Builder? subBuilder = builder.scopeBuilder[afterDot];
1951+
if (subBuilder is MemberBuilder) {
1952+
if (subBuilder.isExtensionInstanceMember) {
1953+
isStatic = false;
1954+
}
1955+
}
1956+
}
1957+
}
1958+
}
19261959

19271960
userCode!.loader.resetSeenMessages();
19281961

@@ -1937,8 +1970,14 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
19371970
return null;
19381971
}
19391972
}
1973+
int index = 0;
19401974
for (String name in definitions.keys) {
1941-
if (!isLegalIdentifier(name)) {
1975+
index++;
1976+
if (!(isLegalIdentifier(name) ||
1977+
(extension != null &&
1978+
!isStatic &&
1979+
index == 1 &&
1980+
isExtensionThisName(name)))) {
19421981
userCode!.loader.addProblem(
19431982
templateIncrementalCompilerIllegalParameter.withArguments(name),
19441983
// TODO: pass variable declarations instead of
@@ -2009,13 +2048,29 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
20092048
positionalParameters: definitions.keys
20102049
.map((name) =>
20112050
new VariableDeclarationImpl(name, 0, type: definitions[name])
2012-
..fileOffset =
2013-
cls?.fileOffset ?? libraryBuilder.library.fileOffset)
2051+
..fileOffset = cls?.fileOffset ??
2052+
extension?.fileOffset ??
2053+
libraryBuilder.library.fileOffset)
20142054
.toList());
20152055

2056+
VariableDeclaration? extensionThis;
2057+
if (extension != null &&
2058+
!isStatic &&
2059+
parameters.positionalParameters.isNotEmpty) {
2060+
// We expect the first parameter to be called #this and be special.
2061+
if (isExtensionThisName(parameters.positionalParameters.first.name)) {
2062+
extensionThis = parameters.positionalParameters.first;
2063+
extensionThis.isLowered = true;
2064+
}
2065+
}
2066+
20162067
debugLibrary.build(userCode!.loader.coreLibrary, modifyTarget: false);
20172068
Expression compiledExpression = await userCode!.loader.buildExpression(
2018-
debugLibrary, className, className != null && !isStatic, parameters);
2069+
debugLibrary,
2070+
className ?? extensionName,
2071+
(className != null && !isStatic) || extensionThis != null,
2072+
parameters,
2073+
extensionThis);
20192074

20202075
Procedure procedure = new Procedure(
20212076
new Name(syntheticProcedureName), ProcedureKind.Method, parameters,
@@ -2026,7 +2081,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
20262081
..parent = parameters;
20272082

20282083
procedure.fileUri = debugLibrary.fileUri;
2029-
procedure.parent = className != null ? cls : libraryBuilder.library;
2084+
procedure.parent = cls ?? libraryBuilder.library;
20302085

20312086
userCode!.uriToSource.remove(debugExprUri);
20322087
userCode!.loader.sourceBytes.remove(debugExprUri);

pkg/front_end/lib/src/fasta/source/source_loader.dart

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import 'package:kernel/ast.dart'
4545
Reference,
4646
Supertype,
4747
TreeNode,
48+
VariableDeclaration,
4849
Version;
4950

5051
import 'package:kernel/class_hierarchy.dart'
@@ -513,26 +514,32 @@ class SourceLoader extends Loader {
513514
}
514515
}
515516

516-
// TODO(johnniwinther,jensj): Handle expression in extensions?
517517
Future<Expression> buildExpression(
518518
SourceLibraryBuilder libraryBuilder,
519-
String? enclosingClass,
519+
String? enclosingClassOrExtension,
520520
bool isClassInstanceMember,
521-
FunctionNode parameters) async {
521+
FunctionNode parameters,
522+
VariableDeclaration? extensionThis) async {
522523
Token token = await tokenize(libraryBuilder, suppressLexicalErrors: false);
523524
DietListener dietListener = createDietListener(libraryBuilder);
524525

525526
Builder parent = libraryBuilder;
526-
if (enclosingClass != null) {
527+
if (enclosingClassOrExtension != null) {
527528
Builder? cls = dietListener.memberScope
528-
.lookup(enclosingClass, -1, libraryBuilder.fileUri);
529+
.lookup(enclosingClassOrExtension, -1, libraryBuilder.fileUri);
529530
if (cls is ClassBuilder) {
530531
parent = cls;
531532
dietListener
532533
..currentDeclaration = cls
533534
..memberScope = cls.scope.copyWithParent(
534535
dietListener.memberScope.withTypeVariables(cls.typeVariables),
535-
"debugExpression in $enclosingClass");
536+
"debugExpression in class $enclosingClassOrExtension");
537+
} else if (cls is ExtensionBuilder) {
538+
parent = cls;
539+
dietListener
540+
..currentDeclaration = cls
541+
..memberScope = cls.scope.copyWithParent(dietListener.memberScope,
542+
"debugExpression in extension $enclosingClassOrExtension");
536543
}
537544
}
538545
ProcedureBuilder builder = new SourceProcedureBuilder(
@@ -562,7 +569,8 @@ class SourceLoader extends Loader {
562569
..parent = parent;
563570
BodyBuilder listener = dietListener.createListener(
564571
builder, dietListener.memberScope,
565-
isDeclarationInstanceMember: isClassInstanceMember);
572+
isDeclarationInstanceMember: isClassInstanceMember,
573+
extensionThis: extensionThis);
566574

567575
return listener.parseSingleExpression(
568576
new Parser(listener,

pkg/front_end/test/fasta/expression_suite.dart

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ class TestCase {
140140

141141
final String className;
142142

143+
final String methodName;
144+
143145
String expression;
144146

145147
List<CompilationResult> results = [];
@@ -153,6 +155,7 @@ class TestCase {
153155
this.isStaticMethod,
154156
this.library,
155157
this.className,
158+
this.methodName,
156159
this.expression);
157160

158161
@override
@@ -260,6 +263,7 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
260263
bool isStaticMethod = false;
261264
Uri library;
262265
String className;
266+
String methodName;
263267
String expression;
264268

265269
dynamic maps = loadYamlNode(contents, sourceUrl: uri);
@@ -281,6 +285,8 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
281285
if (uri.fragment != null && uri.fragment != '') {
282286
className = uri.fragment;
283287
}
288+
} else if (key == "method") {
289+
methodName = value as String;
284290
} else if (key == "definitions") {
285291
definitions = (value as YamlList).map((x) => x as String).toList();
286292
} else if (key == "type_definitions") {
@@ -292,8 +298,17 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
292298
expression = value;
293299
}
294300
}
295-
var test = new TestCase(description, entryPoint, import, definitions,
296-
typeDefinitions, isStaticMethod, library, className, expression);
301+
var test = new TestCase(
302+
description,
303+
entryPoint,
304+
import,
305+
definitions,
306+
typeDefinitions,
307+
isStaticMethod,
308+
library,
309+
className,
310+
methodName,
311+
expression);
297312
var result = test.validate();
298313
if (result != null) {
299314
return new Result.fail(tests, result);
@@ -325,13 +340,15 @@ class CompileExpression extends Step<List<TestCase>, List<TestCase>, Context> {
325340
}
326341

327342
Procedure compiledProcedure = await compiler.compileExpression(
328-
test.expression,
329-
definitions,
330-
typeParams,
331-
"debugExpr",
332-
test.library,
333-
test.className,
334-
test.isStaticMethod);
343+
test.expression,
344+
definitions,
345+
typeParams,
346+
"debugExpr",
347+
test.library,
348+
className: test.className,
349+
methodName: test.methodName,
350+
isStatic: test.isStaticMethod,
351+
);
335352
List<DiagnosticMessage> errors = context.takeErrors();
336353
test.results.add(new CompilationResult(compiledProcedure, errors));
337354
if (compiledProcedure != null) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2021, 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+
entry_point: "main.dart"
6+
definitions: ["#this"]
7+
position: "main.dart"
8+
method: "Foo.parseAsInt"
9+
expression: |
10+
() { print(getFortyTwo()); return this; }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Errors: {
2+
}
3+
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(lowered dynamic #this) → dynamic
4+
return () → dynamic {
5+
dart.core::print(main::Foo|getFortyTwo(#this as{TypeError,ForDynamic} dart.core::String*));
6+
return #this;
7+
};

0 commit comments

Comments
 (0)