[CFE] Expression evaluation: Use dynamic get etc and fixup names to access private stuff when possible

For expression evaluation we want to be "more than dart" in that if for
instance we can see (in the debugger) that a List contains `B`s (even
if it's typed as containing `A`s) we'd like to be able to access things
on `B` (without manually having to cast to either `B` or `dynamic`).

Furthermore - when we in the debugger can see that it's a `B`, and that
`B` has, say, a field `_privateField` or a method `_privateMethod` we'd
like to be able to access that even if `B` is in another library.

This CL - for expression evaluation - makes dynamic accesses and calls
where we would normally issue a "missing getter" (etc) error, and tries
to create a `Name` so private access is possible.

Change-Id: I887318a50413e9a5f11ec685b27719edd312dca0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/446260
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
Jens Johansen
2025-08-26 00:27:19 -07:00
committed by Commit Queue
parent d0849997cc
commit 1f845d1eb7
35 changed files with 844 additions and 39 deletions
@@ -1066,8 +1066,10 @@ void runAgnosticSharedTestsShard1(
breakpointId: 'innerScopeBP',
expression: 'notInScope',
expectedError:
"Error: The getter 'notInScope' isn't defined for the"
" type 'C'.",
"DartError: NoSuchMethodError: 'notInScope'\n"
'method not found\n'
"Receiver: Instance of 'C'\n"
'Arguments: []\n',
);
});
@@ -1076,8 +1078,10 @@ void runAgnosticSharedTestsShard1(
breakpointId: 'innerScopeBP',
expression: 'innerNotInScope',
expectedError:
"Error: The getter 'innerNotInScope' isn't defined for the"
" type 'C'.",
"DartError: NoSuchMethodError: 'innerNotInScope'\n"
'method not found\n'
"Receiver: Instance of 'C'\n"
'Arguments: []\n',
);
});
});
@@ -1105,7 +1109,11 @@ void runAgnosticSharedTestsShard1(
await driver.checkInFrame(
breakpointId: 'parseIntPlusOneBP',
expression: 'typo',
expectedError: "Error: The getter 'typo' isn't defined",
expectedError:
"DartError: NoSuchMethodError: 'typo'\n"
'method not found\n'
'Receiver: "1234"\n'
'Arguments: []\n',
);
});
@@ -1183,7 +1191,11 @@ void runAgnosticSharedTestsShard1(
await driver.checkInFrame(
breakpointId: 'methodBP',
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the type 'C'",
expectedError:
"DartError: NoSuchMethodError: 'typo'\n"
'method not found\n'
"Receiver: Instance of 'C'\n"
'Arguments: []\n',
);
});
@@ -1510,7 +1522,11 @@ void runAgnosticSharedTestsShard2(
await driver.checkInFrame(
breakpointId: 'constructorBP',
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the type 'C'",
expectedError:
"DartError: NoSuchMethodError: 'typo'\n"
'method not found\n'
"Receiver: Instance of 'C'\n"
'Arguments: []\n',
);
});
@@ -1645,7 +1661,11 @@ void runAgnosticSharedTestsShard2(
await driver.checkInFrame(
breakpointId: 'asyncTestBP1',
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the type 'D'",
expectedError:
"DartError: NoSuchMethodError: 'typo'\n"
'method not found\n'
"Receiver: Instance of 'D'\n"
'Arguments: []\n',
);
});
@@ -10,6 +10,7 @@ import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
show ScannerConfiguration;
import 'package:front_end/src/base/name_space.dart';
import 'package:front_end/src/type_inference/inference_results.dart';
import 'package:front_end/src/type_inference/object_access_target.dart';
import 'package:kernel/binary/ast_from_binary.dart'
show
BinaryBuilderWithMetadata,
@@ -20,7 +21,7 @@ import 'package:kernel/binary/ast_from_binary.dart'
import 'package:kernel/canonical_name.dart'
show CanonicalNameError, CanonicalNameSdkError;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClosedWorldClassHierarchy;
show ClassHierarchy, ClassHierarchySubtypes, ClosedWorldClassHierarchy;
import 'package:kernel/dart_scope_calculator.dart'
show DartScope, DartScopeBuilder2;
import 'package:kernel/kernel.dart'
@@ -35,6 +36,7 @@ import 'package:kernel/kernel.dart'
ExtensionType,
ExtensionTypeDeclaration,
FunctionNode,
InterfaceType,
Library,
LibraryDependency,
LibraryPart,
@@ -50,10 +52,11 @@ import 'package:kernel/kernel.dart'
TreeNode,
TypeParameter,
VariableDeclaration,
VariableGet,
VariableSet,
VisitorDefault,
VisitorVoidMixin,
VariableGet,
VariableSet;
Member;
import 'package:kernel/kernel.dart' as kernel show Combinator;
import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/changed_structure_notifier.dart'
@@ -92,7 +95,7 @@ import '../source/source_library_builder.dart'
import '../source/source_loader.dart';
import '../type_inference/inference_helper.dart' show InferenceHelper;
import '../type_inference/inference_visitor.dart'
show ExpressionEvaluationHelper;
show ExpressionEvaluationHelper, OverwrittenInterfaceMember;
import '../util/error_reporter_file_copier.dart' show saveAsGzip;
import '../util/experiment_environment_getter.dart'
show enableIncrementalCompilerBenchmarking, getExperimentEnvironment;
@@ -1953,8 +1956,10 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
new Name(syntheticProcedureName), ProcedureKind.Method, parameters,
isStatic: isStatic, fileUri: debugLibrary.fileUri);
ClassHierarchy hierarchy = lastGoodKernelTarget.loader.hierarchy;
ExpressionEvaluationHelper expressionEvaluationHelper =
new ExpressionEvaluationHelperImpl(extraKnownVariables);
new ExpressionEvaluationHelperImpl(extraKnownVariables, hierarchy);
Expression compiledExpression = await lastGoodKernelTarget.loader
.buildExpression(
@@ -2188,8 +2193,10 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
// Coverage-ignore(suite): Not run.
class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper {
final Set<VariableDeclarationImpl> knownButUnavailable = {};
final ClassHierarchy hierarchy;
ExpressionEvaluationHelperImpl(List<VariableDeclarationImpl> extraKnown) {
ExpressionEvaluationHelperImpl(
List<VariableDeclarationImpl> extraKnown, this.hierarchy) {
for (VariableDeclarationImpl variable in extraKnown) {
if (variable.isConst) {
// We allow const variables - these are inlined (we check
@@ -2232,6 +2239,47 @@ class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper {
includeExpression: false,
));
}
@override
OverwrittenInterfaceMember? overwriteFindInterfaceMember({
required ObjectAccessTarget target,
required DartType receiverType,
required Name name,
}) {
// On a missing target, rewrite to a dynamic target instead.
if (target.kind == ObjectAccessTargetKind.missing) {
// On a private name, try to find a descendant of receiverType
// that has the name.
ClassHierarchy hierarchy = this.hierarchy;
if (name.isPrivate &&
receiverType is InterfaceType &&
hierarchy is ClosedWorldClassHierarchy) {
// Find all libraries that contains a subtype of this type with a
// textually matching name.
ClassHierarchySubtypes subtypeInformation =
hierarchy.computeSubtypesInformation();
Set<Library> foundMatchInLibrary = {};
for (Class cls
in subtypeInformation.getSubtypesOf(receiverType.classNode)) {
for (Member member in cls.members) {
if (member.name.text == name.text) {
foundMatchInLibrary.add(cls.enclosingLibrary);
break;
}
}
}
// If we only found one such library we overwrite the name so the VM
// will mangle the names right and find the wanted target.
if (foundMatchInLibrary.length == 1 &&
name.library != foundMatchInLibrary.first) {
name = new Name(name.text, foundMatchInLibrary.first);
}
}
return new OverwrittenInterfaceMember(
target: const ObjectAccessTarget.dynamic(), name: name);
}
return null;
}
}
// Coverage-ignore(suite): Not run.
@@ -72,7 +72,6 @@ import 'shared_type_analyzer.dart';
import 'stack_values.dart';
import 'type_constraint_gatherer.dart';
import 'type_inference_engine.dart';
import 'type_inferrer.dart' show TypeInferrerImpl;
import 'type_schema.dart' show UnknownType, isKnown;
abstract class InferenceVisitor {
@@ -174,18 +173,13 @@ class InferenceVisitorImpl extends InferenceVisitorBase
// variable was declared outside the try statement or local function.
bool _inTryOrLocalFunction = false;
/// Helper used to issue correct error messages and avoid access to
/// unavailable variables upon expression evaluation.
final ExpressionEvaluationHelper? expressionEvaluationHelper;
InferenceVisitorImpl(
TypeInferrerImpl inferrer,
InferenceHelper helper,
super.inferrer,
super.helper,
this._constructorBuilder,
this.operations,
this.typeAnalyzerOptions,
this.expressionEvaluationHelper)
: super(inferrer, helper);
super.expressionEvaluationHelper);
@override
int get stackHeight => _rewriteStack.length;
@@ -7292,6 +7286,16 @@ class InferenceVisitorImpl extends InferenceVisitorBase
leftType, binaryName, fileOffset,
includeExtensionMethods: true, isSetter: false);
if (expressionEvaluationHelper != null) {
// Coverage-ignore-block(suite): Not run.
OverwrittenInterfaceMember? overWritten =
expressionEvaluationHelper?.overwriteFindInterfaceMember(
target: binaryTarget, name: binaryName, receiverType: leftType);
if (overWritten != null) {
binaryTarget = overWritten.target;
}
}
MethodContravarianceCheckKind binaryCheckKind =
preCheckInvocationContravariance(leftType, binaryTarget,
isThisReceiver: false);
@@ -7462,6 +7466,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
expressionType, unaryName, fileOffset,
includeExtensionMethods: true, isSetter: false);
if (expressionEvaluationHelper != null) {
// Coverage-ignore-block(suite): Not run.
OverwrittenInterfaceMember? overWritten =
expressionEvaluationHelper?.overwriteFindInterfaceMember(
target: unaryTarget,
name: unaryName,
receiverType: expressionType);
if (overWritten != null) {
unaryTarget = overWritten.target;
}
}
MethodContravarianceCheckKind unaryCheckKind =
preCheckInvocationContravariance(expressionType, unaryTarget,
isThisReceiver: false);
@@ -7589,6 +7605,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase
Expression readIndex,
DartType indexType,
MethodContravarianceCheckKind readCheckKind) {
if (expressionEvaluationHelper != null) {
// Coverage-ignore-block(suite): Not run.
OverwrittenInterfaceMember? overWritten =
expressionEvaluationHelper?.overwriteFindInterfaceMember(
target: readTarget,
name: indexGetName,
receiverType: receiverType);
if (overWritten != null) {
readTarget = overWritten.target;
}
}
Expression read;
DartType readType = readTarget.getReturnType(this);
switch (readTarget.kind) {
@@ -7731,6 +7758,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase
DartType indexType,
Expression value,
DartType valueType) {
if (expressionEvaluationHelper != null) {
// Coverage-ignore-block(suite): Not run.
OverwrittenInterfaceMember? overWritten =
expressionEvaluationHelper?.overwriteFindInterfaceMember(
target: writeTarget,
name: indexSetName,
receiverType: receiverType);
if (overWritten != null) {
writeTarget = overWritten.target;
}
}
Expression write;
switch (writeTarget.kind) {
case ObjectAccessTargetKind.missing:
@@ -7902,6 +7940,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
Expression value,
{required DartType valueType,
required bool forEffect}) {
if (expressionEvaluationHelper != null) {
// Coverage-ignore-block(suite): Not run.
OverwrittenInterfaceMember? overWritten =
expressionEvaluationHelper?.overwriteFindInterfaceMember(
target: writeTarget,
name: propertyName,
receiverType: receiverType);
if (overWritten != null) {
writeTarget = overWritten.target;
propertyName = overWritten.name;
}
}
Expression write;
DartType writeType = valueType;
switch (writeTarget.kind) {
@@ -12824,4 +12874,18 @@ abstract class ExpressionEvaluationHelper {
ExpressionInferenceResult? visitVariableSet(
VariableSet node, DartType typeContext, InferenceHelper helper);
OverwrittenInterfaceMember? overwriteFindInterfaceMember({
required ObjectAccessTarget target,
required DartType receiverType,
required Name name,
});
}
// Coverage-ignore(suite): Not run.
class OverwrittenInterfaceMember {
final ObjectAccessTarget target;
final Name name;
OverwrittenInterfaceMember({required this.target, required this.name});
}
@@ -141,7 +141,12 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
final InferenceHelper _helper;
InferenceVisitorBase(this._inferrer, this._helper);
/// Helper used to issue correct error messages and avoid access to
/// unavailable variables upon expression evaluation.
final ExpressionEvaluationHelper? expressionEvaluationHelper;
InferenceVisitorBase(
this._inferrer, this._helper, this.expressionEvaluationHelper);
AssignedVariables<TreeNode, VariableDeclaration> get assignedVariables =>
_inferrer.assignedVariables;
@@ -3797,6 +3802,17 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
isSetter: false,
);
if (expressionEvaluationHelper != null) {
// Coverage-ignore-block(suite): Not run.
OverwrittenInterfaceMember? overWritten =
expressionEvaluationHelper?.overwriteFindInterfaceMember(
target: target, name: name, receiverType: receiverType);
if (overWritten != null) {
target = overWritten.target;
name = overWritten.name;
}
}
switch (target.kind) {
case ObjectAccessTargetKind.instanceMember:
case ObjectAccessTargetKind.objectMember:
@@ -4840,6 +4856,20 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
includeExtensionMethods: true,
isSetter: false,
);
if (expressionEvaluationHelper != null) {
// Coverage-ignore-block(suite): Not run.
OverwrittenInterfaceMember? overWritten =
expressionEvaluationHelper?.overwriteFindInterfaceMember(
target: readTarget,
name: propertyName,
receiverType: receiverType);
if (overWritten != null) {
readTarget = overWritten.target;
propertyName = overWritten.name;
}
}
// Coverage-ignore(suite): Not run.
readType ??= readTarget.getGetterType(this);
+14 -4
View File
@@ -26,6 +26,7 @@ import 'package:front_end/src/compute_platform_binaries_location.dart'
show computePlatformBinariesLocation;
import 'package:front_end/src/kernel/utils.dart'
show serializeComponent, serializeProcedure;
import 'package:front_end/src/source/source_loader.dart';
import 'package:front_end/src/testing/compiler_common.dart';
import "package:kernel/ast.dart"
show
@@ -95,10 +96,12 @@ class Context extends ChainContext {
}
class CompilationResult {
Library? compiledInLibrary;
Procedure? compiledProcedure;
List<CfeDiagnosticMessage> errors;
CompilationResult(this.compiledProcedure, this.errors);
CompilationResult(
this.compiledInLibrary, this.compiledProcedure, this.errors);
String printResult(Uri entryPoint, Context context) {
StringBuffer buffer = new StringBuffer();
@@ -119,8 +122,8 @@ class CompilationResult {
if (compiledProcedure == null) {
buffer.write("<no procedure>");
} else {
Printer printer = new Printer(buffer);
printer.visitProcedure(compiledProcedure!);
Printer printer = new Printer(buffer, showLibraryForNames: true);
printer.writeProcedureInLibrary(compiledProcedure!, compiledInLibrary!);
printer.writeConstantTable(new Component());
}
Uri base = entryPoint.resolve(".");
@@ -434,6 +437,12 @@ class CompileExpression extends Step<List<TestCase>, List<TestCase>, Context> {
}
}
SourceLoader loader = compiler.kernelTargetForTesting!.loader;
Library? libraryLookup = (loader.lookupCompilationUnit(test.library) ??
loader.lookupCompilationUnitByFileUri(test.library))
?.libraryBuilder
.library;
Procedure? compiledProcedure = await compiler.compileExpression(
test.expression,
definitions,
@@ -447,7 +456,8 @@ class CompileExpression extends Step<List<TestCase>, List<TestCase>, Context> {
offset: test.offset ?? TreeNode.noOffset,
);
List<CfeDiagnosticMessage> errors = context.takeErrors();
test.results.add(new CompilationResult(compiledProcedure, errors));
test.results
.add(new CompilationResult(libraryLookup, compiledProcedure, errors));
if (compiledProcedure != null) {
// Confirm we can serialize generated procedure.
compilerResult.component.computeCanonicalNames();
@@ -1090,6 +1090,7 @@ man
manage
managed
manages
mangle
mangled
manifest
manipulating
@@ -1909,6 +1910,7 @@ testers
tex
textualize
textualized
textually
tfa
th
thereby
@@ -0,0 +1,46 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
sources: |
import "dart:developer";
abstract class A {}
class B extends A {
final int value;
B(this.value);
}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B(42)]);
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 133
scriptUri: main.dart
expression: |
list.first.value
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<#lib1::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{#lib1::A}{dynamic}.value;
@@ -0,0 +1,48 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Accessing a private name is a different story though - but here - with it
# being in the same library - it should be fine.
sources: |
import "dart:developer";
abstract class A {}
class B extends A {
final int _value;
B(this._value);
}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B(42)]);
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 135
scriptUri: main.dart
expression: |
list.first._value
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<#lib1::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{#lib1::A}{dynamic}._value;
@@ -0,0 +1,55 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Accessing a private name is a different story though - but here - where there
# is a single library that has a subclass of A (i.e. B) with a matching name
# we should be able to use that.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B(42)]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
final int _value;
B(this._value);
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first._value
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}{dynamic}.lib::_value;
@@ -0,0 +1,63 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Accessing a private name is a different story though - but here - where there
# are move than one library that has a subclass of A (both B and C) with a
# matching name we can't pick one.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib1.dart";
import "lib2.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B(42)]);
}
lib1.dart: |
library lib1;
import "main.dart";
class B extends A {
final int _value;
B(this._value);
}
lib2.dart: |
library lib2;
import "main.dart";
class C extends A {
final int _value;
C(this._value);
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first._value
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}{dynamic}._value;
@@ -0,0 +1,55 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Accessing a private name is a different story though - but here - where there
# is a single library that has a subclass of A (i.e. B) with a matching name
# we should be able to use that.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B(42)]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
int _value;
B(this._value);
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first._value = 41
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}.{dynamic}lib::_value = 41;
@@ -0,0 +1,52 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Here a method call.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B()]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
String hello() => "hello";
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first.hello()
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}{dynamic}.hello();
@@ -0,0 +1,52 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Here a private method call.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B()]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
String _hello() => "hello";
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first._hello()
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}{dynamic}.lib::_hello();
@@ -0,0 +1,52 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Here an index get.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B()]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
int operator [](int index) => 42;
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first[1]
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}{dynamic}.[](1);
@@ -0,0 +1,52 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Here an index set.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B()]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
void operator []=(int index, int value) {}
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first[1] = 42
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return let final main::A #t1 = list.{dart.core::_GrowableList::first}{main::A} in let final dart.core::int #t2 = 1 in let final dart.core::int #t3 = 42 in let final void #t4 = #t1{dynamic}.[]=(#t2, #t3) in #t3;
@@ -0,0 +1,52 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Here an unary expression.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B()]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
int operator -() => 42;
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
-list.first
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}{dynamic}.unary-();
@@ -0,0 +1,52 @@
# Copyright (c) 2025, 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.
# Definition, offset, method etc extracted by starting the VM with
# `-DDFE_VERBOSE=true`, e.g.
# ```
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
# --disable-service-auth-codes --pause_isolates_on_start inputFile.dart
# ```
# and then issuing the expression compilation.
#
# A list of "A"s has a "B" in it. Given that it's a List<A> we can't access
# B-members in Dart - but expression evaluation should be able to.
# Here a binary expression.
sources:
main.dart: |
library main;
import "dart:developer";
import "lib.dart";
abstract class A {}
void foo (List<A> list) {
debugger();
for(var element in list) {
print(element);
}
}
void main() {
foo([new B()]);
}
lib.dart: |
library lib;
import "main.dart";
class B extends A {
int operator +(int i) => 40 + i;
}
definitions: ["list"]
definition_types: ["dart:core", "_GrowableList", "1", "1", "org-dartlang-test:///a/b/c/main.dart", "A", "1", "0"]
type_definitions: []
type_bounds: []
type_defaults: []
method: "foo"
static: true
offset: 94
scriptUri: main.dart
expression: |
list.first + 2
@@ -0,0 +1,4 @@
Errors: {
}
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_GrowableList<main::A> list) → dynamic
return list.{dart.core::_GrowableList::first}{main::A}{dynamic}.+(2);
@@ -41,10 +41,8 @@ worlds:
expression: print("1234".parseInt())
- uri: lib2.dart
expression: print("1234".parseInt())
errors: true
- uri: lib3.dart
expression: print("1234".parseInt())
errors: true
- uri: main.dart
expression: print("1234".parseInt())
- uri: main.dart
@@ -86,10 +84,8 @@ worlds:
expression: print("1234".parseInt())
- uri: lib2.dart
expression: print("1234".parseInt())
errors: true
- uri: lib3.dart
expression: print("1234".parseInt())
errors: true
- uri: main.dart
expression: print("1234".parseInt())
- uri: main.dart
@@ -1,2 +1,2 @@
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return dart.core::print(invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:14: Error: The method 'parseInt' isn't defined for the type 'String'.\nTry correcting the name to the name of an existing method, or defining a method named 'parseInt'.\nprint(\"1234\".parseInt())\n ^^^^^^^^" in "1234"{<unresolved>}.parseInt());
return dart.core::print("1234"{dynamic}.parseInt());
@@ -1,2 +1,2 @@
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return dart.core::print(invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:14: Error: The method 'parseInt' isn't defined for the type 'String'.\nTry correcting the name to the name of an existing method, or defining a method named 'parseInt'.\nprint(\"1234\".parseInt())\n ^^^^^^^^" in "1234"{<unresolved>}.parseInt());
return dart.core::print("1234"{dynamic}.parseInt());
@@ -1,2 +1,2 @@
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return dart.core::print(invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:14: Error: The method 'parseInt' isn't defined for the type 'String'.\nTry correcting the name to the name of an existing method, or defining a method named 'parseInt'.\nprint(\"1234\".parseInt())\n ^^^^^^^^" in "1234"{<unresolved>}.parseInt());
return dart.core::print("1234"{dynamic}.parseInt());
@@ -1,2 +1,2 @@
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return dart.core::print(invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:14: Error: The method 'parseInt' isn't defined for the type 'String'.\nTry correcting the name to the name of an existing method, or defining a method named 'parseInt'.\nprint(\"1234\".parseInt())\n ^^^^^^^^" in "1234"{<unresolved>}.parseInt());
return dart.core::print("1234"{dynamic}.parseInt());
+9 -1
View File
@@ -240,6 +240,7 @@ class Printer extends VisitorDefault<void> with VisitorVoidMixin {
int column = 0;
bool showOffsets;
bool showMetadata;
bool showLibraryForNames;
Library? _currentLibrary;
static final int SPACE = 0;
@@ -251,6 +252,7 @@ class Printer extends VisitorDefault<void> with VisitorVoidMixin {
{NameSystem? syntheticNames,
this.showOffsets = false,
this.showMetadata = false,
this.showLibraryForNames = false,
this.importTable,
this.annotator,
this.metadata})
@@ -431,6 +433,12 @@ class Printer extends VisitorDefault<void> with VisitorVoidMixin {
outerPrinter: this, importsToPrint: imports);
}
void writeProcedureInLibrary(Procedure procedure, Library library) {
_currentLibrary = library;
visitProcedure(procedure);
_currentLibrary = null;
}
void printLibraryImportTable(LibraryImportTable imports) {
for (Library library in imports.importedLibraries) {
String importPath = imports.getImportPath(library);
@@ -704,7 +712,7 @@ class Printer extends VisitorDefault<void> with VisitorVoidMixin {
}
void writeName(Name name, {bool showLibrary = false}) {
if (showLibrary &&
if ((showLibrary || showLibraryForNames) &&
name.isPrivate &&
name.libraryReference != _currentLibrary?.reference) {
writeWord('${getLibraryReference(name.libraryReference!)}::${name.text}');
+6 -2
View File
@@ -71,8 +71,12 @@ final evalTests = <IsolateTest>[
print(result);
expect(result.valueAsString, '10005');
await expectError(
() => service.evaluate(isolateId, instance.id!, 'this + frog'),
result = await service.evaluate(isolateId, instance.id!, 'this + frog');
print(result);
expect(result, isA<ErrorRef>());
expect(
result.message,
contains("Class 'int' has no instance getter 'frog'"),
);
},
resumeIsolate,