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