/Users/lyon/j4p/src/javassist/CtBehavior.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 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;
17
18 import javassist.bytecode.*;
19 import javassist.compiler.Javac;
20 import javassist.compiler.CompileError;
21 import javassist.expr.ExprEditor;
22
23 /**
24 * <code>CtBehavior</code> is the abstract super class of
25 * <code>CtMethod</code> and <code>CtConstructor</code>.
26 */
27 public abstract class CtBehavior extends CtMember {
28 protected MethodInfo methodInfo;
29
30 protected CtBehavior(CtClass clazz, MethodInfo minfo) {
31 super(clazz);
32 methodInfo = minfo;
33 }
34
35 /**
36 * Returns the MethodInfo representing this member in the
37 * class file.
38 */
39 public MethodInfo getMethodInfo() {
40 declaringClass.checkModify();
41 return methodInfo;
42 }
43
44 /**
45 * Undocumented method. Do not use; internal-use only.
46 */
47 public MethodInfo getMethodInfo2() {
48 return methodInfo;
49 }
50
51 /**
52 * Obtains the modifiers of the member.
53 *
54 * @return modifiers encoded with
55 * <code>javassist.Modifier</code>.
56 * @see Modifier
57 */
58 public int getModifiers() {
59 return AccessFlag.toModifier(methodInfo.getAccessFlags());
60 }
61
62 /**
63 * Sets the encoded modifiers of the member.
64 *
65 * @see Modifier
66 */
67 public void setModifiers(int mod) {
68 declaringClass.checkModify();
69 methodInfo.setAccessFlags(AccessFlag.of(mod));
70 }
71
72 /**
73 * Obtains the name of this member.
74 *
75 * @see CtConstructor#getName()
76 */
77 public abstract String getName();
78
79 /**
80 * Obtains parameter types of this member.
81 */
82 public CtClass[] getParameterTypes() throws NotFoundException {
83 return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
84 declaringClass.getClassPool());
85 }
86
87 /**
88 * Obtains the type of the returned value.
89 */
90 CtClass getReturnType0() throws NotFoundException {
91 return Descriptor.getReturnType(methodInfo.getDescriptor(),
92 declaringClass.getClassPool());
93 }
94
95 /**
96 * Returns the character string representing the parameter types
97 * and the return type. If two members have the same parameter types
98 * and the return type, <code>getSignature()</code> returns the
99 * same string.
100 */
101 public String getSignature() {
102 return methodInfo.getDescriptor();
103 }
104
105 /**
106 * Obtains exceptions that this member may throw.
107 */
108 public CtClass[] getExceptionTypes() throws NotFoundException {
109 String[] exceptions;
110 ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
111 if (ea == null)
112 exceptions = null;
113 else
114 exceptions = ea.getExceptions();
115
116 return declaringClass.getClassPool().get(exceptions);
117 }
118
119 /**
120 * Sets exceptions that this member may throw.
121 */
122 public void setExceptionTypes(CtClass[] types) throws NotFoundException {
123 declaringClass.checkModify();
124 if (types == null) {
125 methodInfo.removeExceptionsAttribute();
126 return;
127 }
128
129 String[] names = new String[types.length];
130 for (int i = 0; i < types.length; ++i)
131 names[i] = types[i].getName();
132
133 ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
134 if (ea == null) {
135 ea = new ExceptionsAttribute(methodInfo.getConstPool());
136 methodInfo.setExceptionsAttribute(ea);
137 }
138
139 ea.setExceptions(names);
140 }
141
142 /**
143 * Returns true if the body is empty.
144 */
145 public abstract boolean isEmpty();
146
147 /**
148 * Sets a member body.
149 *
150 * @param src the source code representing the member body.
151 * It must be a single statement or block.
152 * If it is <code>null</code>, the substituted member
153 * body does nothing except returning zero or null.
154 */
155 public void setBody(String src) throws CannotCompileException {
156 declaringClass.checkModify();
157 try {
158 Javac jv = new Javac(declaringClass);
159 Bytecode b = jv.compileBody(this, src);
160 methodInfo.setCodeAttribute(b.toCodeAttribute());
161 methodInfo.setAccessFlags(methodInfo.getAccessFlags()
162 & ~AccessFlag.ABSTRACT);
163 } catch (CompileError e) {
164 throw new CannotCompileException(e);
165 }
166 }
167
168 static void setBody0(CtClass srcClass, MethodInfo srcInfo,
169 CtClass destClass, MethodInfo destInfo,
170 ClassMap map)
171 throws CannotCompileException {
172 destClass.checkModify();
173
174 if (map == null)
175 map = new ClassMap();
176
177 map.put(srcClass.getName(), destClass.getName());
178 try {
179 CodeAttribute cattr = srcInfo.getCodeAttribute();
180 if (cattr != null) {
181 ConstPool cp = destInfo.getConstPool();
182 CodeAttribute ca = (CodeAttribute) cattr.copy(cp, map);
183 destInfo.setCodeAttribute(ca);
184 }
185 } catch (CodeAttribute.RuntimeCopyException e) {
186 /* the exception may be thrown by copy() in CodeAttribute.
187 */
188 throw new CannotCompileException(e);
189 }
190
191 destInfo.setAccessFlags(destInfo.getAccessFlags()
192 & ~AccessFlag.ABSTRACT);
193 }
194
195 /**
196 * Obtains an attribute with the given name.
197 * If that attribute is not found in the class file, this
198 * method returns null.
199 *
200 * @param name attribute name
201 */
202 public byte[] getAttribute(String name) {
203 AttributeInfo ai = methodInfo.getAttribute(name);
204 if (ai == null)
205 return null;
206 else
207 return ai.get();
208 }
209
210 /**
211 * Adds an attribute. The attribute is saved in the class file.
212 *
213 * @param name attribute name
214 * @param data attribute value
215 */
216 public void setAttribute(String name, byte[] data) {
217 declaringClass.checkModify();
218 methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
219 name, data));
220 }
221
222 /**
223 * Declares to use <code>$cflow</code> for this member;
224 * If <code>$cflow</code> is used, the class files modified
225 * with Javassist requires a support class
226 * <code>javassist.runtime.Cflow</code> at runtime
227 * (other Javassist classes are not required at runtime).
228 *
229 * <p>Every <code>$cflow</code> variable is given a unique name.
230 * For example, if the given name is <code>"Point.paint"</code>,
231 * then the variable is indicated by <code>$cflow(Point.paint)</code>.
232 *
233 * @param name <code>$cflow</code> name. It can include
234 * alphabets, numbers, <code>_</code>,
235 * <code>$</code>, and <code>.</code> (dot).
236 *
237 * @see javassist.runtime.Cflow
238 */
239 public void useCflow(String name) throws CannotCompileException {
240 CtClass cc = declaringClass;
241 cc.checkModify();
242 ClassPool pool = cc.getClassPool();
243 String fname;
244 int i = 0;
245 while (true) {
246 fname = "_cflow$" + i++;
247 try {
248 cc.getDeclaredField(fname);
249 } catch (NotFoundException e) {
250 break;
251 }
252 }
253
254 pool.recordCflow(name, declaringClass.getName(), fname);
255 try {
256 CtClass type = pool.get("javassist.runtime.Cflow");
257 CtField field = new CtField(type, fname, cc);
258 field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
259 cc.addField(field, CtField.Initializer.byNew(type));
260 insertBefore(fname + ".enter();");
261 String src = fname + ".exit();";
262 insertAfter(src, true);
263 } catch (NotFoundException e) {
264 throw new CannotCompileException(e);
265 }
266 }
267
268 /**
269 * Modifies the member body.
270 *
271 * @param converter specifies how to modify.
272 */
273 public void instrument(CodeConverter converter)
274 throws CannotCompileException {
275 declaringClass.checkModify();
276 ConstPool cp = methodInfo.getConstPool();
277 converter.doit(getDeclaringClass(), methodInfo, cp);
278 }
279
280 /**
281 * Modifies the member body.
282 *
283 * @param editor specifies how to modify.
284 */
285 public void instrument(ExprEditor editor)
286 throws CannotCompileException {
287 // if the class is not frozen,
288 // does not trun the modified flag on.
289 if (declaringClass.isFrozen())
290 declaringClass.checkModify();
291
292 if (editor.doit(declaringClass, methodInfo))
293 declaringClass.checkModify();
294 }
295
296 /**
297 * Inserts bytecode at the beginning of the body.
298 *
299 * @param src the source code representing the inserted bytecode.
300 * It must be a single statement or block.
301 */
302 public void insertBefore(String src) throws CannotCompileException {
303 declaringClass.checkModify();
304 CodeAttribute ca = methodInfo.getCodeAttribute();
305 if (ca == null)
306 throw new CannotCompileException("no method body");
307
308 CodeIterator iterator = ca.iterator();
309 Javac jv = new Javac(declaringClass);
310 try {
311 jv.recordParams(getParameterTypes(),
312 Modifier.isStatic(getModifiers()));
313 jv.compileStmnt(src);
314 Bytecode b = jv.getBytecode();
315 int stack = b.getMaxStack();
316 int locals = b.getMaxLocals();
317
318 if (stack > ca.getMaxStack())
319 ca.setMaxStack(stack);
320
321 if (locals > ca.getMaxLocals())
322 ca.setMaxLocals(locals);
323
324 int pos = iterator.insertEx(b.get());
325 iterator.insert(b.getExceptionTable(), pos);
326 } catch (NotFoundException e) {
327 throw new CannotCompileException(e);
328 } catch (CompileError e) {
329 throw new CannotCompileException(e);
330 } catch (BadBytecode e) {
331 throw new CannotCompileException(e);
332 }
333 }
334
335 /**
336 * Inserts bytecode at the end of the body.
337 * The bytecode is inserted just before every return insturction.
338 * It is not executed when an exception is thrown.
339 *
340 * @param src the source code representing the inserted bytecode.
341 * It must be a single statement or block.
342 */
343 public void insertAfter(String src)
344 throws CannotCompileException {
345 insertAfter(src, false);
346 }
347
348 /**
349 * Inserts bytecode at the end of the body.
350 * The bytecode is inserted just before every return insturction.
351 *
352 * @param src the source code representing the inserted bytecode.
353 * It must be a single statement or block.
354 * @param asFinally true if the inserted bytecode is executed
355 * not only when the control normally returns
356 * but also when an exception is thrown.
357 */
358 public void insertAfter(String src, boolean asFinally)
359 throws CannotCompileException {
360 declaringClass.checkModify();
361 ConstPool pool = methodInfo.getConstPool();
362 CodeAttribute ca = methodInfo.getCodeAttribute();
363 if (ca == null)
364 throw new CannotCompileException("no method body");
365
366 CodeIterator iterator = ca.iterator();
367 int retAddr = ca.getMaxLocals();
368 Bytecode b = new Bytecode(pool, 0, retAddr + 1);
369 b.setStackDepth(ca.getMaxStack() + 1);
370 Javac jv = new Javac(b, declaringClass);
371 try {
372 jv.recordParams(getParameterTypes(),
373 Modifier.isStatic(getModifiers()));
374 CtClass rtype = getReturnType0();
375 int varNo = jv.recordReturnType(rtype, true);
376
377 int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo);
378
379 byte[] save = makeSaveCode(pool, rtype, varNo);
380 byte[] restore = makeRestoreCode(b, pool, rtype, varNo);
381
382 b.addAstore(retAddr);
383 jv.compileStmnt(src);
384 b.addRet(retAddr);
385 ca.setMaxStack(b.getMaxStack());
386 ca.setMaxLocals(b.getMaxLocals());
387
388 int gapPos = iterator.append(b.get());
389 iterator.append(b.getExceptionTable(), gapPos);
390
391 if (asFinally)
392 ca.getExceptionTable().add(0, gapPos, gapPos, 0);
393
394 int gapLen = iterator.getCodeLength() - gapPos - handlerLen;
395 int subr = iterator.getCodeLength() - gapLen;
396
397 while (iterator.hasNext()) {
398 int pos = iterator.next();
399 if (pos >= subr)
400 break;
401
402 int c = iterator.byteAt(pos);
403 if (c == Opcode.ARETURN || c == Opcode.IRETURN
404 || c == Opcode.FRETURN || c == Opcode.LRETURN
405 || c == Opcode.DRETURN || c == Opcode.RETURN) {
406 insertJSR(iterator, subr, pos, save, restore);
407 subr = iterator.getCodeLength() - gapLen;
408 }
409 }
410 } catch (NotFoundException e) {
411 throw new CannotCompileException(e);
412 } catch (CompileError e) {
413 throw new CannotCompileException(e);
414 } catch (BadBytecode e) {
415 throw new CannotCompileException(e);
416 }
417 }
418
419 private byte[] makeSaveCode(ConstPool cp, CtClass rtype, int varNo) {
420 Bytecode b = new Bytecode(cp, 0, 0);
421 if (rtype == CtClass.voidType) {
422 b.addOpcode(Opcode.ACONST_NULL);
423 b.addAstore(varNo);
424 return b.get();
425 } else {
426 b.addStore(varNo, rtype);
427 return b.get();
428 }
429 }
430
431 private byte[] makeRestoreCode(Bytecode code, ConstPool cp,
432 CtClass rtype, int varNo) {
433 if (rtype == CtClass.voidType) {
434 if (code.getMaxLocals() < 1)
435 code.setMaxLocals(1);
436
437 return new byte[0];
438 } else {
439 Bytecode b = new Bytecode(cp, 0, 0);
440 b.addLoad(varNo, rtype);
441 return b.get();
442 }
443 }
444
445 private void insertJSR(CodeIterator iterator, int subr, int pos,
446 byte[] save, byte[] restore)
447 throws BadBytecode {
448 int gapSize = 5 + save.length + restore.length;
449 boolean wide = subr - pos > Short.MAX_VALUE - gapSize - 4;
450 gapSize = iterator.insertGap(pos, wide ? gapSize : gapSize - 2);
451
452 iterator.write(save, pos);
453 pos += save.length;
454 if (wide) {
455 iterator.writeByte(Opcode.JSR_W, pos);
456 iterator.write32bit(subr - pos + gapSize, pos + 1);
457 pos += 5;
458 } else {
459 iterator.writeByte(Opcode.JSR, pos);
460 iterator.write16bit(subr - pos + gapSize, pos + 1);
461 pos += 3;
462 }
463
464 iterator.write(restore, pos);
465 }
466
467 private int insertAfterHandler(boolean asFinally, Bytecode b,
468 CtClass rtype, int returnVarNo) {
469 if (!asFinally)
470 return 0;
471
472 int var = b.getMaxLocals();
473 b.incMaxLocals(1);
474 int pc = b.currentPc();
475 b.addAstore(var);
476 if (rtype.isPrimitive()) {
477 char c = ((CtPrimitiveType) rtype).getDescriptor();
478 if (c == 'D') {
479 b.addDconst(0.0);
480 b.addDstore(returnVarNo);
481 } else if (c == 'F') {
482 b.addFconst(0);
483 b.addFstore(returnVarNo);
484 } else if (c == 'J') {
485 b.addLconst(0);
486 b.addLstore(returnVarNo);
487 } else if (c != 'V') { // int, boolean, char, short, ...
488 b.addIconst(0);
489 b.addIstore(returnVarNo);
490 }
491 } else {
492 b.addOpcode(Opcode.ACONST_NULL);
493 b.addAstore(returnVarNo);
494 }
495
496 b.addOpcode(Opcode.JSR);
497 int pc2 = b.currentPc();
498 b.addIndex(0); // correct later
499 b.addAload(var);
500 b.addOpcode(Opcode.ATHROW);
501 int pc3 = b.currentPc();
502 b.write16bit(pc2, pc3 - pc2 + 1);
503 return pc3 - pc;
504 }
505
506 /* -- OLD version --
507
508 public void insertAfter(String src) throws CannotCompileException {
509 declaringClass.checkModify();
510 CodeAttribute ca = methodInfo.getCodeAttribute();
511 CodeIterator iterator = ca.iterator();
512 Bytecode b = new Bytecode(methodInfo.getConstPool(),
513 ca.getMaxStack(), ca.getMaxLocals());
514 b.setStackDepth(ca.getMaxStack());
515 Javac jv = new Javac(b, declaringClass);
516 try {
517 jv.recordParams(getParameterTypes(),
518 Modifier.isStatic(getModifiers()));
519 CtClass rtype = getReturnType0();
520 int varNo = jv.recordReturnType(rtype, true);
521 boolean isVoid = rtype == CtClass.voidType;
522 if (isVoid) {
523 b.addOpcode(Opcode.ACONST_NULL);
524 b.addAstore(varNo);
525 jv.compileStmnt(src);
526 }
527 else {
528 b.addStore(varNo, rtype);
529 jv.compileStmnt(src);
530 b.addLoad(varNo, rtype);
531 }
532
533 byte[] code = b.get();
534 ca.setMaxStack(b.getMaxStack());
535 ca.setMaxLocals(b.getMaxLocals());
536 while (iterator.hasNext()) {
537 int pos = iterator.next();
538 int c = iterator.byteAt(pos);
539 if (c == Opcode.ARETURN || c == Opcode.IRETURN
540 || c == Opcode.FRETURN || c == Opcode.LRETURN
541 || c == Opcode.DRETURN || c == Opcode.RETURN)
542 iterator.insert(pos, code);
543 }
544 }
545 catch (NotFoundException e) {
546 throw new CannotCompileException(e);
547 }
548 catch (CompileError e) {
549 throw new CannotCompileException(e);
550 }
551 catch (BadBytecode e) {
552 throw new CannotCompileException(e);
553 }
554 }
555 */
556
557 /**
558 * Adds a catch clause that handles an exception thrown in the
559 * body. The catch clause must end with a return or throw statement.
560 *
561 * @param src the source code representing the catch clause.
562 * It must be a single statement or block.
563 * @param exceptionType the type of the exception handled by the
564 * catch clause.
565 */
566 public void addCatch(String src, CtClass exceptionType)
567 throws CannotCompileException {
568 addCatch(src, exceptionType, "$e");
569 }
570
571 /**
572 * Adds a catch clause that handles an exception thrown in the
573 * body. The catch clause must end with a return or throw statement.
574 *
575 * @param src the source code representing the catch clause.
576 * It must be a single statement or block.
577 * @param exceptionType the type of the exception handled by the
578 * catch clause.
579 * @param exceptionName the name of the variable containing the
580 * caught exception, for example,
581 * <code>$e</code>.
582 */
583 public void addCatch(String src, CtClass exceptionType,
584 String exceptionName)
585 throws CannotCompileException {
586 declaringClass.checkModify();
587 ConstPool cp = methodInfo.getConstPool();
588 CodeAttribute ca = methodInfo.getCodeAttribute();
589 CodeIterator iterator = ca.iterator();
590 Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
591 b.setStackDepth(1);
592 Javac jv = new Javac(b, declaringClass);
593 try {
594 jv.recordParams(getParameterTypes(),
595 Modifier.isStatic(getModifiers()));
596 int var = jv.recordVariable(exceptionType, exceptionName);
597 b.addAstore(var);
598 jv.compileStmnt(src);
599
600 int stack = b.getMaxStack();
601 int locals = b.getMaxLocals();
602
603 if (stack > ca.getMaxStack())
604 ca.setMaxStack(stack);
605
606 if (locals > ca.getMaxLocals())
607 ca.setMaxLocals(locals);
608
609 int len = iterator.getCodeLength();
610 int pos = iterator.append(b.get());
611 ca.getExceptionTable().add(0, len, len,
612 cp.addClassInfo(exceptionType));
613 iterator.append(b.getExceptionTable(), pos);
614 } catch (NotFoundException e) {
615 throw new CannotCompileException(e);
616 } catch (CompileError e) {
617 throw new CannotCompileException(e);
618 }
619 }
620 }
621