[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 <goderbauer@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Daco Harkes
2026-02-26 07:21:09 -08:00
committed by Commit Queue
parent f125fd9d08
commit 8457c9dc3b
7 changed files with 264 additions and 3 deletions
+14
View File
@@ -2377,8 +2377,22 @@ class SsaCodeGenerator implements HVisitor<void>, 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(
+3 -3
View File
@@ -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,
@@ -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;
@@ -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;
@@ -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;
}
@@ -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 {}
}
@@ -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"
}
]
}
]
}
}