blob: 1515932f0dacca7f5930acd6cb6f8e9763603f12 [file] [log] [blame]
// Copyright (c) 2019, 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.analysis.proto.schema;
import static com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo.BITS_PER_HAS_BITS_WORD;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.ProgramField;
import java.util.List;
import java.util.OptionalInt;
public class ProtoFieldInfo {
private final int number;
private final ProtoFieldType type;
/**
* Index into {@link ProtoMessageInfo#oneOfObjects} or {@link ProtoMessageInfo#hasBitsObjects}.
* Only used for oneof and proto2 singular fields.
*/
private OptionalInt auxData;
/**
* For any non-oneof field, the first entry will be a reference to a java.lang.String literal. For
* repeated message fields, the second entry will be a reference to java.lang.Class for the
* message.
*/
private final List<ProtoObject> objects;
public ProtoFieldInfo(
int number, ProtoFieldType type, OptionalInt auxData, List<ProtoObject> objects) {
this.number = number;
this.type = type;
this.auxData = auxData;
this.objects = objects;
}
public boolean hasAuxData() {
return auxData.isPresent();
}
public int getAuxData() {
assert hasAuxData();
return auxData.getAsInt();
}
void setAuxData(int value) {
assert hasAuxData();
auxData = OptionalInt.of(value);
}
public int getNumber() {
return number;
}
public List<ProtoObject> getObjects() {
return objects;
}
public ProtoFieldType getType() {
return type;
}
/**
* For singular/repeated message-type fields, the type of the message.
*
* <p>This isn't populated for map-type fields because that's a bit difficult, but this doesn't
* matter in practice. We only use this for determining whether to retain a field based on whether
* it reaches a map/required field, but there's no need to go through that when we're already at
* one.
*/
public DexType getBaseMessageType(ProtoFieldTypeFactory factory) {
if (type.isOneOf()) {
ProtoFieldType actualFieldType = type.asOneOf().getActualFieldType(factory);
if (actualFieldType.isGroup() || actualFieldType.isMessage()) {
ProtoObject object = objects.get(0);
assert object.isProtoTypeObject();
return object.asProtoTypeObject().getType();
}
return null;
}
if (type.isMessage() || type.isGroup()) {
ProtoObject object = objects.get(0);
assert object.isLiveProtoFieldObject();
return object.asLiveProtoFieldObject().getField().type;
}
if (type.isMessageList() || type.isGroupList()) {
ProtoObject object = objects.get(1);
assert object.isProtoTypeObject();
return object.asProtoTypeObject().getType();
}
return null;
}
/**
* (Proto2 singular fields only.)
*
* <p>Java field for denoting the presence of a protobuf field.
*
* <p>The generated Java code for:
*
* <pre>
* message MyMessage {
* optional int32 foo = 123;
* optional int32 bar = 456;
* }
* </pre>
*
* looks like:
*
* <pre>
* boolean hasFoo() { return bitField0_ & 0x1; }
* boolean hasBar() { return bitField0_ & 0x2; }
* </pre>
*/
public boolean hasHazzerBitField(ProtoMessageInfo protoMessageInfo) {
return type.hasAuxData(protoMessageInfo.isProto2());
}
public ProgramField getHazzerBitField(
AppView<? extends AppInfoWithClassHierarchy> appView, ProtoMessageInfo protoMessageInfo) {
assert hasHazzerBitField(protoMessageInfo);
int hasBitsIndex = getAuxData() / BITS_PER_HAS_BITS_WORD;
assert hasBitsIndex < protoMessageInfo.numberOfHasBitsObjects();
ProtoObject object = protoMessageInfo.getHasBitsObjects().get(hasBitsIndex);
assert object.isLiveProtoFieldObject();
FieldResolutionResult resolutionResult =
appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
if (resolutionResult.isSuccessfulResolution()) {
return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
}
return null;
}
public int getHazzerBitFieldIndex(ProtoMessageInfo protoMessageInfo) {
assert hasHazzerBitField(protoMessageInfo);
return (getAuxData() % BITS_PER_HAS_BITS_WORD) + 1;
}
/**
* (One-of fields only.)
*
* <p>Java field identifying what the containing oneof is currently being used for.
*
* <p>The generated Java code for:
*
* <pre>
* message MyMessage {
* oneof my_oneof {
* int32 x = 123;
* ...
* }
* }
* </pre>
*
* looks like:
*
* <pre>
* ... getMyOneofCase() { return myOneofCase_; }
* int getX() {
* if (myOneofCase_ == 123) return (Integer) myOneof_;
* return 0;
* }
* </pre>
*/
public ProgramField getOneOfCaseField(
AppView<? extends AppInfoWithClassHierarchy> appView, ProtoMessageInfo protoMessageInfo) {
assert type.isOneOf();
ProtoObject object = protoMessageInfo.getOneOfObjects().get(getAuxData()).getOneOfCaseObject();
assert object.isLiveProtoFieldObject();
FieldResolutionResult resolutionResult =
appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
if (resolutionResult.isSuccessfulResolution()) {
return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
}
return null;
}
/**
* Data about the field as referenced from the Java implementation.
*
* <p>Java field into which the value is stored; constituents of a oneof all share the same
* storage.
*/
public ProgramField getValueStorage(
AppView<? extends AppInfoWithClassHierarchy> appView, ProtoMessageInfo protoMessageInfo) {
ProtoObject object =
type.isOneOf()
? protoMessageInfo.getOneOfObjects().get(getAuxData()).getOneOfObject()
: objects.get(0);
assert object.isLiveProtoFieldObject();
FieldResolutionResult resolutionResult =
appView.appInfo().resolveField(object.asLiveProtoFieldObject().getField());
if (resolutionResult.isSuccessfulResolution()) {
return resolutionResult.asSuccessfulResolution().getResolutionPair().asProgramField();
}
return null;
}
@Override
public String toString() {
StringBuilder builder =
new StringBuilder("ProtoFieldInfo(number=")
.append(number)
.append(", type=")
.append(type)
.append(", aux data=")
.append(auxData)
.append(", objects=[");
if (objects.size() > 0) {
builder.append(objects.get(0));
for (int i = 1; i < objects.size(); i++) {
builder.append(", ").append(objects.get(i));
}
}
return builder.append("])").toString();
}
}