From 6aaacd96767eae3bf716a542ac67bcff7336380d Mon Sep 17 00:00:00 2001 From: chiba Date: Thu, 27 Oct 2005 11:04:32 +0000 Subject: [PATCH] Implemented CtConstructor#toMethod(). git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@212 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/javassist/CtBehavior.java | 39 ++++++++ src/main/javassist/CtConstructor.java | 99 +++++++++++++------ src/main/javassist/CtMethod.java | 40 ++++---- src/main/javassist/bytecode/CodeIterator.java | 18 ++-- src/main/javassist/bytecode/Descriptor.java | 81 +++++++++++++++ tutorial/tutorial.html | 17 +++- 6 files changed, 230 insertions(+), 64 deletions(-) diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java index 3f271217..c556910b 100644 --- a/src/main/javassist/CtBehavior.java +++ b/src/main/javassist/CtBehavior.java @@ -34,6 +34,45 @@ public abstract class CtBehavior extends CtMember { methodInfo = minfo; } + /** + * @param isCons true if this is a constructor. + */ + void copy(CtBehavior src, boolean isCons, ClassMap map) + throws CannotCompileException + { + CtClass declaring = declaringClass; + MethodInfo srcInfo = src.methodInfo; + CtClass srcClass = src.getDeclaringClass(); + ConstPool cp = declaring.getClassFile2().getConstPool(); + if (map == null) + map = new ClassMap(); + + map.put(srcClass.getName(), declaring.getName()); + try { + boolean patch = false; + CtClass srcSuper = srcClass.getSuperclass(); + String destSuperName = declaring.getSuperclass().getName(); + if (srcSuper != null) { + String srcSuperName = srcSuper.getName(); + if (!srcSuperName.equals(destSuperName)) + if (srcSuperName.equals(CtClass.javaLangObject)) + patch = true; + else + map.put(srcSuperName, destSuperName); + } + + methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); + if (isCons && patch) + methodInfo.setSuperclass(destSuperName); + } + catch (NotFoundException e) { + throw new CannotCompileException(e); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + protected void extendToString(StringBuffer buffer) { buffer.append(' '); buffer.append(getName()); diff --git a/src/main/javassist/CtConstructor.java b/src/main/javassist/CtConstructor.java index 46fc334a..7bcf2622 100644 --- a/src/main/javassist/CtConstructor.java +++ b/src/main/javassist/CtConstructor.java @@ -98,36 +98,7 @@ public final class CtConstructor extends CtBehavior { throws CannotCompileException { this((MethodInfo)null, declaring); - MethodInfo srcInfo = src.methodInfo; - CtClass srcClass = src.getDeclaringClass(); - ConstPool cp = declaring.getClassFile2().getConstPool(); - if (map == null) - map = new ClassMap(); - - map.put(srcClass.getName(), declaring.getName()); - try { - boolean patch = false; - CtClass srcSuper = srcClass.getSuperclass(); - String destSuperName = declaring.getSuperclass().getName(); - if (srcSuper != null) { - String srcSuperName = srcSuper.getName(); - if (!srcSuperName.equals(destSuperName)) - if (srcSuperName.equals(CtClass.javaLangObject)) - patch = true; - else - map.put(srcSuperName, destSuperName); - } - - methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); - if (patch) - methodInfo.setSuperclass(destSuperName); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } + copy(src, true, map); } /** @@ -272,4 +243,72 @@ public final class CtConstructor extends CtBehavior { throw new CannotCompileException(e); } } + + /** + * Makes a copy of this constructor and converts it into a method. + * The signature of the mehtod is the same as the that of this constructor. + * The return type is void. The resulting method must be + * appended to the class specified by declaring. + * If this constructor is a static initializer, the resulting method takes + * no parameter. + * + *

An occurrence of another constructor call this() + * or a super constructor call super() is + * eliminated from the resulting method. + * + *

The immediate super class of the class declaring this constructor + * must be also a super class of the class declaring the resulting method. + * If the constructor accesses a field, the class declaring the resulting method + * must also declare a field with the same name and type. + * + * @param name the name of the resulting method. + * @param declaring the class declaring the resulting method. + */ + public CtMethod toMethod(String name, CtClass declaring) + throws CannotCompileException + { + CtMethod method = new CtMethod(null, declaring); + method.copy(this, false, null); + if (isConstructor()) { + MethodInfo minfo = method.getMethodInfo2(); + CodeAttribute ca = minfo.getCodeAttribute(); + if (ca != null) + removeConsCall(ca); + } + + method.setName(name); + return method; + } + + private static void removeConsCall(CodeAttribute ca) + throws CannotCompileException + { + CodeIterator iterator = ca.iterator(); + try { + int pos = iterator.skipConstructor(); + if (pos >= 0) { + int mref = iterator.u16bitAt(pos + 1); + String desc = ca.getConstPool().getMethodrefType(mref); + int num = Descriptor.numOfParameters(desc) + 1; + if (num > 3) + iterator.insertGap(pos, num - 3); + + iterator.writeByte(Opcode.POP, pos++); // this + iterator.writeByte(Opcode.NOP, pos); + iterator.writeByte(Opcode.NOP, pos + 1); + Descriptor.Iterator it = new Descriptor.Iterator(desc); + while (true) { + it.next(); + if (it.isParameter()) + iterator.writeByte(it.is2byte() ? Opcode.POP2 : Opcode.POP, + pos++); + else + break; + } + } + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } } diff --git a/src/main/javassist/CtMethod.java b/src/main/javassist/CtMethod.java index 9c1532d0..043c8d3b 100644 --- a/src/main/javassist/CtMethod.java +++ b/src/main/javassist/CtMethod.java @@ -22,6 +22,7 @@ import javassist.bytecode.*; * *

See the super class CtBehavior since * a number of useful methods are in CtBehavior. + * A number of useful factory methods are in CtNewMethod. * * @see CtClass#getDeclaredMethods() * @see CtNewMethod @@ -106,30 +107,23 @@ public final class CtMethod extends CtBehavior { throws CannotCompileException { this(null, declaring); - MethodInfo srcInfo = src.methodInfo; - CtClass srcClass = src.getDeclaringClass(); - ConstPool cp = declaring.getClassFile2().getConstPool(); - if (map == null) - map = new ClassMap(); + copy(src, false, map); + } - map.put(srcClass.getName(), declaring.getName()); - try { - CtClass srcSuper = srcClass.getSuperclass(); - if (srcSuper != null) { - String srcSuperName = srcSuper.getName(); - if (!srcSuperName.equals(CtClass.javaLangObject)) - map.put(srcSuperName, - declaring.getSuperclass().getName()); - } - - methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); - } - catch (NotFoundException e) { - throw new CannotCompileException(e); - } - catch (BadBytecode e) { - throw new CannotCompileException(e); - } + /** + * Compiles the given source code and creates a method. + * This method simply delegates to make() in + * CtNewMethod. See it for more details. + * CtNewMethod has a number of useful factory methods. + * + * @param src the source text. + * @param declaring the class to which the created method is added. + * @see CtNewMethod.make(String, CtClass) + */ + public static CtMethod make(String src, CtClass declaring) + throws CannotCompileException + { + return CtNewMethod.make(src, declaring); } /** diff --git a/src/main/javassist/bytecode/CodeIterator.java b/src/main/javassist/bytecode/CodeIterator.java index 531dfaa1..ab9ce7d9 100644 --- a/src/main/javassist/bytecode/CodeIterator.java +++ b/src/main/javassist/bytecode/CodeIterator.java @@ -154,11 +154,11 @@ public class CodeIterator implements Opcode { } /** - * Moves to the first instruction following - * constructor invocation super() or this(). + * Moves to the instruction for + * either super() or this(). * - *

This method skips all the instructions for executing - * super() or this(), which should be + *

This method skips all the instructions for computing arguments + * to super() or this(), which should be * placed at the beginning of a constructor body. * *

This method returns the index of INVOKESPECIAL instruction @@ -176,10 +176,9 @@ public class CodeIterator implements Opcode { } /** - * Moves to the first instruction following super - * constructor invocation super(). + * Moves to the instruction for super(). * - *

This method skips all the instructions for executing + *

This method skips all the instructions for computing arguments to * super(), which should be * placed at the beginning of a constructor body. * @@ -199,10 +198,9 @@ public class CodeIterator implements Opcode { } /** - * Moves to the first instruction following explicit - * constructor invocation this(). + * Moves to the instruction for this(). * - *

This method skips all the instructions for executing + *

This method skips all the instructions for computing arguments to * this(), which should be * placed at the beginning of a constructor body. * diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java index a09f198b..b67e7a49 100644 --- a/src/main/javassist/bytecode/Descriptor.java +++ b/src/main/javassist/bytecode/Descriptor.java @@ -665,4 +665,85 @@ public class Descriptor { return n; } + + /** + * An Iterator over a descriptor. + */ + public static class Iterator { + private String desc; + private int index, curPos; + private boolean param; + + /** + * Constructs an iterator. + * + * @param s descriptor. + */ + public Iterator(String s) { + desc = s; + index = curPos = 0; + param = false; + } + + /** + * Returns true if the iteration has more elements. + */ + public boolean hasNext() { + return index < desc.length(); + } + + /** + * Returns true if the current element is a parameter type. + */ + public boolean isParameter() { return param; } + + /** + * Returns the first character of the current element. + * @return + */ + public char currentChar() { return desc.charAt(curPos); } + + /** + * Returns true if the current element is double or long type. + */ + public boolean is2byte() { + char c = currentChar(); + return c == 'D' || c == 'J'; + } + + /** + * Returns the position of the next type character. + * That type character becomes a new current element. + */ + public int next() { + int nextPos = index; + char c = desc.charAt(nextPos); + if (c == '(') { + ++index; + c = desc.charAt(++nextPos); + param = true; + } + + if (c == ')') { + ++index; + c = desc.charAt(++nextPos); + param = false; + } + + while (c == '[') + c = desc.charAt(++nextPos); + + if (c == 'L') { + nextPos = desc.indexOf(';', nextPos) + 1; + if (nextPos <= 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + else + ++nextPos; + + curPos = index; + index = nextPos; + return curPos; + } + } } diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html index 29c56a68..c2d54a87 100644 --- a/tutorial/tutorial.html +++ b/tutorial/tutorial.html @@ -63,6 +63,8 @@ In the case of the program shown above, the test.Rectangle is obtained from the ClassPool object and it is assigned to a variable cc. +The ClassPool object returned by getDfault() +searches the default system search path.

From the implementation viewpoint, ClassPool is a hash table of CtClass objects, which uses the class names as @@ -90,6 +92,17 @@ modified bytecode. To obtain the bytecode, call toBytecode(): byte[] b = cc.toBytecode(); +

You can directly load the CtClass as well: + +

+ +

toClass() requests the context class loader for the current +thread to load the class file represented by the CtClass. It +returns a java.lang.Class object representing the loaded class. +For more details, please see the following section. +

Defining a new class

@@ -483,7 +496,7 @@ the easiest way for modifying the classes is as follows: ClassPool.get(),
  • 2. Modify it, and
  • 3. Call writeFile() or toBytecode() - on that CtClass object. + on that CtClass object to obtain a modified class file.

    If whether a class is modified or not is determined at load time, @@ -496,7 +509,9 @@ by Javassist.


    +

    3.1 The toClass method in CtClass

    +

    The CtClass provides a convenience method toClass(), which requests the context class loader for -- 2.39.5