169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpackage sample.evolve;
269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport javassist.*;
469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/**
669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Evolution provides a set of methods for instrumenting bytecodes.
769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * For class evolution, updatable class A is renamed to B. Then an abstract
969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * class named A is produced as the super class of B. If the original class A
1069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * has a public method m(), then the abstract class A has an abstract method
1169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * m().
1269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
1369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * abstract class A abstract m() _makeInstance() | class A --------> class B m()
1469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * m()
1569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal *
1669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Also, all the other classes are translated so that "new A(i)" in the methods
1769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * is replaced with "_makeInstance(i)". This makes it possible to change the
1869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * behavior of the instantiation of the class A.
1969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */
2069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpublic class Evolution implements Translator {
2169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final static String handlerMethod = "_makeInstance";
2269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final static String latestVersionField = VersionManager.latestVersionField;
2469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public final static String versionManagerMethod = "initialVersion";
2669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static CtMethod trapMethod;
2869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
2969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private static final int initialVersion = 0;
3069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private ClassPool pool;
3269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private String updatableClassName = null;
3469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private CtClass updatableClass = null;
3669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
3769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public void start(ClassPool _pool) throws NotFoundException {
3869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        pool = _pool;
3969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
4069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // Get the definition of Sample.make() and store it into trapMethod
4169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // for later use.
4269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        trapMethod = _pool.getMethod("sample.evolve.Sample", "make");
4369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
4469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
4569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public void onLoad(ClassPool _pool, String classname)
4669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throws NotFoundException, CannotCompileException {
4769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        onLoadUpdatable(classname);
4869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
4969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        /*
5069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal         * Replaces all the occurrences of the new operator with a call to
5169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal         * _makeInstance().
5269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal         */
5369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass clazz = _pool.get(classname);
5469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass absClass = updatableClass;
5569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CodeConverter converter = new CodeConverter();
5669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        converter.replaceNew(absClass, absClass, handlerMethod);
5769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        clazz.instrument(converter);
5869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
5969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
6069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    private void onLoadUpdatable(String classname) throws NotFoundException,
6169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CannotCompileException {
6269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // if the class is a concrete class,
6369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // classname is <updatableClassName>$$<version>.
6469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
6569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int i = classname.lastIndexOf("$$");
6669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (i <= 0)
6769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return;
6869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
6969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        String orgname = classname.substring(0, i);
7069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (!orgname.equals(updatableClassName))
7169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            return;
7269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
7369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int version;
7469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        try {
7569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            version = Integer.parseInt(classname.substring(i + 2));
7669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
7769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        catch (NumberFormatException e) {
7869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new NotFoundException(classname, e);
7969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
8069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass clazz = pool.getAndRename(orgname, classname);
8269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        makeConcreteClass(clazz, updatableClass, version);
8369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
8469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
8569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /*
8669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Register an updatable class.
8769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
8869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    public void makeUpdatable(String classname) throws NotFoundException,
8969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CannotCompileException {
9069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        if (pool == null)
9169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throw new RuntimeException(
9269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    "Evolution has not been linked to ClassPool.");
9369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
9469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass c = pool.get(classname);
9569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        updatableClassName = classname;
9669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        updatableClass = makeAbstractClass(c);
9769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
9869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
9969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
10069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Produces an abstract class.
10169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
10269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected CtClass makeAbstractClass(CtClass clazz)
10369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            throws CannotCompileException, NotFoundException {
10469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int i;
10569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
10669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtClass absClass = pool.makeClass(clazz.getName());
10769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
10869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        absClass.setSuperclass(clazz.getSuperclass());
10969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        absClass.setInterfaces(clazz.getInterfaces());
11069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        // absClass.inheritAllConstructors();
11269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtField fld = new CtField(pool.get("java.lang.Class"),
11469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                latestVersionField, absClass);
11569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
11669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
11769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtField.Initializer finit = CtField.Initializer.byCall(pool
11869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                .get("sample.evolve.VersionManager"), versionManagerMethod,
11969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                new String[] { clazz.getName() });
12069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        absClass.addField(fld, finit);
12169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
12269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtField[] fs = clazz.getDeclaredFields();
12369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (i = 0; i < fs.length; ++i) {
12469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtField f = fs[i];
12569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (Modifier.isPublic(f.getModifiers()))
12669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                absClass.addField(new CtField(f.getType(), f.getName(),
12769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        absClass));
12869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
12969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
13069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtConstructor[] cs = clazz.getDeclaredConstructors();
13169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (i = 0; i < cs.length; ++i) {
13269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtConstructor c = cs[i];
13369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            int mod = c.getModifiers();
13469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (Modifier.isPublic(mod)) {
13569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                CtMethod wm = CtNewMethod.wrapped(absClass, handlerMethod, c
13669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        .getParameterTypes(), c.getExceptionTypes(),
13769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                        trapMethod, null, absClass);
13869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
13969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                absClass.addMethod(wm);
14069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            }
14169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
14269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
14369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtMethod[] ms = clazz.getDeclaredMethods();
14469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (i = 0; i < ms.length; ++i) {
14569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtMethod m = ms[i];
14669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            int mod = m.getModifiers();
14769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (Modifier.isPublic(mod))
14869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                if (Modifier.isStatic(mod))
14969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    throw new CannotCompileException(
15069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                            "static methods are not supported.");
15169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                else {
15269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    CtMethod m2 = CtNewMethod.abstractMethod(m.getReturnType(),
15369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                            m.getName(), m.getParameterTypes(), m
15469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                                    .getExceptionTypes(), absClass);
15569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                    absClass.addMethod(m2);
15669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                }
15769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
15869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
15969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        return absClass;
16069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
16169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
16269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    /**
16369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Modifies the given class file so that it is a subclass of the abstract
16469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * class produced by makeAbstractClass().
16569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     *
16669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * Note: the naming convention must be consistent with
16769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     * VersionManager.update().
16869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal     */
16969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    protected void makeConcreteClass(CtClass clazz, CtClass abstractClass,
17069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            int version) throws CannotCompileException, NotFoundException {
17169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        int i;
17269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        clazz.setSuperclass(abstractClass);
17369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CodeConverter converter = new CodeConverter();
17469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtField[] fs = clazz.getDeclaredFields();
17569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (i = 0; i < fs.length; ++i) {
17669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            CtField f = fs[i];
17769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            if (Modifier.isPublic(f.getModifiers()))
17869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal                converter.redirectFieldAccess(f, abstractClass, f.getName());
17969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        }
18069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
18169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtConstructor[] cs = clazz.getDeclaredConstructors();
18269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (i = 0; i < cs.length; ++i)
18369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            cs[i].instrument(converter);
18469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal
18569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        CtMethod[] ms = clazz.getDeclaredMethods();
18669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal        for (i = 0; i < ms.length; ++i)
18769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal            ms[i].instrument(converter);
18869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal    }
18969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal}
190