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