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.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  */
30 public 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      */
Insn(Rop opcode, SourcePosition position, RegisterSpec result, RegisterSpecList sources)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
equals(Object other)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
hashCode()90     public final int hashCode() {
91         return System.identityHashCode(this);
92     }
93 
94     /** {@inheritDoc} */
95     @Override
toString()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      */
toHuman()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      */
getInlineString()115     public String getInlineString() {
116         return null;
117     }
118 
119     /**
120      * Gets the opcode.
121      *
122      * @return {@code non-null;} the opcode
123      */
getOpcode()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      */
getPosition()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      */
getResult()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      */
getLocalAssignment()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      */
getSources()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      */
canThrow()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      */
getCatches()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      */
accept(Visitor visitor)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      */
withAddedCatch(Type type)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      */
withRegisterOffset(int delta)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      */
withSourceLiteral()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      */
copy()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      */
equalsHandleNulls(Object a, Object b)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      */
contentEquals(Insn b)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      */
withNewRegisters(RegisterSpec result, RegisterSpecList sources)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      */
toStringWithInline(String extra)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      */
toHumanWithInline(String extra)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          */
visitPlainInsn(PlainInsn insn)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          */
visitPlainCstInsn(PlainCstInsn insn)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          */
visitSwitchInsn(SwitchInsn insn)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          */
visitThrowingCstInsn(ThrowingCstInsn insn)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          */
visitThrowingInsn(ThrowingInsn insn)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          */
visitFillArrayDataInsn(FillArrayDataInsn insn)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} */
visitPlainInsn(PlainInsn insn)427         public void visitPlainInsn(PlainInsn insn) {
428             // This space intentionally left blank.
429         }
430 
431         /** {@inheritDoc} */
visitPlainCstInsn(PlainCstInsn insn)432         public void visitPlainCstInsn(PlainCstInsn insn) {
433             // This space intentionally left blank.
434         }
435 
436         /** {@inheritDoc} */
visitSwitchInsn(SwitchInsn insn)437         public void visitSwitchInsn(SwitchInsn insn) {
438             // This space intentionally left blank.
439         }
440 
441         /** {@inheritDoc} */
visitThrowingCstInsn(ThrowingCstInsn insn)442         public void visitThrowingCstInsn(ThrowingCstInsn insn) {
443             // This space intentionally left blank.
444         }
445 
446         /** {@inheritDoc} */
visitThrowingInsn(ThrowingInsn insn)447         public void visitThrowingInsn(ThrowingInsn insn) {
448             // This space intentionally left blank.
449         }
450 
451         /** {@inheritDoc} */
visitFillArrayDataInsn(FillArrayDataInsn insn)452         public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
453             // This space intentionally left blank.
454         }
455     }
456 }
457