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 /** Default descriptor for signature polymorphic methods. */ 32 private static final CstString DEFAULT_DESCRIPTOR = 33 new CstString("([Ljava/lang/Object;)Ljava/lang/Object;"); 34 35 /** Descriptor for VarHandle set methods. */ 36 private static final CstString VARHANDLE_SET_DESCRIPTOR = 37 new CstString("([Ljava/lang/Object;)V"); 38 39 /** Descriptor for VarHandle compare-and-set methods. */ 40 private static final CstString VARHANDLE_COMPARE_AND_SET_DESCRIPTOR = 41 new CstString("([Ljava/lang/Object;)Z"); 42 43 /** {@code non-null;} list of exceptions caught */ 44 private final TypeList catches; 45 46 /** 47 * {@code non-null;} method as it appears at the call site of the original 48 * invoke-virtual instruction. This is used to construct the invoke method 49 * to target and the call-site prototype. 50 */ 51 private final CstMethodRef callSiteMethod; 52 53 /** 54 * {@code non-null;} signature polymorphic method. 55 */ 56 private final CstMethodRef polymorphicMethod; 57 58 /** 59 * {@code non-null;} the call site prototype. 60 */ 61 private final CstProtoRef callSiteProto; 62 63 /** 64 * Constructs an instance. 65 * 66 * @param opcode {@code non-null;} the opcode 67 * @param position {@code non-null;} source position 68 * @param sources {@code non-null;} specs for all the sources 69 * @param catches {@code non-null;} list of exceptions caught 70 * @param callSiteMethod {@code non-null;} the method called by 71 * invoke-virtual that this instance will replace. 72 */ InvokePolymorphicInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, TypeList catches, CstMethodRef callSiteMethod)73 public InvokePolymorphicInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, TypeList catches, 74 CstMethodRef callSiteMethod) { 75 super(opcode, position, null, sources); 76 77 if (opcode.getBranchingness() != Rop.BRANCH_THROW) { 78 throw new IllegalArgumentException("opcode with invalid branchingness: " + opcode.getBranchingness()); 79 } 80 81 if (catches == null) { 82 throw new NullPointerException("catches == null"); 83 } 84 this.catches = catches; 85 86 if (callSiteMethod == null) { 87 throw new NullPointerException("callSiteMethod == null"); 88 } else if (!callSiteMethod.isSignaturePolymorphic()) { 89 throw new IllegalArgumentException("callSiteMethod is not signature polymorphic"); 90 } 91 92 this.callSiteMethod = callSiteMethod; 93 this.polymorphicMethod = makePolymorphicMethod(callSiteMethod); 94 this.callSiteProto = makeCallSiteProto(callSiteMethod); 95 } 96 97 /** {@inheritDoc} */ 98 @Override getCatches()99 public TypeList getCatches() { 100 return this.catches; 101 } 102 103 /** {@inheritDoc} */ 104 @Override accept(Visitor visitor)105 public void accept(Visitor visitor) { 106 visitor.visitInvokePolymorphicInsn(this); 107 } 108 109 /** {@inheritDoc} */ 110 @Override withAddedCatch(Type type)111 public Insn withAddedCatch(Type type) { 112 return new InvokePolymorphicInsn(getOpcode(), getPosition(), 113 getSources(), catches.withAddedType(type), getCallSiteMethod()); 114 } 115 116 /** {@inheritDoc} */ 117 @Override withRegisterOffset(int delta)118 public Insn withRegisterOffset(int delta) { 119 return new InvokePolymorphicInsn(getOpcode(), getPosition(), 120 getSources().withOffset(delta), 121 catches, getCallSiteMethod()); 122 } 123 124 /** {@inheritDoc} */ 125 @Override withNewRegisters(RegisterSpec result, RegisterSpecList sources)126 public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { 127 return new InvokePolymorphicInsn(getOpcode(), getPosition(), 128 sources, catches, getCallSiteMethod()); 129 } 130 131 /** 132 * Gets the method as it appears at the call site of the original 133 * invoke-virtual instruction. 134 * 135 * @return {@code non-null;} the original method reference 136 */ getCallSiteMethod()137 public CstMethodRef getCallSiteMethod() { 138 return callSiteMethod; 139 } 140 141 /** 142 * Gets the method to be invoked. This will be will either be 143 * {@code java.lang.invoke.MethodHandle.invoke()} or 144 * {@code java.lang.invoke.MethodHandle.invokeExact()}. 145 * 146 * @return {@code non-null;} method reference to be invoked 147 */ getPolymorphicMethod()148 public CstMethodRef getPolymorphicMethod() { 149 return polymorphicMethod; 150 } 151 152 /** 153 * Gets the call site prototype. The call site prototype is provided 154 * as an argument to invoke-polymorphic to enable type checking and 155 * type conversion. 156 * 157 * @return {@code non-null;} Prototype reference for call site 158 */ getCallSiteProto()159 public CstProtoRef getCallSiteProto() { 160 return callSiteProto; 161 } 162 163 /** {@inheritDoc} */ 164 @Override getInlineString()165 public String getInlineString() { 166 return getPolymorphicMethod().toString() + " " + 167 getCallSiteProto().toString() + " " + 168 ThrowingInsn.toCatchString(catches); 169 } 170 makePolymorphicMethod(final CstMethodRef callSiteMethod)171 private static CstMethodRef makePolymorphicMethod(final CstMethodRef callSiteMethod) { 172 CstType definingClass= callSiteMethod.getDefiningClass(); 173 CstString cstMethodName = callSiteMethod.getNat().getName(); 174 String methodName = callSiteMethod.getNat().getName().getString(); 175 176 if (definingClass.equals(CstType.METHOD_HANDLE)) { 177 if (methodName.equals("invoke") || methodName.equals("invokeExact")) { 178 CstNat cstNat = new CstNat(cstMethodName, DEFAULT_DESCRIPTOR); 179 return new CstMethodRef(definingClass, cstNat); 180 } 181 } 182 183 if (definingClass.equals(CstType.VAR_HANDLE)) { 184 switch (methodName) { 185 case "compareAndExchange": 186 case "compareAndExchangeAcquire": 187 case "compareAndExchangeRelease": 188 case "get": 189 case "getAcquire": 190 case "getAndAdd": 191 case "getAndAddAcquire": 192 case "getAndAddRelease": 193 case "getAndBitwiseAnd": 194 case "getAndBitwiseAndAcquire": 195 case "getAndBitwiseAndRelease": 196 case "getAndBitwiseOr": 197 case "getAndBitwiseOrAcquire": 198 case "getAndBitwiseOrRelease": 199 case "getAndBitwiseXor": 200 case "getAndBitwiseXorAcquire": 201 case "getAndBitwiseXorRelease": 202 case "getAndSet": 203 case "getAndSetAcquire": 204 case "getAndSetRelease": 205 case "getOpaque": 206 case "getVolatile": 207 { 208 CstNat cstNat = new CstNat(cstMethodName, DEFAULT_DESCRIPTOR); 209 return new CstMethodRef(definingClass, cstNat); 210 } 211 case "set": 212 case "setOpaque": 213 case "setRelease": 214 case "setVolatile": 215 { 216 CstNat cstNat = new CstNat(cstMethodName, VARHANDLE_SET_DESCRIPTOR); 217 return new CstMethodRef(definingClass, cstNat); 218 } 219 case "compareAndSet": 220 case "weakCompareAndSet": 221 case "weakCompareAndSetAcquire": 222 case "weakCompareAndSetPlain": 223 case "weakCompareAndSetRelease": 224 { 225 CstNat cstNat = new CstNat(cstMethodName, VARHANDLE_COMPARE_AND_SET_DESCRIPTOR); 226 return new CstMethodRef(definingClass, cstNat); 227 } 228 default: 229 break; 230 } 231 } 232 throw new IllegalArgumentException("Unknown signature polymorphic method: " + 233 callSiteMethod.toHuman()); 234 } 235 makeCallSiteProto(final CstMethodRef callSiteMethod)236 private static CstProtoRef makeCallSiteProto(final CstMethodRef callSiteMethod) { 237 return new CstProtoRef(callSiteMethod.getPrototype(true)); 238 } 239 } 240