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
16package javassist.compiler;
17
18import javassist.*;
19import javassist.bytecode.*;
20import 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 */
26public 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