blob: e5def1da4e86cd3509d547c18c0f563c3f8aace1 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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.android.dx.rop.code;
import com.android.dx.rop.cst.CstMethodRef;
import com.android.dx.rop.cst.CstNat;
import com.android.dx.rop.cst.CstProtoRef;
import com.android.dx.rop.cst.CstString;
import com.android.dx.rop.cst.CstType;
import com.android.dx.rop.type.Type;
import com.android.dx.rop.type.TypeList;
/**
* An invoke-polymorphic instruction. This is a throwing instruction with
* multiple constants.
*/
public class InvokePolymorphicInsn extends Insn {
private static final CstString INVOKE_DESCRIPTOR =
new CstString("([Ljava/lang/Object;)Ljava/lang/Object;");
/** {@code non-null;} list of exceptions caught */
private final TypeList catches;
/**
* {@code non-null;} method as it appears at the call site of the original
* invoke-virtual instruction. This is used to construct the invoke method
* to target and the call-site prototype.
*/
private final CstMethodRef callSiteMethod;
/**
* {@code non-null;} method to invoke, either {@code java.lang.invoke.MethodHandle.invoke} or
* {@code java.lang.invoke.MethodHandle.invokeExact}.
*/
private final CstMethodRef invokeMethod;
/**
* {@code non-null;} the call site prototype.
*/
private final CstProtoRef callSiteProto;
/**
* Constructs an instance.
*
* @param opcode {@code non-null;} the opcode
* @param position {@code non-null;} source position
* @param sources {@code non-null;} specs for all the sources
* @param catches {@code non-null;} list of exceptions caught
* @param callSiteMethod {@code non-null;} the method called by
* invoke-virtual that this instance will replace.
*/
public InvokePolymorphicInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, TypeList catches,
CstMethodRef callSiteMethod) {
super(opcode, position, null, sources);
if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
throw new IllegalArgumentException("opcode with invalid branchingness: " + opcode.getBranchingness());
}
if (catches == null) {
throw new NullPointerException("catches == null");
}
this.catches = catches;
if (callSiteMethod == null) {
throw new NullPointerException("callSiteMethod == null");
} else if (!callSiteMethod.isSignaturePolymorphic()) {
throw new IllegalArgumentException("callSiteMethod is not signature polymorphic");
}
this.callSiteMethod = callSiteMethod;
this.invokeMethod = makeInvokeMethod(callSiteMethod);
this.callSiteProto = makeCallSiteProto(callSiteMethod);
}
/** {@inheritDoc} */
@Override
public TypeList getCatches() {
return this.catches;
}
/** {@inheritDoc} */
@Override
public void accept(Visitor visitor) {
visitor.visitInvokePolymorphicInsn(this);
}
/** {@inheritDoc} */
@Override
public Insn withAddedCatch(Type type) {
return new InvokePolymorphicInsn(getOpcode(), getPosition(),
getSources(), catches.withAddedType(type), getCallSiteMethod());
}
/** {@inheritDoc} */
@Override
public Insn withRegisterOffset(int delta) {
return new InvokePolymorphicInsn(getOpcode(), getPosition(),
getSources().withOffset(delta),
catches, getCallSiteMethod());
}
/** {@inheritDoc} */
@Override
public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) {
return new InvokePolymorphicInsn(getOpcode(), getPosition(),
sources, catches, getCallSiteMethod());
}
/**
* Gets the method as it appears at the call site of the original
* invoke-virtual instruction.
*
* @return {@code non-null;} the original method reference
*/
public CstMethodRef getCallSiteMethod() {
return callSiteMethod;
}
/**
* Gets the method to be invoked. This will be will either be
* {@code java.lang.invoke.MethodHandle.invoke()} or
* {@code java.lang.invoke.MethodHandle.invokeExact()}.
*
* @return {@code non-null;} method reference to be invoked
*/
public CstMethodRef getInvokeMethod() {
return invokeMethod;
}
/**
* Gets the call site prototype. The call site prototype is provided
* as an argument to invoke-polymorphic to enable type checking and
* type conversion.
*
* @return {@code non-null;} Prototype reference for call site
*/
public CstProtoRef getCallSiteProto() {
return callSiteProto;
}
/** {@inheritDoc} */
@Override
public String getInlineString() {
return getInvokeMethod().toString() + " " +
getCallSiteProto().toString() + " " +
ThrowingInsn.toCatchString(catches);
}
private static CstMethodRef makeInvokeMethod(final CstMethodRef callSiteMethod) {
// The name is either invoke or invokeExact. The INVOKE_DESCRIPTOR is fixed.
CstNat cstNat = new CstNat(callSiteMethod.getNat().getName(), INVOKE_DESCRIPTOR);
return new CstMethodRef(callSiteMethod.getDefiningClass(), cstNat);
}
private static CstProtoRef makeCallSiteProto(final CstMethodRef callSiteMethod) {
return new CstProtoRef(callSiteMethod.getPrototype(true));
}
}