[tfa,aot,dart2wasm] Allow tree-shaking of extension type members independently of their tear-offs
ExtensionTypeMemberDescriptor.memberReference and
ExtensionMemberDescriptor.memberReference are now nullable.
TFA-based tree shaker can now set them to null when extension type
member is not used and removed but corresponding lowered tear-off is
still used and retained.
Front-end never sets them to null and requires non-null memberReferences
when consuming kernel.
TEST=pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart
Fixes https://github.com/flutter/flutter/issues/155624
Change-Id: I71f98c02f4659ff72a8c1d7fc6c578e8b8e26d82
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401382
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart b/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
index 21ddf2c..5fed2db 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
@@ -250,7 +250,7 @@
bool get isSetter => kind == ExtensionMemberKind.Setter;
bool get isMethod => kind == ExtensionMemberKind.Method;
- bool get isExternal => (memberReference.asProcedure).isExternal;
+ bool get isExternal => (memberReference!.asProcedure).isExternal;
}
extension ProcedureExtension on Procedure {
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index 03b8836..b70deea 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -981,7 +981,7 @@
if (_processedExtensionLibraries.contains(library)) return;
for (var extension in library.extensions) {
for (var descriptor in extension.memberDescriptors) {
- var reference = descriptor.memberReference;
+ var reference = descriptor.memberReference!;
var onType = extension.onType;
bool isInteropOnType = false;
Annotatable? cls;
@@ -1127,7 +1127,7 @@
for (var extensionType in library.extensionTypeDeclarations) {
if (isInteropExtensionType(extensionType)) {
for (var descriptor in extensionType.memberDescriptors) {
- final reference = descriptor.memberReference;
+ final reference = descriptor.memberReference!;
_extensionTypeMemberIndex[reference] = descriptor;
_extensionTypeIndex[reference] = extensionType;
final tearOffReference = descriptor.tearOffReference;
diff --git a/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart b/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart
index 85f3fef..992d58b 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart
@@ -563,7 +563,7 @@
break;
default:
for (final descriptor in interopTypeDecl.memberDescriptors) {
- final descriptorNode = descriptor.memberReference.node;
+ final descriptorNode = descriptor.memberReference!.node;
if (descriptorNode is Procedure &&
_extensionIndex.isLiteralConstructor(descriptorNode)) {
_diagnosticReporter.report(
diff --git a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
index 7c2477f..9e25d2e 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
@@ -222,7 +222,7 @@
// setters return their return and parameter types, respectively.
DartType _getTypeOfDescriptor(ExtensionMemberDescriptor interopDescriptor) {
// CFE creates static procedures for each extension member.
- var interopMember = interopDescriptor.memberReference.asProcedure;
+ var interopMember = interopDescriptor.memberReference!.asProcedure;
if (interopDescriptor.isGetter) {
return interopMember.function.returnType;
@@ -365,7 +365,7 @@
_descriptorToExtensionName[descriptor] =
extension.isUnnamedExtension ? '<unnamed>' : extension.name;
var name = js_interop.getJSName(
- descriptor.memberReference.asMember,
+ descriptor.memberReference!.asMember,
);
if (name.isEmpty) name = descriptor.name.text;
exportNameToDescriptors!
diff --git a/pkg/dart2wasm/lib/record_class_generator.dart b/pkg/dart2wasm/lib/record_class_generator.dart
index d8ab225..90b6a2b 100644
--- a/pkg/dart2wasm/lib/record_class_generator.dart
+++ b/pkg/dart2wasm/lib/record_class_generator.dart
@@ -139,7 +139,7 @@
.singleWhere((e) => e.name == 'WasmArrayExt')
.memberDescriptors
.singleWhere((member) => member.name.text == '[]')
- .memberReference
+ .memberReference!
.node as Procedure;
late final Constructor wasmArrayLiteralConstructor =
diff --git a/pkg/front_end/lib/src/dill/dill_extension_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
index 71eb09f..6aad377 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
@@ -42,14 +42,14 @@
switch (descriptor.kind) {
case ExtensionMemberKind.Method:
if (descriptor.isStatic) {
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionStaticMethodBuilder(
procedure, descriptor, libraryBuilder, this),
setter: false);
} else {
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
assert(tearOff != null, "No tear found for ${descriptor}");
nameSpace.addLocalMember(
@@ -60,7 +60,7 @@
}
break;
case ExtensionMemberKind.Getter:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionGetterBuilder(
@@ -68,7 +68,7 @@
setter: false);
break;
case ExtensionMemberKind.Field:
- Field field = descriptor.memberReference.asField;
+ Field field = descriptor.memberReference!.asField;
nameSpace.addLocalMember(
name.text,
new DillExtensionFieldBuilder(
@@ -76,7 +76,7 @@
setter: false);
break;
case ExtensionMemberKind.Setter:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionSetterBuilder(
@@ -84,7 +84,7 @@
setter: true);
break;
case ExtensionMemberKind.Operator:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionOperatorBuilder(
diff --git a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
index a04f762..45eed1d 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
@@ -79,14 +79,14 @@
switch (descriptor.kind) {
case ExtensionTypeMemberKind.Method:
if (descriptor.isStatic) {
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionTypeStaticMethodBuilder(
procedure, descriptor, libraryBuilder, this),
setter: false);
} else {
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
assert(tearOff != null, "No tear found for ${descriptor}");
nameSpace.addLocalMember(
@@ -97,7 +97,7 @@
}
break;
case ExtensionTypeMemberKind.Getter:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionTypeGetterBuilder(
@@ -105,7 +105,7 @@
setter: false);
break;
case ExtensionTypeMemberKind.Field:
- Field field = descriptor.memberReference.asField;
+ Field field = descriptor.memberReference!.asField;
nameSpace.addLocalMember(
name.text,
new DillExtensionTypeFieldBuilder(
@@ -113,7 +113,7 @@
setter: false);
break;
case ExtensionTypeMemberKind.Setter:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionTypeSetterBuilder(
@@ -121,7 +121,7 @@
setter: true);
break;
case ExtensionTypeMemberKind.Operator:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
nameSpace.addLocalMember(
name.text,
new DillExtensionTypeOperatorBuilder(
@@ -129,7 +129,7 @@
setter: false);
break;
case ExtensionTypeMemberKind.Constructor:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
nameSpace.addConstructor(
name.text,
@@ -138,7 +138,7 @@
break;
case ExtensionTypeMemberKind.Factory:
case ExtensionTypeMemberKind.RedirectingFactory:
- Procedure procedure = descriptor.memberReference.asProcedure;
+ Procedure procedure = descriptor.memberReference!.asProcedure;
Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
nameSpace.addConstructor(
name.text,
diff --git a/pkg/front_end/lib/src/kernel/exhaustiveness.dart b/pkg/front_end/lib/src/kernel/exhaustiveness.dart
index e25a930..378e492 100644
--- a/pkg/front_end/lib/src/kernel/exhaustiveness.dart
+++ b/pkg/front_end/lib/src/kernel/exhaustiveness.dart
@@ -199,7 +199,7 @@
fieldTypes[new NameKey(descriptor.name.text)] =
functionType.returnType;
case ExtensionTypeMemberKind.Getter:
- Procedure member = descriptor.memberReference.asProcedure;
+ Procedure member = descriptor.memberReference!.asProcedure;
FunctionType functionType = member.getterType as FunctionType;
if (extensionTypeDeclaration.typeParameters.isNotEmpty) {
functionType = FunctionTypeInstantiator.instantiate(
diff --git a/pkg/front_end/lib/src/testing/id_testing_utils.dart b/pkg/front_end/lib/src/testing/id_testing_utils.dart
index ebbea44..9d6753c 100644
--- a/pkg/front_end/lib/src/testing/id_testing_utils.dart
+++ b/pkg/front_end/lib/src/testing/id_testing_utils.dart
@@ -911,7 +911,8 @@
}
return [
- descriptorToText(descriptor.memberReference, forTearOff: false),
+ if (descriptor.memberReference != null)
+ descriptorToText(descriptor.memberReference!, forTearOff: false),
if (descriptor.tearOffReference != null)
descriptorToText(descriptor.tearOffReference!, forTearOff: true),
];
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index ab9f04e..3f36b5a 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
type ComponentFile {
UInt32 magic = 0x90ABCDEF;
- UInt32 formatVersion = 121;
+ UInt32 formatVersion = 122;
Byte[10] shortSdkHash;
List<String> problemsAsJson; // Described in problems.md.
Library[] libraries;
@@ -358,8 +358,8 @@
Name name;
ExtensionMemberKind kind;
Byte flags (isStatic);
- MemberReference member;
- MemberReference tearOff;
+ MemberReference member; // May be NullReference.
+ MemberReference tearOff; // May be NullReference.
}
type ExtensionTypeDeclaration extends Node {
@@ -384,8 +384,8 @@
Name name;
ExtensionTypeMemberKind kind;
Byte flags (isStatic);
- MemberReference member;
- MemberReference tearOff;
+ MemberReference member; // May be NullReference.
+ MemberReference tearOff; // May be NullReference.
}
abstract type Member extends Node {}
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index ca62b0e..4ef352d 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -1600,12 +1600,12 @@
Name name = readName();
int kind = readByte();
int flags = readByte();
- CanonicalName memberName = readNonNullCanonicalNameReference();
+ CanonicalName? memberName = readNullableCanonicalNameReference();
CanonicalName? tearOffName = readNullableCanonicalNameReference();
return new ExtensionMemberDescriptor(
name: name,
kind: ExtensionMemberKind.values[kind],
- memberReference: memberName.reference,
+ memberReference: memberName?.reference,
tearOffReference: tearOffName?.reference)
..flags = flags;
}
@@ -1692,12 +1692,12 @@
Name name = readName();
int kind = readByte();
int flags = readByte();
- CanonicalName memberName = readNonNullCanonicalNameReference();
+ CanonicalName? memberName = readNullableCanonicalNameReference();
CanonicalName? tearOffName = readNullableCanonicalNameReference();
return new ExtensionTypeMemberDescriptor(
name: name,
kind: ExtensionTypeMemberKind.values[kind],
- memberReference: memberName.reference,
+ memberReference: memberName?.reference,
tearOffReference: tearOffName?.reference)
..flags = flags;
}
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index c0315d4..0b4e4dc 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -2648,10 +2648,14 @@
writeName(descriptor.name);
writeByte(descriptor.kind.index);
writeByte(descriptor.flags);
- assert(descriptor.memberReference.canonicalName != null,
+ final Reference? memberReference = descriptor.memberReference;
+ assert(memberReference == null || memberReference.canonicalName != null,
"No canonical name for ${descriptor}.");
- writeNonNullCanonicalNameReference(descriptor.memberReference);
- writeNullAllowedCanonicalNameReference(descriptor.tearOffReference);
+ writeNullAllowedCanonicalNameReference(memberReference);
+ final Reference? tearOffReference = descriptor.tearOffReference;
+ assert(tearOffReference == null || tearOffReference.canonicalName != null,
+ "No canonical name for ${descriptor} tear-off.");
+ writeNullAllowedCanonicalNameReference(tearOffReference);
}
}
@@ -2689,14 +2693,14 @@
writeName(descriptor.name);
writeByte(descriptor.kind.index);
writeByte(descriptor.flags);
- assert(descriptor.memberReference.canonicalName != null,
+ final Reference? memberReference = descriptor.memberReference;
+ assert(memberReference == null || memberReference.canonicalName != null,
"No canonical name for ${descriptor}.");
- writeNonNullCanonicalNameReference(descriptor.memberReference);
- assert(
- descriptor.tearOffReference == null ||
- descriptor.tearOffReference?.canonicalName != null,
+ writeNullAllowedCanonicalNameReference(memberReference);
+ final Reference? tearOffReference = descriptor.tearOffReference;
+ assert(tearOffReference == null || tearOffReference.canonicalName != null,
"No canonical name for ${descriptor} tear-off.");
- writeNullAllowedCanonicalNameReference(descriptor.tearOffReference);
+ writeNullAllowedCanonicalNameReference(tearOffReference);
}
}
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 33ec5d2..a330cbc 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -226,7 +226,7 @@
/// Internal version of kernel binary format.
/// Bump it when making incompatible changes in kernel binaries.
/// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
- static const int BinaryFormatVersion = 121;
+ static const int BinaryFormatVersion = 122;
}
abstract class ConstantTag {
diff --git a/pkg/kernel/lib/src/ast/declarations.dart b/pkg/kernel/lib/src/ast/declarations.dart
index 1867c62..8cd1e91 100644
--- a/pkg/kernel/lib/src/ast/declarations.dart
+++ b/pkg/kernel/lib/src/ast/declarations.dart
@@ -748,7 +748,9 @@
int flags = 0;
/// Reference to the top-level member created for the extension method.
- final Reference memberReference;
+ /// This member reference is not null after the front-end but can
+ /// be cleared by certain back-ends (e.g. VM/AOT) if member is not used.
+ final Reference? memberReference;
/// Reference to the top-level member created for the extension member tear
/// off, if any.
@@ -761,6 +763,7 @@
required this.memberReference,
required this.tearOffReference}) {
this.isStatic = isStatic;
+ assert(memberReference != null || tearOffReference != null);
}
/// Return `true` if the extension method was declared as `static`.
@@ -773,7 +776,7 @@
@override
String toString() {
return 'ExtensionMemberDescriptor($name,$kind,'
- '${memberReference.toStringInternal()},isStatic=${isStatic})';
+ '${memberReference?.toStringInternal()},isStatic=${isStatic})';
}
}
@@ -1034,7 +1037,9 @@
/// Reference to the top-level member created for the extension type
/// declaration member.
- final Reference memberReference;
+ /// This member reference is not null after the front-end but can
+ /// be cleared by certain back-ends (e.g. VM/AOT) if member is not used.
+ final Reference? memberReference;
/// Reference to the top-level member created for the extension type
/// declaration member tear off, if any.
@@ -1047,6 +1052,7 @@
required this.memberReference,
required this.tearOffReference}) {
this.isStatic = isStatic;
+ assert(memberReference != null || tearOffReference != null);
}
/// Return `true` if the extension type declaration member was declared as
@@ -1060,7 +1066,7 @@
@override
String toString() {
return 'ExtensionTypeMemberDescriptor($name,$kind,'
- '${memberReference.toStringInternal()},isStatic=${isStatic},'
+ '${memberReference?.toStringInternal()},isStatic=${isStatic},'
'${tearOffReference?.toStringInternal()})';
}
}
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 8590c14..bebd9fa 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1307,9 +1307,13 @@
endLine(';');
}
- writeReference(descriptor.memberReference, isTearOff: false);
- if (descriptor.tearOffReference != null) {
- writeReference(descriptor.tearOffReference!, isTearOff: true);
+ final Reference? memberReference = descriptor.memberReference;
+ if (memberReference != null) {
+ writeReference(memberReference, isTearOff: false);
+ }
+ final Reference? tearOffReference = descriptor.tearOffReference;
+ if (tearOffReference != null) {
+ writeReference(tearOffReference, isTearOff: true);
}
});
--indentation;
@@ -1387,9 +1391,13 @@
endLine(';');
}
- writeReference(descriptor.memberReference, isTearOff: false);
- if (descriptor.tearOffReference != null) {
- writeReference(descriptor.tearOffReference!, isTearOff: true);
+ final Reference? memberReference = descriptor.memberReference;
+ if (memberReference != null) {
+ writeReference(memberReference, isTearOff: false);
+ }
+ final Reference? tearOffReference = descriptor.tearOffReference;
+ if (tearOffReference != null) {
+ writeReference(tearOffReference, isTearOff: true);
}
});
--indentation;
diff --git a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
index 97d9b59..023c423 100644
--- a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
+++ b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
@@ -716,8 +716,8 @@
// there is a use case for this.
continue;
}
- final Procedure method = member.memberReference.asProcedure;
- if (_hasWidgetFactoryAnnotation(method)) {
+ final Procedure? method = member.memberReference?.asProcedure;
+ if (method != null && _hasWidgetFactoryAnnotation(method)) {
_maybeAddNamedParameter(
method.function,
new VariableDeclaration(
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index ffbefa0..f3bbd3e 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -428,14 +428,16 @@
for (Extension extension in library.extensions) {
for (ExtensionMemberDescriptor descriptor
in extension.memberDescriptors) {
- Reference memberReference = descriptor.memberReference;
- map[memberReference] = descriptor;
- Member member = memberReference.asMember;
- if (!member.isExtensionMember) {
- problem(
- member,
- "Member $member (${descriptor}) from $extension is not marked "
- "as an extension member.");
+ Reference? memberReference = descriptor.memberReference;
+ if (memberReference != null) {
+ map[memberReference] = descriptor;
+ Member member = memberReference.asMember;
+ if (!member.isExtensionMember) {
+ problem(
+ member,
+ "Member $member (${descriptor}) from $extension is not "
+ " marked as an extension member.");
+ }
}
Reference? tearOffReference = descriptor.tearOffReference;
if (tearOffReference != null) {
@@ -448,6 +450,12 @@
"marked as an extension member.");
}
}
+ if (memberReference == null && tearOffReference == null) {
+ problem(
+ extension,
+ "Both member and tear-off references are null in "
+ "the descriptor $descriptor from $extension.");
+ }
}
}
}
@@ -463,14 +471,17 @@
in library.extensionTypeDeclarations) {
for (ExtensionTypeMemberDescriptor descriptor
in extensionTypeDeclaration.memberDescriptors) {
- Reference memberReference = descriptor.memberReference;
- map[memberReference] = descriptor;
- Member member = memberReference.asMember;
- if (!member.isExtensionTypeMember) {
- problem(
- member,
- "Member $member (${descriptor}) from $extensionTypeDeclaration "
- "is not marked as an extension type member.");
+ Reference? memberReference = descriptor.memberReference;
+ if (memberReference != null) {
+ map[memberReference] = descriptor;
+ Member member = memberReference.asMember;
+ if (!member.isExtensionTypeMember) {
+ problem(
+ member,
+ "Member $member (${descriptor}) from "
+ "$extensionTypeDeclaration is not marked as an extension "
+ "type member.");
+ }
}
Reference? tearOffReference = descriptor.tearOffReference;
if (tearOffReference != null) {
@@ -484,6 +495,12 @@
"type member.");
}
}
+ if (memberReference == null && tearOffReference == null) {
+ problem(
+ extensionTypeDeclaration,
+ "Both member and tear-off references are null in "
+ "the descriptor $descriptor from $extensionTypeDeclaration.");
+ }
}
}
}
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index f92bd18..18d1031 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -959,7 +959,7 @@
// The AST should have exactly one [Extension] for [m].
final extension = m.enclosingLibrary.extensions.firstWhere((extension) {
return extension.memberDescriptors.any((descriptor) =>
- descriptor.memberReference.asMember == m ||
+ descriptor.memberReference?.asMember == m ||
descriptor.tearOffReference?.asMember == m);
});
@@ -976,7 +976,7 @@
.enclosingLibrary.extensionTypeDeclarations
.firstWhere((extensionTypeDeclaration) {
return extensionTypeDeclaration.memberDescriptors.any((descriptor) =>
- descriptor.memberReference.asMember == m ||
+ descriptor.memberReference?.asMember == m ||
descriptor.tearOffReference?.asMember == m);
});
@@ -2098,25 +2098,26 @@
// To avoid depending on the order in which members and extensions are
// visited during the transformation, we handle both cases: either the
// member was already removed or it will be removed later.
- final Reference memberReference = descriptor.memberReference;
- final bool memberIsBound = memberReference.node != null;
+ final Reference? memberReference = descriptor.memberReference;
+ final bool memberIsBound = memberReference?.node != null;
final bool isMemberUsed =
- memberIsBound && shaker.isMemberUsed(memberReference.asMember);
+ memberIsBound && shaker.isMemberUsed(memberReference!.asMember);
final Reference? tearOffReference = descriptor.tearOffReference;
final bool tearOffIsBound = tearOffReference?.node != null;
final bool isTearOffUsed =
tearOffIsBound && shaker.isMemberUsed(tearOffReference!.asMember);
if (isMemberUsed || isTearOffUsed) {
- assert(!isTearOffUsed || isMemberUsed,
- "Tear-off used without the member of $descriptor");
- if (!isTearOffUsed) {
- // Clear the tear-off reference since it is not used.
+ if ((!isMemberUsed && memberReference != null) ||
+ (!isTearOffUsed && tearOffReference != null)) {
+ // Clear references.
descriptor = ExtensionMemberDescriptor(
name: descriptor.name,
kind: descriptor.kind,
isStatic: descriptor.isStatic,
- memberReference: descriptor.memberReference,
- tearOffReference: null);
+ memberReference:
+ isMemberUsed ? descriptor.memberReference : null,
+ tearOffReference:
+ isTearOffUsed ? descriptor.tearOffReference : null);
}
node.memberDescriptors[writeIndex++] = descriptor;
}
@@ -2142,25 +2143,26 @@
// declarations are visited during the transformation, we handle both
// cases: either the member was already removed or it will be removed
// later.
- final Reference memberReference = descriptor.memberReference;
- final bool memberIsBound = memberReference.node != null;
+ final Reference? memberReference = descriptor.memberReference;
+ final bool memberIsBound = memberReference?.node != null;
final bool isMemberUsed =
- memberIsBound && shaker.isMemberUsed(memberReference.asMember);
+ memberIsBound && shaker.isMemberUsed(memberReference!.asMember);
final Reference? tearOffReference = descriptor.tearOffReference;
final bool tearOffIsBound = tearOffReference?.node != null;
final bool isTearOffUsed =
tearOffIsBound && shaker.isMemberUsed(tearOffReference!.asMember);
if (isMemberUsed || isTearOffUsed) {
- assert(!isTearOffUsed || isMemberUsed,
- "Tear-off used without the member of $descriptor");
- if (!isTearOffUsed) {
- // Clear the tear-off reference since it is not used.
+ if ((!isMemberUsed && memberReference != null) ||
+ (!isTearOffUsed && tearOffReference != null)) {
+ // Clear references.
descriptor = ExtensionTypeMemberDescriptor(
name: descriptor.name,
kind: descriptor.kind,
isStatic: descriptor.isStatic,
- memberReference: descriptor.memberReference,
- tearOffReference: null);
+ memberReference:
+ isMemberUsed ? descriptor.memberReference : null,
+ tearOffReference:
+ isTearOffUsed ? descriptor.tearOffReference : null);
}
node.memberDescriptors[writeIndex++] = descriptor;
}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart
new file mode 100644
index 0000000..22ffc9f
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2024, 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.
+
+// Regression test for https://github.com/flutter/flutter/issues/155624.
+// Verifies that TFA correctly tree shakes redirecting factory from
+// extension types when tear-off of the factory is used.
+
+extension type ET1(int id) {
+ ET1.c1() : this(0);
+ ET1.c2(this.id);
+ factory ET1.f1() = ET1.c1;
+ factory ET1.f2(int v) => ET1.c2(v);
+}
+
+void main() {
+ print(ET1.f1);
+ print(ET1.f2);
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart.expect
new file mode 100644
index 0000000..fe436a3
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart.expect
@@ -0,0 +1,60 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+
+extension type ET1(core::int id) {
+ constructor • = self::ET1|constructor#;
+ constructor c1 = self::ET1|constructor#c1;
+ constructor c2 = self::ET1|constructor#c2;
+ static redirecting-factory tearoff f1 = self::ET1|constructor#_#f1#tearOff;
+ static factory f2 = self::ET1|constructor#f2;
+ static factory tearoff f2 = self::ET1|constructor#_#f2#tearOff;
+}
+
+[@vm.inferred-return-type.metadata=dart.core::_Smi (value: 0)]
+[@vm.unboxing-info.metadata=()->i]
+static extension-type-member method ET1|constructor#() → self::ET1% /* erasure=core::int, declared=! */ {
+ lowered final self::ET1% /* erasure=core::int, declared=! */ #this = #C1;
+ return #this;
+}
+
+[@vm.inferred-return-type.metadata=dart.core::_Smi (value: 0)]
+[@vm.unboxing-info.metadata=()->i]
+static extension-type-member method ET1|constructor#c1() → self::ET1% /* erasure=core::int, declared=! */ {
+ lowered final self::ET1% /* erasure=core::int, declared=! */ #this;
+ #this = [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::ET1|constructor#();
+ return #this;
+}
+
+[@vm.inferred-return-type.metadata=int]
+[@vm.unboxing-info.metadata=(i)->i]
+static extension-type-member method ET1|constructor#c2([@vm.inferred-arg-type.metadata=int] core::int id) → self::ET1% /* erasure=core::int, declared=! */ {
+ lowered final self::ET1% /* erasure=core::int, declared=! */ #this = id;
+ return #this;
+}
+
+[@vm.inferred-return-type.metadata=dart.core::_Smi (value: 0)]
+[@vm.unboxing-info.metadata=()->i]
+static extension-type-member method ET1|constructor#_#f1#tearOff() → self::ET1% /* erasure=core::int, declared=! */
+ return [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::ET1|constructor#c1();
+
+[@vm.inferred-return-type.metadata=int]
+[@vm.unboxing-info.metadata=(i)->i]
+static extension-type-member method ET1|constructor#f2([@vm.inferred-arg-type.metadata=int] core::int v) → self::ET1% /* erasure=core::int, declared=! */
+ return [@vm.inferred-type.metadata=int] self::ET1|constructor#c2(v);
+
+[@vm.inferred-return-type.metadata=int]
+[@vm.unboxing-info.metadata=(b)->i]
+static extension-type-member method ET1|constructor#_#f2#tearOff(core::int v) → self::ET1% /* erasure=core::int, declared=! */
+ return [@vm.inferred-type.metadata=int] self::ET1|constructor#f2(v);
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+static method main() → void {
+ core::print(#C2);
+ core::print(#C3);
+}
+constants {
+ #C1 = 0
+ #C2 = static-tearoff self::ET1|constructor#_#f1#tearOff
+ #C3 = static-tearoff self::ET1|constructor#_#f2#tearOff
+}
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 879f6aa..9748a75 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -18,7 +18,7 @@
// package:kernel/binary.md.
static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
-static const uint32_t kSupportedKernelFormatVersion = 121;
+static const uint32_t kSupportedKernelFormatVersion = 122;
// Keep in sync with package:kernel/lib/binary/tag.dart
#define KERNEL_TAG_LIST(V) \