Version 2.17.0-11.0.dev
Merge commit '0e5a539315e690eb1bfac9ecf8a676822fcbf714' into 'dev'
diff --git a/pkg/vm/lib/transformations/to_string_transformer.dart b/pkg/vm/lib/transformations/to_string_transformer.dart
index 0cd1b5f..824282d 100644
--- a/pkg/vm/lib/transformations/to_string_transformer.dart
+++ b/pkg/vm/lib/transformations/to_string_transformer.dart
@@ -19,6 +19,8 @@
/// 'package:flutter/foundation.dart'.
final Set<String> _packageUris;
+ final Map<Class, bool> _inheritedKeepAnnotations = {};
+
/// Turn 'dart:ui' into 'dart:ui', or
/// 'package:flutter/src/semantics_event.dart' into 'package:flutter'.
String _importUriToPackage(Uri importUri) =>
@@ -29,7 +31,18 @@
.contains(_importUriToPackage(node.enclosingLibrary.importUri));
}
- bool _hasKeepAnnotation(Procedure node) {
+ bool _hasKeepAnnotation(Procedure node) =>
+ _hasPragma(node, 'flutter:keep-to-string');
+
+ bool _hasKeepAnnotationOnClass(Class node) =>
+ _hasPragma(node, 'flutter:keep-to-string-in-subtypes');
+
+ bool _hasInheritedKeepAnnotation(Class node) =>
+ _inheritedKeepAnnotations[node] ??= (_hasKeepAnnotationOnClass(node) ||
+ node.supers
+ .any((Supertype t) => _hasInheritedKeepAnnotation(t.classNode)));
+
+ bool _hasPragma(Annotatable node, String pragma) {
for (ConstantExpression expression
in node.annotations.whereType<ConstantExpression>()) {
if (expression.constant is! InstanceConstant) {
@@ -43,8 +56,7 @@
for (var fieldRef in constant.fieldValues.keys) {
if (fieldRef.asField.name.text == 'name') {
Constant? name = constant.fieldValues[fieldRef];
- return name is StringConstant &&
- name.value == 'flutter:keep-to-string';
+ return name is StringConstant && name.value == pragma;
}
}
return false;
@@ -61,7 +73,8 @@
!node.isAbstract &&
!node.enclosingClass!.isEnum &&
_isInTargetPackage(node) &&
- !_hasKeepAnnotation(node)) {
+ !_hasKeepAnnotation(node) &&
+ !_hasInheritedKeepAnnotation(node.enclosingClass!)) {
node.function.body!.replaceWith(
ReturnStatement(
SuperMethodInvocation(
diff --git a/pkg/vm/testcases/transformations/to_string_transformer/lib/main.dart b/pkg/vm/testcases/transformations/to_string_transformer/lib/main.dart
index 96544f2..aa53235 100644
--- a/pkg/vm/testcases/transformations/to_string_transformer/lib/main.dart
+++ b/pkg/vm/testcases/transformations/to_string_transformer/lib/main.dart
@@ -5,6 +5,7 @@
import 'dart:convert';
const keepToString = pragma('flutter:keep-to-string');
+const keepToStringInSubtypes = pragma('flutter:keep-to-string-in-subtypes');
String toString() => 'I am static';
@@ -26,9 +27,28 @@
String toString() => 'I am a Keep';
}
+@keepToStringInSubtypes
+class Base1 {}
+
+class Base2 extends Base1 {}
+
+class Base3 extends Object with Base2 {}
+
+class KeepInherited implements Base3 {
+ @override
+ String toString() => 'Heir';
+}
+
+class MyException implements Exception {
+ @override
+ String toString() => 'A very detailed message';
+}
+
void main() {
final IFoo foo = Foo();
print(foo.toString());
print(Keep().toString());
print(FooEnum.B.toString());
+ print(KeepInherited().toString());
+ print(MyException().toString());
}
diff --git a/pkg/vm/testcases/transformations/to_string_transformer/not_transformed.expect b/pkg/vm/testcases/transformations/to_string_transformer/not_transformed.expect
index 29c959a..6d48b41 100644
--- a/pkg/vm/testcases/transformations/to_string_transformer/not_transformed.expect
+++ b/pkg/vm/testcases/transformations/to_string_transformer/not_transformed.expect
@@ -39,7 +39,45 @@
method toString() → core::String
return "I am a Keep";
}
+@#C16
+class Base1 extends core::Object {
+ synthetic constructor •() → self::Base1
+ : super core::Object::•()
+ ;
+}
+class Base2 extends self::Base1 {
+ synthetic constructor •() → self::Base2
+ : super self::Base1::•()
+ ;
+}
+abstract class _Base3&Object&Base2 extends core::Object implements self::Base2 /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •() → self::_Base3&Object&Base2
+ : super core::Object::•()
+ ;
+}
+class Base3 extends self::_Base3&Object&Base2 {
+ synthetic constructor •() → self::Base3
+ : super self::_Base3&Object&Base2::•()
+ ;
+}
+class KeepInherited extends core::Object implements self::Base3 {
+ synthetic constructor •() → self::KeepInherited
+ : super core::Object::•()
+ ;
+ @#C1
+ method toString() → core::String
+ return "Heir";
+}
+class MyException extends core::Object implements core::Exception {
+ synthetic constructor •() → self::MyException
+ : super core::Object::•()
+ ;
+ @#C1
+ method toString() → core::String
+ return "A very detailed message";
+}
static const field core::pragma keepToString = #C14;
+static const field core::pragma keepToStringInSubtypes = #C16;
static method toString() → core::String
return "I am static";
static method main() → void {
@@ -47,6 +85,8 @@
core::print(foo.{self::IFoo::toString}(){() → core::String});
core::print(new self::Keep::•().{self::Keep::toString}(){() → core::String});
core::print(#C7.{self::FooEnum::toString}(){() → core::String});
+ core::print(new self::KeepInherited::•().{self::KeepInherited::toString}(){() → core::String});
+ core::print(new self::MyException::•().{self::MyException::toString}(){() → core::String});
}
constants {
#C1 = core::_Override {}
@@ -63,4 +103,6 @@
#C12 = "flutter:keep-to-string"
#C13 = null
#C14 = core::pragma {name:#C12, options:#C13}
+ #C15 = "flutter:keep-to-string-in-subtypes"
+ #C16 = core::pragma {name:#C15, options:#C13}
}
diff --git a/pkg/vm/testcases/transformations/to_string_transformer/transformed.expect b/pkg/vm/testcases/transformations/to_string_transformer/transformed.expect
index dca5efb..75d87ae 100644
--- a/pkg/vm/testcases/transformations/to_string_transformer/transformed.expect
+++ b/pkg/vm/testcases/transformations/to_string_transformer/transformed.expect
@@ -39,7 +39,45 @@
method toString() → core::String
return "I am a Keep";
}
+@#C16
+class Base1 extends core::Object {
+ synthetic constructor •() → self::Base1
+ : super core::Object::•()
+ ;
+}
+class Base2 extends self::Base1 {
+ synthetic constructor •() → self::Base2
+ : super self::Base1::•()
+ ;
+}
+abstract class _Base3&Object&Base2 extends core::Object implements self::Base2 /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •() → self::_Base3&Object&Base2
+ : super core::Object::•()
+ ;
+}
+class Base3 extends self::_Base3&Object&Base2 {
+ synthetic constructor •() → self::Base3
+ : super self::_Base3&Object&Base2::•()
+ ;
+}
+class KeepInherited extends core::Object implements self::Base3 {
+ synthetic constructor •() → self::KeepInherited
+ : super core::Object::•()
+ ;
+ @#C1
+ method toString() → core::String
+ return "Heir";
+}
+class MyException extends core::Object implements core::Exception {
+ synthetic constructor •() → self::MyException
+ : super core::Object::•()
+ ;
+ @#C1
+ method toString() → core::String
+ return "A very detailed message";
+}
static const field core::pragma keepToString = #C14;
+static const field core::pragma keepToStringInSubtypes = #C16;
static method toString() → core::String
return "I am static";
static method main() → void {
@@ -47,6 +85,8 @@
core::print(foo.{self::IFoo::toString}(){() → core::String});
core::print(new self::Keep::•().{self::Keep::toString}(){() → core::String});
core::print(#C7.{self::FooEnum::toString}(){() → core::String});
+ core::print(new self::KeepInherited::•().{self::KeepInherited::toString}(){() → core::String});
+ core::print(new self::MyException::•().{self::MyException::toString}(){() → core::String});
}
constants {
#C1 = core::_Override {}
@@ -63,4 +103,6 @@
#C12 = "flutter:keep-to-string"
#C13 = null
#C14 = core::pragma {name:#C12, options:#C13}
+ #C15 = "flutter:keep-to-string-in-subtypes"
+ #C16 = core::pragma {name:#C15, options:#C13}
}
diff --git a/runtime/docs/pragmas.md b/runtime/docs/pragmas.md
index 589699c..77d6d8e 100644
--- a/runtime/docs/pragmas.md
+++ b/runtime/docs/pragmas.md
@@ -41,3 +41,14 @@
| Pragma | Meaning |
| --- | --- |
| `vm:testing.unsafe.trace-entrypoints-fn` | [Observing which flow-graph-level entry-point was used when a function was called](compiler/frontend/testing_trace_entrypoints_pragma.md) |
+
+## Flutter toString transformer pragmas
+
+These pragmas are useful to exclude certain toString methods from toString transformation,
+which is enabled with `--delete-tostring-package-uri` option in kernel compilers and
+used by Flutter to remove certain toString methods in release mode to reduce size.
+
+| Pragma | Meaning |
+| --- | --- |
+| `flutter:keep-to-string` | Avoid transforming the annotated toString method. |
+| `flutter:keep-to-string-in-subtypes` | Avoid transforming toString methods in all subtypes of the annotated class. |
diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart
index 6190c83..00d69ce 100644
--- a/sdk/lib/core/errors.dart
+++ b/sdk/lib/core/errors.dart
@@ -64,6 +64,7 @@
/// For example, the [String.contains] method will use a [RangeError]
/// if its `startIndex` isn't in the range `0..length`,
/// which is easily created by `RangeError.range(startIndex, 0, length)`.
+@pragma('flutter:keep-to-string-in-subtypes')
class Error {
Error(); // Prevent use as mixin.
diff --git a/sdk/lib/core/exceptions.dart b/sdk/lib/core/exceptions.dart
index 889c991..f522d27 100644
--- a/sdk/lib/core/exceptions.dart
+++ b/sdk/lib/core/exceptions.dart
@@ -16,6 +16,7 @@
/// is discouraged in library code since it doesn't give users a precise
/// type they can catch. It may be reasonable to use instances of this
/// class in tests or during development.
+@pragma('flutter:keep-to-string-in-subtypes')
abstract class Exception {
factory Exception([var message]) => _Exception(message);
}
diff --git a/tools/VERSION b/tools/VERSION
index 5ade2e0..de7cb11 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 10
+PRERELEASE 11
PRERELEASE_PATCH 0
\ No newline at end of file