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.dexgen.rop.code;
18
19import com.android.dexgen.rop.cst.Constant;
20import com.android.dexgen.rop.cst.CstUtf8;
21import com.android.dexgen.rop.type.StdTypeList;
22import com.android.dexgen.rop.type.Type;
23import com.android.dexgen.rop.type.TypeList;
24import com.android.dexgen.util.ToHuman;
25
26/**
27 * A register-based instruction. An instruction is the combination of
28 * an opcode (which specifies operation and source/result types), a
29 * list of actual sources and result registers/values, and additional
30 * information.
31 */
32public abstract class Insn implements ToHuman {
33    /** {@code non-null;} opcode */
34    private final Rop opcode;
35
36    /** {@code non-null;} source position */
37    private final SourcePosition position;
38
39    /** {@code null-ok;} spec for the result of this instruction, if any */
40    private final RegisterSpec result;
41
42    /** {@code non-null;} specs for all the sources of this instruction */
43    private final RegisterSpecList sources;
44
45    /**
46     * Constructs an instance.
47     *
48     * @param opcode {@code non-null;} the opcode
49     * @param position {@code non-null;} source position
50     * @param result {@code null-ok;} spec for the result, if any
51     * @param sources {@code non-null;} specs for all the sources
52     */
53    public Insn(Rop opcode, SourcePosition position, RegisterSpec result,
54                RegisterSpecList sources) {
55        if (opcode == null) {
56            throw new NullPointerException("opcode == null");
57        }
58
59        if (position == null) {
60            throw new NullPointerException("position == null");
61        }
62
63        if (sources == null) {
64            throw new NullPointerException("sources == null");
65        }
66
67        this.opcode = opcode;
68        this.position = position;
69        this.result = result;
70        this.sources = sources;
71    }
72
73    /**
74     * {@inheritDoc}
75     *
76     * Instances of this class compare by identity. That is,
77     * {@code x.equals(y)} is only true if {@code x == y}.
78     */
79    @Override
80    public final boolean equals(Object other) {
81        return (this == other);
82    }
83
84    /**
85     * {@inheritDoc}
86     *
87     * This implementation returns the identity hashcode of this
88     * instance. This is proper, since instances of this class compare
89     * by identity (see {@link #equals}).
90     */
91    @Override
92    public final int hashCode() {
93        return System.identityHashCode(this);
94    }
95
96    /** {@inheritDoc} */
97    @Override
98    public String toString() {
99        return toStringWithInline(getInlineString());
100    }
101
102    /**
103     * Gets a human-oriented (and slightly lossy) string for this instance.
104     *
105     * @return {@code non-null;} the human string form
106     */
107    public String toHuman() {
108        return toHumanWithInline(getInlineString());
109    }
110
111    /**
112     * Gets an "inline" string portion for toHuman(), if available. This
113     * is the portion that appears after the Rop opcode
114     *
115     * @return {@code null-ok;} if non-null, the inline text for toHuman()
116     */
117    public String getInlineString() {
118        return null;
119    }
120
121    /**
122     * Gets the opcode.
123     *
124     * @return {@code non-null;} the opcode
125     */
126    public final Rop getOpcode() {
127        return opcode;
128    }
129
130    /**
131     * Gets the source position.
132     *
133     * @return {@code non-null;} the source position
134     */
135    public final SourcePosition getPosition() {
136        return position;
137    }
138
139    /**
140     * Gets the result spec, if any. A return value of {@code null}
141     * means this instruction returns nothing.
142     *
143     * @return {@code null-ok;} the result spec, if any
144     */
145    public final RegisterSpec getResult() {
146        return result;
147    }
148
149    /**
150     * Gets the spec of a local variable assignment that occurs at this
151     * instruction, or null if no local variable assignment occurs. This
152     * may be the result register, or for {@code mark-local} insns
153     * it may be the source.
154     *
155     * @return {@code null-ok;} a named register spec or null
156     */
157    public final RegisterSpec getLocalAssignment() {
158        RegisterSpec assignment;
159        if (opcode.getOpcode() == RegOps.MARK_LOCAL) {
160            assignment = sources.get(0);
161        } else {
162            assignment = result;
163        }
164
165        if (assignment == null) {
166            return null;
167        }
168
169        LocalItem localItem = assignment.getLocalItem();
170
171        if (localItem == null) {
172            return null;
173        }
174
175        return assignment;
176    }
177
178    /**
179     * Gets the source specs.
180     *
181     * @return {@code non-null;} the source specs
182     */
183    public final RegisterSpecList getSources() {
184        return sources;
185    }
186
187    /**
188     * Gets whether this instruction can possibly throw an exception. This
189     * is just a convenient wrapper for {@code getOpcode().canThrow()}.
190     *
191     * @return {@code true} iff this instruction can possibly throw
192     */
193    public final boolean canThrow() {
194        return opcode.canThrow();
195    }
196
197    /**
198     * Gets the list of possibly-caught exceptions. This returns {@link
199     * StdTypeList#EMPTY} if this instruction has no handlers,
200     * which can be <i>either</i> if this instruction can't possibly
201     * throw or if it merely doesn't handle any of its possible
202     * exceptions. To determine whether this instruction can throw,
203     * use {@link #canThrow}.
204     *
205     * @return {@code non-null;} the catches list
206     */
207    public abstract TypeList getCatches();
208
209    /**
210     * Calls the appropriate method on the given visitor, depending on the
211     * class of this instance. Subclasses must override this.
212     *
213     * @param visitor {@code non-null;} the visitor to call on
214     */
215    public abstract void accept(Visitor visitor);
216
217    /**
218     * Returns an instance that is just like this one, except that it
219     * has a catch list with the given item appended to the end. This
220     * method throws an exception if this instance can't possibly
221     * throw. To determine whether this instruction can throw, use
222     * {@link #canThrow}.
223     *
224     * @param type {@code non-null;} type to append to the catch list
225     * @return {@code non-null;} an appropriately-constructed instance
226     */
227    public abstract Insn withAddedCatch(Type type);
228
229    /**
230     * Returns an instance that is just like this one, except that all
231     * register references have been offset by the given delta.
232     *
233     * @param delta the amount to offset register references by
234     * @return {@code non-null;} an appropriately-constructed instance
235     */
236    public abstract Insn withRegisterOffset(int delta);
237
238    /**
239     * Returns an instance that is just like this one, except that, if
240     * possible, the insn is converted into a version in which the last
241     * source (if it is a constant) is represented directly rather than
242     * as a register reference. {@code this} is returned in cases where
243     * the translation is not possible.
244     *
245     * @return {@code non-null;} an appropriately-constructed instance
246     */
247    public Insn withLastSourceLiteral() {
248        return this;
249    }
250
251    /**
252     * Returns an exact copy of this Insn
253     *
254     * @return {@code non-null;} an appropriately-constructed instance
255     */
256    public Insn copy() {
257        return withRegisterOffset(0);
258    }
259
260
261    /**
262     * Compares, handling nulls safely
263     *
264     * @param a first object
265     * @param b second object
266     * @return true if they're equal or both null.
267     */
268    private static boolean equalsHandleNulls (Object a, Object b) {
269        return (a == b) || ((a != null) && a.equals(b));
270    }
271
272    /**
273     * Compares Insn contents, since {@code Insn.equals()} is defined
274     * to be an identity compare. Insn's are {@code contentEquals()}
275     * if they have the same opcode, registers, source position, and other
276     * metadata.
277     *
278     * @return true in the case described above
279     */
280    public boolean contentEquals(Insn b) {
281        return opcode == b.getOpcode()
282                && position.equals(b.getPosition())
283                && (getClass() == b.getClass())
284                && equalsHandleNulls(result, b.getResult())
285                && equalsHandleNulls(sources, b.getSources())
286                && StdTypeList.equalContents(getCatches(), b.getCatches());
287    }
288
289    /**
290     * Returns an instance that is just like this one, except
291     * with new result and source registers.
292     *
293     * @param result {@code null-ok;} new result register
294     * @param sources {@code non-null;} new sources registers
295     * @return {@code non-null;} an appropriately-constructed instance
296     */
297    public abstract Insn withNewRegisters(RegisterSpec result,
298            RegisterSpecList sources);
299
300    /**
301     * Returns the string form of this instance, with the given bit added in
302     * the standard location for an inline argument.
303     *
304     * @param extra {@code null-ok;} the inline argument string
305     * @return {@code non-null;} the string form
306     */
307    protected final String toStringWithInline(String extra) {
308        StringBuffer sb = new StringBuffer(80);
309
310        sb.append("Insn{");
311        sb.append(position);
312        sb.append(' ');
313        sb.append(opcode);
314
315        if (extra != null) {
316            sb.append(' ');
317            sb.append(extra);
318        }
319
320        sb.append(" :: ");
321
322        if (result != null) {
323            sb.append(result);
324            sb.append(" <- ");
325        }
326
327        sb.append(sources);
328        sb.append('}');
329
330        return sb.toString();
331    }
332
333    /**
334     * Returns the human string form of this instance, with the given
335     * bit added in the standard location for an inline argument.
336     *
337     * @param extra {@code null-ok;} the inline argument string
338     * @return {@code non-null;} the human string form
339     */
340    protected final String toHumanWithInline(String extra) {
341        StringBuffer sb = new StringBuffer(80);
342
343        sb.append(position);
344        sb.append(": ");
345        sb.append(opcode.getNickname());
346
347        if (extra != null) {
348            sb.append("(");
349            sb.append(extra);
350            sb.append(")");
351        }
352
353        if (result == null) {
354            sb.append(" .");
355        } else {
356            sb.append(" ");
357            sb.append(result.toHuman());
358        }
359
360        sb.append(" <-");
361
362        int sz = sources.size();
363        if (sz == 0) {
364            sb.append(" .");
365        } else {
366            for (int i = 0; i < sz; i++) {
367                sb.append(" ");
368                sb.append(sources.get(i).toHuman());
369            }
370        }
371
372        return sb.toString();
373    }
374
375
376    /**
377     * Visitor interface for this (outer) class.
378     */
379    public static interface Visitor {
380        /**
381         * Visits a {@link PlainInsn}.
382         *
383         * @param insn {@code non-null;} the instruction to visit
384         */
385        public void visitPlainInsn(PlainInsn insn);
386
387        /**
388         * Visits a {@link PlainCstInsn}.
389         *
390         * @param insn {@code non-null;} the instruction to visit
391         */
392        public void visitPlainCstInsn(PlainCstInsn insn);
393
394        /**
395         * Visits a {@link SwitchInsn}.
396         *
397         * @param insn {@code non-null;} the instruction to visit
398         */
399        public void visitSwitchInsn(SwitchInsn insn);
400
401        /**
402         * Visits a {@link ThrowingCstInsn}.
403         *
404         * @param insn {@code non-null;} the instruction to visit
405         */
406        public void visitThrowingCstInsn(ThrowingCstInsn insn);
407
408        /**
409         * Visits a {@link ThrowingInsn}.
410         *
411         * @param insn {@code non-null;} the instruction to visit
412         */
413        public void visitThrowingInsn(ThrowingInsn insn);
414
415        /**
416         * Visits a {@link FillArrayDataInsn}.
417         *
418         * @param insn {@code non-null;} the instruction to visit
419         */
420        public void visitFillArrayDataInsn(FillArrayDataInsn insn);
421    }
422
423    /**
424     * Base implementation of {@link Visitor}, which has empty method
425     * bodies for all methods.
426     */
427    public static class BaseVisitor implements Visitor {
428        /** {@inheritDoc} */
429        public void visitPlainInsn(PlainInsn insn) {
430            // This space intentionally left blank.
431        }
432
433        /** {@inheritDoc} */
434        public void visitPlainCstInsn(PlainCstInsn insn) {
435            // This space intentionally left blank.
436        }
437
438        /** {@inheritDoc} */
439        public void visitSwitchInsn(SwitchInsn insn) {
440            // This space intentionally left blank.
441        }
442
443        /** {@inheritDoc} */
444        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
445            // This space intentionally left blank.
446        }
447
448        /** {@inheritDoc} */
449        public void visitThrowingInsn(ThrowingInsn insn) {
450            // This space intentionally left blank.
451        }
452
453        /** {@inheritDoc} */
454        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
455            // This space intentionally left blank.
456        }
457    }
458}
459