1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.dx.rop.code; 17 18 import com.android.dx.rop.cst.CstMethodRef; 19 import com.android.dx.rop.cst.CstNat; 20 import com.android.dx.rop.cst.CstProtoRef; 21 import com.android.dx.rop.cst.CstString; 22 import com.android.dx.rop.cst.CstType; 23 import com.android.dx.rop.type.Type; 24 import com.android.dx.rop.type.TypeList; 25 26 /** 27 * An invoke-polymorphic instruction. This is a throwing instruction with 28 * multiple constants. 29 */ 30 public class InvokePolymorphicInsn extends Insn { 31 private static final CstString INVOKE_DESCRIPTOR = 32 new CstString("([Ljava/lang/Object;)Ljava/lang/Object;"); 33 34 /** {@code non-null;} list of exceptions caught */ 35 private final TypeList catches; 36 37 /** 38 * {@code non-null;} method as it appears at the call site of the original 39 * invoke-virtual instruction. This is used to construct the invoke method 40 * to target and the call-site prototype. 41 */ 42 private final CstMethodRef callSiteMethod; 43 44 /** 45 * {@code non-null;} method to invoke, either {@code java.lang.invoke.MethodHandle.invoke} or 46 * {@code java.lang.invoke.MethodHandle.invokeExact}. 47 */ 48 private final CstMethodRef invokeMethod; 49 50 /** 51 * {@code non-null;} the call site prototype. 52 */ 53 private final CstProtoRef callSiteProto; 54 55 /** 56 * Constructs an instance. 57 * 58 * @param opcode {@code non-null;} the opcode 59 * @param position {@code non-null;} source position 60 * @param sources {@code non-null;} specs for all the sources 61 * @param catches {@code non-null;} list of exceptions caught 62 * @param callSiteMethod {@code non-null;} the method called by 63 * invoke-virtual that this instance will replace. 64 */ InvokePolymorphicInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, TypeList catches, CstMethodRef callSiteMethod)65 public InvokePolymorphicInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, TypeList catches, 66 CstMethodRef callSiteMethod) { 67 super(opcode, position, null, sources); 68 69 if (opcode.getBranchingness() != Rop.BRANCH_THROW) { 70 throw new IllegalArgumentException("opcode with invalid branchingness: " + opcode.getBranchingness()); 71 } 72 73 if (catches == null) { 74 throw new NullPointerException("catches == null"); 75 } 76 this.catches = catches; 77 78 if (callSiteMethod == null) { 79 throw new NullPointerException("callSiteMethod == null"); 80 } else if (!callSiteMethod.isSignaturePolymorphic()) { 81 throw new IllegalArgumentException("callSiteMethod is not signature polymorphic"); 82 } 83 84 this.callSiteMethod = callSiteMethod; 85 this.invokeMethod = makeInvokeMethod(callSiteMethod); 86 this.callSiteProto = makeCallSiteProto(callSiteMethod); 87 } 88 89 /** {@inheritDoc} */ 90 @Override getCatches()91 public TypeList getCatches() { 92 return this.catches; 93 } 94 95 /** {@inheritDoc} */ 96 @Override accept(Visitor visitor)97 public void accept(Visitor visitor) { 98 visitor.visitInvokePolymorphicInsn(this); 99 } 100 101 /** {@inheritDoc} */ 102 @Override withAddedCatch(Type type)103 public Insn withAddedCatch(Type type) { 104 return new InvokePolymorphicInsn(getOpcode(), getPosition(), 105 getSources(), catches.withAddedType(type), getCallSiteMethod()); 106 } 107 108 /** {@inheritDoc} */ 109 @Override withRegisterOffset(int delta)110 public Insn withRegisterOffset(int delta) { 111 return new InvokePolymorphicInsn(getOpcode(), getPosition(), 112 getSources().withOffset(delta), 113 catches, getCallSiteMethod()); 114 } 115 116 /** {@inheritDoc} */ 117 @Override withNewRegisters(RegisterSpec result, RegisterSpecList sources)118 public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { 119 return new InvokePolymorphicInsn(getOpcode(), getPosition(), 120 sources, catches, getCallSiteMethod()); 121 } 122 123 /** 124 * Gets the method as it appears at the call site of the original 125 * invoke-virtual instruction. 126 * 127 * @return {@code non-null;} the original method reference 128 */ getCallSiteMethod()129 public CstMethodRef getCallSiteMethod() { 130 return callSiteMethod; 131 } 132 133 /** 134 * Gets the method to be invoked. This will be will either be 135 * {@code java.lang.invoke.MethodHandle.invoke()} or 136 * {@code java.lang.invoke.MethodHandle.invokeExact()}. 137 * 138 * @return {@code non-null;} method reference to be invoked 139 */ getInvokeMethod()140 public CstMethodRef getInvokeMethod() { 141 return invokeMethod; 142 } 143 144 /** 145 * Gets the call site prototype. The call site prototype is provided 146 * as an argument to invoke-polymorphic to enable type checking and 147 * type conversion. 148 * 149 * @return {@code non-null;} Prototype reference for call site 150 */ getCallSiteProto()151 public CstProtoRef getCallSiteProto() { 152 return callSiteProto; 153 } 154 155 /** {@inheritDoc} */ 156 @Override getInlineString()157 public String getInlineString() { 158 return getInvokeMethod().toString() + " " + 159 getCallSiteProto().toString() + " " + 160 ThrowingInsn.toCatchString(catches); 161 } 162 makeInvokeMethod(final CstMethodRef callSiteMethod)163 private static CstMethodRef makeInvokeMethod(final CstMethodRef callSiteMethod) { 164 // The name is either invoke or invokeExact. The INVOKE_DESCRIPTOR is fixed. 165 CstNat cstNat = new CstNat(callSiteMethod.getNat().getName(), INVOKE_DESCRIPTOR); 166 return new CstMethodRef(CstType.METHOD_HANDLE, cstNat); 167 } 168 makeCallSiteProto(final CstMethodRef callSiteMethod)169 private static CstProtoRef makeCallSiteProto(final CstMethodRef callSiteMethod) { 170 return new CstProtoRef(callSiteMethod.getPrototype(true)); 171 } 172 } 173