1 /*
2  * Copyright (C) 2007 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 
17 package com.android.dx.rop.code;
18 
19 import com.android.dx.rop.type.StdTypeList;
20 import com.android.dx.rop.type.Type;
21 import com.android.dx.rop.type.TypeList;
22 import com.android.dx.util.Hex;
23 
24 /**
25  * Class that describes all the immutable parts of register-based operations.
26  */
27 public final class Rop {
28     /** minimum {@code BRANCH_*} value */
29     public static final int BRANCH_MIN = 1;
30 
31     /** indicates a non-branching op */
32     public static final int BRANCH_NONE = 1;
33 
34     /** indicates a function/method return */
35     public static final int BRANCH_RETURN = 2;
36 
37     /** indicates an unconditional goto */
38     public static final int BRANCH_GOTO = 3;
39 
40     /** indicates a two-way branch */
41     public static final int BRANCH_IF = 4;
42 
43     /** indicates a switch-style branch */
44     public static final int BRANCH_SWITCH = 5;
45 
46     /** indicates a throw-style branch (both always-throws and may-throw) */
47     public static final int BRANCH_THROW = 6;
48 
49     /** maximum {@code BRANCH_*} value */
50     public static final int BRANCH_MAX = 6;
51 
52     /** the opcode; one of the constants in {@link RegOps} */
53     private final int opcode;
54 
55     /**
56      * {@code non-null;} result type of this operation; {@link Type#VOID} for
57      * no-result operations
58      */
59     private final Type result;
60 
61     /** {@code non-null;} types of all the sources of this operation */
62     private final TypeList sources;
63 
64     /** {@code non-null;} list of possible types thrown by this operation */
65     private final TypeList exceptions;
66 
67     /**
68      * the branchingness of this op; one of the {@code BRANCH_*}
69      * constants in this class
70      */
71     private final int branchingness;
72 
73     /** whether this is a function/method call op or similar */
74     private final boolean isCallLike;
75 
76     /** {@code null-ok;} nickname, if specified (used for debugging) */
77     private final String nickname;
78 
79     /**
80      * Constructs an instance. This method is private. Use one of the
81      * public constructors.
82      *
83      * @param opcode the opcode; one of the constants in {@link RegOps}
84      * @param result {@code non-null;} result type of this operation; {@link
85      * Type#VOID} for no-result operations
86      * @param sources {@code non-null;} types of all the sources of this operation
87      * @param exceptions {@code non-null;} list of possible types thrown by this
88      * operation
89      * @param branchingness the branchingness of this op; one of the
90      * {@code BRANCH_*} constants
91      * @param isCallLike whether the op is a function/method call or similar
92      * @param nickname {@code null-ok;} optional nickname (used for debugging)
93      */
Rop(int opcode, Type result, TypeList sources, TypeList exceptions, int branchingness, boolean isCallLike, String nickname)94     public Rop(int opcode, Type result, TypeList sources,
95                TypeList exceptions, int branchingness, boolean isCallLike,
96                String nickname) {
97         if (result == null) {
98             throw new NullPointerException("result == null");
99         }
100 
101         if (sources == null) {
102             throw new NullPointerException("sources == null");
103         }
104 
105         if (exceptions == null) {
106             throw new NullPointerException("exceptions == null");
107         }
108 
109         if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) {
110             throw new IllegalArgumentException("bogus branchingness");
111         }
112 
113         if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) {
114             throw new IllegalArgumentException("exceptions / branchingness " +
115                                                "mismatch");
116         }
117 
118         this.opcode = opcode;
119         this.result = result;
120         this.sources = sources;
121         this.exceptions = exceptions;
122         this.branchingness = branchingness;
123         this.isCallLike = isCallLike;
124         this.nickname = nickname;
125     }
126 
127     /**
128      * Constructs an instance. The constructed instance is never a
129      * call-like op (see {@link #isCallLike}).
130      *
131      * @param opcode the opcode; one of the constants in {@link RegOps}
132      * @param result {@code non-null;} result type of this operation; {@link
133      * Type#VOID} for no-result operations
134      * @param sources {@code non-null;} types of all the sources of this operation
135      * @param exceptions {@code non-null;} list of possible types thrown by this
136      * operation
137      * @param branchingness the branchingness of this op; one of the
138      * {@code BRANCH_*} constants
139      * @param nickname {@code null-ok;} optional nickname (used for debugging)
140      */
Rop(int opcode, Type result, TypeList sources, TypeList exceptions, int branchingness, String nickname)141     public Rop(int opcode, Type result, TypeList sources,
142                TypeList exceptions, int branchingness, String nickname) {
143         this(opcode, result, sources, exceptions, branchingness, false,
144              nickname);
145     }
146 
147     /**
148      * Constructs a no-exception instance. The constructed instance is never a
149      * call-like op (see {@link #isCallLike}).
150      *
151      * @param opcode the opcode; one of the constants in {@link RegOps}
152      * @param result {@code non-null;} result type of this operation; {@link
153      * Type#VOID} for no-result operations
154      * @param sources {@code non-null;} types of all the sources of this operation
155      * @param branchingness the branchingness of this op; one of the
156      * {@code BRANCH_*} constants
157      * @param nickname {@code null-ok;} optional nickname (used for debugging)
158      */
Rop(int opcode, Type result, TypeList sources, int branchingness, String nickname)159     public Rop(int opcode, Type result, TypeList sources, int branchingness,
160                String nickname) {
161         this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false,
162              nickname);
163     }
164 
165     /**
166      * Constructs a non-branching no-exception instance. The
167      * {@code branchingness} is always {@code BRANCH_NONE},
168      * and it is never a call-like op (see {@link #isCallLike}).
169      *
170      * @param opcode the opcode; one of the constants in {@link RegOps}
171      * @param result {@code non-null;} result type of this operation; {@link
172      * Type#VOID} for no-result operations
173      * @param sources {@code non-null;} types of all the sources of this operation
174      * @param nickname {@code null-ok;} optional nickname (used for debugging)
175      */
Rop(int opcode, Type result, TypeList sources, String nickname)176     public Rop(int opcode, Type result, TypeList sources, String nickname) {
177         this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE,
178              false, nickname);
179     }
180 
181     /**
182      * Constructs a non-empty exceptions instance. Its
183      * {@code branchingness} is always {@code BRANCH_THROW},
184      * but it is never a call-like op (see {@link #isCallLike}).
185      *
186      * @param opcode the opcode; one of the constants in {@link RegOps}
187      * @param result {@code non-null;} result type of this operation; {@link
188      * Type#VOID} for no-result operations
189      * @param sources {@code non-null;} types of all the sources of this operation
190      * @param exceptions {@code non-null;} list of possible types thrown by this
191      * operation
192      * @param nickname {@code null-ok;} optional nickname (used for debugging)
193      */
Rop(int opcode, Type result, TypeList sources, TypeList exceptions, String nickname)194     public Rop(int opcode, Type result, TypeList sources, TypeList exceptions,
195                String nickname) {
196         this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false,
197              nickname);
198     }
199 
200     /**
201      * Constructs a non-nicknamed instance with non-empty exceptions, which
202      * is always a call-like op (see {@link #isCallLike}). Its
203      * {@code branchingness} is always {@code BRANCH_THROW}.
204      *
205      * @param opcode the opcode; one of the constants in {@link RegOps}
206      * @param sources {@code non-null;} types of all the sources of this operation
207      * @param exceptions {@code non-null;} list of possible types thrown by this
208      * operation
209      */
Rop(int opcode, TypeList sources, TypeList exceptions)210     public Rop(int opcode, TypeList sources, TypeList exceptions) {
211         this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true,
212              null);
213     }
214 
215     /** {@inheritDoc} */
216     @Override
equals(Object other)217     public boolean equals(Object other) {
218         if (this == other) {
219             // Easy out.
220             return true;
221         }
222 
223         if (!(other instanceof Rop)) {
224             return false;
225         }
226 
227         Rop rop = (Rop) other;
228 
229         return (opcode == rop.opcode) &&
230             (branchingness == rop.branchingness) &&
231             (result == rop.result) &&
232             sources.equals(rop.sources) &&
233             exceptions.equals(rop.exceptions);
234     }
235 
236     /** {@inheritDoc} */
237     @Override
hashCode()238     public int hashCode() {
239         int h = (opcode * 31) + branchingness;
240         h = (h * 31) + result.hashCode();
241         h = (h * 31) + sources.hashCode();
242         h = (h * 31) + exceptions.hashCode();
243 
244         return h;
245     }
246 
247     /** {@inheritDoc} */
248     @Override
toString()249     public String toString() {
250         StringBuffer sb = new StringBuffer(40);
251 
252         sb.append("Rop{");
253 
254         sb.append(RegOps.opName(opcode));
255 
256         if (result != Type.VOID) {
257             sb.append(" ");
258             sb.append(result);
259         } else {
260             sb.append(" .");
261         }
262 
263         sb.append(" <-");
264 
265         int sz = sources.size();
266         if (sz == 0) {
267             sb.append(" .");
268         } else {
269             for (int i = 0; i < sz; i++) {
270                 sb.append(' ');
271                 sb.append(sources.getType(i));
272             }
273         }
274 
275         if (isCallLike) {
276             sb.append(" call");
277         }
278 
279         sz = exceptions.size();
280         if (sz != 0) {
281             sb.append(" throws");
282             for (int i = 0; i < sz; i++) {
283                 sb.append(' ');
284                 Type one = exceptions.getType(i);
285                 if (one == Type.THROWABLE) {
286                     sb.append("<any>");
287                 } else {
288                     sb.append(exceptions.getType(i));
289                 }
290             }
291         } else {
292             switch (branchingness) {
293                 case BRANCH_NONE:   sb.append(" flows"); break;
294                 case BRANCH_RETURN: sb.append(" returns"); break;
295                 case BRANCH_GOTO:   sb.append(" gotos"); break;
296                 case BRANCH_IF:     sb.append(" ifs"); break;
297                 case BRANCH_SWITCH: sb.append(" switches"); break;
298                 default: sb.append(" " + Hex.u1(branchingness)); break;
299             }
300         }
301 
302         sb.append('}');
303 
304         return sb.toString();
305     }
306 
307     /**
308      * Gets the opcode.
309      *
310      * @return the opcode
311      */
getOpcode()312     public int getOpcode() {
313         return opcode;
314     }
315 
316     /**
317      * Gets the result type. A return value of {@link Type#VOID}
318      * means this operation returns nothing.
319      *
320      * @return {@code null-ok;} the result spec
321      */
getResult()322     public Type getResult() {
323         return result;
324     }
325 
326     /**
327      * Gets the source types.
328      *
329      * @return {@code non-null;} the source types
330      */
getSources()331     public TypeList getSources() {
332         return sources;
333     }
334 
335     /**
336      * Gets the list of exception types that might be thrown.
337      *
338      * @return {@code non-null;} the list of exception types
339      */
getExceptions()340     public TypeList getExceptions() {
341         return exceptions;
342     }
343 
344     /**
345      * Gets the branchingness of this instance.
346      *
347      * @return the branchingness
348      */
getBranchingness()349     public int getBranchingness() {
350         return branchingness;
351     }
352 
353     /**
354      * Gets whether this opcode is a function/method call or similar.
355      *
356      * @return {@code true} iff this opcode is call-like
357      */
isCallLike()358     public boolean isCallLike() {
359         return isCallLike;
360     }
361 
362 
363     /**
364      * Gets whether this opcode is commutative (the order of its sources are
365      * unimportant) or not. All commutative Rops have exactly two sources and
366      * have no branchiness.
367      *
368      * @return true if rop is commutative
369      */
isCommutative()370     public boolean isCommutative() {
371         switch (opcode) {
372             case RegOps.AND:
373             case RegOps.OR:
374             case RegOps.XOR:
375             case RegOps.ADD:
376             case RegOps.MUL:
377                 return true;
378             default:
379                 return false;
380         }
381     }
382 
383     /**
384      * Gets the nickname. If this instance has no nickname, this returns
385      * the result of calling {@link #toString}.
386      *
387      * @return {@code non-null;} the nickname
388      */
getNickname()389     public String getNickname() {
390         if (nickname != null) {
391             return nickname;
392         }
393 
394         return toString();
395     }
396 
397     /**
398      * Gets whether this operation can possibly throw an exception. This
399      * is just a convenient wrapper for
400      * {@code getExceptions().size() != 0}.
401      *
402      * @return {@code true} iff this operation can possibly throw
403      */
canThrow()404     public final boolean canThrow() {
405         return (exceptions.size() != 0);
406     }
407 }
408