001: package sample.evolve;
002:
003: import javassist.*;
004:
005: /**
006: * Evolution provides a set of methods for instrumenting bytecodes.
007: *
008: * For class evolution, updatable class A is renamed to B. Then an abstract
009: * class named A is produced as the super class of B. If the original class A
010: * has a public method m(), then the abstract class A has an abstract method
011: * m().
012: *
013: * abstract class A abstract m() _makeInstance() | class A --------> class B m()
014: * m()
015: *
016: * Also, all the other classes are translated so that "new A(i)" in the methods
017: * is replaced with "_makeInstance(i)". This makes it possible to change the
018: * behavior of the instantiation of the class A.
019: */
020: public class Evolution implements Translator {
021: public final static String handlerMethod = "_makeInstance";
022:
023: public final static String latestVersionField = VersionManager.latestVersionField;
024:
025: public final static String versionManagerMethod = "initialVersion";
026:
027: private static CtMethod trapMethod;
028:
029: private static final int initialVersion = 0;
030:
031: private ClassPool pool;
032:
033: private String updatableClassName = null;
034:
035: private CtClass updatableClass = null;
036:
037: public void start(ClassPool _pool) throws NotFoundException {
038: pool = _pool;
039:
040: // Get the definition of Sample.make() and store it into trapMethod
041: // for later use.
042: trapMethod = _pool.getMethod("sample.evolve.Sample", "make");
043: }
044:
045: public void onLoad(ClassPool _pool, String classname)
046: throws NotFoundException, CannotCompileException {
047: onLoadUpdatable(classname);
048:
049: /*
050: * Replaces all the occurrences of the new operator with a call to
051: * _makeInstance().
052: */
053: CtClass clazz = _pool.get(classname);
054: CtClass absClass = updatableClass;
055: CodeConverter converter = new CodeConverter();
056: converter.replaceNew(absClass, absClass, handlerMethod);
057: clazz.instrument(converter);
058: }
059:
060: private void onLoadUpdatable(String classname)
061: throws NotFoundException, CannotCompileException {
062: // if the class is a concrete class,
063: // classname is <updatableClassName>$$<version>.
064:
065: int i = classname.lastIndexOf("$$");
066: if (i <= 0)
067: return;
068:
069: String orgname = classname.substring(0, i);
070: if (!orgname.equals(updatableClassName))
071: return;
072:
073: int version;
074: try {
075: version = Integer.parseInt(classname.substring(i + 2));
076: } catch (NumberFormatException e) {
077: throw new NotFoundException(classname, e);
078: }
079:
080: CtClass clazz = pool.getAndRename(orgname, classname);
081: makeConcreteClass(clazz, updatableClass, version);
082: }
083:
084: /*
085: * Register an updatable class.
086: */
087: public void makeUpdatable(String classname)
088: throws NotFoundException, CannotCompileException {
089: if (pool == null)
090: throw new RuntimeException(
091: "Evolution has not been linked to ClassPool.");
092:
093: CtClass c = pool.get(classname);
094: updatableClassName = classname;
095: updatableClass = makeAbstractClass(c);
096: }
097:
098: /**
099: * Produces an abstract class.
100: */
101: protected CtClass makeAbstractClass(CtClass clazz)
102: throws CannotCompileException, NotFoundException {
103: int i;
104:
105: CtClass absClass = pool.makeClass(clazz.getName());
106: absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
107: absClass.setSuperclass(clazz.getSuperclass());
108: absClass.setInterfaces(clazz.getInterfaces());
109:
110: // absClass.inheritAllConstructors();
111:
112: CtField fld = new CtField(pool.get("java.lang.Class"),
113: latestVersionField, absClass);
114: fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
115:
116: CtField.Initializer finit = CtField.Initializer.byCall(pool
117: .get("sample.evolve.VersionManager"),
118: versionManagerMethod, new String[] { clazz.getName() });
119: absClass.addField(fld, finit);
120:
121: CtField[] fs = clazz.getDeclaredFields();
122: for (i = 0; i < fs.length; ++i) {
123: CtField f = fs[i];
124: if (Modifier.isPublic(f.getModifiers()))
125: absClass.addField(new CtField(f.getType(), f.getName(),
126: absClass));
127: }
128:
129: CtConstructor[] cs = clazz.getDeclaredConstructors();
130: for (i = 0; i < cs.length; ++i) {
131: CtConstructor c = cs[i];
132: int mod = c.getModifiers();
133: if (Modifier.isPublic(mod)) {
134: CtMethod wm = CtNewMethod.wrapped(absClass,
135: handlerMethod, c.getParameterTypes(), c
136: .getExceptionTypes(), trapMethod, null,
137: absClass);
138: wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
139: absClass.addMethod(wm);
140: }
141: }
142:
143: CtMethod[] ms = clazz.getDeclaredMethods();
144: for (i = 0; i < ms.length; ++i) {
145: CtMethod m = ms[i];
146: int mod = m.getModifiers();
147: if (Modifier.isPublic(mod))
148: if (Modifier.isStatic(mod))
149: throw new CannotCompileException(
150: "static methods are not supported.");
151: else {
152: CtMethod m2 = CtNewMethod.abstractMethod(m
153: .getReturnType(), m.getName(), m
154: .getParameterTypes(),
155: m.getExceptionTypes(), absClass);
156: absClass.addMethod(m2);
157: }
158: }
159:
160: return absClass;
161: }
162:
163: /**
164: * Modifies the given class file so that it is a subclass of the abstract
165: * class produced by makeAbstractClass().
166: *
167: * Note: the naming convention must be consistent with
168: * VersionManager.update().
169: */
170: protected void makeConcreteClass(CtClass clazz,
171: CtClass abstractClass, int version)
172: throws CannotCompileException, NotFoundException {
173: int i;
174: clazz.setSuperclass(abstractClass);
175: CodeConverter converter = new CodeConverter();
176: CtField[] fs = clazz.getDeclaredFields();
177: for (i = 0; i < fs.length; ++i) {
178: CtField f = fs[i];
179: if (Modifier.isPublic(f.getModifiers()))
180: converter.redirectFieldAccess(f, abstractClass, f
181: .getName());
182: }
183:
184: CtConstructor[] cs = clazz.getDeclaredConstructors();
185: for (i = 0; i < cs.length; ++i)
186: cs[i].instrument(converter);
187:
188: CtMethod[] ms = clazz.getDeclaredMethods();
189: for (i = 0; i < ms.length; ++i)
190: ms[i].instrument(converter);
191: }
192: }
|