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