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