[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:
Alexander Markov 2019-01-11 20:46:30 +00:00 committed by commit-bot@chromium.org
parent 602aa203b2
commit d821a2ec96
4 changed files with 94 additions and 0 deletions

View file

@ -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];

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}