[record_use] Record instances in calls
So that calls with const instances as arguments can be recorded.
Tested: pkg/vm/testcases/transformations/record_use/*
Change-Id: I918e7486b2772bb6d014abb9f01f4347e24ac0b1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/436300
Commit-Queue: Moritz Sümmermann <mosum@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
diff --git a/pkg/vm/lib/transformations/record_use/record_instance.dart b/pkg/vm/lib/transformations/record_use/record_instance.dart
index 35c0229..7287e08 100644
--- a/pkg/vm/lib/transformations/record_use/record_instance.dart
+++ b/pkg/vm/lib/transformations/record_use/record_instance.dart
@@ -81,15 +81,7 @@
ast.InstanceConstant constant,
) => InstanceReference(
location: expression.location!.recordLocation(_source, exactLocation),
- instanceConstant: _fieldsFromConstant(constant),
+ instanceConstant: evaluateInstanceConstant(constant),
loadingUnit: loadingUnitForNode(expression, _loadingUnits).toString(),
);
-
- InstanceConstant _fieldsFromConstant(ast.InstanceConstant constant) =>
- InstanceConstant(
- fields: constant.fieldValues.map(
- (key, value) =>
- MapEntry(key.asField.name.text, evaluateConstant(value)),
- ),
- );
}
diff --git a/pkg/vm/lib/transformations/record_use/record_use.dart b/pkg/vm/lib/transformations/record_use/record_use.dart
index b58e957..9f8c6a9 100644
--- a/pkg/vm/lib/transformations/record_use/record_use.dart
+++ b/pkg/vm/lib/transformations/record_use/record_use.dart
@@ -129,9 +129,9 @@
ast.ListConstant() => ListConstant(
constant.entries.map(evaluateConstant).toList(),
),
+ ast.InstanceConstant() => evaluateInstanceConstant(constant),
// The following are not supported, but theoretically could be, so they
// are listed explicitly here.
- ast.InstanceConstant() => _unsupported('InstanceConstant'),
ast.AuxiliaryConstant() => _unsupported('AuxiliaryConstant'),
ast.SetConstant() => _unsupported('SetConstant'),
ast.RecordConstant() => _unsupported('RecordConstant'),
@@ -151,6 +151,14 @@
ast.BasicLiteral() => _unsupported(expression.runtimeType.toString()),
};
+InstanceConstant evaluateInstanceConstant(ast.InstanceConstant constant) =>
+ InstanceConstant(
+ fields: constant.fieldValues.map(
+ (key, value) =>
+ MapEntry(key.asField.name.text, evaluateConstant(value)),
+ ),
+ );
+
Never _unsupported(String constantType) =>
throw UnsupportedError('$constantType is not supported for recording.');
diff --git a/pkg/vm/testcases/transformations/record_use/record_enum.dart b/pkg/vm/testcases/transformations/record_use/record_enum.dart
new file mode 100644
index 0000000..fbe3694
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_enum.dart
@@ -0,0 +1,23 @@
+// 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.
+
+import 'package:meta/meta.dart' show RecordUse;
+
+void main() {
+ doSomething();
+}
+
+@MyClass(A.a)
+void doSomething() {
+ print('a');
+}
+
+@RecordUse()
+class MyClass {
+ final A a;
+
+ const MyClass(this.a);
+}
+
+enum A { a, b }
diff --git a/pkg/vm/testcases/transformations/record_use/record_enum.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/record_enum.dart.aot.expect
new file mode 100644
index 0000000..3ff4659
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_enum.dart.aot.expect
@@ -0,0 +1,39 @@
+library #lib;
+import self as self;
+import "package:meta/meta.dart" as meta;
+import "dart:core" as core;
+
+import "package:meta/meta.dart" show RecordUse;
+
+@#C1
+class MyClass extends core::Object /*hasConstConstructor*/ {
+
+ [@vm.inferred-type.metadata=#lib::A (value: const #lib::A{dart.core::_Enum.index: 0, dart.core::_Enum._name: "a"})]
+ [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
+ final field self::A a;
+}
+class A extends core::_Enum /*isEnum*/ {
+
+ [@vm.inferred-return-type.metadata=!]
+ [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3]
+ method core::_enumToString() → core::String
+ return "A.${[@vm.direct-call.metadata=dart.core::_Enum._name] this.{core::_Enum::_name}{core::String}}";
+}
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+static method main() → void {
+ self::doSomething();
+}
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+@#C5
+static method doSomething() → void {
+ core::print("a");
+}
+constants {
+ #C1 = meta::RecordUse {}
+ #C2 = 0
+ #C3 = "a"
+ #C4 = self::A {index:#C2, _name:#C3}
+ #C5 = self::MyClass {a:#C4}
+}
diff --git a/pkg/vm/testcases/transformations/record_use/record_enum.dart.json.expect b/pkg/vm/testcases/transformations/record_use/record_enum.dart.json.expect
new file mode 100644
index 0000000..3d66bb8
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_enum.dart.json.expect
@@ -0,0 +1,52 @@
+{
+ "metadata": {
+ "comment": "Recorded usages of objects tagged with a `RecordUse` annotation",
+ "version": "0.2.0"
+ },
+ "constants": [
+ {
+ "type": "int",
+ "value": 0
+ },
+ {
+ "type": "String",
+ "value": "a"
+ },
+ {
+ "type": "Instance",
+ "value": {
+ "index": 0,
+ "_name": 1
+ }
+ },
+ {
+ "type": "Instance",
+ "value": {
+ "a": 2
+ }
+ }
+ ],
+ "locations": [
+ {
+ "uri": "record_enum.dart"
+ }
+ ],
+ "recordings": [
+ {
+ "definition": {
+ "identifier": {
+ "uri": "record_enum.dart",
+ "name": "MyClass"
+ },
+ "loading_unit": "1"
+ },
+ "instances": [
+ {
+ "constant_index": 3,
+ "loading_unit": "1",
+ "@": 0
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/pkg/vm/testcases/transformations/record_use/record_instance_constant.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/record_instance_constant.dart.aot.expect
new file mode 100644
index 0000000..25d53475
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_instance_constant.dart.aot.expect
@@ -0,0 +1,38 @@
+library #lib;
+import self as self;
+import "package:meta/meta.dart" as meta;
+import "dart:core" as core;
+
+import "package:meta/meta.dart" show RecordUse;
+
+@#C1
+class MyClass extends core::Object /*hasConstConstructor*/ {
+
+ [@vm.inferred-type.metadata=#lib::A (value: const #lib::A{#lib::A.i: 42})]
+ [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
+ final field self::A a;
+}
+class A extends core::Object /*hasConstConstructor*/ {
+
+ [@vm.inferred-type.metadata=dart.core::_Smi (value: 42)]
+ [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2]
+ [@vm.unboxing-info.metadata=()->i]
+ final field core::int i;
+}
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+static method main() → void {
+ self::doSomething();
+}
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+@#C4
+static method doSomething() → void {
+ core::print("a");
+}
+constants {
+ #C1 = meta::RecordUse {}
+ #C2 = 42
+ #C3 = self::A {i:#C2}
+ #C4 = self::MyClass {a:#C3}
+}
diff --git a/pkg/vm/testcases/transformations/record_use/record_instance_constant.dart.json.expect b/pkg/vm/testcases/transformations/record_use/record_instance_constant.dart.json.expect
new file mode 100644
index 0000000..8014626
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_instance_constant.dart.json.expect
@@ -0,0 +1,47 @@
+{
+ "metadata": {
+ "comment": "Recorded usages of objects tagged with a `RecordUse` annotation",
+ "version": "0.2.0"
+ },
+ "constants": [
+ {
+ "type": "int",
+ "value": 42
+ },
+ {
+ "type": "Instance",
+ "value": {
+ "i": 0
+ }
+ },
+ {
+ "type": "Instance",
+ "value": {
+ "a": 1
+ }
+ }
+ ],
+ "locations": [
+ {
+ "uri": "record_instance_constant.dart"
+ }
+ ],
+ "recordings": [
+ {
+ "definition": {
+ "identifier": {
+ "uri": "record_instance_constant.dart",
+ "name": "MyClass"
+ },
+ "loading_unit": "1"
+ },
+ "instances": [
+ {
+ "constant_index": 2,
+ "loading_unit": "1",
+ "@": 0
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart b/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart
new file mode 100644
index 0000000..70de329
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart
@@ -0,0 +1,25 @@
+// 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.
+
+import 'package:meta/meta.dart' show RecordUse;
+
+void main() {
+ doSomething();
+}
+
+@MyClass(const A())
+void doSomething() {
+ print('a');
+}
+
+@RecordUse()
+class MyClass {
+ final A a;
+
+ const MyClass(this.a);
+}
+
+class A {
+ const A();
+}
diff --git a/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart.aot.expect b/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart.aot.expect
new file mode 100644
index 0000000..cdc4c90
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart.aot.expect
@@ -0,0 +1,32 @@
+library #lib;
+import self as self;
+import "package:meta/meta.dart" as meta;
+import "dart:core" as core;
+
+import "package:meta/meta.dart" show RecordUse;
+
+@#C1
+class MyClass extends core::Object /*hasConstConstructor*/ {
+
+ [@vm.inferred-type.metadata=#lib::A (value: const #lib::A{})]
+ [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
+ final field self::A a;
+}
+class A extends core::Object /*hasConstConstructor*/ {
+}
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+static method main() → void {
+ self::doSomething();
+}
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+@#C3
+static method doSomething() → void {
+ core::print("a");
+}
+constants {
+ #C1 = meta::RecordUse {}
+ #C2 = self::A {}
+ #C3 = self::MyClass {a:#C2}
+}
diff --git a/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart.json.expect b/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart.json.expect
new file mode 100644
index 0000000..861020d
--- /dev/null
+++ b/pkg/vm/testcases/transformations/record_use/record_instance_constant_empty.dart.json.expect
@@ -0,0 +1,40 @@
+{
+ "metadata": {
+ "comment": "Recorded usages of objects tagged with a `RecordUse` annotation",
+ "version": "0.2.0"
+ },
+ "constants": [
+ {
+ "type": "Instance"
+ },
+ {
+ "type": "Instance",
+ "value": {
+ "a": 0
+ }
+ }
+ ],
+ "locations": [
+ {
+ "uri": "record_instance_constant_empty.dart"
+ }
+ ],
+ "recordings": [
+ {
+ "definition": {
+ "identifier": {
+ "uri": "record_instance_constant_empty.dart",
+ "name": "MyClass"
+ },
+ "loading_unit": "1"
+ },
+ "instances": [
+ {
+ "constant_index": 1,
+ "loading_unit": "1",
+ "@": 0
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file