blob: ed4f706ea68522db22d7c34a1ff74042340a29f2 [file] [log] [blame]
// Copyright 2019 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.android.desugar.nest;
import static com.google.common.base.Preconditions.checkState;
import com.google.auto.value.AutoValue;
import com.google.devtools.build.android.desugar.nest.ClassMemberTrackReason.MemberUseKind;
import java.util.Arrays;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/** The key that indexes a class member, including fields, constructors and methods. */
public abstract class ClassMemberKey {
/**
* The class or interface that owns the class member, i.e. the immediate enclosing class of the
* declaration site of a field, constructor or method.
*/
public abstract String owner();
/** The simple name of the class member. */
public abstract String name();
/** The descriptor of the class member. */
public abstract String descriptor();
/** Whether member key represents a constructor. */
public final boolean isConstructor() {
return "<init>".equals(name());
}
/** The binary name of the nest host that the member owner is affiliated with. */
public final String nestHost() {
return NestDesugarHelper.nestHost(owner());
}
/**
* The binary name of the nest companion that the member owner is affiliated with. One nest has at
* most one associated nest companion class.
*/
final String nestCompanion() {
return NestDesugarHelper.nestCompanion(owner());
}
/**
* The binary simple name for a class member mangled with its owner name to avoid member name
* duplication.
*/
final String ownerMangledName() {
return owner().replace('/', '_') + '$' + name();
}
/** The simple name with name suffix. */
final String nameWithSuffix(String suffix) {
return name() + '$' + suffix;
}
/** The key to index a class or interface field. */
@AutoValue
public abstract static class FieldKey extends ClassMemberKey {
/** The factory method for {@link FieldKey}. */
public static FieldKey create(String ownerClass, String name, String descriptor) {
return new AutoValue_ClassMemberKey_FieldKey(ownerClass, name, descriptor);
}
/**
* Accepts {@link FieldInstrVisitor} to perform distinct operations based on different
* invocation codes.
*/
public final <R, P> R accept(
MemberUseKind fieldInstrOpcode,
FieldInstrVisitor<R, ? super FieldKey, P> visitor,
P param) {
switch (fieldInstrOpcode) {
case GETSTATIC:
return visitor.visitGetStatic(this, param);
case PUTSTATIC:
return visitor.visitPutStatic(this, param);
case GETFIELD:
return visitor.visitGetField(this, param);
case PUTFIELD:
return visitor.visitPutField(this, param);
default:
throw new AssertionError(
String.format(
"Unexpected opcode(%s): Expect one of {GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD}"
+ " for field instructions.",
fieldInstrOpcode));
}
}
/**
* Returns the bridge method for reading a static field, identified by (getstatic) instruction.
*/
public final MethodKey bridgeOfStaticRead() {
return MethodKey.create(
owner(), nameWithSuffix("bridge_getter"), Type.getMethodDescriptor(getFieldType()));
}
/**
* Returns the bridge method for reading an instance field, identified by (getfield)
* instruction.
*/
public final MethodKey bridgeOfInstanceRead() {
return MethodKey.create(
owner(),
nameWithSuffix("bridge_getter"),
Type.getMethodDescriptor(getFieldType(), Type.getObjectType(owner())));
}
/**
* Returns the bridge method for writing a static field, identified by (putstatic) instruction.
*/
public final MethodKey bridgeOfStaticWrite() {
return MethodKey.create(
owner(),
nameWithSuffix("bridge_setter"),
Type.getMethodDescriptor(getFieldType(), getFieldType()));
}
/**
* Returns the bridge method for writing an instance field, identified by (putfield)
* instruction.
*/
public final MethodKey bridgeOfInstanceWrite() {
return MethodKey.create(
owner(),
nameWithSuffix("bridge_setter"),
Type.getMethodDescriptor(getFieldType(), Type.getObjectType(owner()), getFieldType()));
}
public Type getFieldType() {
return Type.getType(descriptor());
}
}
/** The key to index a class or interface method or constructor. */
@AutoValue
public abstract static class MethodKey extends ClassMemberKey {
/** The factory method for {@link MethodKey}. */
public static MethodKey create(String ownerClass, String name, String descriptor) {
return new AutoValue_ClassMemberKey_MethodKey(ownerClass, name, descriptor);
}
/** The return type of a method. */
public Type getReturnType() {
return Type.getReturnType(descriptor());
}
/** The formal parameter types of a method. */
public Type[] getArgumentTypes() {
return Type.getArgumentTypes(descriptor());
}
/** The synthetic constructor for a private constructor. */
public final MethodKey bridgeOfConstructor() {
checkState(isConstructor(), "Expect to use for a constructor but is %s", this);
Type companionClassType = Type.getObjectType(nestCompanion());
Type[] argumentTypes = getArgumentTypes();
Type[] bridgeConstructorArgTypes = Arrays.copyOf(argumentTypes, argumentTypes.length + 1);
bridgeConstructorArgTypes[argumentTypes.length] = companionClassType;
return MethodKey.create(
owner(), name(), Type.getMethodDescriptor(getReturnType(), bridgeConstructorArgTypes));
}
/** The synthetic bridge method for a private static method in a class. */
final MethodKey bridgeOfClassStaticMethod() {
checkState(!isConstructor(), "Expect a non-constructor method but is a constructor %s", this);
return MethodKey.create(owner(), nameWithSuffix("bridge"), descriptor());
}
/** The synthetic bridge method for a private instance method in a class. */
final MethodKey bridgeOfClassInstanceMethod() {
return MethodKey.create(
owner(), nameWithSuffix("bridge"), instanceMethodToStaticDescriptor(this));
}
/** The substitute method for a private static method in an interface. */
final MethodKey substituteOfInterfaceStaticMethod() {
checkState(!isConstructor(), "Expect a non-constructor: %s", this);
return MethodKey.create(owner(), ownerMangledName(), descriptor());
}
/** The substitute method for a private instance method in an interface. */
final MethodKey substituteOfInterfaceInstanceMethod() {
return MethodKey.create(owner(), ownerMangledName(), instanceMethodToStaticDescriptor(this));
}
/** The descriptor of the static version of a given instance method. */
private static String instanceMethodToStaticDescriptor(MethodKey methodKey) {
checkState(!methodKey.isConstructor(), "Expect a Non-constructor method: %s", methodKey);
Type[] argumentTypes = methodKey.getArgumentTypes();
Type[] bridgeMethodArgTypes = new Type[argumentTypes.length + 1];
bridgeMethodArgTypes[0] = Type.getObjectType(methodKey.owner());
System.arraycopy(argumentTypes, 0, bridgeMethodArgTypes, 1, argumentTypes.length);
return Type.getMethodDescriptor(methodKey.getReturnType(), bridgeMethodArgTypes);
}
/**
* Accepts a {@link MethodInstrVisitor} that visits all kinds of method invocation instructions.
*/
public final <R, P> R accept(
MemberUseKind methodInstrOpcode,
boolean isInterface,
MethodInstrVisitor<R, ? super MethodKey, P> visitor,
P param) {
if (isConstructor()) {
checkState(methodInstrOpcode == MemberUseKind.INVOKESPECIAL);
return visitor.visitConstructorInvokeSpecial(this, param);
} else if (isInterface) {
switch (methodInstrOpcode) {
case INVOKESPECIAL:
// Super call to an non-abstract instance interface method.
return visitor.visitInterfaceInvokeSpecial(this, param);
case INVOKESTATIC:
return visitor.visitInterfaceInvokeStatic(this, param);
case INVOKEINTERFACE:
return visitor.visitInvokeInterface(this, param);
default:
throw new AssertionError(
String.format(
"Unexpected Opcode: (%s) invoked on Interface Method(%s).\n"
+ "Opcode Reference: {INVOKEVIRTUAL(182), INVOKESPECIAL(183),"
+ " INVOKESTATIC(184), INVOKEINTERFACE(185), INVOKEDYNAMIC(186)}",
methodInstrOpcode, this));
}
} else {
switch (methodInstrOpcode) {
case INVOKEVIRTUAL:
return visitor.visitInvokeVirtual(this, param);
case INVOKESPECIAL:
return visitor.visitInvokeSpecial(this, param);
case INVOKESTATIC:
return visitor.visitInvokeStatic(this, param);
case INVOKEDYNAMIC:
return visitor.visitInvokeDynamic(this, param);
default:
throw new AssertionError(
String.format(
"Unexpected Opcode: (%s) invoked on class Method(%s).\n"
+ "Opcode Reference: {INVOKEVIRTUAL(182), INVOKESPECIAL(183),"
+ " INVOKESTATIC(184), INVOKEINTERFACE(185), INVOKEDYNAMIC(186)}",
methodInstrOpcode, this));
}
}
}
}
/** A unit data object represents a class or interface declaration. */
// TODO(deltazulu): Consider @AutoValue-ize this class. (String[] as attribute is not supported).
static final class MethodDeclInfo {
final MethodKey methodKey;
final int ownerAccess;
final int memberAccess;
final String signature;
final String[] exceptions;
MethodDeclInfo(
MethodKey methodKey,
int ownerAccess,
int memberAccess,
String signature,
String[] exceptions) {
this.methodKey = methodKey;
this.ownerAccess = ownerAccess;
this.memberAccess = memberAccess;
this.signature = signature;
this.exceptions = exceptions;
}
public final <R, P> R accept(MethodDeclVisitor<R, ? super MethodDeclInfo, P> visitor, P param) {
if (methodKey.isConstructor()) {
return visitor.visitClassConstructor(this, param);
}
boolean isInterface = (ownerAccess & Opcodes.ACC_INTERFACE) != 0;
boolean isStatic = (memberAccess & Opcodes.ACC_STATIC) != 0;
if (isInterface) {
return isStatic
? visitor.visitInterfaceStaticMethod(this, param)
: visitor.visitInterfaceInstanceMethod(this, param);
} else {
return isStatic
? visitor.visitClassStaticMethod(this, param)
: visitor.visitClassInstanceMethod(this, param);
}
}
}
/** A visitor for all method invocation instructions in combination with method types. */
public interface MethodInstrVisitor<R, K extends MethodKey, P> {
R visitInvokeVirtual(K methodKey, P param);
R visitInvokeSpecial(K methodKey, P param);
R visitConstructorInvokeSpecial(K methodKey, P param);
R visitInterfaceInvokeSpecial(K methodKey, P param);
R visitInvokeStatic(K methodKey, P param);
R visitInterfaceInvokeStatic(K methodKey, P param);
R visitInvokeInterface(K methodKey, P param);
R visitInvokeDynamic(K methodKey, P param);
}
/** Visits all field access instructions. */
public interface FieldInstrVisitor<R, K extends FieldKey, P> {
R visitGetStatic(K fieldKey, P param);
R visitPutStatic(K fieldKey, P param);
R visitGetField(K fieldKey, P param);
R visitPutField(K fieldKey, P param);
}
/** A visitor that directs different operations based on the method types. */
public interface MethodDeclVisitor<R, K extends MethodDeclInfo, P> {
R visitClassConstructor(K methodDeclInfo, P param);
R visitClassStaticMethod(K methodDeclInfo, P param);
R visitClassInstanceMethod(K methodDeclInfo, P param);
R visitInterfaceStaticMethod(K methodDeclInfo, P param);
R visitInterfaceInstanceMethod(K methodDeclInfo, P param);
}
}