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