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.Javac; 20import javassist.compiler.CompileError; 21 22/** 23 * An instance of CtConstructor represents a constructor. 24 * It may represent a static constructor 25 * (class initializer). To distinguish a constructor and a class 26 * initializer, call <code>isClassInitializer()</code>. 27 * 28 * <p>See the super class <code>CtBehavior</code> as well since 29 * a number of useful methods are in <code>CtBehavior</code>. 30 * 31 * @see CtClass#getDeclaredConstructors() 32 * @see CtClass#getClassInitializer() 33 * @see CtNewConstructor 34 */ 35public final class CtConstructor extends CtBehavior { 36 protected CtConstructor(MethodInfo minfo, CtClass declaring) { 37 super(declaring, minfo); 38 } 39 40 /** 41 * Creates a constructor with no constructor body. 42 * The created constructor 43 * must be added to a class with <code>CtClass.addConstructor()</code>. 44 * 45 * <p>The created constructor does not include a constructor body, 46 * which must be specified with <code>setBody()</code>. 47 * 48 * @param declaring the class to which the created method is added. 49 * @param parameters a list of the parameter types 50 * 51 * @see CtClass#addConstructor(CtConstructor) 52 * @see CtConstructor#setBody(String) 53 * @see CtConstructor#setBody(CtConstructor,ClassMap) 54 */ 55 public CtConstructor(CtClass[] parameters, CtClass declaring) { 56 this((MethodInfo)null, declaring); 57 ConstPool cp = declaring.getClassFile2().getConstPool(); 58 String desc = Descriptor.ofConstructor(parameters); 59 methodInfo = new MethodInfo(cp, "<init>", desc); 60 setModifiers(Modifier.PUBLIC); 61 } 62 63 /** 64 * Creates a copy of a <code>CtConstructor</code> object. 65 * The created constructor must be 66 * added to a class with <code>CtClass.addConstructor()</code>. 67 * 68 * <p>All occurrences of class names in the created constructor 69 * are replaced with names specified by 70 * <code>map</code> if <code>map</code> is not <code>null</code>. 71 * 72 * <p>By default, all the occurrences of the names of the class 73 * declaring <code>src</code> and the superclass are replaced 74 * with the name of the class and the superclass that 75 * the created constructor is added to. 76 * This is done whichever <code>map</code> is null or not. 77 * To prevent this replacement, call <code>ClassMap.fix()</code> 78 * or <code>put()</code> to explicitly specify replacement. 79 * 80 * <p><b>Note:</b> if the <code>.class</code> notation (for example, 81 * <code>String.class</code>) is included in an expression, the 82 * Javac compiler may produce a helper method. 83 * Since this constructor never 84 * copies this helper method, the programmers have the responsiblity of 85 * copying it. Otherwise, use <code>Class.forName()</code> in the 86 * expression. 87 * 88 * @param src the source method. 89 * @param declaring the class to which the created method is added. 90 * @param map the hashtable associating original class names 91 * with substituted names. 92 * It can be <code>null</code>. 93 * 94 * @see CtClass#addConstructor(CtConstructor) 95 * @see ClassMap#fix(String) 96 */ 97 public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map) 98 throws CannotCompileException 99 { 100 this((MethodInfo)null, declaring); 101 copy(src, true, map); 102 } 103 104 /** 105 * Returns true if this object represents a constructor. 106 */ 107 public boolean isConstructor() { 108 return methodInfo.isConstructor(); 109 } 110 111 /** 112 * Returns true if this object represents a static initializer. 113 */ 114 public boolean isClassInitializer() { 115 return methodInfo.isStaticInitializer(); 116 } 117 118 /** 119 * Returns the constructor name followed by parameter types 120 * such as <code>javassist.CtConstructor(CtClass[],CtClass)</code>. 121 * 122 * @since 3.5 123 */ 124 public String getLongName() { 125 return getDeclaringClass().getName() 126 + (isConstructor() ? Descriptor.toString(getSignature()) 127 : ("." + MethodInfo.nameClinit + "()")); 128 } 129 130 /** 131 * Obtains the name of this constructor. 132 * It is the same as the simple name of the class declaring this 133 * constructor. If this object represents a class initializer, 134 * then this method returns <code>"<clinit>"</code>. 135 */ 136 public String getName() { 137 if (methodInfo.isStaticInitializer()) 138 return MethodInfo.nameClinit; 139 else 140 return declaringClass.getSimpleName(); 141 } 142 143 /** 144 * Returns true if the constructor (or static initializer) 145 * is the default one. This method returns true if the constructor 146 * takes some arguments but it does not perform anything except 147 * calling <code>super()</code> (the no-argument constructor of 148 * the super class). 149 */ 150 public boolean isEmpty() { 151 CodeAttribute ca = getMethodInfo2().getCodeAttribute(); 152 if (ca == null) 153 return false; // native or abstract?? 154 // they are not allowed, though. 155 156 ConstPool cp = ca.getConstPool(); 157 CodeIterator it = ca.iterator(); 158 try { 159 int pos, desc; 160 int op0 = it.byteAt(it.next()); 161 return op0 == Opcode.RETURN // empty static initializer 162 || (op0 == Opcode.ALOAD_0 163 && it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL 164 && (desc = cp.isConstructor(getSuperclassName(), 165 it.u16bitAt(pos + 1))) != 0 166 && "()V".equals(cp.getUtf8Info(desc)) 167 && it.byteAt(it.next()) == Opcode.RETURN 168 && !it.hasNext()); 169 } 170 catch (BadBytecode e) {} 171 return false; 172 } 173 174 private String getSuperclassName() { 175 ClassFile cf = declaringClass.getClassFile2(); 176 return cf.getSuperclass(); 177 } 178 179 /** 180 * Returns true if this constructor calls a constructor 181 * of the super class. This method returns false if it 182 * calls another constructor of this class by <code>this()</code>. 183 */ 184 public boolean callsSuper() throws CannotCompileException { 185 CodeAttribute codeAttr = methodInfo.getCodeAttribute(); 186 if (codeAttr != null) { 187 CodeIterator it = codeAttr.iterator(); 188 try { 189 int index = it.skipSuperConstructor(); 190 return index >= 0; 191 } 192 catch (BadBytecode e) { 193 throw new CannotCompileException(e); 194 } 195 } 196 197 return false; 198 } 199 200 /** 201 * Sets a constructor body. 202 * 203 * @param src the source code representing the constructor body. 204 * It must be a single statement or block. 205 * If it is <code>null</code>, the substituted 206 * constructor body does nothing except calling 207 * <code>super()</code>. 208 */ 209 public void setBody(String src) throws CannotCompileException { 210 if (src == null) 211 if (isClassInitializer()) 212 src = ";"; 213 else 214 src = "super();"; 215 216 super.setBody(src); 217 } 218 219 /** 220 * Copies a constructor body from another constructor. 221 * 222 * <p>All occurrences of the class names in the copied body 223 * are replaced with the names specified by 224 * <code>map</code> if <code>map</code> is not <code>null</code>. 225 * 226 * @param src the method that the body is copied from. 227 * @param map the hashtable associating original class names 228 * with substituted names. 229 * It can be <code>null</code>. 230 */ 231 public void setBody(CtConstructor src, ClassMap map) 232 throws CannotCompileException 233 { 234 setBody0(src.declaringClass, src.methodInfo, 235 declaringClass, methodInfo, map); 236 } 237 238 /** 239 * Inserts bytecode just after another constructor in the super class 240 * or this class is called. 241 * It does not work if this object represents a class initializer. 242 * 243 * @param src the source code representing the inserted bytecode. 244 * It must be a single statement or block. 245 */ 246 public void insertBeforeBody(String src) throws CannotCompileException { 247 CtClass cc = declaringClass; 248 cc.checkModify(); 249 if (isClassInitializer()) 250 throw new CannotCompileException("class initializer"); 251 252 CodeAttribute ca = methodInfo.getCodeAttribute(); 253 CodeIterator iterator = ca.iterator(); 254 Bytecode b = new Bytecode(methodInfo.getConstPool(), 255 ca.getMaxStack(), ca.getMaxLocals()); 256 b.setStackDepth(ca.getMaxStack()); 257 Javac jv = new Javac(b, cc); 258 try { 259 jv.recordParams(getParameterTypes(), false); 260 jv.compileStmnt(src); 261 ca.setMaxStack(b.getMaxStack()); 262 ca.setMaxLocals(b.getMaxLocals()); 263 iterator.skipConstructor(); 264 int pos = iterator.insertEx(b.get()); 265 iterator.insert(b.getExceptionTable(), pos); 266 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); 267 } 268 catch (NotFoundException e) { 269 throw new CannotCompileException(e); 270 } 271 catch (CompileError e) { 272 throw new CannotCompileException(e); 273 } 274 catch (BadBytecode e) { 275 throw new CannotCompileException(e); 276 } 277 } 278 279 /* This method is called by addCatch() in CtBehavior. 280 * super() and this() must not be in a try statement. 281 */ 282 int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { 283 CodeIterator ci = ca.iterator(); 284 try { 285 ci.skipConstructor(); 286 return ci.next(); 287 } 288 catch (BadBytecode e) { 289 throw new CannotCompileException(e); 290 } 291 } 292 293 /** 294 * Makes a copy of this constructor and converts it into a method. 295 * The signature of the mehtod is the same as the that of this constructor. 296 * The return type is <code>void</code>. The resulting method must be 297 * appended to the class specified by <code>declaring</code>. 298 * If this constructor is a static initializer, the resulting method takes 299 * no parameter. 300 * 301 * <p>An occurrence of another constructor call <code>this()</code> 302 * or a super constructor call <code>super()</code> is 303 * eliminated from the resulting method. 304 * 305 * <p>The immediate super class of the class declaring this constructor 306 * must be also a super class of the class declaring the resulting method. 307 * If the constructor accesses a field, the class declaring the resulting method 308 * must also declare a field with the same name and type. 309 * 310 * @param name the name of the resulting method. 311 * @param declaring the class declaring the resulting method. 312 */ 313 public CtMethod toMethod(String name, CtClass declaring) 314 throws CannotCompileException 315 { 316 return toMethod(name, declaring, null); 317 } 318 319 /** 320 * Makes a copy of this constructor and converts it into a method. 321 * The signature of the method is the same as the that of this constructor. 322 * The return type is <code>void</code>. The resulting method must be 323 * appended to the class specified by <code>declaring</code>. 324 * If this constructor is a static initializer, the resulting method takes 325 * no parameter. 326 * 327 * <p>An occurrence of another constructor call <code>this()</code> 328 * or a super constructor call <code>super()</code> is 329 * eliminated from the resulting method. 330 * 331 * <p>The immediate super class of the class declaring this constructor 332 * must be also a super class of the class declaring the resulting method 333 * (this is obviously true if the second parameter <code>declaring</code> is 334 * the same as the class declaring this constructor). 335 * If the constructor accesses a field, the class declaring the resulting method 336 * must also declare a field with the same name and type. 337 * 338 * @param name the name of the resulting method. 339 * @param declaring the class declaring the resulting method. 340 * It is normally the same as the class declaring this 341 * constructor. 342 * @param map the hash table associating original class names 343 * with substituted names. The original class names will be 344 * replaced while making a copy. 345 * <code>map</code> can be <code>null</code>. 346 */ 347 public CtMethod toMethod(String name, CtClass declaring, ClassMap map) 348 throws CannotCompileException 349 { 350 CtMethod method = new CtMethod(null, declaring); 351 method.copy(this, false, map); 352 if (isConstructor()) { 353 MethodInfo minfo = method.getMethodInfo2(); 354 CodeAttribute ca = minfo.getCodeAttribute(); 355 if (ca != null) { 356 removeConsCall(ca); 357 try { 358 methodInfo.rebuildStackMapIf6(declaring.getClassPool(), 359 declaring.getClassFile2()); 360 } 361 catch (BadBytecode e) { 362 throw new CannotCompileException(e); 363 } 364 } 365 } 366 367 method.setName(name); 368 return method; 369 } 370 371 private static void removeConsCall(CodeAttribute ca) 372 throws CannotCompileException 373 { 374 CodeIterator iterator = ca.iterator(); 375 try { 376 int pos = iterator.skipConstructor(); 377 if (pos >= 0) { 378 int mref = iterator.u16bitAt(pos + 1); 379 String desc = ca.getConstPool().getMethodrefType(mref); 380 int num = Descriptor.numOfParameters(desc) + 1; 381 if (num > 3) 382 pos = iterator.insertGapAt(pos, num - 3, false).position; 383 384 iterator.writeByte(Opcode.POP, pos++); // this 385 iterator.writeByte(Opcode.NOP, pos); 386 iterator.writeByte(Opcode.NOP, pos + 1); 387 Descriptor.Iterator it = new Descriptor.Iterator(desc); 388 while (true) { 389 it.next(); 390 if (it.isParameter()) 391 iterator.writeByte(it.is2byte() ? Opcode.POP2 : Opcode.POP, 392 pos++); 393 else 394 break; 395 } 396 } 397 } 398 catch (BadBytecode e) { 399 throw new CannotCompileException(e); 400 } 401 } 402} 403