[CFE] Expression evaluation: access private members in superclasses

In 1f845d1eb7 expression compilation for
expression evaluation started supporting access to private members, but
didn't search through the superclasses, nor did it take setter vs
non-setteres into account.
This CL handles more situations.

Change-Id: Ifcd7ec34ecab0d8f2c50506ff8c24e6590ddafa2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/458260
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
This commit is contained in:
Jens Johansen
2025-11-03 00:45:21 -08:00
committed by Commit Queue
parent 7dd8aed41b
commit ccac64c181
15 changed files with 340 additions and 6 deletions
@@ -56,8 +56,8 @@ import 'package:kernel/kernel.dart'
VariableSet,
VisitorDefault,
VisitorVoidMixin,
Member,
TypeParameterType;
TypeParameterType,
Field;
import 'package:kernel/kernel.dart' as kernel show Combinator;
import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/changed_structure_notifier.dart'
@@ -2551,6 +2551,7 @@ class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper {
required ObjectAccessTarget target,
required DartType receiverType,
required Name name,
required bool setter,
}) {
// On a missing target, rewrite to a dynamic target instead.
if (target.kind == ObjectAccessTargetKind.missing) {
@@ -2565,14 +2566,37 @@ class ExpressionEvaluationHelperImpl implements ExpressionEvaluationHelper {
ClassHierarchySubtypes subtypeInformation = hierarchy
.computeSubtypesInformation();
Set<Library> foundMatchInLibrary = {};
Set<Class> visited = {};
nextSubtype:
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 (cls.isAbstract) continue;
Class? clsOrSuper = cls;
while (clsOrSuper != null) {
if (!visited.add(clsOrSuper)) break;
for (Procedure procedure in clsOrSuper.procedures) {
if (procedure.name.text == name.text) {
// Name match.
if (procedure.isAbstract) continue;
if (setter && !procedure.isSetter) {
continue;
} else if (!setter && procedure.isSetter) {
continue;
}
foundMatchInLibrary.add(clsOrSuper.enclosingLibrary);
continue nextSubtype;
}
}
for (Field field in clsOrSuper.fields) {
if (field.name.text == name.text) {
// Name match.
if (setter && !field.hasSetter) continue;
foundMatchInLibrary.add(clsOrSuper.enclosingLibrary);
continue nextSubtype;
}
}
clsOrSuper = clsOrSuper.superclass;
}
}
// If we only found one such library we overwrite the name so the VM
@@ -10399,6 +10399,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
target: binaryTarget,
name: binaryName,
receiverType: leftType,
setter: false,
);
if (overWritten != null) {
binaryTarget = overWritten.target;
@@ -10593,6 +10594,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
target: unaryTarget,
name: unaryName,
receiverType: expressionType,
setter: false,
);
if (overWritten != null) {
unaryTarget = overWritten.target;
@@ -10754,6 +10756,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
target: readTarget,
name: indexGetName,
receiverType: receiverType,
setter: false,
);
if (overWritten != null) {
readTarget = overWritten.target;
@@ -10909,6 +10912,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
target: writeTarget,
name: indexSetName,
receiverType: receiverType,
setter: true,
);
if (overWritten != null) {
writeTarget = overWritten.target;
@@ -11126,6 +11130,7 @@ class InferenceVisitorImpl extends InferenceVisitorBase
target: writeTarget,
name: propertyName,
receiverType: receiverType,
setter: true,
);
if (overWritten != null) {
writeTarget = overWritten.target;
@@ -16843,6 +16848,7 @@ abstract class ExpressionEvaluationHelper {
required ObjectAccessTarget target,
required DartType receiverType,
required Name name,
required bool setter,
});
}
@@ -3685,6 +3685,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
target: target,
name: name,
receiverType: receiverType,
setter: false,
);
if (overWritten != null) {
target = overWritten.target;
@@ -5111,6 +5112,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
target: readTarget,
name: propertyName,
receiverType: receiverType,
setter: false,
);
if (overWritten != null) {
readTarget = overWritten.target;
@@ -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.
#
# Private member from superclass.
sources:
main.dart: |
import 'dart:developer';
import 'lib.dart';
class _A extends B {
void foo() {
debugger();
print("hello");
}
}
void main() {
_A().foo();
}
lib.dart: |
abstract class B {
Object? _foo;
}
definitions: []
definition_types: []
type_definitions: []
type_bounds: []
type_defaults: []
position: "#_A"
method: "foo"
static: false
offset: 85
scriptUri: main.dart
expression: |
this._foo
@@ -0,0 +1,4 @@
Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return this{dynamic}.#lib1::_foo;
@@ -0,0 +1,50 @@
# 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.
# Getter/setter from superclass.
sources:
main.dart: |
import 'dart:developer';
import 'lib.dart';
class _A extends B {
void foo() {
debugger();
print("hello");
}
}
void main() {
_A().foo();
}
lib.dart: |
import 'lib2.dart';
abstract class B extends C {
Object? get _a => null;
set _b(dynamic x) {}
}
lib2.dart: |
abstract class C {
set _a(dynamic x) {}
Object? get _b => null;
}
definitions: []
definition_types: []
type_definitions: []
type_bounds: []
type_defaults: []
position: "#_A"
method: "foo"
static: false
offset: 85
scriptUri: main.dart
expression: |
() {
this._a;
this._a = 42;
this._b;
this._b = 42;
}()
@@ -0,0 +1,9 @@
Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return (() → Null {
this{dynamic}.#lib1::_a;
this.{dynamic}#lib2::_a = 42;
this{dynamic}.#lib2::_b;
this.{dynamic}#lib1::_b = 42;
})(){() → Null};
@@ -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.
# Only 1 non-abstract method.
sources:
main.dart: |
import 'dart:developer';
import 'lib.dart';
abstract class _A extends B {
void foo() {
debugger();
print("hello");
}
}
abstract class C extends _A {
String? get _foo;
}
void main() {
D().foo();
}
lib.dart: |
import 'main.dart';
abstract class B {
}
class D extends C {
String get _foo => "hello";
}
definitions: []
definition_types: []
type_definitions: []
type_bounds: []
type_defaults: []
position: "#_A"
method: "foo"
static: false
offset: 94
scriptUri: main.dart
expression: |
_foo
@@ -0,0 +1,4 @@
Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return this{dynamic}.#lib1::_foo;
@@ -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.
# 2 non-abstract methods, but only 1 non-abstract class.
sources:
main.dart: |
import 'dart:developer';
import 'lib.dart';
abstract class _A extends B {
void foo() {
debugger();
print("hello");
}
}
abstract class C extends _A {
String? get _foo => "C";
}
void main() {
D().foo();
}
lib.dart: |
import 'main.dart';
abstract class B {
}
class D extends C {
String get _foo => "D";
}
definitions: []
definition_types: []
type_definitions: []
type_bounds: []
type_defaults: []
position: "#_A"
method: "foo"
static: false
offset: 94
scriptUri: main.dart
expression: |
_foo
@@ -0,0 +1,4 @@
Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return this{dynamic}.#lib1::_foo;
@@ -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.
# 2 non-abstract methods, 2 non-abstract class.
sources:
main.dart: |
import 'dart:developer';
import 'lib.dart';
abstract class _A extends B {
void foo() {
debugger();
print("hello");
}
}
class C extends _A {
String? get _foo => "C";
}
void main() {
D().foo();
}
lib.dart: |
import 'main.dart';
abstract class B {
}
class D extends C {
String get _foo => "D";
}
definitions: []
definition_types: []
type_definitions: []
type_bounds: []
type_defaults: []
position: "#_A"
method: "foo"
static: false
offset: 94
scriptUri: main.dart
expression: |
_foo
@@ -0,0 +1,4 @@
Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return this{dynamic}._foo;
@@ -0,0 +1,39 @@
# 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.
# Late final field has a setter.
sources:
main.dart: |
import 'dart:developer';
import 'lib.dart';
class _A extends B {
void foo() {
debugger();
print("hello");
}
}
void main() {
_A().foo();
}
lib.dart: |
import 'main.dart';
class B {
late final int? _a;
}
definitions: []
definition_types: []
type_definitions: []
type_bounds: []
type_defaults: []
position: "#_A"
method: "foo"
static: false
offset: 85
scriptUri: main.dart
expression: |
_a = 42
@@ -0,0 +1,4 @@
Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic
return this.{dynamic}#lib1::_a = 42;