From ccac64c1810c2730cf663f2a090548f27cc26df9 Mon Sep 17 00:00:00 2001 From: Jens Johansen Date: Mon, 3 Nov 2025 00:45:21 -0800 Subject: [PATCH] [CFE] Expression evaluation: access private members in superclasses In 1f845d1eb7e8da739412ab6ad72283e95c185fa9 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 Commit-Queue: Jens Johansen --- .../lib/src/base/incremental_compiler.dart | 36 ++++++++++--- .../src/type_inference/inference_visitor.dart | 6 +++ .../inference_visitor_base.dart | 2 + .../beyond_dart_semantics_11.expression.yaml | 46 +++++++++++++++++ ...d_dart_semantics_11.expression.yaml.expect | 4 ++ .../beyond_dart_semantics_12.expression.yaml | 50 +++++++++++++++++++ ...d_dart_semantics_12.expression.yaml.expect | 9 ++++ .../beyond_dart_semantics_13.expression.yaml | 46 +++++++++++++++++ ...d_dart_semantics_13.expression.yaml.expect | 4 ++ .../beyond_dart_semantics_14.expression.yaml | 46 +++++++++++++++++ ...d_dart_semantics_14.expression.yaml.expect | 4 ++ .../beyond_dart_semantics_15.expression.yaml | 46 +++++++++++++++++ ...d_dart_semantics_15.expression.yaml.expect | 4 ++ .../beyond_dart_semantics_16.expression.yaml | 39 +++++++++++++++ ...d_dart_semantics_16.expression.yaml.expect | 4 ++ 15 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml.expect create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml.expect create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml.expect create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml.expect create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml.expect create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml create mode 100644 pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml.expect diff --git a/pkg/front_end/lib/src/base/incremental_compiler.dart b/pkg/front_end/lib/src/base/incremental_compiler.dart index 3d4d1716541..91742e4d9f7 100644 --- a/pkg/front_end/lib/src/base/incremental_compiler.dart +++ b/pkg/front_end/lib/src/base/incremental_compiler.dart @@ -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 foundMatchInLibrary = {}; + Set 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 diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/type_inference/inference_visitor.dart index 57f53d1e78f..15d163d7615 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart @@ -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, }); } diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart index c4428a09ff4..1ae78dd1f99 100644 --- a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart +++ b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart @@ -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; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml new file mode 100644 index 00000000000..2339aedd4b1 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml @@ -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 diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml.expect new file mode 100644 index 00000000000..b4b64844660 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_11.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}.#lib1::_foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml new file mode 100644 index 00000000000..6fb685e00db --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml @@ -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; + }() diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml.expect new file mode 100644 index 00000000000..46b3df82d81 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_12.expression.yaml.expect @@ -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}; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml new file mode 100644 index 00000000000..d21987c3bce --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml @@ -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 diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml.expect new file mode 100644 index 00000000000..b4b64844660 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_13.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}.#lib1::_foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml new file mode 100644 index 00000000000..bd5c0064296 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml @@ -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 diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml.expect new file mode 100644 index 00000000000..b4b64844660 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_14.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}.#lib1::_foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml new file mode 100644 index 00000000000..9eefdfd9c9d --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml @@ -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 diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml.expect new file mode 100644 index 00000000000..90e04bc1151 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_15.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this{dynamic}._foo; diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml new file mode 100644 index 00000000000..fcbeebc6eb5 --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml @@ -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 diff --git a/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml.expect b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml.expect new file mode 100644 index 00000000000..e69239564cb --- /dev/null +++ b/pkg/front_end/testcases/expression/beyond_dart_semantics_16.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return this.{dynamic}#lib1::_a = 42;