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