Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist.compiler;
     17 
     18 import javassist.*;
     19 import javassist.bytecode.*;
     20 import java.util.HashMap;
     21 
     22 /**
     23  * AccessorMaker maintains accessors to private members of an enclosing
     24  * class.  It is necessary for compiling a method in an inner class.
     25  */
     26 public class AccessorMaker {
     27     private CtClass clazz;
     28     private int uniqueNumber;
     29     private HashMap accessors;
     30 
     31     static final String lastParamType = "javassist.runtime.Inner";
     32 
     33     public AccessorMaker(CtClass c) {
     34         clazz = c;
     35         uniqueNumber = 1;
     36         accessors = new HashMap();
     37     }
     38 
     39     public String getConstructor(CtClass c, String desc, MethodInfo orig)
     40         throws CompileError
     41     {
     42         String key = "<init>:" + desc;
     43         String consDesc = (String)accessors.get(key);
     44         if (consDesc != null)
     45             return consDesc;     // already exists.
     46 
     47         consDesc = Descriptor.appendParameter(lastParamType, desc);
     48         ClassFile cf = clazz.getClassFile();    // turn on the modified flag.
     49         try {
     50             ConstPool cp = cf.getConstPool();
     51             ClassPool pool = clazz.getClassPool();
     52             MethodInfo minfo
     53                 = new MethodInfo(cp, MethodInfo.nameInit, consDesc);
     54             minfo.setAccessFlags(0);
     55             minfo.addAttribute(new SyntheticAttribute(cp));
     56             ExceptionsAttribute ea = orig.getExceptionsAttribute();
     57             if (ea != null)
     58                 minfo.addAttribute(ea.copy(cp, null));
     59 
     60             CtClass[] params = Descriptor.getParameterTypes(desc, pool);
     61             Bytecode code = new Bytecode(cp);
     62             code.addAload(0);
     63             int regno = 1;
     64             for (int i = 0; i < params.length; ++i)
     65                 regno += code.addLoad(regno, params[i]);
     66             code.setMaxLocals(regno + 1);    // the last parameter is added.
     67             code.addInvokespecial(clazz, MethodInfo.nameInit, desc);
     68 
     69             code.addReturn(null);
     70             minfo.setCodeAttribute(code.toCodeAttribute());
     71             cf.addMethod(minfo);
     72         }
     73         catch (CannotCompileException e) {
     74             throw new CompileError(e);
     75         }
     76         catch (NotFoundException e) {
     77             throw new CompileError(e);
     78         }
     79 
     80         accessors.put(key, consDesc);
     81         return consDesc;
     82     }
     83 
     84     /**
     85      * Returns the name of the method for accessing a private method.
     86      *
     87      * @param name      the name of the private method.
     88      * @param desc      the descriptor of the private method.
     89      * @param accDesc   the descriptor of the accessor method.  The first
     90      *                  parameter type is <code>clazz</code>.
     91      *                  If the private method is static,
     92      *              <code>accDesc<code> must be identical to <code>desc</code>.
     93      *
     94      * @param orig      the method info of the private method.
     95      * @return
     96      */
     97     public String getMethodAccessor(String name, String desc, String accDesc,
     98                                     MethodInfo orig)
     99         throws CompileError
    100     {
    101         String key = name + ":" + desc;
    102         String accName = (String)accessors.get(key);
    103         if (accName != null)
    104             return accName;     // already exists.
    105 
    106         ClassFile cf = clazz.getClassFile();    // turn on the modified flag.
    107         accName = findAccessorName(cf);
    108         try {
    109             ConstPool cp = cf.getConstPool();
    110             ClassPool pool = clazz.getClassPool();
    111             MethodInfo minfo
    112                 = new MethodInfo(cp, accName, accDesc);
    113             minfo.setAccessFlags(AccessFlag.STATIC);
    114             minfo.addAttribute(new SyntheticAttribute(cp));
    115             ExceptionsAttribute ea = orig.getExceptionsAttribute();
    116             if (ea != null)
    117                 minfo.addAttribute(ea.copy(cp, null));
    118 
    119             CtClass[] params = Descriptor.getParameterTypes(accDesc, pool);
    120             int regno = 0;
    121             Bytecode code = new Bytecode(cp);
    122             for (int i = 0; i < params.length; ++i)
    123                 regno += code.addLoad(regno, params[i]);
    124 
    125             code.setMaxLocals(regno);
    126             if (desc == accDesc)
    127                 code.addInvokestatic(clazz, name, desc);
    128             else
    129                 code.addInvokevirtual(clazz, name, desc);
    130 
    131             code.addReturn(Descriptor.getReturnType(desc, pool));
    132             minfo.setCodeAttribute(code.toCodeAttribute());
    133             cf.addMethod(minfo);
    134         }
    135         catch (CannotCompileException e) {
    136             throw new CompileError(e);
    137         }
    138         catch (NotFoundException e) {
    139             throw new CompileError(e);
    140         }
    141 
    142         accessors.put(key, accName);
    143         return accName;
    144     }
    145 
    146     /**
    147      * Returns the method_info representing the added getter.
    148      */
    149     public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static)
    150         throws CompileError
    151     {
    152         String fieldName = finfo.getName();
    153         String key = fieldName + ":getter";
    154         Object res = accessors.get(key);
    155         if (res != null)
    156             return (MethodInfo)res;     // already exists.
    157 
    158         ClassFile cf = clazz.getClassFile();    // turn on the modified flag.
    159         String accName = findAccessorName(cf);
    160         try {
    161             ConstPool cp = cf.getConstPool();
    162             ClassPool pool = clazz.getClassPool();
    163             String fieldType = finfo.getDescriptor();
    164             String accDesc;
    165             if (is_static)
    166                 accDesc = "()" + fieldType;
    167             else
    168                 accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType;
    169 
    170             MethodInfo minfo = new MethodInfo(cp, accName, accDesc);
    171             minfo.setAccessFlags(AccessFlag.STATIC);
    172             minfo.addAttribute(new SyntheticAttribute(cp));
    173             Bytecode code = new Bytecode(cp);
    174             if (is_static) {
    175                 code.addGetstatic(Bytecode.THIS, fieldName, fieldType);
    176             }
    177             else {
    178                 code.addAload(0);
    179                 code.addGetfield(Bytecode.THIS, fieldName, fieldType);
    180                 code.setMaxLocals(1);
    181             }
    182 
    183             code.addReturn(Descriptor.toCtClass(fieldType, pool));
    184             minfo.setCodeAttribute(code.toCodeAttribute());
    185             cf.addMethod(minfo);
    186             accessors.put(key, minfo);
    187             return minfo;
    188         }
    189         catch (CannotCompileException e) {
    190             throw new CompileError(e);
    191         }
    192         catch (NotFoundException e) {
    193             throw new CompileError(e);
    194         }
    195     }
    196 
    197     /**
    198      * Returns the method_info representing the added setter.
    199      */
    200     public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static)
    201         throws CompileError
    202     {
    203         String fieldName = finfo.getName();
    204         String key = fieldName + ":setter";
    205         Object res = accessors.get(key);
    206         if (res != null)
    207             return (MethodInfo)res;     // already exists.
    208 
    209         ClassFile cf = clazz.getClassFile();    // turn on the modified flag.
    210         String accName = findAccessorName(cf);
    211         try {
    212             ConstPool cp = cf.getConstPool();
    213             ClassPool pool = clazz.getClassPool();
    214             String fieldType = finfo.getDescriptor();
    215             String accDesc;
    216             if (is_static)
    217                 accDesc = "(" + fieldType + ")V";
    218             else
    219                 accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V";
    220 
    221             MethodInfo minfo = new MethodInfo(cp, accName, accDesc);
    222             minfo.setAccessFlags(AccessFlag.STATIC);
    223             minfo.addAttribute(new SyntheticAttribute(cp));
    224             Bytecode code = new Bytecode(cp);
    225             int reg;
    226             if (is_static) {
    227                 reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool));
    228                 code.addPutstatic(Bytecode.THIS, fieldName, fieldType);
    229             }
    230             else {
    231                 code.addAload(0);
    232                 reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool))
    233                       + 1;
    234                 code.addPutfield(Bytecode.THIS, fieldName, fieldType);
    235             }
    236 
    237             code.addReturn(null);
    238             code.setMaxLocals(reg);
    239             minfo.setCodeAttribute(code.toCodeAttribute());
    240             cf.addMethod(minfo);
    241             accessors.put(key, minfo);
    242             return minfo;
    243         }
    244         catch (CannotCompileException e) {
    245             throw new CompileError(e);
    246         }
    247         catch (NotFoundException e) {
    248             throw new CompileError(e);
    249         }
    250     }
    251 
    252     private String findAccessorName(ClassFile cf) {
    253         String accName;
    254         do {
    255             accName = "access$" + uniqueNumber++;
    256         } while (cf.getMethod(accName) != null);
    257         return accName;
    258     }
    259 }
    260