Home | History | Annotate | Download | only in direct
      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.direct;
     18 
     19 import com.android.dx.cf.code.ByteOps;
     20 import com.android.dx.cf.code.BytecodeArray;
     21 import com.android.dx.cf.code.SwitchList;
     22 import com.android.dx.cf.iface.ParseObserver;
     23 import com.android.dx.rop.cst.Constant;
     24 import com.android.dx.rop.cst.CstDouble;
     25 import com.android.dx.rop.cst.CstFloat;
     26 import com.android.dx.rop.cst.CstInteger;
     27 import com.android.dx.rop.cst.CstKnownNull;
     28 import com.android.dx.rop.cst.CstLong;
     29 import com.android.dx.rop.cst.CstType;
     30 import com.android.dx.rop.type.Type;
     31 import com.android.dx.util.ByteArray;
     32 import com.android.dx.util.Hex;
     33 import java.util.ArrayList;
     34 
     35 /**
     36  * Bytecode visitor to use when "observing" bytecode getting parsed.
     37  */
     38 public class CodeObserver implements BytecodeArray.Visitor {
     39     /** {@code non-null;} actual array of bytecode */
     40     private final ByteArray bytes;
     41 
     42     /** {@code non-null;} observer to inform of parsing */
     43     private final ParseObserver observer;
     44 
     45     /**
     46      * Constructs an instance.
     47      *
     48      * @param bytes {@code non-null;} actual array of bytecode
     49      * @param observer {@code non-null;} observer to inform of parsing
     50      */
     51     public CodeObserver(ByteArray bytes, ParseObserver observer) {
     52         if (bytes == null) {
     53             throw new NullPointerException("bytes == null");
     54         }
     55 
     56         if (observer == null) {
     57             throw new NullPointerException("observer == null");
     58         }
     59 
     60         this.bytes = bytes;
     61         this.observer = observer;
     62     }
     63 
     64     /** {@inheritDoc} */
     65     public void visitInvalid(int opcode, int offset, int length) {
     66         observer.parsed(bytes, offset, length, header(offset));
     67     }
     68 
     69     /** {@inheritDoc} */
     70     public void visitNoArgs(int opcode, int offset, int length, Type type) {
     71         observer.parsed(bytes, offset, length, header(offset));
     72     }
     73 
     74     /** {@inheritDoc} */
     75     public void visitLocal(int opcode, int offset, int length,
     76             int idx, Type type, int value) {
     77         String idxStr = (length <= 3) ? Hex.u1(idx) : Hex.u2(idx);
     78         boolean argComment = (length == 1);
     79         String valueStr = "";
     80 
     81         if (opcode == ByteOps.IINC) {
     82             valueStr = ", #" +
     83                 ((length <= 3) ? Hex.s1(value) : Hex.s2(value));
     84         }
     85 
     86         String catStr = "";
     87         if (type.isCategory2()) {
     88             catStr = (argComment ? "," : " //") + " category-2";
     89         }
     90 
     91         observer.parsed(bytes, offset, length,
     92                         header(offset) + (argComment ? " // " : " ") +
     93                         idxStr + valueStr + catStr);
     94     }
     95 
     96     /** {@inheritDoc} */
     97     public void visitConstant(int opcode, int offset, int length,
     98             Constant cst, int value) {
     99         if (cst instanceof CstKnownNull) {
    100             // This is aconst_null.
    101             visitNoArgs(opcode, offset, length, null);
    102             return;
    103         }
    104 
    105         if (cst instanceof CstInteger) {
    106             visitLiteralInt(opcode, offset, length, value);
    107             return;
    108         }
    109 
    110         if (cst instanceof CstLong) {
    111             visitLiteralLong(opcode, offset, length,
    112                              ((CstLong) cst).getValue());
    113             return;
    114         }
    115 
    116         if (cst instanceof CstFloat) {
    117             visitLiteralFloat(opcode, offset, length,
    118                               ((CstFloat) cst).getIntBits());
    119             return;
    120         }
    121 
    122         if (cst instanceof CstDouble) {
    123             visitLiteralDouble(opcode, offset, length,
    124                              ((CstDouble) cst).getLongBits());
    125             return;
    126         }
    127 
    128         String valueStr = "";
    129         if (value != 0) {
    130             valueStr = ", ";
    131             if (opcode == ByteOps.MULTIANEWARRAY) {
    132                 valueStr += Hex.u1(value);
    133             } else {
    134                 valueStr += Hex.u2(value);
    135             }
    136         }
    137 
    138         observer.parsed(bytes, offset, length,
    139                         header(offset) + " " + cst + valueStr);
    140     }
    141 
    142     /** {@inheritDoc} */
    143     public void visitBranch(int opcode, int offset, int length,
    144                             int target) {
    145         String targetStr = (length <= 3) ? Hex.u2(target) : Hex.u4(target);
    146         observer.parsed(bytes, offset, length,
    147                         header(offset) + " " + targetStr);
    148     }
    149 
    150     /** {@inheritDoc} */
    151     public void visitSwitch(int opcode, int offset, int length,
    152             SwitchList cases, int padding) {
    153         int sz = cases.size();
    154         StringBuffer sb = new StringBuffer(sz * 20 + 100);
    155 
    156         sb.append(header(offset));
    157         if (padding != 0) {
    158             sb.append(" // padding: " + Hex.u4(padding));
    159         }
    160         sb.append('\n');
    161 
    162         for (int i = 0; i < sz; i++) {
    163             sb.append("  ");
    164             sb.append(Hex.s4(cases.getValue(i)));
    165             sb.append(": ");
    166             sb.append(Hex.u2(cases.getTarget(i)));
    167             sb.append('\n');
    168         }
    169 
    170         sb.append("  default: ");
    171         sb.append(Hex.u2(cases.getDefaultTarget()));
    172 
    173         observer.parsed(bytes, offset, length, sb.toString());
    174     }
    175 
    176     /** {@inheritDoc} */
    177     public void visitNewarray(int offset, int length, CstType cst,
    178             ArrayList<Constant> intVals) {
    179         String commentOrSpace = (length == 1) ? " // " : " ";
    180         String typeName = cst.getClassType().getComponentType().toHuman();
    181 
    182         observer.parsed(bytes, offset, length,
    183                         header(offset) + commentOrSpace + typeName);
    184     }
    185 
    186     /** {@inheritDoc} */
    187     public void setPreviousOffset(int offset) {
    188         // Do nothing
    189     }
    190 
    191     /** {@inheritDoc} */
    192     public int getPreviousOffset() {
    193         return -1;
    194     }
    195 
    196     /**
    197      * Helper to produce the first bit of output for each instruction.
    198      *
    199      * @param offset the offset to the start of the instruction
    200      */
    201     private String header(int offset) {
    202         /*
    203          * Note: This uses the original bytecode, not the
    204          * possibly-transformed one.
    205          */
    206         int opcode = bytes.getUnsignedByte(offset);
    207         String name = ByteOps.opName(opcode);
    208 
    209         if (opcode == ByteOps.WIDE) {
    210             opcode = bytes.getUnsignedByte(offset + 1);
    211             name += " " + ByteOps.opName(opcode);
    212         }
    213 
    214         return Hex.u2(offset) + ": " + name;
    215     }
    216 
    217     /**
    218      * Helper for {@link #visitConstant} where the constant is an
    219      * {@code int}.
    220      *
    221      * @param opcode the opcode
    222      * @param offset offset to the instruction
    223      * @param length instruction length
    224      * @param value constant value
    225      */
    226     private void visitLiteralInt(int opcode, int offset, int length,
    227             int value) {
    228         String commentOrSpace = (length == 1) ? " // " : " ";
    229         String valueStr;
    230 
    231         opcode = bytes.getUnsignedByte(offset); // Compare with orig op below.
    232         if ((length == 1) || (opcode == ByteOps.BIPUSH)) {
    233             valueStr = "#" + Hex.s1(value);
    234         } else if (opcode == ByteOps.SIPUSH) {
    235             valueStr = "#" + Hex.s2(value);
    236         } else {
    237             valueStr = "#" + Hex.s4(value);
    238         }
    239 
    240         observer.parsed(bytes, offset, length,
    241                         header(offset) + commentOrSpace + valueStr);
    242     }
    243 
    244     /**
    245      * Helper for {@link #visitConstant} where the constant is a
    246      * {@code long}.
    247      *
    248      * @param opcode the opcode
    249      * @param offset offset to the instruction
    250      * @param length instruction length
    251      * @param value constant value
    252      */
    253     private void visitLiteralLong(int opcode, int offset, int length,
    254             long value) {
    255         String commentOrLit = (length == 1) ? " // " : " #";
    256         String valueStr;
    257 
    258         if (length == 1) {
    259             valueStr = Hex.s1((int) value);
    260         } else {
    261             valueStr = Hex.s8(value);
    262         }
    263 
    264         observer.parsed(bytes, offset, length,
    265                         header(offset) + commentOrLit + valueStr);
    266     }
    267 
    268     /**
    269      * Helper for {@link #visitConstant} where the constant is a
    270      * {@code float}.
    271      *
    272      * @param opcode the opcode
    273      * @param offset offset to the instruction
    274      * @param length instruction length
    275      * @param bits constant value, as float-bits
    276      */
    277     private void visitLiteralFloat(int opcode, int offset, int length,
    278             int bits) {
    279         String optArg = (length != 1) ? " #" + Hex.u4(bits) : "";
    280 
    281         observer.parsed(bytes, offset, length,
    282                         header(offset) + optArg + " // " +
    283                         Float.intBitsToFloat(bits));
    284     }
    285 
    286     /**
    287      * Helper for {@link #visitConstant} where the constant is a
    288      * {@code double}.
    289      *
    290      * @param opcode the opcode
    291      * @param offset offset to the instruction
    292      * @param length instruction length
    293      * @param bits constant value, as double-bits
    294      */
    295     private void visitLiteralDouble(int opcode, int offset, int length,
    296             long bits) {
    297         String optArg = (length != 1) ? " #" + Hex.u8(bits) : "";
    298 
    299         observer.parsed(bytes, offset, length,
    300                         header(offset) + optArg + " // " +
    301                         Double.longBitsToDouble(bits));
    302     }
    303 }
    304