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
17package com.android.dx.rop.code;
18
19import com.android.dx.rop.type.StdTypeList;
20import com.android.dx.rop.type.Type;
21import com.android.dx.rop.type.TypeList;
22import com.android.dx.util.Hex;
23
24/**
25 * Class that describes all the immutable parts of register-based operations.
26 */
27public 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     */
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     */
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     */
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     */
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     */
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     */
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
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
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
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     */
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     */
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     */
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     */
340    public TypeList getExceptions() {
341        return exceptions;
342    }
343
344    /**
345     * Gets the branchingness of this instance.
346     *
347     * @return the branchingness
348     */
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     */
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     */
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     */
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     */
404    public final boolean canThrow() {
405        return (exceptions.size() != 0);
406    }
407}
408