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