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.cf.code;
     18 
     19 import com.android.dx.rop.code.LocalItem;
     20 import com.android.dx.rop.code.RegisterSpec;
     21 import com.android.dx.rop.cst.Constant;
     22 import com.android.dx.rop.type.Prototype;
     23 import com.android.dx.rop.type.StdTypeList;
     24 import com.android.dx.rop.type.Type;
     25 import com.android.dx.rop.type.TypeBearer;
     26 import java.util.ArrayList;
     27 
     28 /**
     29  * Base implementation of {@link Machine}.
     30  *
     31  * <p><b>Note:</b> For the most part, the documentation for this class
     32  * ignores the distinction between {@link Type} and {@link
     33  * TypeBearer}.</p>
     34  */
     35 public abstract class BaseMachine implements Machine {
     36     /* {@code non-null;} the prototype for the associated method */
     37     private final Prototype prototype;
     38 
     39     /** {@code non-null;} primary arguments */
     40     private TypeBearer[] args;
     41 
     42     /** {@code >= 0;} number of primary arguments */
     43     private int argCount;
     44 
     45     /** {@code null-ok;} type of the operation, if salient */
     46     private Type auxType;
     47 
     48     /** auxiliary {@code int} argument */
     49     private int auxInt;
     50 
     51     /** {@code null-ok;} auxiliary constant argument */
     52     private Constant auxCst;
     53 
     54     /** auxiliary branch target argument */
     55     private int auxTarget;
     56 
     57     /** {@code null-ok;} auxiliary switch cases argument */
     58     private SwitchList auxCases;
     59 
     60     /** {@code null-ok;} auxiliary initial value list for newarray */
     61     private ArrayList<Constant> auxInitValues;
     62 
     63     /** {@code >= -1;} last local accessed */
     64     private int localIndex;
     65 
     66     /** specifies if local has info in the local variable table */
     67     private boolean localInfo;
     68 
     69     /** {@code null-ok;} local target spec, if salient and calculated */
     70     private RegisterSpec localTarget;
     71 
     72     /** {@code non-null;} results */
     73     private TypeBearer[] results;
     74 
     75     /**
     76      * {@code >= -1;} count of the results, or {@code -1} if no results
     77      * have been set
     78      */
     79     private int resultCount;
     80 
     81     /**
     82      * Constructs an instance.
     83      *
     84      * @param prototype {@code non-null;} the prototype for the
     85      * associated method
     86      */
     87     public BaseMachine(Prototype prototype) {
     88         if (prototype == null) {
     89             throw new NullPointerException("prototype == null");
     90         }
     91 
     92         this.prototype = prototype;
     93         args = new TypeBearer[10];
     94         results = new TypeBearer[6];
     95         clearArgs();
     96     }
     97 
     98     /** {@inheritDoc} */
     99     public Prototype getPrototype() {
    100         return prototype;
    101     }
    102 
    103     /** {@inheritDoc} */
    104     public final void clearArgs() {
    105         argCount = 0;
    106         auxType = null;
    107         auxInt = 0;
    108         auxCst = null;
    109         auxTarget = 0;
    110         auxCases = null;
    111         auxInitValues = null;
    112         localIndex = -1;
    113         localInfo = false;
    114         localTarget = null;
    115         resultCount = -1;
    116     }
    117 
    118     /** {@inheritDoc} */
    119     public final void popArgs(Frame frame, int count) {
    120         ExecutionStack stack = frame.getStack();
    121 
    122         clearArgs();
    123 
    124         if (count > args.length) {
    125             // Grow args, and add a little extra room to grow even more.
    126             args = new TypeBearer[count + 10];
    127         }
    128 
    129         for (int i = count - 1; i >= 0; i--) {
    130             args[i] = stack.pop();
    131         }
    132 
    133         argCount = count;
    134     }
    135 
    136     /** {@inheritDoc} */
    137     public void popArgs(Frame frame, Prototype prototype) {
    138         StdTypeList types = prototype.getParameterTypes();
    139         int size = types.size();
    140 
    141         // Use the above method to do the actual popping...
    142         popArgs(frame, size);
    143 
    144         // ...and then verify the popped types.
    145 
    146         for (int i = 0; i < size; i++) {
    147             if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) {
    148                 throw new SimException("at stack depth " + (size - 1 - i) +
    149                         ", expected type " + types.getType(i).toHuman() +
    150                         " but found " + args[i].getType().toHuman());
    151             }
    152         }
    153     }
    154 
    155     public final void popArgs(Frame frame, Type type) {
    156         // Use the above method to do the actual popping...
    157         popArgs(frame, 1);
    158 
    159         // ...and then verify the popped type.
    160         if (! Merger.isPossiblyAssignableFrom(type, args[0])) {
    161             throw new SimException("expected type " + type.toHuman() +
    162                     " but found " + args[0].getType().toHuman());
    163         }
    164     }
    165 
    166     /** {@inheritDoc} */
    167     public final void popArgs(Frame frame, Type type1, Type type2) {
    168         // Use the above method to do the actual popping...
    169         popArgs(frame, 2);
    170 
    171         // ...and then verify the popped types.
    172 
    173         if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
    174             throw new SimException("expected type " + type1.toHuman() +
    175                     " but found " + args[0].getType().toHuman());
    176         }
    177 
    178         if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
    179             throw new SimException("expected type " + type2.toHuman() +
    180                     " but found " + args[1].getType().toHuman());
    181         }
    182     }
    183 
    184     /** {@inheritDoc} */
    185     public final void popArgs(Frame frame, Type type1, Type type2,
    186             Type type3) {
    187         // Use the above method to do the actual popping...
    188         popArgs(frame, 3);
    189 
    190         // ...and then verify the popped types.
    191 
    192         if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
    193             throw new SimException("expected type " + type1.toHuman() +
    194                     " but found " + args[0].getType().toHuman());
    195         }
    196 
    197         if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
    198             throw new SimException("expected type " + type2.toHuman() +
    199                     " but found " + args[1].getType().toHuman());
    200         }
    201 
    202         if (! Merger.isPossiblyAssignableFrom(type3, args[2])) {
    203             throw new SimException("expected type " + type3.toHuman() +
    204                     " but found " + args[2].getType().toHuman());
    205         }
    206     }
    207 
    208     /** {@inheritDoc} */
    209     public final void localArg(Frame frame, int idx) {
    210         clearArgs();
    211         args[0] = frame.getLocals().get(idx);
    212         argCount = 1;
    213         localIndex = idx;
    214     }
    215 
    216     /** {@inheritDoc} */
    217     public final void localInfo(boolean local) {
    218         localInfo = local;
    219     }
    220 
    221     /** {@inheritDoc} */
    222     public final void auxType(Type type) {
    223         auxType = type;
    224     }
    225 
    226     /** {@inheritDoc} */
    227     public final void auxIntArg(int value) {
    228         auxInt = value;
    229     }
    230 
    231     /** {@inheritDoc} */
    232     public final void auxCstArg(Constant cst) {
    233         if (cst == null) {
    234             throw new NullPointerException("cst == null");
    235         }
    236 
    237         auxCst = cst;
    238     }
    239 
    240     /** {@inheritDoc} */
    241     public final void auxTargetArg(int target) {
    242         auxTarget = target;
    243     }
    244 
    245     /** {@inheritDoc} */
    246     public final void auxSwitchArg(SwitchList cases) {
    247         if (cases == null) {
    248             throw new NullPointerException("cases == null");
    249         }
    250 
    251         auxCases = cases;
    252     }
    253 
    254     /** {@inheritDoc} */
    255     public final void auxInitValues(ArrayList<Constant> initValues) {
    256         auxInitValues = initValues;
    257     }
    258 
    259     /** {@inheritDoc} */
    260     public final void localTarget(int idx, Type type, LocalItem local) {
    261         localTarget = RegisterSpec.makeLocalOptional(idx, type, local);
    262     }
    263 
    264     /**
    265      * Gets the number of primary arguments.
    266      *
    267      * @return {@code >= 0;} the number of primary arguments
    268      */
    269     protected final int argCount() {
    270         return argCount;
    271     }
    272 
    273     /**
    274      * Gets the width of the arguments (where a category-2 value counts as
    275      * two).
    276      *
    277      * @return {@code >= 0;} the argument width
    278      */
    279     protected final int argWidth() {
    280         int result = 0;
    281 
    282         for (int i = 0; i < argCount; i++) {
    283             result += args[i].getType().getCategory();
    284         }
    285 
    286         return result;
    287     }
    288 
    289     /**
    290      * Gets the {@code n}th primary argument.
    291      *
    292      * @param n {@code >= 0, < argCount();} which argument
    293      * @return {@code non-null;} the indicated argument
    294      */
    295     protected final TypeBearer arg(int n) {
    296         if (n >= argCount) {
    297             throw new IllegalArgumentException("n >= argCount");
    298         }
    299 
    300         try {
    301             return args[n];
    302         } catch (ArrayIndexOutOfBoundsException ex) {
    303             // Translate the exception.
    304             throw new IllegalArgumentException("n < 0");
    305         }
    306     }
    307 
    308     /**
    309      * Gets the type auxiliary argument.
    310      *
    311      * @return {@code null-ok;} the salient type
    312      */
    313     protected final Type getAuxType() {
    314         return auxType;
    315     }
    316 
    317     /**
    318      * Gets the {@code int} auxiliary argument.
    319      *
    320      * @return the argument value
    321      */
    322     protected final int getAuxInt() {
    323         return auxInt;
    324     }
    325 
    326     /**
    327      * Gets the constant auxiliary argument.
    328      *
    329      * @return {@code null-ok;} the argument value
    330      */
    331     protected final Constant getAuxCst() {
    332         return auxCst;
    333     }
    334 
    335     /**
    336      * Gets the branch target auxiliary argument.
    337      *
    338      * @return the argument value
    339      */
    340     protected final int getAuxTarget() {
    341         return auxTarget;
    342     }
    343 
    344     /**
    345      * Gets the switch cases auxiliary argument.
    346      *
    347      * @return {@code null-ok;} the argument value
    348      */
    349     protected final SwitchList getAuxCases() {
    350         return auxCases;
    351     }
    352 
    353     /**
    354      * Gets the init values auxiliary argument.
    355      *
    356      * @return {@code null-ok;} the argument value
    357      */
    358     protected final ArrayList<Constant> getInitValues() {
    359         return auxInitValues;
    360     }
    361     /**
    362      * Gets the last local index accessed.
    363      *
    364      * @return {@code >= -1;} the salient local index or {@code -1} if none
    365      * was set since the last time {@link #clearArgs} was called
    366      */
    367     protected final int getLocalIndex() {
    368         return localIndex;
    369     }
    370 
    371     /**
    372      * Gets whether the loaded local has info in the local variable table.
    373      *
    374      * @return {@code true} if local arg has info in the local variable table
    375      */
    376     protected final boolean getLocalInfo() {
    377         return localInfo;
    378     }
    379 
    380     /**
    381      * Gets the target local register spec of the current operation, if any.
    382      * The local target spec is the combination of the values indicated
    383      * by a previous call to {@link #localTarget} with the type of what
    384      * should be the sole result set by a call to {@link #setResult} (or
    385      * the combination {@link #clearResult} then {@link #addResult}.
    386      *
    387      * @param isMove {@code true} if the operation being performed on the
    388      * local is a move. This will cause constant values to be propagated
    389      * to the returned local
    390      * @return {@code null-ok;} the salient register spec or {@code null} if no
    391      * local target was set since the last time {@link #clearArgs} was
    392      * called
    393      */
    394     protected final RegisterSpec getLocalTarget(boolean isMove) {
    395         if (localTarget == null) {
    396             return null;
    397         }
    398 
    399         if (resultCount != 1) {
    400             throw new SimException("local target with " +
    401                     ((resultCount == 0) ? "no" : "multiple") + " results");
    402         }
    403 
    404         TypeBearer result = results[0];
    405         Type resultType = result.getType();
    406         Type localType = localTarget.getType();
    407 
    408         if (resultType == localType) {
    409             /*
    410              * If this is to be a move operation and the result is a
    411              * known value, make the returned localTarget embody that
    412              * value.
    413              */
    414             if (isMove) {
    415                 return localTarget.withType(result);
    416             } else {
    417                 return localTarget;
    418             }
    419         }
    420 
    421         if (! Merger.isPossiblyAssignableFrom(localType, resultType)) {
    422             // The result and local types are inconsistent. Complain!
    423             throwLocalMismatch(resultType, localType);
    424             return null;
    425         }
    426 
    427         if (localType == Type.OBJECT) {
    428             /*
    429              * The result type is more specific than the local type,
    430              * so use that instead.
    431              */
    432             localTarget = localTarget.withType(result);
    433         }
    434 
    435         return localTarget;
    436     }
    437 
    438     /**
    439      * Clears the results.
    440      */
    441     protected final void clearResult() {
    442         resultCount = 0;
    443     }
    444 
    445     /**
    446      * Sets the results list to be the given single value.
    447      *
    448      * <p><b>Note:</b> If there is more than one result value, the
    449      * others may be added by using {@link #addResult}.</p>
    450      *
    451      * @param result {@code non-null;} result value
    452      */
    453     protected final void setResult(TypeBearer result) {
    454         if (result == null) {
    455             throw new NullPointerException("result == null");
    456         }
    457 
    458         results[0] = result;
    459         resultCount = 1;
    460     }
    461 
    462     /**
    463      * Adds an additional element to the list of results.
    464      *
    465      * @see #setResult
    466      *
    467      * @param result {@code non-null;} result value
    468      */
    469     protected final void addResult(TypeBearer result) {
    470         if (result == null) {
    471             throw new NullPointerException("result == null");
    472         }
    473 
    474         results[resultCount] = result;
    475         resultCount++;
    476     }
    477 
    478     /**
    479      * Gets the count of results. This throws an exception if results were
    480      * never set. (Explicitly clearing the results counts as setting them.)
    481      *
    482      * @return {@code >= 0;} the count
    483      */
    484     protected final int resultCount() {
    485         if (resultCount < 0) {
    486             throw new SimException("results never set");
    487         }
    488 
    489         return resultCount;
    490     }
    491 
    492     /**
    493      * Gets the width of the results (where a category-2 value counts as
    494      * two).
    495      *
    496      * @return {@code >= 0;} the result width
    497      */
    498     protected final int resultWidth() {
    499         int width = 0;
    500 
    501         for (int i = 0; i < resultCount; i++) {
    502             width += results[i].getType().getCategory();
    503         }
    504 
    505         return width;
    506     }
    507 
    508     /**
    509      * Gets the {@code n}th result value.
    510      *
    511      * @param n {@code >= 0, < resultCount();} which result
    512      * @return {@code non-null;} the indicated result value
    513      */
    514     protected final TypeBearer result(int n) {
    515         if (n >= resultCount) {
    516             throw new IllegalArgumentException("n >= resultCount");
    517         }
    518 
    519         try {
    520             return results[n];
    521         } catch (ArrayIndexOutOfBoundsException ex) {
    522             // Translate the exception.
    523             throw new IllegalArgumentException("n < 0");
    524         }
    525     }
    526 
    527     /**
    528      * Stores the results of the latest operation into the given frame. If
    529      * there is a local target (see {@link #localTarget}), then the sole
    530      * result is stored to that target; otherwise any results are pushed
    531      * onto the stack.
    532      *
    533      * @param frame {@code non-null;} frame to operate on
    534      */
    535     protected final void storeResults(Frame frame) {
    536         if (resultCount < 0) {
    537             throw new SimException("results never set");
    538         }
    539 
    540         if (resultCount == 0) {
    541             // Nothing to do.
    542             return;
    543         }
    544 
    545         if (localTarget != null) {
    546             /*
    547              * Note: getLocalTarget() doesn't necessarily return
    548              * localTarget directly.
    549              */
    550             frame.getLocals().set(getLocalTarget(false));
    551         } else {
    552             ExecutionStack stack = frame.getStack();
    553             for (int i = 0; i < resultCount; i++) {
    554                 if (localInfo) {
    555                     stack.setLocal();
    556                 }
    557                 stack.push(results[i]);
    558             }
    559         }
    560     }
    561 
    562     /**
    563      * Throws an exception that indicates a mismatch in local variable
    564      * types.
    565      *
    566      * @param found {@code non-null;} the encountered type
    567      * @param local {@code non-null;} the local variable's claimed type
    568      */
    569     public static void throwLocalMismatch(TypeBearer found,
    570             TypeBearer local) {
    571         throw new SimException("local variable type mismatch: " +
    572                 "attempt to set or access a value of type " +
    573                 found.toHuman() +
    574                 " using a local variable of type " +
    575                 local.toHuman() +
    576                 ". This is symptomatic of .class transformation tools " +
    577                 "that ignore local variable information.");
    578     }
    579 }
    580