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