[CFE] Expression compilation: Use static type for extension types

This - combiend with sending the scriptUri and offset - allows us to
expression evaluate stuff on extension types.

Change-Id: I0db6c1f52ad3db4ce1a1a2a721e8f3e70033f9ec
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/339900
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
This commit is contained in:
Jens Johansen 2023-12-06 12:03:09 +00:00 committed by Commit Queue
parent 85cb56dd20
commit f5743b9f44
10 changed files with 268 additions and 7 deletions

View file

@ -25,6 +25,8 @@ import 'package:kernel/canonical_name.dart'
show CanonicalNameError, CanonicalNameSdkError;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClosedWorldClassHierarchy;
import 'package:kernel/dart_scope_calculator.dart'
show DartScope, DartScopeBuilder2;
import 'package:kernel/kernel.dart'
show
Class,
@ -32,12 +34,14 @@ import 'package:kernel/kernel.dart'
DartType,
Expression,
Extension,
ExtensionType,
FunctionNode,
Library,
LibraryDependency,
LibraryPart,
Name,
NamedNode,
Node,
NonNullableByDefaultCompiledMode,
Procedure,
ProcedureKind,
@ -47,7 +51,9 @@ import 'package:kernel/kernel.dart'
Supertype,
TreeNode,
TypeParameter,
VariableDeclaration;
VariableDeclaration,
VisitorDefault,
VisitorVoidMixin;
import 'package:kernel/kernel.dart' as kernel show Combinator;
import 'package:kernel/target/changed_structure_notifier.dart'
show ChangedStructureNotifier;
@ -1789,22 +1795,52 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
@override
Future<Procedure?> compileExpression(
String expression,
Map<String, DartType> definitions,
Map<String, DartType> inputDefinitions,
List<TypeParameter> typeDefinitions,
String syntheticProcedureName,
Uri libraryUri, {
String? className,
String? methodName,
int offset = -1,
int offset = TreeNode.noOffset,
String? scriptUri,
bool isStatic = false,
}) async {
IncrementalKernelTarget? lastGoodKernelTarget = this._lastGoodKernelTarget;
assert(_dillLoadedData != null && lastGoodKernelTarget != null);
Map<String, DartType> usedDefinitions =
new Map<String, DartType>.of(inputDefinitions);
return await context.runInContext((_) async {
LibraryBuilder libraryBuilder =
lastGoodKernelTarget!.loader.readAsEntryPoint(libraryUri);
if (scriptUri != null && offset != TreeNode.noOffset) {
Uri? scriptUriAsUri = Uri.tryParse(scriptUri);
if (scriptUriAsUri != null) {
Library library = libraryBuilder.library;
Class? cls;
if (className != null) {
for (Class c in library.classes) {
if (c.name == className) {
cls = c;
break;
}
}
}
DartScope foundScope = DartScopeBuilder2.findScopeFromOffsetAndClass(
library, scriptUriAsUri, cls, offset);
// For now, if any definition is (or contains) an Extension Type,
// we'll overwrite the given (runtime?) definitions so we know about
// the extension type.
for (MapEntry<String, DartType> def
in foundScope.definitions.entries) {
if (_ExtensionTypeFinder.isOrContainsExtensionType(def.value)) {
usedDefinitions[def.key] = def.value;
}
}
}
}
_ticker.logMs("Loaded library $libraryUri");
Class? cls;
@ -1854,7 +1890,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
}
}
int index = 0;
for (String name in definitions.keys) {
for (String name in usedDefinitions.keys) {
index++;
if (!(isLegalIdentifier(name) ||
(extension != null &&
@ -1961,9 +1997,9 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
// https://github.com/dart-lang/sdk/issues/44158
FunctionNode parameters = new FunctionNode(null,
typeParameters: typeDefinitions,
positionalParameters: definitions.keys
.map<VariableDeclaration>((name) =>
new VariableDeclarationImpl(name, type: definitions[name])
positionalParameters: usedDefinitions.entries
.map<VariableDeclaration>((MapEntry<String, DartType> def) =>
new VariableDeclarationImpl(def.key, type: def.value)
..fileOffset = cls?.fileOffset ??
extension?.fileOffset ??
libraryBuilder.library.fileOffset)
@ -2219,6 +2255,28 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
}
}
class _ExtensionTypeFinder extends VisitorDefault<void> with VisitorVoidMixin {
static bool isOrContainsExtensionType(DartType type) {
if (type is ExtensionType) return true;
_ExtensionTypeFinder finder = new _ExtensionTypeFinder();
type.accept(finder);
return finder._foundExtensionType;
}
@override
void visitExtensionType(ExtensionType node) {
_foundExtensionType = true;
}
@override
void defaultNode(Node node) {
if (_foundExtensionType) return;
node.visitChildren(this);
}
bool _foundExtensionType = false;
}
/// Translate a parts "partUri" to an actual uri with handling of invalid uris.
///
/// ```

View file

@ -40,6 +40,7 @@ import "package:kernel/ast.dart"
Library,
Member,
Procedure,
TreeNode,
TypeParameter;
import 'package:kernel/target/targets.dart' show TargetFlags;
import 'package:kernel/text/ast_to_text.dart' show Printer;
@ -152,6 +153,10 @@ class TestCase {
final String? methodName;
final int? offset;
final String? scriptUri;
String expression;
List<CompilationResult> results = [];
@ -169,6 +174,8 @@ class TestCase {
this.library,
this.className,
this.methodName,
this.offset,
this.scriptUri,
this.expression);
@override
@ -266,6 +273,8 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
Uri? library;
String? className;
String? methodName;
int? offset;
String? scriptUri;
String? expression;
dynamic maps = loadYamlNode(contents, sourceUrl: uri);
@ -310,6 +319,11 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
isStaticMethod = value;
} else if (key == "expression") {
expression = value;
} else if (key == "offset") {
offset = value;
} else if (key == "scriptUri") {
Uri uri = entryPoint.resolveUri(Uri.parse(value as String));
scriptUri = uri.toString();
} else {
throw new UnsupportedError("Unknown key: ${key}");
}
@ -332,6 +346,8 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
library,
className,
methodName,
offset,
scriptUri,
expression);
tests.add(test);
}
@ -386,6 +402,8 @@ class CompileExpression extends Step<List<TestCase>, List<TestCase>, Context> {
className: test.className,
methodName: test.methodName,
isStatic: test.isStaticMethod,
scriptUri: test.scriptUri,
offset: test.offset ?? TreeNode.noOffset,
);
List<DiagnosticMessage> errors = context.takeErrors();
test.results.add(new CompilationResult(compiledProcedure, errors));

View file

@ -0,0 +1,36 @@
# Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
sources: |
//@dart=3.3
void main() {
Foo f = new Foo(42);
print(f);
print(f.value);
f.printValue();
f.printThis();
}
extension type Foo(int value) {
void printValue() {
print("This foos value is '$value'");
}
void printThis() {
print("This foos this value is '$this'");
}
}
definitions: ["f"]
# int
definition_types: ["dart:core", "int", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "main"
static: true
offset: 64 # at the start of the 'print(f.value);' line.
scriptUri: main.dart
expression: |
f.value

View file

@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(#lib1::Foo /* = dart.core::int */ f) → dynamic
return f as{Unchecked} dart.core::int;

View file

@ -0,0 +1,51 @@
# Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
sources:
foo.dart: |
//@dart=3.3
part 'bar.dart';
void foo() {
Foo x = new Foo(42);
print(x);
print(x.value);
x.printFoo();
}
extension type Foo(int value) {
void printFoo() {
print("This foos value is '$value'");
}
}
bar.dart: |
//@dart=3.3
part of 'foo.dart';
void bar() {
Bar x = new Bar('');
print(x);
print(x.value);
x.printBar();
}
extension type Bar(String value) {
void printBar() {
print("This bars value is '$value'");
}
}
definitions: ["x"]
# String
definition_types: ["dart:core", "String", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
entry_point: "foo.dart" # the main library
method: "main"
static: true
offset: 83 # at the start of the 'print(x.value);' line. In both files!
scriptUri: bar.dart
expression: |
x.printBar()

View file

@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(#lib1::Bar /* = dart.core::String */ x) → dynamic
return #lib1::Bar|printBar(x);

View file

@ -0,0 +1,51 @@
# Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
sources:
foo.dart: |
//@dart=3.3
part 'bar.dart';
void foo() {
Foo x = new Foo(42);
print(x);
print(x.value);
x.printFoo();
}
extension type Foo(int value) {
void printFoo() {
print("This foos value is '$value'");
}
}
bar.dart: |
//@dart=3.3
part of 'foo.dart';
void bar() {
Bar x = new Bar('');
print(x);
print(x.value);
x.printBar();
}
extension type Bar(String value) {
void printBar() {
print("This bars value is '$value'");
}
}
definitions: ["x"]
# int
definition_types: ["dart:core", "int", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
entry_point: "foo.dart" # the main library
method: "main"
static: true
offset: 83 # at the start of the 'print(x.value);' line. In both files!
scriptUri: foo.dart
expression: |
x.printFoo()

View file

@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(#lib1::Foo /* = dart.core::int */ x) → dynamic
return #lib1::Foo|printFoo(x);

View file

@ -0,0 +1,31 @@
# Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
sources: |
//@dart=3.3
void main() {
Foo f = new Foo(42);
List<Foo> foos = [f];
foos.first.printValue();
}
extension type Foo(int value) {
void printValue() {
print("This foos value is '$value'");
}
}
definitions: ["f", "foos"]
# int, List<int>
definition_types: ["dart:core", "int", "1", "0", "dart:core", "List", "1", "1", "dart:core", "int", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "main"
static: true
offset: 76 # at the start of the 'foos.first.printValue();' line.
scriptUri: main.dart
expression: |
foos.first.value

View file

@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(#lib1::Foo /* = dart.core::int */ f, dart.core::List<#lib1::Foo /* = dart.core::int */> foos) → dynamic
return foos.{dart.core::Iterable::first}{#lib1::Foo% /* = dart.core::int */} as{Unchecked} dart.core::int;