blob: 64b1ef0e329e5ba1648c01e89ee5dd7c6739474e [file] [log] [blame]
// Copyright (c) 2020, the R8 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.
package com.android.tools.r8.shaking;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Visibility;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public class ClassInitFieldSynthesizer {
private final AppView<AppInfoWithLiveness> appView;
private final DexField clinitField;
private final InitClassLens.Builder lensBuilder = InitClassLens.builder();
public ClassInitFieldSynthesizer(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
this.clinitField = appView.dexItemFactory().objectMembers.clinitField;
}
public void run(ExecutorService executorService) throws ExecutionException {
ThreadUtils.processMap(
appView.appInfo().initClassReferences, this::synthesizeClassInitField, executorService);
appView.setInitClassLens(lensBuilder.build());
}
private void synthesizeClassInitField(DexType type, Visibility minimumRequiredVisibility) {
DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
if (clazz == null) {
assert false;
return;
}
// Use an existing static field if there is one.
DexEncodedField encodedClinitField = null;
for (DexEncodedField staticField : clazz.staticFields()) {
// We need to field to be accessible from the contexts in which it is accessed.
if (!isMinimumRequiredVisibility(staticField, minimumRequiredVisibility)) {
continue;
}
// When compiling for dex, we can't use wide fields since we've only allocated a single
// register for the out-value of each ClassInit instruction
if (staticField.field.type.isWideType()) {
continue;
}
encodedClinitField = staticField;
break;
}
if (encodedClinitField == null) {
FieldAccessFlags accessFlags =
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC
| Constants.ACC_FINAL
| Constants.ACC_PUBLIC
| Constants.ACC_STATIC);
encodedClinitField =
new DexEncodedField(
appView.dexItemFactory().createField(clazz.type, clinitField.type, clinitField.name),
accessFlags,
DexAnnotationSet.empty(),
null);
clazz.appendStaticField(encodedClinitField);
}
lensBuilder.map(type, encodedClinitField.field);
}
private boolean isMinimumRequiredVisibility(
DexEncodedField field, Visibility minimumRequiredVisibility) {
if (field.isPublic()) {
return true;
}
switch (minimumRequiredVisibility) {
case PROTECTED:
return field.isProtected();
case PACKAGE_PRIVATE:
return field.isPackagePrivate() || field.isProtected();
case PUBLIC:
return false;
default:
throw new Unreachable();
}
}
}