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.compiler; 17 18 import javassist.*; 19 import javassist.bytecode.*; 20 import java.util.HashMap; 21 22 /** 23 * AccessorMaker maintains accessors to private members of an enclosing 24 * class. It is necessary for compiling a method in an inner class. 25 */ 26 public class AccessorMaker { 27 private CtClass clazz; 28 private int uniqueNumber; 29 private HashMap accessors; 30 31 static final String lastParamType = "javassist.runtime.Inner"; 32 33 public AccessorMaker(CtClass c) { 34 clazz = c; 35 uniqueNumber = 1; 36 accessors = new HashMap(); 37 } 38 39 public String getConstructor(CtClass c, String desc, MethodInfo orig) 40 throws CompileError 41 { 42 String key = "<init>:" + desc; 43 String consDesc = (String)accessors.get(key); 44 if (consDesc != null) 45 return consDesc; // already exists. 46 47 consDesc = Descriptor.appendParameter(lastParamType, desc); 48 ClassFile cf = clazz.getClassFile(); // turn on the modified flag. 49 try { 50 ConstPool cp = cf.getConstPool(); 51 ClassPool pool = clazz.getClassPool(); 52 MethodInfo minfo 53 = new MethodInfo(cp, MethodInfo.nameInit, consDesc); 54 minfo.setAccessFlags(0); 55 minfo.addAttribute(new SyntheticAttribute(cp)); 56 ExceptionsAttribute ea = orig.getExceptionsAttribute(); 57 if (ea != null) 58 minfo.addAttribute(ea.copy(cp, null)); 59 60 CtClass[] params = Descriptor.getParameterTypes(desc, pool); 61 Bytecode code = new Bytecode(cp); 62 code.addAload(0); 63 int regno = 1; 64 for (int i = 0; i < params.length; ++i) 65 regno += code.addLoad(regno, params[i]); 66 code.setMaxLocals(regno + 1); // the last parameter is added. 67 code.addInvokespecial(clazz, MethodInfo.nameInit, desc); 68 69 code.addReturn(null); 70 minfo.setCodeAttribute(code.toCodeAttribute()); 71 cf.addMethod(minfo); 72 } 73 catch (CannotCompileException e) { 74 throw new CompileError(e); 75 } 76 catch (NotFoundException e) { 77 throw new CompileError(e); 78 } 79 80 accessors.put(key, consDesc); 81 return consDesc; 82 } 83 84 /** 85 * Returns the name of the method for accessing a private method. 86 * 87 * @param name the name of the private method. 88 * @param desc the descriptor of the private method. 89 * @param accDesc the descriptor of the accessor method. The first 90 * parameter type is <code>clazz</code>. 91 * If the private method is static, 92 * <code>accDesc<code> must be identical to <code>desc</code>. 93 * 94 * @param orig the method info of the private method. 95 * @return 96 */ 97 public String getMethodAccessor(String name, String desc, String accDesc, 98 MethodInfo orig) 99 throws CompileError 100 { 101 String key = name + ":" + desc; 102 String accName = (String)accessors.get(key); 103 if (accName != null) 104 return accName; // already exists. 105 106 ClassFile cf = clazz.getClassFile(); // turn on the modified flag. 107 accName = findAccessorName(cf); 108 try { 109 ConstPool cp = cf.getConstPool(); 110 ClassPool pool = clazz.getClassPool(); 111 MethodInfo minfo 112 = new MethodInfo(cp, accName, accDesc); 113 minfo.setAccessFlags(AccessFlag.STATIC); 114 minfo.addAttribute(new SyntheticAttribute(cp)); 115 ExceptionsAttribute ea = orig.getExceptionsAttribute(); 116 if (ea != null) 117 minfo.addAttribute(ea.copy(cp, null)); 118 119 CtClass[] params = Descriptor.getParameterTypes(accDesc, pool); 120 int regno = 0; 121 Bytecode code = new Bytecode(cp); 122 for (int i = 0; i < params.length; ++i) 123 regno += code.addLoad(regno, params[i]); 124 125 code.setMaxLocals(regno); 126 if (desc == accDesc) 127 code.addInvokestatic(clazz, name, desc); 128 else 129 code.addInvokevirtual(clazz, name, desc); 130 131 code.addReturn(Descriptor.getReturnType(desc, pool)); 132 minfo.setCodeAttribute(code.toCodeAttribute()); 133 cf.addMethod(minfo); 134 } 135 catch (CannotCompileException e) { 136 throw new CompileError(e); 137 } 138 catch (NotFoundException e) { 139 throw new CompileError(e); 140 } 141 142 accessors.put(key, accName); 143 return accName; 144 } 145 146 /** 147 * Returns the method_info representing the added getter. 148 */ 149 public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static) 150 throws CompileError 151 { 152 String fieldName = finfo.getName(); 153 String key = fieldName + ":getter"; 154 Object res = accessors.get(key); 155 if (res != null) 156 return (MethodInfo)res; // already exists. 157 158 ClassFile cf = clazz.getClassFile(); // turn on the modified flag. 159 String accName = findAccessorName(cf); 160 try { 161 ConstPool cp = cf.getConstPool(); 162 ClassPool pool = clazz.getClassPool(); 163 String fieldType = finfo.getDescriptor(); 164 String accDesc; 165 if (is_static) 166 accDesc = "()" + fieldType; 167 else 168 accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType; 169 170 MethodInfo minfo = new MethodInfo(cp, accName, accDesc); 171 minfo.setAccessFlags(AccessFlag.STATIC); 172 minfo.addAttribute(new SyntheticAttribute(cp)); 173 Bytecode code = new Bytecode(cp); 174 if (is_static) { 175 code.addGetstatic(Bytecode.THIS, fieldName, fieldType); 176 } 177 else { 178 code.addAload(0); 179 code.addGetfield(Bytecode.THIS, fieldName, fieldType); 180 code.setMaxLocals(1); 181 } 182 183 code.addReturn(Descriptor.toCtClass(fieldType, pool)); 184 minfo.setCodeAttribute(code.toCodeAttribute()); 185 cf.addMethod(minfo); 186 accessors.put(key, minfo); 187 return minfo; 188 } 189 catch (CannotCompileException e) { 190 throw new CompileError(e); 191 } 192 catch (NotFoundException e) { 193 throw new CompileError(e); 194 } 195 } 196 197 /** 198 * Returns the method_info representing the added setter. 199 */ 200 public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static) 201 throws CompileError 202 { 203 String fieldName = finfo.getName(); 204 String key = fieldName + ":setter"; 205 Object res = accessors.get(key); 206 if (res != null) 207 return (MethodInfo)res; // already exists. 208 209 ClassFile cf = clazz.getClassFile(); // turn on the modified flag. 210 String accName = findAccessorName(cf); 211 try { 212 ConstPool cp = cf.getConstPool(); 213 ClassPool pool = clazz.getClassPool(); 214 String fieldType = finfo.getDescriptor(); 215 String accDesc; 216 if (is_static) 217 accDesc = "(" + fieldType + ")V"; 218 else 219 accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V"; 220 221 MethodInfo minfo = new MethodInfo(cp, accName, accDesc); 222 minfo.setAccessFlags(AccessFlag.STATIC); 223 minfo.addAttribute(new SyntheticAttribute(cp)); 224 Bytecode code = new Bytecode(cp); 225 int reg; 226 if (is_static) { 227 reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool)); 228 code.addPutstatic(Bytecode.THIS, fieldName, fieldType); 229 } 230 else { 231 code.addAload(0); 232 reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool)) 233 + 1; 234 code.addPutfield(Bytecode.THIS, fieldName, fieldType); 235 } 236 237 code.addReturn(null); 238 code.setMaxLocals(reg); 239 minfo.setCodeAttribute(code.toCodeAttribute()); 240 cf.addMethod(minfo); 241 accessors.put(key, minfo); 242 return minfo; 243 } 244 catch (CannotCompileException e) { 245 throw new CompileError(e); 246 } 247 catch (NotFoundException e) { 248 throw new CompileError(e); 249 } 250 } 251 252 private String findAccessorName(ClassFile cf) { 253 String accName; 254 do { 255 accName = "access$" + uniqueNumber++; 256 } while (cf.getMethod(accName) != null); 257 return accName; 258 } 259 } 260