blob: ca868c591a7211b70953de9447f4534522396dde [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.langmodel;
import static com.google.common.base.Preconditions.checkState;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import org.objectweb.asm.Type;
/** The key to index a class or interface method or constructor. */
@AutoValue
public abstract class MethodKey extends ClassMemberKey<MethodKey> {
/** The factory method for {@link MethodKey}. */
public static MethodKey create(ClassName ownerClass, String name, String descriptor) {
checkState(
descriptor.isEmpty() // Allows empty descriptor for non-overloaded methods.
|| descriptor.startsWith("("),
"Expected a method descriptor. Actual: (%s#%s:%s)",
ownerClass,
name,
descriptor);
return new AutoValue_MethodKey(ownerClass, name, descriptor);
}
/** The return type of a method. */
public Type getReturnType() {
return Type.getReturnType(descriptor());
}
/** The return type of a method. */
public ClassName getReturnTypeName() {
return ClassName.create(Type.getReturnType(descriptor()));
}
/** The formal parameter types of a method. */
public Type[] getArgumentTypeArray() {
return Type.getArgumentTypes(descriptor());
}
/** The formal parameter types of a method. */
public ImmutableList<Type> getArgumentTypes() {
return ImmutableList.copyOf(getArgumentTypeArray());
}
/** The synthetic constructor for a private constructor. */
public final MethodKey bridgeOfConstructor(ClassName nestCompanion) {
checkState(isConstructor(), "Expect to use for a constructor but is %s", this);
Type companionClassType = nestCompanion.toAsmObjectType();
ImmutableList<Type> argumentTypes = getArgumentTypes();
ImmutableList<Type> bridgeConstructorArgTypes =
ImmutableList.<Type>builder().addAll(argumentTypes).add(companionClassType).build();
return create(
owner(),
name(),
Type.getMethodDescriptor(getReturnType(), bridgeConstructorArgTypes.toArray(new Type[0])));
}
/** The synthetic bridge method for a private static method in a class. */
public final MethodKey bridgeOfClassStaticMethod() {
checkState(!isConstructor(), "Expect a non-constructor method but is a constructor %s", this);
return create(owner(), nameWithSuffix("bridge"), descriptor());
}
/** The synthetic bridge method for a private instance method in a class. */
public final MethodKey bridgeOfClassInstanceMethod() {
return create(owner(), nameWithSuffix("bridge"), instanceMethodToStaticDescriptor(this));
}
/** The substitute method for a private static method in an interface. */
public final MethodKey substituteOfInterfaceStaticMethod() {
checkState(!isConstructor(), "Expect a non-constructor: %s", this);
return create(owner(), name(), descriptor());
}
/** The substitute method for a private instance method in an interface. */
public final MethodKey substituteOfInterfaceInstanceMethod() {
return create(owner(), name(), 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);
ImmutableList<Type> argumentTypes = methodKey.getArgumentTypes();
ImmutableList<Type> bridgeMethodArgTypes =
ImmutableList.<Type>builder()
.add(methodKey.ownerAsmObjectType())
.addAll(argumentTypes)
.build();
return Type.getMethodDescriptor(
methodKey.getReturnType(), bridgeMethodArgTypes.toArray(new Type[0]));
}
@Override
public MethodKey acceptTypeMapper(TypeMapper typeMapper) {
return MethodKey.create(typeMapper.map(owner()), name(), typeMapper.mapDesc(descriptor()));
}
/**
* 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));
}
}
}
}