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.type.StdTypeList;
     20 import com.android.dx.rop.type.Type;
     21 import com.android.dx.rop.type.TypeList;
     22 import com.android.dx.util.Hex;
     23 
     24 /**
     25  * Class that describes all the immutable parts of register-based operations.
     26  */
     27 public final class Rop {
     28     /** minimum {@code BRANCH_*} value */
     29     public static final int BRANCH_MIN = 1;
     30 
     31     /** indicates a non-branching op */
     32     public static final int BRANCH_NONE = 1;
     33 
     34     /** indicates a function/method return */
     35     public static final int BRANCH_RETURN = 2;
     36 
     37     /** indicates an unconditional goto */
     38     public static final int BRANCH_GOTO = 3;
     39 
     40     /** indicates a two-way branch */
     41     public static final int BRANCH_IF = 4;
     42 
     43     /** indicates a switch-style branch */
     44     public static final int BRANCH_SWITCH = 5;
     45 
     46     /** indicates a throw-style branch (both always-throws and may-throw) */
     47     public static final int BRANCH_THROW = 6;
     48 
     49     /** maximum {@code BRANCH_*} value */
     50     public static final int BRANCH_MAX = 6;
     51 
     52     /** the opcode; one of the constants in {@link RegOps} */
     53     private final int opcode;
     54 
     55     /**
     56      * {@code non-null;} result type of this operation; {@link Type#VOID} for
     57      * no-result operations
     58      */
     59     private final Type result;
     60 
     61     /** {@code non-null;} types of all the sources of this operation */
     62     private final TypeList sources;
     63 
     64     /** {@code non-null;} list of possible types thrown by this operation */
     65     private final TypeList exceptions;
     66 
     67     /**
     68      * the branchingness of this op; one of the {@code BRANCH_*}
     69      * constants in this class
     70      */
     71     private final int branchingness;
     72 
     73     /** whether this is a function/method call op or similar */
     74     private final boolean isCallLike;
     75 
     76     /** {@code null-ok;} nickname, if specified (used for debugging) */
     77     private final String nickname;
     78 
     79     /**
     80      * Constructs an instance. This method is private. Use one of the
     81      * public constructors.
     82      *
     83      * @param opcode the opcode; one of the constants in {@link RegOps}
     84      * @param result {@code non-null;} result type of this operation; {@link
     85      * Type#VOID} for no-result operations
     86      * @param sources {@code non-null;} types of all the sources of this operation
     87      * @param exceptions {@code non-null;} list of possible types thrown by this
     88      * operation
     89      * @param branchingness the branchingness of this op; one of the
     90      * {@code BRANCH_*} constants
     91      * @param isCallLike whether the op is a function/method call or similar
     92      * @param nickname {@code null-ok;} optional nickname (used for debugging)
     93      */
     94     public Rop(int opcode, Type result, TypeList sources,
     95                TypeList exceptions, int branchingness, boolean isCallLike,
     96                String nickname) {
     97         if (result == null) {
     98             throw new NullPointerException("result == null");
     99         }
    100 
    101         if (sources == null) {
    102             throw new NullPointerException("sources == null");
    103         }
    104 
    105         if (exceptions == null) {
    106             throw new NullPointerException("exceptions == null");
    107         }
    108 
    109         if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) {
    110             throw new IllegalArgumentException("bogus branchingness");
    111         }
    112 
    113         if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) {
    114             throw new IllegalArgumentException("exceptions / branchingness " +
    115                                                "mismatch");
    116         }
    117 
    118         this.opcode = opcode;
    119         this.result = result;
    120         this.sources = sources;
    121         this.exceptions = exceptions;
    122         this.branchingness = branchingness;
    123         this.isCallLike = isCallLike;
    124         this.nickname = nickname;
    125     }
    126 
    127     /**
    128      * Constructs an instance. The constructed instance is never a
    129      * call-like op (see {@link #isCallLike}).
    130      *
    131      * @param opcode the opcode; one of the constants in {@link RegOps}
    132      * @param result {@code non-null;} result type of this operation; {@link
    133      * Type#VOID} for no-result operations
    134      * @param sources {@code non-null;} types of all the sources of this operation
    135      * @param exceptions {@code non-null;} list of possible types thrown by this
    136      * operation
    137      * @param branchingness the branchingness of this op; one of the
    138      * {@code BRANCH_*} constants
    139      * @param nickname {@code null-ok;} optional nickname (used for debugging)
    140      */
    141     public Rop(int opcode, Type result, TypeList sources,
    142                TypeList exceptions, int branchingness, String nickname) {
    143         this(opcode, result, sources, exceptions, branchingness, false,
    144              nickname);
    145     }
    146 
    147     /**
    148      * Constructs a no-exception instance. The constructed instance is never a
    149      * call-like op (see {@link #isCallLike}).
    150      *
    151      * @param opcode the opcode; one of the constants in {@link RegOps}
    152      * @param result {@code non-null;} result type of this operation; {@link
    153      * Type#VOID} for no-result operations
    154      * @param sources {@code non-null;} types of all the sources of this operation
    155      * @param branchingness the branchingness of this op; one of the
    156      * {@code BRANCH_*} constants
    157      * @param nickname {@code null-ok;} optional nickname (used for debugging)
    158      */
    159     public Rop(int opcode, Type result, TypeList sources, int branchingness,
    160                String nickname) {
    161         this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false,
    162              nickname);
    163     }
    164 
    165     /**
    166      * Constructs a non-branching no-exception instance. The
    167      * {@code branchingness} is always {@code BRANCH_NONE},
    168      * and it is never a call-like op (see {@link #isCallLike}).
    169      *
    170      * @param opcode the opcode; one of the constants in {@link RegOps}
    171      * @param result {@code non-null;} result type of this operation; {@link
    172      * Type#VOID} for no-result operations
    173      * @param sources {@code non-null;} types of all the sources of this operation
    174      * @param nickname {@code null-ok;} optional nickname (used for debugging)
    175      */
    176     public Rop(int opcode, Type result, TypeList sources, String nickname) {
    177         this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE,
    178              false, nickname);
    179     }
    180 
    181     /**
    182      * Constructs a non-empty exceptions instance. Its
    183      * {@code branchingness} is always {@code BRANCH_THROW},
    184      * but it is never a call-like op (see {@link #isCallLike}).
    185      *
    186      * @param opcode the opcode; one of the constants in {@link RegOps}
    187      * @param result {@code non-null;} result type of this operation; {@link
    188      * Type#VOID} for no-result operations
    189      * @param sources {@code non-null;} types of all the sources of this operation
    190      * @param exceptions {@code non-null;} list of possible types thrown by this
    191      * operation
    192      * @param nickname {@code null-ok;} optional nickname (used for debugging)
    193      */
    194     public Rop(int opcode, Type result, TypeList sources, TypeList exceptions,
    195                String nickname) {
    196         this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false,
    197              nickname);
    198     }
    199 
    200     /**
    201      * Constructs a non-nicknamed instance with non-empty exceptions, which
    202      * is always a call-like op (see {@link #isCallLike}). Its
    203      * {@code branchingness} is always {@code BRANCH_THROW}.
    204      *
    205      * @param opcode the opcode; one of the constants in {@link RegOps}
    206      * @param sources {@code non-null;} types of all the sources of this operation
    207      * @param exceptions {@code non-null;} list of possible types thrown by this
    208      * operation
    209      */
    210     public Rop(int opcode, TypeList sources, TypeList exceptions) {
    211         this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true,
    212              null);
    213     }
    214 
    215     /** {@inheritDoc} */
    216     @Override
    217     public boolean equals(Object other) {
    218         if (this == other) {
    219             // Easy out.
    220             return true;
    221         }
    222 
    223         if (!(other instanceof Rop)) {
    224             return false;
    225         }
    226 
    227         Rop rop = (Rop) other;
    228 
    229         return (opcode == rop.opcode) &&
    230             (branchingness == rop.branchingness) &&
    231             (result == rop.result) &&
    232             sources.equals(rop.sources) &&
    233             exceptions.equals(rop.exceptions);
    234     }
    235 
    236     /** {@inheritDoc} */
    237     @Override
    238     public int hashCode() {
    239         int h = (opcode * 31) + branchingness;
    240         h = (h * 31) + result.hashCode();
    241         h = (h * 31) + sources.hashCode();
    242         h = (h * 31) + exceptions.hashCode();
    243 
    244         return h;
    245     }
    246 
    247     /** {@inheritDoc} */
    248     @Override
    249     public String toString() {
    250         StringBuffer sb = new StringBuffer(40);
    251 
    252         sb.append("Rop{");
    253 
    254         sb.append(RegOps.opName(opcode));
    255 
    256         if (result != Type.VOID) {
    257             sb.append(" ");
    258             sb.append(result);
    259         } else {
    260             sb.append(" .");
    261         }
    262 
    263         sb.append(" <-");
    264 
    265         int sz = sources.size();
    266         if (sz == 0) {
    267             sb.append(" .");
    268         } else {
    269             for (int i = 0; i < sz; i++) {
    270                 sb.append(' ');
    271                 sb.append(sources.getType(i));
    272             }
    273         }
    274 
    275         if (isCallLike) {
    276             sb.append(" call");
    277         }
    278 
    279         sz = exceptions.size();
    280         if (sz != 0) {
    281             sb.append(" throws");
    282             for (int i = 0; i < sz; i++) {
    283                 sb.append(' ');
    284                 Type one = exceptions.getType(i);
    285                 if (one == Type.THROWABLE) {
    286                     sb.append("<any>");
    287                 } else {
    288                     sb.append(exceptions.getType(i));
    289                 }
    290             }
    291         } else {
    292             switch (branchingness) {
    293                 case BRANCH_NONE:   sb.append(" flows"); break;
    294                 case BRANCH_RETURN: sb.append(" returns"); break;
    295                 case BRANCH_GOTO:   sb.append(" gotos"); break;
    296                 case BRANCH_IF:     sb.append(" ifs"); break;
    297                 case BRANCH_SWITCH: sb.append(" switches"); break;
    298                 default: sb.append(" " + Hex.u1(branchingness)); break;
    299             }
    300         }
    301 
    302         sb.append('}');
    303 
    304         return sb.toString();
    305     }
    306 
    307     /**
    308      * Gets the opcode.
    309      *
    310      * @return the opcode
    311      */
    312     public int getOpcode() {
    313         return opcode;
    314     }
    315 
    316     /**
    317      * Gets the result type. A return value of {@link Type#VOID}
    318      * means this operation returns nothing.
    319      *
    320      * @return {@code null-ok;} the result spec
    321      */
    322     public Type getResult() {
    323         return result;
    324     }
    325 
    326     /**
    327      * Gets the source types.
    328      *
    329      * @return {@code non-null;} the source types
    330      */
    331     public TypeList getSources() {
    332         return sources;
    333     }
    334 
    335     /**
    336      * Gets the list of exception types that might be thrown.
    337      *
    338      * @return {@code non-null;} the list of exception types
    339      */
    340     public TypeList getExceptions() {
    341         return exceptions;
    342     }
    343 
    344     /**
    345      * Gets the branchingness of this instance.
    346      *
    347      * @return the branchingness
    348      */
    349     public int getBranchingness() {
    350         return branchingness;
    351     }
    352 
    353     /**
    354      * Gets whether this opcode is a function/method call or similar.
    355      *
    356      * @return {@code true} iff this opcode is call-like
    357      */
    358     public boolean isCallLike() {
    359         return isCallLike;
    360     }
    361 
    362 
    363     /**
    364      * Gets whether this opcode is commutative (the order of its sources are
    365      * unimportant) or not. All commutative Rops have exactly two sources and
    366      * have no branchiness.
    367      *
    368      * @return true if rop is commutative
    369      */
    370     public boolean isCommutative() {
    371         switch (opcode) {
    372             case RegOps.AND:
    373             case RegOps.OR:
    374             case RegOps.XOR:
    375             case RegOps.ADD:
    376             case RegOps.MUL:
    377                 return true;
    378             default:
    379                 return false;
    380         }
    381     }
    382 
    383     /**
    384      * Gets the nickname. If this instance has no nickname, this returns
    385      * the result of calling {@link #toString}.
    386      *
    387      * @return {@code non-null;} the nickname
    388      */
    389     public String getNickname() {
    390         if (nickname != null) {
    391             return nickname;
    392         }
    393 
    394         return toString();
    395     }
    396 
    397     /**
    398      * Gets whether this operation can possibly throw an exception. This
    399      * is just a convenient wrapper for
    400      * {@code getExceptions().size() != 0}.
    401      *
    402      * @return {@code true} iff this operation can possibly throw
    403      */
    404     public final boolean canThrow() {
    405         return (exceptions.size() != 0);
    406     }
    407 }
    408