Package org.fakereplace.core

Source Code of org.fakereplace.core.Transformer

/*
* Copyright 2012, Stuart Douglas, and individual contributors as indicated
* by the @authors tag.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.fakereplace.core;

import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

import javassist.ClassPool;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.DuplicateMemberException;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import org.fakereplace.api.Extension;
import org.fakereplace.api.environment.CurrentEnvironment;
import org.fakereplace.data.BaseClassData;
import org.fakereplace.data.ClassDataStore;
import org.fakereplace.data.InstanceTracker;
import org.fakereplace.manip.Manipulator;
import org.fakereplace.manip.util.ManipulationUtils;
import org.fakereplace.reflection.ReflectionInstrumentationSetup;
import org.fakereplace.transformation.FakereplaceTransformer;
import org.fakereplace.util.NoInstrument;

/**
* This file is the transformer that instruments classes as they are added to
* the system.
*
* @author stuart
*/
public class Transformer implements FakereplaceTransformer {

    private static final Manipulator manipulator = new Manipulator();

    private final Set<String> trackedInstances = new HashSet<String>();

    private final List<FakereplaceTransformer> integrationTransformers = new CopyOnWriteArrayList<FakereplaceTransformer>();

    Transformer(Set<Extension> extension) {
        ReflectionInstrumentationSetup.setup(manipulator);
        for (Extension i : extension) {
            trackedInstances.addAll(i.getTrackedInstanceClassNames());
            List<FakereplaceTransformer> t = i.getTransformers();
            if (t != null) {
                integrationTransformers.addAll(t);
            }
        }
    }

    public boolean transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, ClassFile file) throws IllegalClassFormatException, BadBytecode {
        boolean modified = false;
        try {
            if (classBeingRedefined != null) {
                ClassDataStore.instance().markClassReplaced(classBeingRedefined);
            }
            for (FakereplaceTransformer i : integrationTransformers) {
                if (i.transform(loader, className, classBeingRedefined, protectionDomain, file)) {
                    modified = true;
                }
            }
            // we do not instrument any classes from fakereplace
            // if we did we get an endless loop
            // we also avoid instrumenting much of the java/lang and
            // java/io namespace except for java/lang/reflect/Proxy
            if (BuiltinClassData.skipInstrumentation(className)) {
                if (classBeingRedefined != null && manipulator.transformClass(file, loader, false)) {
                    modified = true;
                }
                return modified;
            }


            if (classBeingRedefined == null) {
                AnnotationsAttribute at = (AnnotationsAttribute) file.getAttribute(AnnotationsAttribute.invisibleTag);
                if (at != null) {
                    // NoInstrument is used for testing or by integration modules
                    Object an = at.getAnnotation(NoInstrument.class.getName());
                    if (an != null) {
                        return modified;
                    }
                }
            }

            if (trackedInstances.contains(file.getName())) {
                makeTrackedInstance(file);
                modified = true;
            }

            final boolean replaceable = CurrentEnvironment.getEnvironment().isClassReplaceable(className, loader);
            if (manipulator.transformClass(file, loader, replaceable)) {
                modified = true;
            }

            if (replaceable) {
                if ((AccessFlag.ENUM & file.getAccessFlags()) == 0 && (AccessFlag.ANNOTATION & file.getAccessFlags()) == 0) {
                    modified = true;

                    CurrentEnvironment.getEnvironment().recordTimestamp(className, loader);
                    if (file.isInterface()) {
                        addAbstractMethodForInstrumentation(file);
                    } else {
                        addMethodForInstrumentation(file);
                        addConstructorForInstrumentation(file);
                        addStaticConstructorForInstrumentation(file);
                    }
                }

                BaseClassData baseData = new BaseClassData(file, loader, replaceable);
                ClassDataStore.instance().saveClassData(loader, baseData.getInternalName(), baseData);
            }
            // SerialVersionUIDChecker.testReflectionInfo(loader, file.getName(),
            // file.getSuperclass(), classfileBuffer);
            return modified;
        } finally {
            if (modified) {
                try {
                    for (MethodInfo method : (List<MethodInfo>) file.getMethods()) {
                        method.rebuildStackMap(ClassPool.getDefault());
                    }
                } catch (BadBytecode e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }

    /**
     * Adds a method to a class that re can redefine when the class is reloaded
     *
     * @param file
     * @throws DuplicateMemberException
     */
    public void addMethodForInstrumentation(ClassFile file) {
        try {
            MethodInfo m = new MethodInfo(file.getConstPool(), Constants.ADDED_METHOD_NAME, Constants.ADDED_METHOD_DESCRIPTOR);
            m.setAccessFlags(0 | AccessFlag.PUBLIC | AccessFlag.SYNTHETIC);

            Bytecode b = new Bytecode(file.getConstPool(), 5, 3);
            if (BuiltinClassData.skipInstrumentation(file.getSuperclass())) {
                b.add(Bytecode.ACONST_NULL);
                b.add(Bytecode.ARETURN);
            } else {
                // delegate to the parent class
                b.add(Bytecode.ALOAD_0);
                b.add(Bytecode.ILOAD_1);
                b.add(Bytecode.ALOAD_2);
                b.addInvokespecial(file.getSuperclass(), Constants.ADDED_METHOD_NAME, Constants.ADDED_METHOD_DESCRIPTOR);
                b.add(Bytecode.ARETURN);

            }
            CodeAttribute ca = b.toCodeAttribute();
            m.setCodeAttribute(ca);
            file.addMethod(m);
        } catch (DuplicateMemberException e) {
            // e.printStackTrace();
        }
        try {
            MethodInfo m = new MethodInfo(file.getConstPool(), Constants.ADDED_STATIC_METHOD_NAME, Constants.ADDED_STATIC_METHOD_DESCRIPTOR);
            m.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC | AccessFlag.SYNTHETIC);
            Bytecode b = new Bytecode(file.getConstPool(), 5, 3);
            b.add(Bytecode.ACONST_NULL);
            b.add(Bytecode.ARETURN);
            CodeAttribute ca = b.toCodeAttribute();
            m.setCodeAttribute(ca);
            file.addMethod(m);

        } catch (DuplicateMemberException e) {
            // e.printStackTrace();
        }
    }

    public static void addStaticConstructorForInstrumentation(ClassFile file) {
        try {
            MethodInfo m = new MethodInfo(file.getConstPool(), "<clinit>", "()V");
            m.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
            Bytecode b = new Bytecode(file.getConstPool());
            b.add(Opcode.RETURN);
            m.setCodeAttribute(b.toCodeAttribute());
            file.addMethod(m);
        } catch (DuplicateMemberException e) {
            // e.printStackTrace();
        }
    }

    /**
     * Adds a method to a class that re can redefine when the class is reloaded
     *
     * @param file
     * @throws DuplicateMemberException
     */
    public void addAbstractMethodForInstrumentation(ClassFile file) {
        try {
            MethodInfo m = new MethodInfo(file.getConstPool(), Constants.ADDED_METHOD_NAME, Constants.ADDED_METHOD_DESCRIPTOR);
            m.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.ABSTRACT | AccessFlag.SYNTHETIC);
            file.addMethod(m);
        } catch (DuplicateMemberException e) {
            // e.printStackTrace();
        }
    }

    void addConstructorForInstrumentation(ClassFile file) {

        MethodInfo ret = new MethodInfo(file.getConstPool(), "<init>", Constants.ADDED_CONSTRUCTOR_DESCRIPTOR);
        Bytecode code = new Bytecode(file.getConstPool());
        // if the class does not have a constructor return
        if (!ManipulationUtils.addBogusConstructorCall(file, code)) {
            return;
        }
        CodeAttribute ca = code.toCodeAttribute();
        ca.setMaxLocals(4);
        ret.setCodeAttribute(ca);
        ret.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.SYNTHETIC);
        try {
            ca.computeMaxStack();
            file.addMethod(ret);
        } catch (DuplicateMemberException e) {

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static Manipulator getManipulator() {
        return manipulator;
    }

    /**
     * modifies a class so that all created instances are registered with
     * InstanceTracker
     *
     * @param file
     * @throws BadBytecode
     */
    public void makeTrackedInstance(ClassFile file) throws BadBytecode {
        for (MethodInfo m : (List<MethodInfo>) file.getMethods()) {
            if (m.getName().equals("<init>")) {
                Bytecode code = new Bytecode(file.getConstPool());
                code.addLdc(file.getName());
                code.addAload(0);
                code.addInvokestatic(InstanceTracker.class.getName(), "add", "(Ljava/lang/String;Ljava/lang/Object;)V");
                CodeIterator it = m.getCodeAttribute().iterator();
                it.skipConstructor();
                it.insert(code.get());
                m.getCodeAttribute().computeMaxStack();
            }
        }
    }
}
TOP

Related Classes of org.fakereplace.core.Transformer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.