Package org.codehaus.aspectwerkz.transform.inlining

Source Code of org.codehaus.aspectwerkz.transform.inlining.InliningWeavingStrategy

/**************************************************************************************

* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved.                 *

* http://aspectwerkz.codehaus.org                                                    *

* ---------------------------------------------------------------------------------- *

* The software in this package is published under the terms 8of the LGPL license      *

* a copy of which has been included with this distribution in the license.txt file.  *

**************************************************************************************/

package org.codehaus.aspectwerkz.transform.inlining;



import gnu.trove.TLongObjectHashMap;



import java.util.ArrayList;

import java.util.HashSet;

import java.util.Iterator;

import java.util.List;

import java.util.Set;



import org.codehaus.aspectwerkz.definition.SystemDefinition;

import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;

import org.codehaus.aspectwerkz.expression.ExpressionContext;

import org.codehaus.aspectwerkz.expression.PointcutType;

import org.codehaus.aspectwerkz.reflect.ClassInfo;

import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;

import org.codehaus.aspectwerkz.transform.Context;

import org.codehaus.aspectwerkz.transform.WeavingStrategy;

import org.codehaus.aspectwerkz.transform.inlining.weaver.AddInterfaceVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.AddMixinMethodsVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.AlreadyAddedMethodAdapter;

import org.codehaus.aspectwerkz.transform.inlining.weaver.ConstructorBodyVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.ConstructorCallVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.FieldSetFieldGetVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.HandlerVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.InstanceLevelAspectVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.JoinPointInitVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.LabelToLineNumberVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.MethodCallVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.MethodExecutionVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.StaticInitializationVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.SerialVersionUidVisitor;

import org.codehaus.aspectwerkz.transform.inlining.weaver.AddWrapperVisitor;

import org.objectweb.asm.ClassReader;

import org.objectweb.asm.ClassVisitor;

import org.objectweb.asm.ClassWriter;

import org.objectweb.asm.attrs.Attributes;



/**

* A weaving strategy implementing a weaving scheme based on statical compilation, and no reflection.

*

* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>

* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>

*/

public class InliningWeavingStrategy implements WeavingStrategy {



    /**

     * Performs the weaving of the target class.

     *

     * @param className

     * @param context

     */

    public void transform(String className, final Context context) {

        try {

            final byte[] bytecode = context.getInitialBytecode();

            final ClassLoader loader = context.getLoader();



            ClassInfo classInfo = AsmClassInfo.getClassInfo(bytecode, loader);



            // skip Java reflect proxies for which we cannot get the resource as a stream

            // which leads to warnings when using annotation matching

            // Note: we use an heuristic assuming JDK proxy are classes named "$..."

            // to avoid to call getSuperClass everytime

            if (classInfo.getName().startsWith("$") && classInfo.getSuperclass().getName().equals("java.lang.reflect.Proxy")) {

                context.setCurrentBytecode(context.getInitialBytecode());

                return;

            }



            //TODO:FIXME match on (within, null, classInfo) should be equivalent to those ones.

            final Set definitions = context.getDefinitions();

            final ExpressionContext[] ctxs = new ExpressionContext[]{

                new ExpressionContext(PointcutType.EXECUTION, classInfo, classInfo),

                new ExpressionContext(PointcutType.CALL, null, classInfo),

                new ExpressionContext(PointcutType.GET, null, classInfo),

                new ExpressionContext(PointcutType.SET, null, classInfo),

                new ExpressionContext(PointcutType.HANDLER, null, classInfo),

                new ExpressionContext(PointcutType.STATIC_INITIALIZATION, classInfo, classInfo),

                new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)

            };



            if (classFilter(definitions, ctxs, classInfo)) {

                return;

            }



            // build the ClassInfo from the bytecode to avoid loading it from the loader resource stream later

            // to support stub weaving

            //AsmClassInfo.getClassInfo(bytecode, loader);



            // compute CALL + GET/SET early matching results to avoid registering useless visitors

            final boolean filterForCall = classFilterFor(

                    definitions, new ExpressionContext[]{

                        new ExpressionContext(PointcutType.CALL, null, classInfo),

                        new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)

                    }

            );//FIXME - within make match all

            final boolean filterForGetSet = classFilterFor(

                    definitions, new ExpressionContext[]{

                        new ExpressionContext(PointcutType.GET, null, classInfo),

                        new ExpressionContext(PointcutType.SET, null, classInfo),

                        new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)

                    }

            );//FIXME - within make match all

            final boolean filterForHandler = classFilterFor(

                    definitions, new ExpressionContext[]{

                        new ExpressionContext(PointcutType.HANDLER, null, classInfo),

                        new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo)

                    }

            );//FIXME - within make match all



            // note: for staticinitialization we do an exact match right there

            boolean filterForStaticinitialization = !classInfo.hasStaticInitializer()

              ||  classFilterFor(definitions, new ExpressionContext[] {

              new ExpressionContext(PointcutType.STATIC_INITIALIZATION,

                                    classInfo.staticInitializer(),

                                    classInfo)

                }

                );

            if (!filterForStaticinitialization) {

                filterForStaticinitialization = !hasPointcut(definitions, new ExpressionContext(

                        PointcutType.STATIC_INITIALIZATION,

                        classInfo.staticInitializer(),

                        classInfo

                ));

            }



            // prepare ctor call jp

            final ClassReader crLookahead = new ClassReader(bytecode);

            TLongObjectHashMap newInvocationsByCallerMemberHash = null;

            if (!filterForCall) {

                newInvocationsByCallerMemberHash = new TLongObjectHashMap();

                crLookahead.accept(

                        new ConstructorCallVisitor.LookaheadNewDupInvokeSpecialInstructionClassAdapter(

                                newInvocationsByCallerMemberHash

                        ),

                        true

                );

            }



            // prepare handler jp, by gathering ALL catch blocks and their exception type

            List catchLabels = new ArrayList();

            if (!filterForHandler) {

                final ClassReader crLookahead2 = new ClassReader(bytecode);

                final ClassWriter cw2 = AsmHelper.newClassWriter(true);



                HandlerVisitor.LookaheadCatchLabelsClassAdapter lookForCatches =

                        new HandlerVisitor.LookaheadCatchLabelsClassAdapter(

                                cw2, loader, classInfo, context, catchLabels

                        );

                // we must visit exactly as we will do further on with debug info (that produces extra labels)

                crLookahead2.accept(lookForCatches, Attributes.getDefaultAttributes(), false);

            }



            // gather wrapper methods to support multi-weaving

            // skip annotations visit and debug info by using the lookahead read-only classreader

            Set addedMethods = new HashSet();

            crLookahead.accept(new AlreadyAddedMethodAdapter(addedMethods), true);



            // -- Phase 1 -- type change

            final ClassWriter writerPhase1 = AsmHelper.newClassWriter(true);

            final ClassReader readerPhase1 = new ClassReader(bytecode);

            ClassVisitor reversedChainPhase1 = writerPhase1;

            reversedChainPhase1 = new AddMixinMethodsVisitor(reversedChainPhase1, classInfo, context, addedMethods);

            reversedChainPhase1 = new AddInterfaceVisitor(reversedChainPhase1, classInfo, context);

            readerPhase1.accept(reversedChainPhase1, Attributes.getDefaultAttributes(), false);

            final byte[] bytesPhase1 = writerPhase1.toByteArray();



            // update the class info

            classInfo = AsmClassInfo.newClassInfo(bytesPhase1, loader);



            // -- Phase 2 -- advices

            final ClassWriter writerPhase2 = AsmHelper.newClassWriter(true);

            final ClassReader readerPhase2 = new ClassReader(bytesPhase1);

            ClassVisitor reversedChainPhase2 = writerPhase2;

            reversedChainPhase2 = new InstanceLevelAspectVisitor(reversedChainPhase2, classInfo, context);

            reversedChainPhase2 = new MethodExecutionVisitor(reversedChainPhase2, classInfo, context, addedMethods);

            reversedChainPhase2 = new ConstructorBodyVisitor(reversedChainPhase2, classInfo, context, addedMethods);

            if(!filterForStaticinitialization) {

              reversedChainPhase2 = new StaticInitializationVisitor(reversedChainPhase2, context, addedMethods);

            }

            reversedChainPhase2 = new HandlerVisitor(reversedChainPhase2, context, catchLabels);

            if (!filterForCall) {

                reversedChainPhase2 = new MethodCallVisitor(reversedChainPhase2, loader, classInfo, context);

                reversedChainPhase2 = new ConstructorCallVisitor(

                        reversedChainPhase2, loader, classInfo, context, newInvocationsByCallerMemberHash

                );

            }

            if (!filterForGetSet) {

                reversedChainPhase2 = new FieldSetFieldGetVisitor(reversedChainPhase2, loader, classInfo, context);

            }

            reversedChainPhase2 = new LabelToLineNumberVisitor(reversedChainPhase2, context);

            readerPhase2.accept(reversedChainPhase2, Attributes.getDefaultAttributes(), false);

            final byte[] bytesPhase2 = writerPhase2.toByteArray();



            context.setCurrentBytecode(bytesPhase2);



            // -- Phase 3 -- serialUID and JoinPoint initialization

            if (context.isAdvised()) {

                final ClassWriter writerPhase3 = AsmHelper.newClassWriter(true);

                ClassReader readerPhase3 = new ClassReader(bytesPhase2);

                ClassVisitor reversedChainPhase3 = writerPhase3;

                reversedChainPhase3 = new SerialVersionUidVisitor.Add(reversedChainPhase3, context, classInfo);

                reversedChainPhase3 = new AddWrapperVisitor(reversedChainPhase3, context, addedMethods);

                reversedChainPhase3 = new JoinPointInitVisitor(reversedChainPhase3, context);

                readerPhase3.accept(reversedChainPhase3, Attributes.getDefaultAttributes(), false);

                final byte[] bytesPhase3 = writerPhase3.toByteArray();



                context.setCurrentBytecode(bytesPhase3);

            }



            // TODO: INNER CLASS OR NOT?

            // loop over emitted jp and flag them as inner classes

//            for (Iterator iterator = ((ContextImpl) context).getEmittedInlinedJoinPoint().iterator(); iterator.hasNext();) {

//                String joinPointClassName = ((ContextImpl.EmittedJoinPoint) iterator.next()).joinPointClassName;

//                int innerIndex = joinPointClassName.lastIndexOf('$');

//                cw.visitInnerClass(joinPointClassName,

//                        joinPointClassName.substring(0, innerIndex),

//                        joinPointClassName.substring(innerIndex + 1, joinPointClassName.length()),

//                        Constants.ACC_PUBLIC + Constants.ACC_STATIC);

//            }



//            // resolve line numbers - debug only

//            List ejp = ((ContextImpl)context).getEmittedJoinPoints();

//            for (Iterator iterator = ejp.iterator(); iterator.hasNext();) {

//                EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) iterator.next();

//                emittedJoinPoint.resolveLineNumber(context);

//                System.out.println(emittedJoinPoint.toString());

//            }



            // NOTE: remove when in release time or in debugging trouble (;-) - Alex)

            // FAKE multiweaving - which is a requirement

            //            Object multi = context.getMetaData("FAKE");

            //            if (multi == null) {

            //                context.addMetaData("FAKE", "FAKE");

            //                transform(className, context);

            //            }



        } catch (Throwable t) {

            t.printStackTrace();

            throw new WrappedRuntimeException(t);

        }

    }



    /**

     * Creates a new transformation context.

     *

     * @param name

     * @param bytecode

     * @param loader

     * @return

     */

    public Context newContext(final String name, final byte[] bytecode, final ClassLoader loader) {

        return new ContextImpl(name, bytecode, loader);

    }



    /**

     * Filters out the classes that are not eligible for transformation.

     *

     * @param definitions the definitions

     * @param ctxs        an array with the contexts

     * @param classInfo   the class to filter

     * @return boolean true if the class should be filtered out

     */

    private static boolean classFilter(final Set definitions,

                                       final ExpressionContext[] ctxs,

                                       final ClassInfo classInfo) {

        if (classInfo.isInterface()) {

            return true;

        }

        for (Iterator defs = definitions.iterator(); defs.hasNext();) {

            if (classFilter((SystemDefinition) defs.next(), ctxs, classInfo)) {

                continue;

            } else {

                return false;

            }

        }

        return true;

    }



    /**

     * Filters out the classes that are not eligible for transformation.

     *

     * @param definition the definition

     * @param ctxs       an array with the contexts

     * @param classInfo  the class to filter

     * @return boolean true if the class should be filtered out

     * @TODO: when a class had execution pointcut that were removed it must be unweaved, thus not filtered out How to

     * handle that? cache lookup? or custom class level attribute ?

     */

    private static boolean classFilter(final SystemDefinition definition,

                                       final ExpressionContext[] ctxs,

                                       final ClassInfo classInfo) {

        if (classInfo.isInterface()) {

            return true;

        }

        String className = classInfo.getName();

        if (definition.inExcludePackage(className)) {

            return true;

        }

        if (!definition.inIncludePackage(className)) {

            return true;

        }

        if (definition.isAdvised(ctxs)) {

            return false;

        }

        if (definition.hasMixin(ctxs)) {

            return false;

        }

        if (definition.hasIntroducedInterface(ctxs)) {

            return false;

        }

        if (definition.inPreparePackage(className)) {

            return false;

        }

        return true;

    }



    private static boolean classFilterFor(final Set definitions,

                                          final ExpressionContext[] ctxs) {

        for (Iterator defs = definitions.iterator(); defs.hasNext();) {

            if (classFilterFor((SystemDefinition) defs.next(), ctxs)) {

                continue;

            } else {

                return false;

            }

        }

        return true;

    }



    private static boolean classFilterFor(final SystemDefinition definition,

                                          final ExpressionContext[] ctxs) {

        if (definition.isAdvised(ctxs)) {

            return false;

        }

        return true;

    }



    private static boolean hasPointcut(final Set definitions,

                                       final ExpressionContext ctx) {

        for (Iterator defs = definitions.iterator(); defs.hasNext();) {

            if (hasPointcut((SystemDefinition) defs.next(), ctx)) {

                return true;

            } else {

                continue;

            }

        }

        return false;

    }



    private static boolean hasPointcut(final SystemDefinition definition,

                                       final ExpressionContext ctx) {

        if (definition.hasPointcut(ctx)) {

            return true;

        }

        return false;

    }

}
TOP

Related Classes of org.codehaus.aspectwerkz.transform.inlining.InliningWeavingStrategy

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.