blob: 29274b27472792a7a4b0f5067fa79d201d8f2431 [file] [log] [blame]
// Copyright (c) 2021, 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.ir.optimize.classinliner.analysis;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
/**
* Non-trivial information (neither BOTTOM nor TOP) about a method's usage of a given parameter.
*
* <p>This is internal to the analysis, since {@link #methodCallsWithParameterAsReceiver} references
* instructions from {@link com.android.tools.r8.ir.code.IRCode}, which makes it unsuited for being
* stored in {@link com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo}.
*/
public class InternalNonEmptyParameterUsage extends ParameterUsage {
private Set<DexType> castsWithParameter;
private Set<DexField> fieldsReadFromParameter;
private Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver;
private boolean isParameterMutated;
private boolean isParameterReturned;
private boolean isParameterUsedAsLock;
InternalNonEmptyParameterUsage(
Set<DexType> castsWithParameter,
Set<DexField> fieldsReadFromParameter,
Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver,
boolean isParameterMutated,
boolean isParameterReturned,
boolean isParameterUsedAsLock) {
assert !castsWithParameter.isEmpty()
|| !fieldsReadFromParameter.isEmpty()
|| !methodCallsWithParameterAsReceiver.isEmpty()
|| isParameterMutated
|| isParameterReturned
|| isParameterUsedAsLock;
this.castsWithParameter = castsWithParameter;
this.fieldsReadFromParameter = fieldsReadFromParameter;
this.methodCallsWithParameterAsReceiver = methodCallsWithParameterAsReceiver;
this.isParameterMutated = isParameterMutated;
this.isParameterReturned = isParameterReturned;
this.isParameterUsedAsLock = isParameterUsedAsLock;
}
static Builder builder() {
return new Builder();
}
Builder builderFromInstance() {
return new Builder(this);
}
@Override
ParameterUsage addCastWithParameter(DexType castType) {
ImmutableSet.Builder<DexType> newCastsWithParameter = ImmutableSet.builder();
newCastsWithParameter.addAll(castsWithParameter);
newCastsWithParameter.add(castType);
return new InternalNonEmptyParameterUsage(
newCastsWithParameter.build(),
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
isParameterMutated,
isParameterReturned,
isParameterUsedAsLock);
}
@Override
InternalNonEmptyParameterUsage addFieldReadFromParameter(DexField field) {
ImmutableSet.Builder<DexField> newFieldsReadFromParameterBuilder = ImmutableSet.builder();
newFieldsReadFromParameterBuilder.addAll(fieldsReadFromParameter);
newFieldsReadFromParameterBuilder.add(field);
return new InternalNonEmptyParameterUsage(
castsWithParameter,
newFieldsReadFromParameterBuilder.build(),
methodCallsWithParameterAsReceiver,
isParameterMutated,
isParameterReturned,
isParameterUsedAsLock);
}
@Override
InternalNonEmptyParameterUsage addMethodCallWithParameterAsReceiver(
InvokeMethodWithReceiver invoke) {
ImmutableSet.Builder<InvokeMethodWithReceiver> newMethodCallsWithParameterAsReceiverBuilder =
ImmutableSet.builder();
newMethodCallsWithParameterAsReceiverBuilder.addAll(methodCallsWithParameterAsReceiver);
newMethodCallsWithParameterAsReceiverBuilder.add(invoke);
return new InternalNonEmptyParameterUsage(
castsWithParameter,
fieldsReadFromParameter,
newMethodCallsWithParameterAsReceiverBuilder.build(),
isParameterMutated,
isParameterReturned,
isParameterUsedAsLock);
}
@Override
public InternalNonEmptyParameterUsage asInternalNonEmpty() {
return this;
}
@Override
ParameterUsage externalize() {
ImmutableMultiset.Builder<DexMethod> methodCallsWithParameterAsReceiverBuilder =
ImmutableMultiset.builder();
methodCallsWithParameterAsReceiver.forEach(
invoke -> methodCallsWithParameterAsReceiverBuilder.add(invoke.getInvokedMethod()));
return new NonEmptyParameterUsage(
castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiverBuilder.build(),
isParameterMutated,
isParameterReturned,
isParameterUsedAsLock);
}
@Override
public boolean isParameterMutated() {
return isParameterMutated;
}
@Override
public boolean isParameterReturned() {
return isParameterReturned;
}
@Override
public boolean isParameterUsedAsLock() {
return isParameterUsedAsLock;
}
InternalNonEmptyParameterUsage join(InternalNonEmptyParameterUsage other) {
return builderFromInstance()
.addCastsWithParameter(other.castsWithParameter)
.addFieldsReadFromParameter(other.fieldsReadFromParameter)
.addMethodCallsWithParameterAsReceiver(other.methodCallsWithParameterAsReceiver)
.joinIsReceiverMutated(other.isParameterMutated)
.joinIsReceiverReturned(other.isParameterReturned)
.joinIsReceiverUsedAsLock(other.isParameterUsedAsLock)
.build();
}
@Override
InternalNonEmptyParameterUsage setParameterMutated() {
if (isParameterMutated) {
return this;
}
return new InternalNonEmptyParameterUsage(
castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
true,
isParameterReturned,
isParameterUsedAsLock);
}
@Override
InternalNonEmptyParameterUsage setParameterReturned() {
if (isParameterReturned) {
return this;
}
return new InternalNonEmptyParameterUsage(
castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
isParameterMutated,
true,
isParameterUsedAsLock);
}
@Override
InternalNonEmptyParameterUsage setParameterUsedAsLock() {
if (isParameterUsedAsLock) {
return this;
}
return new InternalNonEmptyParameterUsage(
castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
isParameterMutated,
isParameterReturned,
true);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != getClass()) {
return false;
}
InternalNonEmptyParameterUsage knownParameterUsage = (InternalNonEmptyParameterUsage) obj;
return isParameterMutated == knownParameterUsage.isParameterMutated
&& isParameterReturned == knownParameterUsage.isParameterReturned
&& isParameterUsedAsLock == knownParameterUsage.isParameterUsedAsLock
&& castsWithParameter.equals(knownParameterUsage.castsWithParameter)
&& fieldsReadFromParameter.equals(knownParameterUsage.fieldsReadFromParameter)
&& methodCallsWithParameterAsReceiver.equals(
knownParameterUsage.methodCallsWithParameterAsReceiver);
}
@Override
public int hashCode() {
int hash =
31 * (31 * (31 + castsWithParameter.hashCode()) + fieldsReadFromParameter.hashCode())
+ methodCallsWithParameterAsReceiver.hashCode();
assert hash
== Objects.hash(
castsWithParameter, fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
hash = (hash << 1) | BooleanUtils.intValue(isParameterMutated);
hash = (hash << 1) | BooleanUtils.intValue(isParameterReturned);
hash = (hash << 1) | BooleanUtils.intValue(isParameterUsedAsLock);
return hash;
}
static class Builder {
private ImmutableSet.Builder<DexType> castsWithParameterBuilder;
private ImmutableSet.Builder<DexField> fieldsReadFromParameterBuilder;
private ImmutableSet.Builder<InvokeMethodWithReceiver>
methodCallsWithParameterAsReceiverBuilder;
private boolean isParameterMutated;
private boolean isParameterReturned;
private boolean isParameterUsedAsLock;
Builder() {
castsWithParameterBuilder = ImmutableSet.builder();
fieldsReadFromParameterBuilder = ImmutableSet.builder();
methodCallsWithParameterAsReceiverBuilder = ImmutableSet.builder();
}
Builder(InternalNonEmptyParameterUsage methodBehavior) {
castsWithParameterBuilder =
ImmutableSet.<DexType>builder().addAll(methodBehavior.castsWithParameter);
fieldsReadFromParameterBuilder =
ImmutableSet.<DexField>builder().addAll(methodBehavior.fieldsReadFromParameter);
methodCallsWithParameterAsReceiverBuilder =
ImmutableSet.<InvokeMethodWithReceiver>builder()
.addAll(methodBehavior.methodCallsWithParameterAsReceiver);
isParameterMutated = methodBehavior.isParameterMutated;
isParameterReturned = methodBehavior.isParameterReturned;
isParameterUsedAsLock = methodBehavior.isParameterUsedAsLock;
}
Builder addCastWithParameter(DexType castType) {
castsWithParameterBuilder.add(castType);
return this;
}
Builder addCastsWithParameter(Collection<DexType> castTypes) {
castsWithParameterBuilder.addAll(castTypes);
return this;
}
Builder addFieldReadFromParameter(DexField fieldReadFromParameter) {
fieldsReadFromParameterBuilder.add(fieldReadFromParameter);
return this;
}
Builder addFieldsReadFromParameter(Collection<DexField> fieldsReadFromParameter) {
fieldsReadFromParameterBuilder.addAll(fieldsReadFromParameter);
return this;
}
Builder addMethodCallWithParameterAsReceiver(
InvokeMethodWithReceiver methodCallWithParameterAsReceiver) {
methodCallsWithParameterAsReceiverBuilder.add(methodCallWithParameterAsReceiver);
return this;
}
Builder addMethodCallsWithParameterAsReceiver(
Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver) {
methodCallsWithParameterAsReceiverBuilder.addAll(methodCallsWithParameterAsReceiver);
return this;
}
Builder joinIsReceiverMutated(boolean isParameterMutated) {
this.isParameterMutated |= isParameterMutated;
return this;
}
Builder joinIsReceiverReturned(boolean isParameterReturned) {
this.isParameterReturned |= isParameterReturned;
return this;
}
Builder joinIsReceiverUsedAsLock(boolean isParameterUsedAsLock) {
this.isParameterUsedAsLock |= isParameterUsedAsLock;
return this;
}
Builder setParameterMutated() {
this.isParameterMutated = true;
return this;
}
Builder setParameterReturned() {
this.isParameterReturned = true;
return this;
}
Builder setParameterUsedAsLock() {
this.isParameterUsedAsLock = true;
return this;
}
InternalNonEmptyParameterUsage build() {
return new InternalNonEmptyParameterUsage(
castsWithParameterBuilder.build(),
fieldsReadFromParameterBuilder.build(),
methodCallsWithParameterAsReceiverBuilder.build(),
isParameterMutated,
isParameterReturned,
isParameterUsedAsLock);
}
}
}