mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:37:43 +00:00
[vm/aot/tfa] Correctly tree shake initializers of write-only fields
Fixes https://github.com/dart-lang/sdk/issues/35632 Change-Id: I9736268087a90fd409509f35bf2fc0637443a9fb Reviewed-on: https://dart-review.googlesource.com/c/89161 Commit-Queue: Alexander Markov <alexmarkov@google.com> Auto-Submit: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
602aa203b2
commit
d821a2ec96
|
@ -220,6 +220,7 @@ class _DirectInvocation extends _Invocation {
|
|||
}
|
||||
fieldValue.setValue(initializerResult, typeFlowAnalysis,
|
||||
field.isStatic ? null : args.receiver);
|
||||
fieldValue.isInitialized = true;
|
||||
return const EmptyType();
|
||||
}
|
||||
|
||||
|
@ -732,6 +733,9 @@ class _FieldValue extends _DependencyTracker {
|
|||
final Summary typeGuardSummary;
|
||||
Type value;
|
||||
|
||||
/// Flag indicating if field initializer was executed.
|
||||
bool isInitialized = false;
|
||||
|
||||
_FieldValue(this.field, this.typeGuardSummary)
|
||||
: staticType = new Type.fromStatic(field.type) {
|
||||
if (field.initializer == null && _isDefaultValueOfFieldObservable()) {
|
||||
|
@ -1385,6 +1389,8 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
hierarchyCache.seal();
|
||||
}
|
||||
|
||||
/// Returns true if analysis found that given member
|
||||
/// could be executed / field could be accessed.
|
||||
bool isMemberUsed(Member member) {
|
||||
if (member is Field) {
|
||||
return _fieldValues.containsKey(member);
|
||||
|
@ -1393,6 +1399,16 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if analysis found that initializer of the given [field]
|
||||
/// could be executed.
|
||||
bool isFieldInitializerUsed(Field field) {
|
||||
final fieldValue = _fieldValues[field];
|
||||
if (fieldValue != null) {
|
||||
return fieldValue.isInitialized;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isClassAllocated(Class c) => hierarchyCache.allocatedClasses.contains(c);
|
||||
|
||||
Call callSite(TreeNode node) => summaryCollector.callSites[node];
|
||||
|
|
|
@ -367,6 +367,8 @@ class TreeShaker {
|
|||
bool isClassAllocated(Class c) => typeFlowAnalysis.isClassAllocated(c);
|
||||
bool isMemberUsed(Member m) => _usedMembers.contains(m);
|
||||
bool isMemberBodyReachable(Member m) => typeFlowAnalysis.isMemberUsed(m);
|
||||
bool isFieldInitializerReachable(Field f) =>
|
||||
typeFlowAnalysis.isFieldInitializerUsed(f);
|
||||
bool isMemberReferencedFromNativeCode(Member m) =>
|
||||
typeFlowAnalysis.nativeCodeOracle.isMemberReferencedFromNativeCode(m);
|
||||
bool isTypedefUsed(Typedef t) => _usedTypedefs.contains(t);
|
||||
|
@ -602,6 +604,30 @@ class _TreeShakerPass1 extends Transformer {
|
|||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitField(Field node) {
|
||||
if (shaker.isMemberBodyReachable(node)) {
|
||||
if (kPrintTrace) {
|
||||
tracePrint("Visiting $node");
|
||||
}
|
||||
shaker.addUsedMember(node);
|
||||
if (node.initializer != null) {
|
||||
if (shaker.isFieldInitializerReachable(node)) {
|
||||
node.transformChildren(this);
|
||||
} else {
|
||||
node.initializer = _makeUnreachableCall([]);
|
||||
}
|
||||
}
|
||||
} else if (shaker.isMemberReferencedFromNativeCode(node)) {
|
||||
// Preserve members referenced from native code to satisfy lookups, even
|
||||
// if they are not reachable. An instance member could be added via
|
||||
// native code entry point but still unreachable if no instances of
|
||||
// its enclosing class are allocated.
|
||||
shaker.addUsedMember(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitMethodInvocation(MethodInvocation node) {
|
||||
node.transformChildren(this);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Tests tree shaking of field initializer for a write-only field.
|
||||
// Regression test for https://github.com/dart-lang/sdk/issues/35632.
|
||||
|
||||
class A {
|
||||
A() {
|
||||
print('A');
|
||||
}
|
||||
}
|
||||
|
||||
var field = A();
|
||||
|
||||
class B {
|
||||
B() {
|
||||
print('B');
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
var instanceField = new B();
|
||||
}
|
||||
|
||||
void main() {
|
||||
field = null;
|
||||
new C().instanceField = null;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
abstract class A extends core::Object {
|
||||
}
|
||||
class B extends core::Object {
|
||||
constructor •() → self::B
|
||||
: super core::Object::•() {
|
||||
core::print("B");
|
||||
}
|
||||
}
|
||||
class C extends core::Object {
|
||||
[@vm.inferred-type.metadata=#lib::B?] [@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] field self::B instanceField = new self::B::•();
|
||||
synthetic constructor •() → self::C
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
[@vm.inferred-type.metadata=dart.core::Null?]static field self::A field = throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
|
||||
static method main() → void {
|
||||
self::field = null;
|
||||
[@vm.direct-call.metadata=#lib::C::instanceField] new self::C::•().{self::C::instanceField} = null;
|
||||
}
|
Loading…
Reference in a new issue