Home | History | Annotate | Download | only in javassist
      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;
     17 
     18 import javassist.bytecode.*;
     19 import javassist.compiler.JvstCodeGen;
     20 import java.util.Hashtable;
     21 import javassist.CtMethod.ConstParameter;
     22 
     23 class 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