blob: c2264f238da4a4d5b18a475b469c163c615c0bb4 [file] [log] [blame]
// Copyright (c) 2017, 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.protolite;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
/**
* Contains common definitions used by the {@link ProtoLiteExtension} for tree shaking and the
* corresponding {@link ProtoLitePruner} code rewriting.
*/
abstract class ProtoLiteBase {
static final int GETTER_NAME_PREFIX_LENGTH = 3;
static final int COUNT_POSTFIX_LENGTH = 5;
final AppInfoWithSubtyping appInfo;
final DexType messageType;
final DexString dynamicMethodName;
final DexString writeToMethodName;
final DexString getSerializedSizeMethodName;
final DexString constructorMethodName;
final DexString setterNamePrefix;
final DexString getterNamePrefix;
final DexString bitFieldPrefix;
final DexString underscore;
ProtoLiteBase(AppInfoWithSubtyping appInfo) {
this.appInfo = appInfo;
DexItemFactory factory = appInfo.dexItemFactory;
this.messageType = factory.createType("Lcom/google/protobuf/GeneratedMessageLite;");
this.dynamicMethodName = factory.createString("dynamicMethod");
this.writeToMethodName = factory.createString("writeTo");
this.getSerializedSizeMethodName = factory.createString("getSerializedSize");
this.constructorMethodName = factory.constructorMethodName;
this.setterNamePrefix = factory.createString("set");
this.getterNamePrefix = factory.createString("get");
this.bitFieldPrefix = factory.createString("bitField");
this.underscore = factory.createString("_");
assert getterNamePrefix.size == GETTER_NAME_PREFIX_LENGTH;
}
/**
* Returns true of the given method is a setter on a message class that does need processing
* by this phase of proto lite shaking.
* <p>
* False positives are ok.
*/
abstract boolean isSetterThatNeedsProcessing(DexEncodedMethod method);
DexField getterToField(DexMethod getter) {
return getterToField(getter, 0);
}
DexField getterToField(DexMethod getter, int postfixLength) {
String getterName = getter.name.toString();
assert getterName.length() > GETTER_NAME_PREFIX_LENGTH + postfixLength;
String fieldName = Character.toLowerCase(getterName.charAt(GETTER_NAME_PREFIX_LENGTH))
+ getterName.substring(GETTER_NAME_PREFIX_LENGTH + 1, getterName.length() - postfixLength)
+ "_";
DexItemFactory factory = appInfo.dexItemFactory;
return factory
.createField(getter.holder, getter.proto.returnType, factory.createString(fieldName));
}
boolean hasSingleIntArgument(DexMethod method) {
return method.getArity() == 1
&& method.proto.parameters.values[0] == appInfo.dexItemFactory.intType;
}
public boolean appliesTo(DexEncodedMethod method) {
if (!method.method.holder.isSubtypeOf(messageType, appInfo)) {
return false;
}
DexClass clazz = appInfo.definitionFor(method.method.holder);
// We only care for the actual leaf classes that implement a specific proto.
if (!clazz.accessFlags.isFinal()) {
return false;
}
DexString methodName = method.method.name;
// We could be even more precise here and check for the signature. However, there is only
// one of each method generated.
return methodName == dynamicMethodName
|| methodName == writeToMethodName
|| methodName == getSerializedSizeMethodName
|| methodName == constructorMethodName
|| isSetterThatNeedsProcessing(method);
}
}