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