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.cf.attrib.AttCode;
     20 import com.android.dx.cf.attrib.AttLineNumberTable;
     21 import com.android.dx.cf.attrib.AttLocalVariableTable;
     22 import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
     23 import com.android.dx.cf.iface.AttributeList;
     24 import com.android.dx.cf.iface.ClassFile;
     25 import com.android.dx.cf.iface.Method;
     26 import com.android.dx.rop.code.AccessFlags;
     27 import com.android.dx.rop.code.SourcePosition;
     28 import com.android.dx.rop.cst.CstNat;
     29 import com.android.dx.rop.cst.CstString;
     30 import com.android.dx.rop.cst.CstType;
     31 import com.android.dx.rop.type.Prototype;
     32 
     33 /**
     34  * Container for all the giblets that make up a concrete Java bytecode method.
     35  * It implements {@link Method}, so it provides all the original access
     36  * (by delegation), but it also constructs and keeps useful versions of
     37  * stuff extracted from the method's {@code Code} attribute.
     38  */
     39 public final class ConcreteMethod implements Method {
     40     /** {@code non-null;} method being wrapped */
     41     private final Method method;
     42 
     43     /** {@code non-null;} the {@code ClassFile} the method belongs to. */
     44     private final ClassFile classFile;
     45 
     46     /** {@code non-null;} the code attribute */
     47     private final AttCode attCode;
     48 
     49     /** {@code non-null;} line number list */
     50     private final LineNumberList lineNumbers;
     51 
     52     /** {@code non-null;} local variable list */
     53     private final LocalVariableList localVariables;
     54 
     55     /**
     56      * Constructs an instance.
     57      *
     58      * @param method {@code non-null;} the method to be based on
     59      * @param classFile {@code non-null;} the class file that contains this method
     60      * @param keepLines whether to keep the line number information
     61      * (if any)
     62      * @param keepLocals whether to keep the local variable
     63      * information (if any)
     64      */
     65     public ConcreteMethod(Method method, ClassFile classFile,
     66             boolean keepLines, boolean keepLocals) {
     67         this.method = method;
     68         this.classFile = classFile;
     69 
     70         AttributeList attribs = method.getAttributes();
     71         this.attCode = (AttCode) attribs.findFirst(AttCode.ATTRIBUTE_NAME);
     72 
     73         AttributeList codeAttribs = attCode.getAttributes();
     74 
     75         /*
     76          * Combine all LineNumberTable attributes into one, with the
     77          * combined result saved into the instance. The following code
     78          * isn't particularly efficient for doing merges, but as far
     79          * as I know, this situation rarely occurs "in the
     80          * wild," so there's not much point in optimizing for it.
     81          */
     82         LineNumberList lnl = LineNumberList.EMPTY;
     83         if (keepLines) {
     84             for (AttLineNumberTable lnt = (AttLineNumberTable)
     85                      codeAttribs.findFirst(AttLineNumberTable.ATTRIBUTE_NAME);
     86                  lnt != null;
     87                  lnt = (AttLineNumberTable) codeAttribs.findNext(lnt)) {
     88                 lnl = LineNumberList.concat(lnl, lnt.getLineNumbers());
     89             }
     90         }
     91         this.lineNumbers = lnl;
     92 
     93         LocalVariableList lvl = LocalVariableList.EMPTY;
     94         if (keepLocals) {
     95             /*
     96              * Do likewise (and with the same caveat) for
     97              * LocalVariableTable and LocalVariableTypeTable attributes.
     98              * This combines both of these kinds of attribute into a
     99              * single LocalVariableList.
    100              */
    101             for (AttLocalVariableTable lvt = (AttLocalVariableTable)
    102                      codeAttribs.findFirst(
    103                              AttLocalVariableTable.ATTRIBUTE_NAME);
    104                  lvt != null;
    105                  lvt = (AttLocalVariableTable) codeAttribs.findNext(lvt)) {
    106 
    107                 lvl = LocalVariableList.concat(lvl, lvt.getLocalVariables());
    108             }
    109 
    110             LocalVariableList typeList = LocalVariableList.EMPTY;
    111             for (AttLocalVariableTypeTable lvtt = (AttLocalVariableTypeTable)
    112                      codeAttribs.findFirst(
    113                              AttLocalVariableTypeTable.ATTRIBUTE_NAME);
    114                  lvtt != null;
    115                  lvtt = (AttLocalVariableTypeTable) codeAttribs.findNext(lvtt)) {
    116                 typeList = LocalVariableList.concat(typeList, lvtt.getLocalVariables());
    117             }
    118 
    119             if (typeList.size() != 0) {
    120 
    121                 lvl = LocalVariableList.mergeDescriptorsAndSignatures(lvl, typeList);
    122             }
    123         }
    124         this.localVariables = lvl;
    125     }
    126 
    127 
    128     /**
    129      * Gets the source file associated with the method if known.
    130      * @return {null-ok;} the source file defining the method if known, null otherwise.
    131      */
    132     public CstString getSourceFile() {
    133         return classFile.getSourceFile();
    134     }
    135 
    136     /**
    137      * Tests whether the method is being defined on an interface.
    138      * @return true if the method is being defined on an interface.
    139      */
    140     public final boolean isDefaultOrStaticInterfaceMethod() {
    141         return (classFile.getAccessFlags() & AccessFlags.ACC_INTERFACE) != 0
    142             && !getNat().isClassInit();
    143     }
    144 
    145     /**
    146      * Tests whether the method is being defined is declared as static.
    147      * @return true if the method is being defined is declared as static.
    148      */
    149     public final boolean isStaticMethod() {
    150         return (getAccessFlags() & AccessFlags.ACC_STATIC) != 0;
    151     }
    152 
    153     /** {@inheritDoc} */
    154     @Override
    155     public CstNat getNat() {
    156         return method.getNat();
    157     }
    158 
    159     /** {@inheritDoc} */
    160     @Override
    161     public CstString getName() {
    162         return method.getName();
    163     }
    164 
    165     /** {@inheritDoc} */
    166     @Override
    167     public CstString getDescriptor() {
    168         return method.getDescriptor();
    169     }
    170 
    171     /** {@inheritDoc} */
    172     @Override
    173     public int getAccessFlags() {
    174         return method.getAccessFlags();
    175     }
    176 
    177     /** {@inheritDoc} */
    178     @Override
    179     public AttributeList getAttributes() {
    180         return method.getAttributes();
    181     }
    182 
    183     /** {@inheritDoc} */
    184     @Override
    185     public CstType getDefiningClass() {
    186         return method.getDefiningClass();
    187     }
    188 
    189     /** {@inheritDoc} */
    190     @Override
    191     public Prototype getEffectiveDescriptor() {
    192         return method.getEffectiveDescriptor();
    193     }
    194 
    195     /**
    196      * Gets the maximum stack size.
    197      *
    198      * @return {@code >= 0;} the maximum stack size
    199      */
    200     public int getMaxStack() {
    201         return attCode.getMaxStack();
    202     }
    203 
    204     /**
    205      * Gets the number of locals.
    206      *
    207      * @return {@code >= 0;} the number of locals
    208      */
    209     public int getMaxLocals() {
    210         return attCode.getMaxLocals();
    211     }
    212 
    213     /**
    214      * Gets the bytecode array.
    215      *
    216      * @return {@code non-null;} the bytecode array
    217      */
    218     public BytecodeArray getCode() {
    219         return attCode.getCode();
    220     }
    221 
    222     /**
    223      * Gets the exception table.
    224      *
    225      * @return {@code non-null;} the exception table
    226      */
    227     public ByteCatchList getCatches() {
    228         return attCode.getCatches();
    229     }
    230 
    231     /**
    232      * Gets the line number list.
    233      *
    234      * @return {@code non-null;} the line number list
    235      */
    236     public LineNumberList getLineNumbers() {
    237         return lineNumbers;
    238     }
    239 
    240     /**
    241      * Gets the local variable list.
    242      *
    243      * @return {@code non-null;} the local variable list
    244      */
    245     public LocalVariableList getLocalVariables() {
    246         return localVariables;
    247     }
    248 
    249     /**
    250      * Returns a {@link SourcePosition} instance corresponding to the
    251      * given bytecode offset.
    252      *
    253      * @param offset {@code >= 0;} the bytecode offset
    254      * @return {@code non-null;} an appropriate instance
    255      */
    256     public SourcePosition makeSourcePosistion(int offset) {
    257         return new SourcePosition(getSourceFile(), offset,
    258                                   lineNumbers.pcToLine(offset));
    259     }
    260 }
    261