• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AccessorMaker(CtClass c)33     public AccessorMaker(CtClass c) {
34         clazz = c;
35         uniqueNumber = 1;
36         accessors = new HashMap();
37     }
38 
getConstructor(CtClass c, String desc, MethodInfo orig)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      */
getMethodAccessor(String name, String desc, String accDesc, MethodInfo orig)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      */
getFieldGetter(FieldInfo finfo, boolean is_static)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      */
getFieldSetter(FieldInfo finfo, boolean is_static)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 
findAccessorName(ClassFile cf)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