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