From 8457c9dc3b1d6fd8eb2cfcb67938dbd290e09e05 Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Thu, 26 Feb 2026 07:21:09 -0800 Subject: [PATCH] [record_use] Static getters and setters Closes: https://github.com/dart-lang/native/issues/2906 TEST=pkg/compiler/test/record_use/record_use_test.dart TEST=pkg/dart2wasm/test/record_use_test.dart TEST=pkg/vm/test/transformations/record_use_test.dart Change-Id: I8c3591e9896b90ca9b26b96506834556a221d47d Cq-Include-Trybots: luci.dart.try:dart2wasm-asserts-linux-chrome-try,dart2wasm-asserts-minified-linux-d8-try,dart2wasm-linux-chrome-try,dart2wasm-linux-d8-try,dart2wasm-linux-firefox-try,dart2wasm-linux-jscm-chrome-try,dart2wasm-linux-optimized-jsc-try,pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-arm64-try,pkg-win-release-try,dart2js-canary-linux-try,dart2js-hostasserts-linux-d8-try,dart2js-linux-chrome-try,dart2js-linux-firefox-try,dart2js-mac-chrome-try,dart2js-mac-safari-try,dart2js-minified-csp-linux-chrome-try,dart2js-minified-linux-d8-try,dart2js-unit-linux-x64-release-try,dart2js-win-chrome-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/483580 Reviewed-by: Michael Goderbauer Commit-Queue: Daco Harkes Reviewed-by: Nate Biggs Reviewed-by: Johnni Winther --- pkg/compiler/lib/src/ssa/codegen.dart | 14 ++ pkg/front_end/lib/src/kernel/record_use.dart | 6 +- .../record_use/record_call.dart | 30 ++++ .../record_use/record_use.dart | 18 +++ .../lib/static_getters_setters.dart | 26 ++++ .../static_getters_setters.dart.aot.expect | 40 ++++++ .../static_getters_setters.dart.json.expect | 133 ++++++++++++++++++ 7 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart create mode 100644 pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.aot.expect create mode 100644 pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.json.expect diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index 7e62ebbf428..df143ef1db0 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -2377,8 +2377,22 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { staticUse = StaticUse.constructorInvoke(element, callStructure); } else if (element.isGetter) { staticUse = StaticUse.staticGet(element); + if (_shouldRecordMethodUses(element)) { + recordedMethodUses = _recordMethodUses( + element, + node.inputs, + node.sourceInformation!, + ); + } } else if (element.isSetter) { staticUse = StaticUse.staticSet(element); + if (_shouldRecordMethodUses(element)) { + recordedMethodUses = _recordMethodUses( + element, + node.inputs, + node.sourceInformation!, + ); + } } else { assert(element.isFunction); CallStructure callStructure = CallStructure.unnamed( diff --git a/pkg/front_end/lib/src/kernel/record_use.dart b/pkg/front_end/lib/src/kernel/record_use.dart index 15c396f81d9..5733d1c9d47 100644 --- a/pkg/front_end/lib/src/kernel/record_use.dart +++ b/pkg/front_end/lib/src/kernel/record_use.dart @@ -148,13 +148,13 @@ void _validateRecordUseDeclaration( ); } - final bool onNonStaticMethod = - node is! Procedure || !node.isStatic || node.kind != ProcedureKind.Method; + final bool onStaticMethod = + node is Procedure && node.isStatic && node.kind != ProcedureKind.Factory; final bool onClassWithoutConstConstructor = node is! Class || !node.constructors.any((constructor) => constructor.isConst); - if (onNonStaticMethod && onClassWithoutConstConstructor) { + if (!onStaticMethod && onClassWithoutConstConstructor) { errorReporter.report( diag.recordUseCannotBePlacedHere.withLocation( fileUri, diff --git a/pkg/vm/lib/transformations/record_use/record_call.dart b/pkg/vm/lib/transformations/record_use/record_call.dart index 8bfb37e84ac..2f78151583c 100644 --- a/pkg/vm/lib/transformations/record_use/record_call.dart +++ b/pkg/vm/lib/transformations/record_use/record_call.dart @@ -36,6 +36,36 @@ class CallRecorder { } } + /// Will record a static get if it is annotated with `@RecordUse`. + void recordStaticGet(ast.StaticGet node) { + final target = node.target; + if (target is ast.Procedure && isBeingRecorded(target)) { + _addToUsage( + target, + CallWithArguments( + positionalArguments: [], + namedArguments: {}, + loadingUnits: [_loadingUnitLookup(node)], + ), + ); + } + } + + /// Will record a static set if it is annotated with `@RecordUse`. + void recordStaticSet(ast.StaticSet node) { + final target = node.target; + if (target is ast.Procedure && isBeingRecorded(target)) { + _addToUsage( + target, + CallWithArguments( + positionalArguments: [_evaluateLiteral(node.value)], + namedArguments: {}, + loadingUnits: [_loadingUnitLookup(node)], + ), + ); + } + } + /// Will record a tear-off if the target is annotated with `@RecordUse`. void recordConstantExpression(ast.ConstantExpression node) { final constant = node.constant; diff --git a/pkg/vm/lib/transformations/record_use/record_use.dart b/pkg/vm/lib/transformations/record_use/record_use.dart index 79fbbdd46db..cb1bdd71cea 100644 --- a/pkg/vm/lib/transformations/record_use/record_use.dart +++ b/pkg/vm/lib/transformations/record_use/record_use.dart @@ -94,6 +94,24 @@ class _RecordUseVisitor extends ast.RecursiveVisitor { super.visitStaticInvocation(node); } + @override + void visitStaticGet(ast.StaticGet node) { + if (_isAnnotation(node)) return; + + staticCallRecorder.recordStaticGet(node); + + super.visitStaticGet(node); + } + + @override + void visitStaticSet(ast.StaticSet node) { + if (_isAnnotation(node)) return; + + staticCallRecorder.recordStaticSet(node); + + super.visitStaticSet(node); + } + @override void visitConstantExpression(ast.ConstantExpression node) { if (_isAnnotation(node)) return; diff --git a/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart b/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart new file mode 100644 index 00000000000..96e990d47f0 --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart @@ -0,0 +1,26 @@ +// Copyright (c) 2026, 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. + +import 'package:meta/meta.dart' show RecordUse; + +class SomeClass { + @RecordUse() + static int get someStaticGetter => 42; + + @RecordUse() + static set someStaticSetter(int value) {} +} + +@RecordUse() +int get someTopLevelGetter => 42; + +@RecordUse() +set someTopLevelSetter(int value) {} + +void main() { + print(SomeClass.someStaticGetter); + SomeClass.someStaticSetter = 123; + print(someTopLevelGetter); + someTopLevelSetter = 456; +} diff --git a/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.aot.expect new file mode 100644 index 00000000000..e8ec4967eff --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.aot.expect @@ -0,0 +1,40 @@ +library #lib; +import self as self; +import "dart:core" as core; +import "package:meta/meta.dart" as meta; + +import "package:meta/meta.dart" show RecordUse; + +abstract class SomeClass extends core::Object { + + [@vm.inferred-return-type.metadata=dart.core::_Smi (value: 42)] + [@vm.unboxing-info.metadata=()->i] + @#C1 + static get someStaticGetter() → core::int + return 42; + + [@vm.unboxing-info.metadata=(i)->b] + @#C1 + static set someStaticSetter([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 123)] core::int value) → void {} +} + +[@vm.inferred-return-type.metadata=dart.core::_Smi (value: 42)] +[@vm.unboxing-info.metadata=()->i] +@#C1 +static get someTopLevelGetter() → core::int + return 42; + +[@vm.unboxing-info.metadata=(i)->b] +@#C1 +static set someTopLevelSetter([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 456)] core::int value) → void {} + +[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)] +static method main() → void { + core::print([@vm.inferred-type.metadata=dart.core::_Smi (value: 42)] self::SomeClass::someStaticGetter); + self::SomeClass::someStaticSetter = 123; + core::print([@vm.inferred-type.metadata=dart.core::_Smi (value: 42)] self::someTopLevelGetter); + self::someTopLevelSetter = 456; +} +constants { + #C1 = meta::RecordUse {} +} diff --git a/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.json.expect b/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.json.expect new file mode 100644 index 00000000000..4b35e3ce67b --- /dev/null +++ b/pkg/vm/testcases/transformations/record_use/lib/static_getters_setters.dart.json.expect @@ -0,0 +1,133 @@ +{ + "constants": [ + { + "type": "int", + "value": 123 + }, + { + "type": "int", + "value": 456 + } + ], + "definitions": [ + { + "path": [ + { + "kind": "class", + "name": "SomeClass" + }, + { + "disambiguators": [ + "static" + ], + "kind": "getter", + "name": "someStaticGetter" + } + ], + "uri": "package:record_use_test/static_getters_setters.dart" + }, + { + "path": [ + { + "kind": "class", + "name": "SomeClass" + }, + { + "disambiguators": [ + "static" + ], + "kind": "setter", + "name": "someStaticSetter" + } + ], + "uri": "package:record_use_test/static_getters_setters.dart" + }, + { + "path": [ + { + "disambiguators": [ + "static" + ], + "kind": "getter", + "name": "someTopLevelGetter" + } + ], + "uri": "package:record_use_test/static_getters_setters.dart" + }, + { + "path": [ + { + "disambiguators": [ + "static" + ], + "kind": "setter", + "name": "someTopLevelSetter" + } + ], + "uri": "package:record_use_test/static_getters_setters.dart" + } + ], + "loading_units": [ + { + "name": "1" + } + ], + "metadata": { + "comment": "Recorded usages of objects tagged with a `RecordUse` annotation.", + "version": "0.4.0" + }, + "uses": { + "static_calls": [ + { + "definition_index": 0, + "uses": [ + { + "loading_unit_indices": [ + 0 + ], + "type": "with_arguments" + } + ] + }, + { + "definition_index": 1, + "uses": [ + { + "loading_unit_indices": [ + 0 + ], + "positional": [ + 0 + ], + "type": "with_arguments" + } + ] + }, + { + "definition_index": 2, + "uses": [ + { + "loading_unit_indices": [ + 0 + ], + "type": "with_arguments" + } + ] + }, + { + "definition_index": 3, + "uses": [ + { + "loading_unit_indices": [ + 0 + ], + "positional": [ + 1 + ], + "type": "with_arguments" + } + ] + } + ] + } +} \ No newline at end of file