blob: 4bba181d9b5385d9f76308c96d4f3355a832b3e9 [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.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<DexField> fieldsReadFromParameter;
private Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver;
private boolean isParameterMutated;
private boolean isParameterReturned;
private boolean isParameterUsedAsLock;
InternalNonEmptyParameterUsage(
Set<DexField> fieldsReadFromParameter,
Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver,
boolean isParameterMutated,
boolean isParameterReturned,
boolean isParameterUsedAsLock) {
assert !fieldsReadFromParameter.isEmpty()
|| !methodCallsWithParameterAsReceiver.isEmpty()
|| isParameterMutated
|| isParameterReturned
|| isParameterUsedAsLock;
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
InternalNonEmptyParameterUsage addFieldReadFromParameter(DexField field) {
ImmutableSet.Builder<DexField> newFieldsReadFromParameterBuilder = ImmutableSet.builder();
newFieldsReadFromParameterBuilder.addAll(fieldsReadFromParameter);
newFieldsReadFromParameterBuilder.add(field);
return new InternalNonEmptyParameterUsage(
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(
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(
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()
.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(
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
true,
isParameterReturned,
isParameterUsedAsLock);
}
@Override
InternalNonEmptyParameterUsage setParameterReturned() {
if (isParameterReturned) {
return this;
}
return new InternalNonEmptyParameterUsage(
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
isParameterMutated,
true,
isParameterUsedAsLock);
}
@Override
InternalNonEmptyParameterUsage setParameterUsedAsLock() {
if (isParameterUsedAsLock) {
return this;
}
return new InternalNonEmptyParameterUsage(
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
isParameterMutated,
isParameterReturned,
true);
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
InternalNonEmptyParameterUsage knownParameterUsage = (InternalNonEmptyParameterUsage) obj;
return isParameterMutated == knownParameterUsage.isParameterMutated
&& isParameterReturned == knownParameterUsage.isParameterReturned
&& isParameterUsedAsLock == knownParameterUsage.isParameterUsedAsLock
&& fieldsReadFromParameter.equals(knownParameterUsage.fieldsReadFromParameter)
&& methodCallsWithParameterAsReceiver.equals(
knownParameterUsage.methodCallsWithParameterAsReceiver);
}
@Override
public int hashCode() {
int hash =
31 * (31 + fieldsReadFromParameter.hashCode())
+ methodCallsWithParameterAsReceiver.hashCode();
assert hash == Objects.hash(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<DexField> fieldsReadFromParameterBuilder;
private ImmutableSet.Builder<InvokeMethodWithReceiver>
methodCallsWithParameterAsReceiverBuilder;
private boolean isParameterMutated;
private boolean isParameterReturned;
private boolean isParameterUsedAsLock;
Builder() {
fieldsReadFromParameterBuilder = ImmutableSet.builder();
methodCallsWithParameterAsReceiverBuilder = ImmutableSet.builder();
}
Builder(InternalNonEmptyParameterUsage methodBehavior) {
fieldsReadFromParameterBuilder =
ImmutableSet.<DexField>builder().addAll(methodBehavior.fieldsReadFromParameter);
methodCallsWithParameterAsReceiverBuilder =
ImmutableSet.<InvokeMethodWithReceiver>builder()
.addAll(methodBehavior.methodCallsWithParameterAsReceiver);
isParameterMutated = methodBehavior.isParameterMutated;
isParameterReturned = methodBehavior.isParameterReturned;
isParameterUsedAsLock = methodBehavior.isParameterUsedAsLock;
}
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(
fieldsReadFromParameterBuilder.build(),
methodCallsWithParameterAsReceiverBuilder.build(),
isParameterMutated,
isParameterReturned,
isParameterUsedAsLock);
}
}
}