• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.dexgen.rop.code;
18 
19 import com.android.dexgen.rop.cst.Constant;
20 import com.android.dexgen.rop.cst.CstUtf8;
21 import com.android.dexgen.rop.type.StdTypeList;
22 import com.android.dexgen.rop.type.Type;
23 import com.android.dexgen.rop.type.TypeList;
24 import 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  */
32 public 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      */
Insn(Rop opcode, SourcePosition position, RegisterSpec result, RegisterSpecList sources)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
equals(Object other)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
hashCode()92     public final int hashCode() {
93         return System.identityHashCode(this);
94     }
95 
96     /** {@inheritDoc} */
97     @Override
toString()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      */
toHuman()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      */
getInlineString()117     public String getInlineString() {
118         return null;
119     }
120 
121     /**
122      * Gets the opcode.
123      *
124      * @return {@code non-null;} the opcode
125      */
getOpcode()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      */
getPosition()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      */
getResult()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      */
getLocalAssignment()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      */
getSources()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      */
canThrow()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      */
getCatches()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      */
accept(Visitor visitor)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      */
withAddedCatch(Type type)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      */
withRegisterOffset(int delta)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      */
withLastSourceLiteral()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      */
copy()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      */
equalsHandleNulls(Object a, Object b)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      */
contentEquals(Insn b)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      */
withNewRegisters(RegisterSpec result, RegisterSpecList sources)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      */
toStringWithInline(String extra)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      */
toHumanWithInline(String extra)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          */
visitPlainInsn(PlainInsn insn)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          */
visitPlainCstInsn(PlainCstInsn insn)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          */
visitSwitchInsn(SwitchInsn insn)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          */
visitThrowingCstInsn(ThrowingCstInsn insn)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          */
visitThrowingInsn(ThrowingInsn insn)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          */
visitFillArrayDataInsn(FillArrayDataInsn insn)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} */
visitPlainInsn(PlainInsn insn)429         public void visitPlainInsn(PlainInsn insn) {
430             // This space intentionally left blank.
431         }
432 
433         /** {@inheritDoc} */
visitPlainCstInsn(PlainCstInsn insn)434         public void visitPlainCstInsn(PlainCstInsn insn) {
435             // This space intentionally left blank.
436         }
437 
438         /** {@inheritDoc} */
visitSwitchInsn(SwitchInsn insn)439         public void visitSwitchInsn(SwitchInsn insn) {
440             // This space intentionally left blank.
441         }
442 
443         /** {@inheritDoc} */
visitThrowingCstInsn(ThrowingCstInsn insn)444         public void visitThrowingCstInsn(ThrowingCstInsn insn) {
445             // This space intentionally left blank.
446         }
447 
448         /** {@inheritDoc} */
visitThrowingInsn(ThrowingInsn insn)449         public void visitThrowingInsn(ThrowingInsn insn) {
450             // This space intentionally left blank.
451         }
452 
453         /** {@inheritDoc} */
visitFillArrayDataInsn(FillArrayDataInsn insn)454         public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
455             // This space intentionally left blank.
456         }
457     }
458 }
459