Home | History | Annotate | Download | only in code
      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.cst.CstUtf8;
     20 import com.android.dx.rop.cst.Constant;
     21 import com.android.dx.rop.type.StdTypeList;
     22 import com.android.dx.rop.type.Type;
     23 import com.android.dx.rop.type.TypeList;
     24 import com.android.dx.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      */
     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