blob: 499f9946e42edd7a46f0ab0ff6ee97382e0b72bc [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.devtools.build.android.desugar.langmodel.LangModelHelper.isCrossMateRefInNest;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.android.desugar.langmodel.ClassName;
import com.google.devtools.build.android.desugar.langmodel.FieldInstrVisitor;
import com.google.devtools.build.android.desugar.langmodel.FieldKey;
import com.google.devtools.build.android.desugar.langmodel.LangModelHelper;
import com.google.devtools.build.android.desugar.langmodel.MemberUseKind;
import com.google.devtools.build.android.desugar.langmodel.MethodInstrVisitor;
import com.google.devtools.build.android.desugar.langmodel.MethodKey;
import javax.annotation.Nullable;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/** A visitor that convert direct access to field/method with access through bridges. */
public final class NestBridgeRefConverter extends MethodVisitor {
private final MethodKey enclosingMethodKey;
private final NestDigest nestDigest;
private final FieldAccessToBridgeRedirector directFieldAccessReplacer;
private final MethodToBridgeRedirector methodToBridgeRedirector;
NestBridgeRefConverter(
@Nullable MethodVisitor methodVisitor, MethodKey methodKey, NestDigest nestDigest) {
super(Opcodes.ASM7, methodVisitor);
this.enclosingMethodKey = methodKey;
this.nestDigest = nestDigest;
directFieldAccessReplacer = new FieldAccessToBridgeRedirector();
methodToBridgeRedirector = new MethodToBridgeRedirector(nestDigest);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
FieldKey fieldKey = FieldKey.create(ClassName.create(owner), name, descriptor);
MemberUseKind useKind = MemberUseKind.fromValue(opcode);
if (isCrossMateRefInNest(fieldKey, enclosingMethodKey)
&& nestDigest.hasAnyUse(fieldKey, useKind)) {
fieldKey.accept(useKind, directFieldAccessReplacer, mv);
return;
}
super.visitFieldInsn(opcode, owner, name, descriptor);
}
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String descriptor, boolean isInterface) {
MethodKey methodKey = MethodKey.create(ClassName.create(owner), name, descriptor);
MemberUseKind useKind = MemberUseKind.fromValue(opcode);
if ((isInterface || isCrossMateRefInNest(methodKey, enclosingMethodKey))
&& nestDigest.hasAnyUse(methodKey, useKind)) {
methodKey.accept(useKind, isInterface, methodToBridgeRedirector, mv);
return;
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
/** A visitor that re-directs the invocation of a method to that of the method's bridge. */
static class MethodToBridgeRedirector
implements MethodInstrVisitor<MethodKey, MethodKey, MethodVisitor> {
private final NestDigest nestDigest;
MethodToBridgeRedirector(NestDigest nestDigest) {
this.nestDigest = nestDigest;
}
@Override
public MethodKey visitInvokeVirtual(MethodKey methodKey, MethodVisitor mv) {
MethodKey bridgeMethodKey = methodKey.bridgeOfClassInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
return methodKey;
}
@Override
public MethodKey visitInvokeSpecial(MethodKey methodKey, MethodVisitor mv) {
MethodKey bridgeMethodKey = methodKey.bridgeOfClassInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
return methodKey;
}
@Override
public MethodKey visitConstructorInvokeSpecial(MethodKey methodKey, MethodVisitor mv) {
ClassName nestCompanion = nestDigest.nestCompanion(ClassName.create(methodKey.ownerName()));
MethodKey constructorBridge = methodKey.bridgeOfConstructor(nestCompanion);
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitMethodInsn(
Opcodes.INVOKESPECIAL,
constructorBridge.ownerName(),
constructorBridge.name(),
constructorBridge.descriptor(),
/* isInterface= */ false);
return methodKey;
}
@Override
public MethodKey visitInterfaceInvokeSpecial(MethodKey methodKey, MethodVisitor mv) {
MethodKey methodBridge = methodKey.substituteOfInterfaceInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
methodBridge.ownerName(),
methodBridge.name(),
methodBridge.descriptor(),
/* isInterface= */ true);
return methodKey;
}
@Override
public MethodKey visitInvokeStatic(MethodKey methodKey, MethodVisitor mv) {
MethodKey bridgeMethodKey = methodKey.bridgeOfClassStaticMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
return methodKey;
}
@Override
public MethodKey visitInterfaceInvokeStatic(MethodKey methodKey, MethodVisitor mv) {
final MethodKey methodBridge = methodKey.substituteOfInterfaceStaticMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
methodBridge.ownerName(),
methodBridge.name(),
methodBridge.descriptor(),
/* isInterface= */ true);
return methodKey;
}
@Override
public MethodKey visitInvokeInterface(MethodKey methodKey, MethodVisitor mv) {
final MethodKey methodBridge = methodKey.substituteOfInterfaceInstanceMethod();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
methodBridge.ownerName(),
methodBridge.name(),
methodBridge.descriptor(),
/* isInterface= */ true);
return methodKey;
}
@Override
public MethodKey visitInvokeDynamic(MethodKey methodKey, MethodVisitor mv) {
throw new UnsupportedOperationException();
}
}
/** A visitor that re-directs field access with bridge method invocation. */
static class FieldAccessToBridgeRedirector
implements FieldInstrVisitor<MethodKey, FieldKey, MethodVisitor> {
@Override
public MethodKey visitGetStatic(FieldKey fieldKey, MethodVisitor mv) {
MethodKey bridgeMethodKey = fieldKey.bridgeOfStaticRead();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
false);
return bridgeMethodKey;
}
@Override
public MethodKey visitPutStatic(FieldKey fieldKey, MethodVisitor mv) {
MethodKey bridgeMethodKey = fieldKey.bridgeOfStaticWrite();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
false);
// The bridge method for an instance field writer pushes the new field value to its invoker
// operand stack, we emit a pop instruction to be consistent with putfield instruction which
// consumes the updated field value on the operand stack.
mv.visitInsn(
LangModelHelper.getTypeSizeAlignedPopOpcode(ImmutableList.of(fieldKey.getFieldType())));
return bridgeMethodKey;
}
@Override
public MethodKey visitGetField(FieldKey fieldKey, MethodVisitor mv) {
MethodKey bridgeMethodKey = fieldKey.bridgeOfInstanceRead();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
false);
return bridgeMethodKey;
}
@Override
public MethodKey visitPutField(FieldKey fieldKey, MethodVisitor mv) {
MethodKey bridgeMethodKey = fieldKey.bridgeOfInstanceWrite();
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
bridgeMethodKey.ownerName(),
bridgeMethodKey.name(),
bridgeMethodKey.descriptor(),
/* isInterface= */ false);
// The bridge method for an instance field writer pushes the new field value to its invoker
// operand stack, we emit a pop instruction to be consistent with putfield instruction which
// consumes the updated field value on the operand stack.
mv.visitInsn(
LangModelHelper.getTypeSizeAlignedPopOpcode(ImmutableList.of(fieldKey.getFieldType())));
return bridgeMethodKey;
}
}
}