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;
17
18import javassist.bytecode.*;
19import javassist.compiler.JvstCodeGen;
20import java.util.Hashtable;
21import javassist.CtMethod.ConstParameter;
22
23class CtNewWrappedMethod {
24
25    private static final String addedWrappedMethod = "_added_m$";
26
27    public static CtMethod wrapped(CtClass returnType, String mname,
28                                   CtClass[] parameterTypes,
29                                   CtClass[] exceptionTypes,
30                                   CtMethod body, ConstParameter constParam,
31                                   CtClass declaring)
32        throws CannotCompileException
33    {
34        CtMethod mt = new CtMethod(returnType, mname, parameterTypes,
35                                   declaring);
36        mt.setModifiers(body.getModifiers());
37        try {
38            mt.setExceptionTypes(exceptionTypes);
39        }
40        catch (NotFoundException e) {
41            throw new CannotCompileException(e);
42        }
43
44        Bytecode code = makeBody(declaring, declaring.getClassFile2(), body,
45                                 parameterTypes, returnType, constParam);
46        mt.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
47        return mt;
48    }
49
50    static Bytecode makeBody(CtClass clazz, ClassFile classfile,
51                             CtMethod wrappedBody,
52                             CtClass[] parameters,
53                             CtClass returnType,
54                             ConstParameter cparam)
55        throws CannotCompileException
56    {
57        boolean isStatic = Modifier.isStatic(wrappedBody.getModifiers());
58        Bytecode code = new Bytecode(classfile.getConstPool(), 0, 0);
59        int stacksize = makeBody0(clazz, classfile, wrappedBody, isStatic,
60                                  parameters, returnType, cparam, code);
61        code.setMaxStack(stacksize);
62        code.setMaxLocals(isStatic, parameters, 0);
63        return code;
64    }
65
66    /* The generated method body does not need a stack map table
67     * because it does not contain a branch instruction.
68     */
69    protected static int makeBody0(CtClass clazz, ClassFile classfile,
70                                   CtMethod wrappedBody,
71                                   boolean isStatic, CtClass[] parameters,
72                                   CtClass returnType, ConstParameter cparam,
73                                   Bytecode code)
74        throws CannotCompileException
75    {
76        if (!(clazz instanceof CtClassType))
77            throw new CannotCompileException("bad declaring class"
78                                             + clazz.getName());
79
80        if (!isStatic)
81            code.addAload(0);
82
83        int stacksize = compileParameterList(code, parameters,
84                                             (isStatic ? 0 : 1));
85        int stacksize2;
86        String desc;
87        if (cparam == null) {
88            stacksize2 = 0;
89            desc = ConstParameter.defaultDescriptor();
90        }
91        else {
92            stacksize2 = cparam.compile(code);
93            desc = cparam.descriptor();
94        }
95
96        checkSignature(wrappedBody, desc);
97
98        String bodyname;
99        try {
100            bodyname = addBodyMethod((CtClassType)clazz, classfile,
101                                     wrappedBody);
102            /* if an exception is thrown below, the method added above
103             * should be removed. (future work :<)
104             */
105        }
106        catch (BadBytecode e) {
107            throw new CannotCompileException(e);
108        }
109
110        if (isStatic)
111            code.addInvokestatic(Bytecode.THIS, bodyname, desc);
112        else
113            code.addInvokespecial(Bytecode.THIS, bodyname, desc);
114
115        compileReturn(code, returnType);        // consumes 2 stack entries
116
117        if (stacksize < stacksize2 + 2)
118            stacksize = stacksize2 + 2;
119
120        return stacksize;
121    }
122
123    private static void checkSignature(CtMethod wrappedBody,
124                                       String descriptor)
125        throws CannotCompileException
126    {
127        if (!descriptor.equals(wrappedBody.getMethodInfo2().getDescriptor()))
128            throw new CannotCompileException(
129                        "wrapped method with a bad signature: "
130                        + wrappedBody.getDeclaringClass().getName()
131                        + '.' + wrappedBody.getName());
132    }
133
134    private static String addBodyMethod(CtClassType clazz,
135                                        ClassFile classfile,
136                                        CtMethod src)
137        throws BadBytecode, CannotCompileException
138    {
139        Hashtable bodies = clazz.getHiddenMethods();
140        String bodyname = (String)bodies.get(src);
141        if (bodyname == null) {
142            do {
143                bodyname = addedWrappedMethod + clazz.getUniqueNumber();
144            } while (classfile.getMethod(bodyname) != null);
145            ClassMap map = new ClassMap();
146            map.put(src.getDeclaringClass().getName(), clazz.getName());
147            MethodInfo body = new MethodInfo(classfile.getConstPool(),
148                                             bodyname, src.getMethodInfo2(),
149                                             map);
150            int acc = body.getAccessFlags();
151            body.setAccessFlags(AccessFlag.setPrivate(acc));
152            body.addAttribute(new SyntheticAttribute(classfile.getConstPool()));
153            // a stack map is copied.  rebuilding it is not needed.
154            classfile.addMethod(body);
155            bodies.put(src, bodyname);
156            CtMember.Cache cache = clazz.hasMemberCache();
157            if (cache != null)
158                cache.addMethod(new CtMethod(body, clazz));
159        }
160
161        return bodyname;
162    }
163
164    /* compileParameterList() returns the stack size used
165     * by the produced code.
166     *
167     * @param regno     the index of the local variable in which
168     *                  the first argument is received.
169     *                  (0: static method, 1: regular method.)
170     */
171    static int compileParameterList(Bytecode code,
172                                    CtClass[] params, int regno) {
173        return JvstCodeGen.compileParameterList(code, params, regno);
174    }
175
176    /*
177     * The produced codes cosume 1 or 2 stack entries.
178     */
179    private static void compileReturn(Bytecode code, CtClass type) {
180        if (type.isPrimitive()) {
181            CtPrimitiveType pt = (CtPrimitiveType)type;
182            if (pt != CtClass.voidType) {
183                String wrapper = pt.getWrapperName();
184                code.addCheckcast(wrapper);
185                code.addInvokevirtual(wrapper, pt.getGetMethodName(),
186                                      pt.getGetMethodDescriptor());
187            }
188
189            code.addOpcode(pt.getReturnOp());
190        }
191        else {
192            code.addCheckcast(type);
193            code.addOpcode(Bytecode.ARETURN);
194        }
195    }
196}
197