All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.weaver.bcel.BcelWorld Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * Contributors:
 *     PARC     initial implementation
 *     Alexandre Vasseur    perClause support for @AJ aspects
 * ******************************************************************/

package org.aspectj.weaver.bcel;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ClassParser;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.generic.FieldInstruction;
import org.aspectj.apache.bcel.generic.INVOKEINTERFACE;
import org.aspectj.apache.bcel.generic.Instruction;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InvokeInstruction;
import org.aspectj.apache.bcel.generic.MULTIANEWARRAY;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.apache.bcel.util.ClassLoaderReference;
import org.aspectj.apache.bcel.util.ClassLoaderRepository;
import org.aspectj.apache.bcel.util.ClassPath;
import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository;
import org.aspectj.apache.bcel.util.Repository;
import org.aspectj.asm.AsmManager;
import org.aspectj.asm.IRelationship;
import org.aspectj.asm.internal.CharOperation;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.bridge.WeaveMessage;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.AnnotationOnTypeMunger;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.Checker;
import org.aspectj.weaver.ICrossReferenceHandler;
import org.aspectj.weaver.IWeavingSupport;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.MemberImpl;
import org.aspectj.weaver.MemberKind;
import org.aspectj.weaver.NewParentTypeMunger;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ReferenceTypeDelegate;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedMemberImpl;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.ResolvedTypeMunger;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.ShadowMunger;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.loadtime.definition.Definition;
import org.aspectj.weaver.loadtime.definition.DocumentParser;
import org.aspectj.weaver.model.AsmRelationshipProvider;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.DeclareParents;
import org.aspectj.weaver.patterns.ParserException;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.TypePattern;
import org.aspectj.weaver.tools.Trace;
import org.aspectj.weaver.tools.TraceFactory;

public class BcelWorld extends World implements Repository {

	private final ClassPathManager classPath;
	protected Repository delegate;
	private BcelWeakClassLoaderReference loaderRef;
	private final BcelWeavingSupport bcelWeavingSupport = new BcelWeavingSupport();
	private boolean isXmlConfiguredWorld = false;
	private WeavingXmlConfig xmlConfiguration;
	private List typeDelegateResolvers;

	private static Trace trace = TraceFactory.getTraceFactory().getTrace(BcelWorld.class);

	public BcelWorld() {
		this("");
	}

	public BcelWorld(String cp) {
		this(makeDefaultClasspath(cp), IMessageHandler.THROW, null);
	}

	public IRelationship.Kind determineRelKind(ShadowMunger munger) {
		AdviceKind ak = ((Advice) munger).getKind();
		if (ak.getKey() == AdviceKind.Before.getKey()) {
			return IRelationship.Kind.ADVICE_BEFORE;
		} else if (ak.getKey() == AdviceKind.After.getKey()) {
			return IRelationship.Kind.ADVICE_AFTER;
		} else if (ak.getKey() == AdviceKind.AfterThrowing.getKey()) {
			return IRelationship.Kind.ADVICE_AFTERTHROWING;
		} else if (ak.getKey() == AdviceKind.AfterReturning.getKey()) {
			return IRelationship.Kind.ADVICE_AFTERRETURNING;
		} else if (ak.getKey() == AdviceKind.Around.getKey()) {
			return IRelationship.Kind.ADVICE_AROUND;
		} else if (ak.getKey() == AdviceKind.CflowEntry.getKey() || ak.getKey() == AdviceKind.CflowBelowEntry.getKey()
				|| ak.getKey() == AdviceKind.InterInitializer.getKey() || ak.getKey() == AdviceKind.PerCflowEntry.getKey()
				|| ak.getKey() == AdviceKind.PerCflowBelowEntry.getKey() || ak.getKey() == AdviceKind.PerThisEntry.getKey()
				|| ak.getKey() == AdviceKind.PerTargetEntry.getKey() || ak.getKey() == AdviceKind.Softener.getKey()
				|| ak.getKey() == AdviceKind.PerTypeWithinEntry.getKey()) {
			// System.err.println("Dont want a message about this: "+ak);
			return null;
		}
		throw new RuntimeException("Shadow.determineRelKind: What the hell is it? " + ak);
	}

	@Override
	public void reportMatch(ShadowMunger munger, Shadow shadow) {
		if (getCrossReferenceHandler() != null) {
			final IRelationship.Kind kind = determineRelKind(munger);
			getCrossReferenceHandler().addCrossReference(
				munger.getSourceLocation(),           // What is being applied?
				shadow.getSourceLocation(),           // Where is it being applied?
				kind == null ? null : kind.getName(), // What kind of advice?
				((Advice) munger).hasDynamicTests()   // Is a runtime test being stuffed in the code?
			);
		}

		if (!getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) {
			reportWeavingMessage(munger, shadow);
		}

		if (getModel() != null) {
			AsmRelationshipProvider.addAdvisedRelationship(getModelAsAsmManager(), shadow, munger);
		}
	}

	/*
	 * Report a message about the advice weave that has occurred. Some messing about to make it pretty ! This code is just asking
	 * for an NPE to occur ...
	 */
	private void reportWeavingMessage(ShadowMunger munger, Shadow shadow) {
		Advice advice = (Advice) munger;
		AdviceKind aKind = advice.getKind();
		// Only report on interesting advice kinds ...
		if (aKind == null || advice.getConcreteAspect() == null) {
			// We suspect someone is programmatically driving the weaver
			// (e.g. IdWeaveTestCase in the weaver testcases)
			return;
		}
		if (!(aKind.equals(AdviceKind.Before) || aKind.equals(AdviceKind.After) || aKind.equals(AdviceKind.AfterReturning)
				|| aKind.equals(AdviceKind.AfterThrowing) || aKind.equals(AdviceKind.Around) || aKind.equals(AdviceKind.Softener))) {
			return;
		}

		// synchronized blocks are implemented with multiple monitor_exit instructions in the bytecode
		// (one for normal exit from the method, one for abnormal exit), we only want to tell the user
		// once we have advised the end of the sync block, even though under the covers we will have
		// woven both exit points
		if (shadow.getKind() == Shadow.SynchronizationUnlock) {
			if (advice.lastReportedMonitorExitJoinpointLocation == null) {
				// this is the first time through, let's continue...
				advice.lastReportedMonitorExitJoinpointLocation = shadow.getSourceLocation();
			} else {
				if (areTheSame(shadow.getSourceLocation(), advice.lastReportedMonitorExitJoinpointLocation)) {
					// Don't report it again!
					advice.lastReportedMonitorExitJoinpointLocation = null;
					return;
				}
				// hmmm, this means some kind of nesting is going on, urgh
				advice.lastReportedMonitorExitJoinpointLocation = shadow.getSourceLocation();
			}
		}

		String description = advice.getKind().toString();
		String advisedType = shadow.getEnclosingType().getName();
		String advisingType = advice.getConcreteAspect().getName();
		Message msg = null;
		if (advice.getKind().equals(AdviceKind.Softener)) {
			msg = WeaveMessage.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_SOFTENS, new String[] { advisedType,
					beautifyLocation(shadow.getSourceLocation()), advisingType, beautifyLocation(munger.getSourceLocation()) },
					advisedType, advisingType);
		} else {
			boolean runtimeTest = advice.hasDynamicTests();
			String joinPointDescription = shadow.toString();
			msg = WeaveMessage
					.constructWeavingMessage(WeaveMessage.WEAVEMESSAGE_ADVISES,
							new String[] { joinPointDescription, advisedType, beautifyLocation(shadow.getSourceLocation()),
									description, advisingType, beautifyLocation(munger.getSourceLocation()),
									(runtimeTest ? " [with runtime test]" : "") }, advisedType, advisingType);
			// Boolean.toString(runtimeTest)});
		}
		getMessageHandler().handleMessage(msg);
	}

	private boolean areTheSame(ISourceLocation locA, ISourceLocation locB) {
		if (locA == null) {
			return locB == null;
		}
		if (locB == null) {
			return false;
		}
		if (locA.getLine() != locB.getLine()) {
			return false;
		}
		File fA = locA.getSourceFile();
		File fB = locA.getSourceFile();
		if (fA == null) {
			return fB == null;
		}
		if (fB == null) {
			return false;
		}
		return fA.getName().equals(fB.getName());
	}

	/*
	 * Ensure we report a nice source location - particular in the case where the source info is missing (binary weave).
	 */
	private String beautifyLocation(ISourceLocation isl) {
		StringBuilder nice = new StringBuilder();
		if (isl == null || isl.getSourceFile() == null || isl.getSourceFile().getName().contains("no debug info available")) {
			nice.append("no debug info available");
		} else {
			// can't use File.getName() as this fails when a Linux box encounters a path created on Windows and vice-versa
			int takeFrom = isl.getSourceFile().getPath().lastIndexOf('/');
			if (takeFrom == -1) {
				takeFrom = isl.getSourceFile().getPath().lastIndexOf('\\');
			}
			int binary = isl.getSourceFile().getPath().lastIndexOf('!');
			if (binary != -1 && binary < takeFrom) {
				// we have been woven by a binary aspect
				String pathToBinaryLoc = isl.getSourceFile().getPath().substring(0, binary + 1);
				if (pathToBinaryLoc.contains(".jar")) {
					// only want to add the extra info if we're from a jar file
					int lastSlash = pathToBinaryLoc.lastIndexOf('/');
					if (lastSlash == -1) {
						lastSlash = pathToBinaryLoc.lastIndexOf('\\');
					}
					nice.append(pathToBinaryLoc.substring(lastSlash + 1));
				}
			}
			nice.append(isl.getSourceFile().getPath().substring(takeFrom + 1));
			if (isl.getLine() != 0) {
				nice.append(":").append(isl.getLine());
			}
			// if it's a binary file then also want to give the file name
			if (isl.getSourceFileName() != null) {
				nice.append("(from " + isl.getSourceFileName() + ")");
			}
		}
		return nice.toString();
	}

	private static List makeDefaultClasspath(String cp) {
		List classPath = new ArrayList<>();
		classPath.addAll(getPathEntries(cp));
		classPath.addAll(getPathEntries(ClassPath.getClassPath()));
		return classPath;

	}

	private static List getPathEntries(String s) {
		List ret = new ArrayList<>();
		StringTokenizer tok = new StringTokenizer(s, File.pathSeparator);
		while (tok.hasMoreTokens()) {
			ret.add(tok.nextToken());
		}
		return ret;
	}

	public BcelWorld(List classPath, IMessageHandler handler, ICrossReferenceHandler xrefHandler) {
		// this.aspectPath = new ClassPathManager(aspectPath, handler);
		this.classPath = new ClassPathManager(classPath, handler);
		setMessageHandler(handler);
		setCrossReferenceHandler(xrefHandler);
		// Tell BCEL to use us for resolving any classes
		delegate = this;
	}

	public BcelWorld(ClassPathManager cpm, IMessageHandler handler, ICrossReferenceHandler xrefHandler) {
		classPath = cpm;
		setMessageHandler(handler);
		setCrossReferenceHandler(xrefHandler);
		// Tell BCEL to use us for resolving any classes
		delegate = this;
	}

	/**
	 * Build a World from a ClassLoader, for LTW support
	 *
	 * @param loader
	 * @param handler
	 * @param xrefHandler
	 */
	public BcelWorld(ClassLoader loader, IMessageHandler handler, ICrossReferenceHandler xrefHandler) {
		classPath = null;
		loaderRef = new BcelWeakClassLoaderReference(loader);
		setMessageHandler(handler);
		setCrossReferenceHandler(xrefHandler);
		// Tell BCEL to use us for resolving any classes
		// delegate = getClassLoaderRepositoryFor(loader);
	}

	public void ensureRepositorySetup() {
		if (delegate == null) {
			delegate = getClassLoaderRepositoryFor(loaderRef);
		}
	}

	public Repository getClassLoaderRepositoryFor(ClassLoaderReference loader) {
		if (bcelRepositoryCaching) {
			return new ClassLoaderRepository(loader);
		} else {
			return new NonCachingClassLoaderRepository(loader);
		}
	}

	public void addPath(String name) {
		classPath.addPath(name, this.getMessageHandler());
	}

	// ---- various interactions with bcel

	public static Type makeBcelType(UnresolvedType type) {
		return Type.getType(type.getErasureSignature());
	}

	static Type[] makeBcelTypes(UnresolvedType[] types) {
		Type[] ret = new Type[types.length];
		for (int i = 0, len = types.length; i < len; i++) {
			ret[i] = makeBcelType(types[i]);
		}
		return ret;
	}

	public static Type[] makeBcelTypes(String[] types) {
		if (types == null || types.length==0 ) {
			return null;
		}
		Type[] ret = new Type[types.length];
		for (int i=0, len=types.length; i", new ResolvedType[] { INT });
		} else if (i instanceof MULTIANEWARRAY) {
			MULTIANEWARRAY arrayInstruction = (MULTIANEWARRAY) i;
			UnresolvedType ut = null;
			short dimensions = arrayInstruction.getDimensions();
			ObjectType ot = arrayInstruction.getLoadClassType(cpg);
			if (ot != null) {
				ut = fromBcel(ot);
				ut = UnresolvedType.makeArray(ut, dimensions);
			} else {
				Type t = arrayInstruction.getType(cpg);
				ut = fromBcel(t);
			}
			ResolvedType[] parms = new ResolvedType[dimensions];
			for (int ii = 0; ii < dimensions; ii++) {
				parms[ii] = INT;
			}
			retval = MemberImpl.method(ut, Modifier.PUBLIC, UnresolvedType.VOID, "", parms);

		} else if (i.opcode == Constants.NEWARRAY) {
			// NEWARRAY arrayInstruction = (NEWARRAY)i;
			Type ot = i.getType();
			UnresolvedType ut = fromBcel(ot);
			retval = MemberImpl.method(ut, Modifier.PUBLIC, UnresolvedType.VOID, "", new ResolvedType[] { INT });
		} else {
			throw new BCException("Cannot create array construction signature for this non-array instruction:" + i);
		}
		return retval;
	}

	public Member makeJoinPointSignatureForMethodInvocation(LazyClassGen cg, InvokeInstruction ii) {
		ConstantPool cpg = cg.getConstantPool();
		String name = ii.getName(cpg);
		String declaring = ii.getClassName(cpg);
		UnresolvedType declaringType = null;

		String signature = ii.getSignature(cpg);

		// 307147
		if (name.startsWith("ajc$privMethod$")) {
			// The invoke is on a privileged accessor. These may be created for different
			// kinds of target, not necessarily just private methods. In bug 307147 it is
			// for a private method. This code is identifying the particular case in 307147
			try {
				declaringType = UnresolvedType.forName(declaring);
				String typeNameAsFoundInAccessorName = declaringType.getName().replace('.', '_');
				int indexInAccessorName = name.lastIndexOf(typeNameAsFoundInAccessorName);
				if (indexInAccessorName != -1) {
					String methodName = name.substring(indexInAccessorName+typeNameAsFoundInAccessorName.length()+1);
					ResolvedType resolvedDeclaringType = declaringType.resolve(this);
					ResolvedMember[] methods = resolvedDeclaringType.getDeclaredMethods();
					for (ResolvedMember method: methods) {
						if (method.getName().equals(methodName) && method.getSignature().equals(signature) && Modifier.isPrivate(method.getModifiers())) {
							return method;
						}
					}
				}
			} catch (Exception e) {
				// Remove this once confident above code isn't having unexpected side effects
				// Added 1.8.7
				e.printStackTrace();
			}
		}

		int modifier = (ii instanceof INVOKEINTERFACE) ? Modifier.INTERFACE
				: (ii.opcode == Constants.INVOKESTATIC) ? Modifier.STATIC : (ii.opcode == Constants.INVOKESPECIAL && !name
						.equals("")) ? Modifier.PRIVATE : 0;

		// in Java 1.4 and after, static method call of super class within
		// subclass method appears
		// as declared by the subclass in the bytecode - but they are not
		// see #104212
		if (ii.opcode == Constants.INVOKESTATIC) {
			ResolvedType appearsDeclaredBy = resolve(declaring);
			// look for the method there
			for (Iterator iterator = appearsDeclaredBy.getMethods(true, true); iterator.hasNext();) {
				ResolvedMember method = iterator.next();
				if (Modifier.isStatic(method.getModifiers())) {
					if (name.equals(method.getName()) && signature.equals(method.getSignature())) {
						// we found it
						declaringType = method.getDeclaringType();
						break;
					}
				}
			}
		}

		if (declaringType == null) {
			if (declaring.charAt(0) == '[') {
				declaringType = UnresolvedType.forSignature(declaring);
			} else {
				declaringType = UnresolvedType.forName(declaring);
			}
		}
		return MemberImpl.method(declaringType, modifier, name, signature);
	}

	@Override
	public String toString() {
		StringBuilder buf = new StringBuilder();
		buf.append("BcelWorld(");
		// buf.append(shadowMungerMap);
		buf.append(")");
		return buf.toString();
	}

	/**
	 * Retrieve a bcel delegate for an aspect - this will return NULL if the delegate is an EclipseSourceType and not a
	 * BcelObjectType - this happens quite often when incrementally compiling.
	 */
	public static BcelObjectType getBcelObjectType(ResolvedType concreteAspect) {
		if (concreteAspect == null) {
			return null;
		}
		if (!(concreteAspect instanceof ReferenceType)) { // Might be Missing
			return null;
		}
		ReferenceTypeDelegate rtDelegate = ((ReferenceType) concreteAspect).getDelegate();
		if (rtDelegate instanceof BcelObjectType) {
			return (BcelObjectType) rtDelegate;
		} else {
			return null;
		}
	}

	public void tidyUp() {
		// At end of compile, close any open files so deletion of those archives
		// is possible
		classPath.closeArchives();
		typeMap.report();
		typeMap.demote(true);
		// ResolvedType.resetPrimitives();
	}

	// / The repository interface methods

	@Override
	public JavaClass findClass(String className) {
		return lookupJavaClass(classPath, className);
	}

	@Override
	public JavaClass loadClass(String className) throws ClassNotFoundException {
		return lookupJavaClass(classPath, className);
	}

	@Override
	public void storeClass(JavaClass clazz) {
		// doesn't need to do anything
	}

	@Override
	public void removeClass(JavaClass clazz) {
		throw new RuntimeException("Not implemented");
	}

	@Override
	public JavaClass loadClass(Class clazz) throws ClassNotFoundException {
		throw new RuntimeException("Not implemented");
	}

	@Override
	public void clear() {
		delegate.clear();
		// throw new RuntimeException("Not implemented");
	}

	/**
	 * The aim of this method is to make sure a particular type is 'ok'. Some operations on the delegate for a type modify it and
	 * this method is intended to undo that... see pr85132
	 */
	@Override
	public void validateType(UnresolvedType type) {
		ResolvedType result = typeMap.get(type.getSignature());
		if (result == null) {
			return; // We haven't heard of it yet
		}
		if (!result.isExposedToWeaver()) {
			return; // cant need resetting
		}
		result.ensureConsistent();
		// If we want to rebuild it 'from scratch' then:
		// ClassParser cp = new ClassParser(new
		// ByteArrayInputStream(newbytes),new String(cs));
		// try {
		// rt.setDelegate(makeBcelObjectType(rt,cp.parse(),true));
		// } catch (ClassFormatException e) {
		// e.printStackTrace();
		// } catch (IOException e) {
		// e.printStackTrace();
		// }
	}

	/**
	 * Apply a single declare parents - return true if we change the type
	 */
	private boolean applyDeclareParents(DeclareParents p, ResolvedType onType) {
		boolean didSomething = false;
		List newParents = p.findMatchingNewParents(onType, true);
		if (!newParents.isEmpty()) {
			didSomething = true;
			BcelObjectType classType = BcelWorld.getBcelObjectType(onType);
			// System.err.println("need to do declare parents for: " + onType);
			for (ResolvedType newParent : newParents) {
				// We set it here so that the imminent matching for ITDs can
				// succeed - we still haven't done the necessary changes to the class file
				// itself (like transform super calls) - that is done in
				// BcelTypeMunger.mungeNewParent()
				// classType.addParent(newParent);
				onType.addParent(newParent);
				ResolvedTypeMunger newParentMunger = new NewParentTypeMunger(newParent, p.getDeclaringType());
				newParentMunger.setSourceLocation(p.getSourceLocation());
				onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, getCrosscuttingMembersSet()
						.findAspectDeclaringParents(p)), false);
			}
		}
		return didSomething;
	}

	/**
	 * Apply a declare @type - return true if we change the type
	 */
	private boolean applyDeclareAtType(DeclareAnnotation decA, ResolvedType onType, boolean reportProblems) {
		boolean didSomething = false;
		if (decA.matches(onType)) {

			if (onType.hasAnnotation(decA.getAnnotation().getType())) {
				// already has it
				return false;
			}

			AnnotationAJ annoX = decA.getAnnotation();

			// check the annotation is suitable for the target
			boolean isOK = checkTargetOK(decA, onType, annoX);

			if (isOK) {
				didSomething = true;
				ResolvedTypeMunger newAnnotationTM = new AnnotationOnTypeMunger(annoX);
				newAnnotationTM.setSourceLocation(decA.getSourceLocation());
				onType.addInterTypeMunger(new BcelTypeMunger(newAnnotationTM, decA.getAspect().resolve(this)), false);
				decA.copyAnnotationTo(onType);
			}
		}
		return didSomething;
	}

	/**
	 * Apply the specified declare @field construct to any matching fields in the specified type.
	 * @param deca the declare annotation targeting fields
	 * @param type the type to check for members matching the declare annotation
	 * @return true if something matched and the type was modified
	 */
	private boolean applyDeclareAtField(DeclareAnnotation deca, ResolvedType type) {
		boolean changedType = false;
		ResolvedMember[] fields = type.getDeclaredFields();
		for (ResolvedMember field: fields) {
			if (deca.matches(field, this)) {
				AnnotationAJ anno = deca.getAnnotation();
				if (!field.hasAnnotation(anno.getType())) {
					field.addAnnotation(anno);
					changedType=true;
				}
			}
		}
		return changedType;
	}

	/**
	 * Checks for an @target() on the annotation and if found ensures it allows the annotation to be attached to the target type
	 * that matched.
	 */
	private boolean checkTargetOK(DeclareAnnotation decA, ResolvedType onType, AnnotationAJ annoX) {
		if (annoX.specifiesTarget()) {
			if ((onType.isAnnotation() && !annoX.allowedOnAnnotationType()) || (!annoX.allowedOnRegularType())) {
				return false;
			}
		}
		return true;
	}

	// Hmmm - very similar to the code in BcelWeaver.weaveParentTypeMungers -
	// this code
	// doesn't need to produce errors/warnings though as it won't really be
	// weaving.
	protected void weaveInterTypeDeclarations(ResolvedType onType) {

		List declareParentsList = getCrosscuttingMembersSet().getDeclareParents();
		if (onType.isRawType()) {
			onType = onType.getGenericType();
		}
		onType.clearInterTypeMungers();

		List decpToRepeat = new ArrayList<>();

		boolean aParentChangeOccurred = false;
		boolean anAnnotationChangeOccurred = false;
		// First pass - apply all decp mungers
		for (DeclareParents decp : declareParentsList) {
			boolean typeChanged = applyDeclareParents(decp, onType);
			if (typeChanged) {
				aParentChangeOccurred = true;
			} else { // Perhaps it would have matched if a 'dec @type' had
				// modified the type
				if (!decp.getChild().isStarAnnotation()) {
					decpToRepeat.add(decp);
				}
			}
		}

		// Still first pass - apply all dec @type mungers
		for (DeclareAnnotation decA : getCrosscuttingMembersSet().getDeclareAnnotationOnTypes()) {
			boolean typeChanged = applyDeclareAtType(decA, onType, true);
			if (typeChanged) {
				anAnnotationChangeOccurred = true;
			}
		}

		// apply declare @field
		for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnFields()) {
			if (applyDeclareAtField(deca,onType)) {
				anAnnotationChangeOccurred = true;
			}
		}

		while ((aParentChangeOccurred || anAnnotationChangeOccurred) && !decpToRepeat.isEmpty()) {
			anAnnotationChangeOccurred = aParentChangeOccurred = false;
			List decpToRepeatNextTime = new ArrayList<>();
			for (DeclareParents decp: decpToRepeat) {
				if (applyDeclareParents(decp, onType)) {
					aParentChangeOccurred = true;
				} else {
					decpToRepeatNextTime.add(decp);
				}
			}

			for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnTypes()) {
				if (applyDeclareAtType(deca, onType, false)) {
					anAnnotationChangeOccurred = true;
				}
			}

			for (DeclareAnnotation deca: getCrosscuttingMembersSet().getDeclareAnnotationOnFields()) {
				if (applyDeclareAtField(deca, onType)) {
					anAnnotationChangeOccurred = true;
				}
			}
			decpToRepeat = decpToRepeatNextTime;
		}

	}

	@Override
	public IWeavingSupport getWeavingSupport() {
		return bcelWeavingSupport;
	}

	@Override
	public void reportCheckerMatch(Checker checker, Shadow shadow) {
		IMessage iMessage = new Message(checker.getMessage(shadow), shadow.toString(), checker.isError() ? IMessage.ERROR
				: IMessage.WARNING, shadow.getSourceLocation(), null, new ISourceLocation[] { checker.getSourceLocation() }, true,
				0, -1, -1);

		getMessageHandler().handleMessage(iMessage);

		if (getCrossReferenceHandler() != null) {
			getCrossReferenceHandler()
					.addCrossReference(
							checker.getSourceLocation(),
							shadow.getSourceLocation(),
							(checker.isError() ? IRelationship.Kind.DECLARE_ERROR.getName() : IRelationship.Kind.DECLARE_WARNING
									.getName()), false);

		}

		if (getModel() != null) {
			AsmRelationshipProvider.addDeclareErrorOrWarningRelationship(getModelAsAsmManager(), shadow, checker);
		}

	}

	public AsmManager getModelAsAsmManager() {
		return (AsmManager) getModel(); // For now... always an AsmManager in a bcel environment
	}

	void raiseError(String message) {
		getMessageHandler().handleMessage(MessageUtil.error(message));
	}

	/**
	 * These are aop.xml files that can be used to alter the aspects that actually apply from those passed in - and also their scope
	 * of application to other files in the system.
	 *
	 * @param xmlFiles list of File objects representing any aop.xml files passed in to configure the build process
	 */
	public void setXmlFiles(List xmlFiles) {
		if (!isXmlConfiguredWorld && !xmlFiles.isEmpty()) {
			raiseError("xml configuration files only supported by the compiler when -xmlConfigured option specified");
			return;
		}
		if (!xmlFiles.isEmpty()) {
			xmlConfiguration = new WeavingXmlConfig(this, WeavingXmlConfig.MODE_COMPILE);
		}
		for (File xmlfile : xmlFiles) {
			try {
				Definition d = DocumentParser.parse(xmlfile.toURI().toURL());
				xmlConfiguration.add(d);
			} catch (MalformedURLException e) {
				raiseError("Unexpected problem processing XML config file '" + xmlfile.getName() + "' :" + e.getMessage());
			} catch (Exception e) {
				raiseError("Unexpected problem processing XML config file '" + xmlfile.getName() + "' :" + e.getMessage());
			}
		}
	}

	/**
	 * Add a scoped aspects where the scoping was defined in an aop.xml file and this world is being used in a LTW configuration
	 */
	public void addScopedAspect(String name, String scope) {
		this.isXmlConfiguredWorld = true;
		if (xmlConfiguration == null) {
			xmlConfiguration = new WeavingXmlConfig(this, WeavingXmlConfig.MODE_LTW);
		}
		xmlConfiguration.addScopedAspect(name, scope);
	}

	public void setXmlConfigured(boolean b) {
		this.isXmlConfiguredWorld = b;
	}

	@Override
	public boolean isXmlConfigured() {
		return isXmlConfiguredWorld && xmlConfiguration != null;
	}

	public WeavingXmlConfig getXmlConfiguration() {
		return xmlConfiguration;
	}

	@Override
	public boolean isAspectIncluded(ResolvedType aspectType) {
		if (!isXmlConfigured()) {
			return true;
		}
		return xmlConfiguration.specifiesInclusionOfAspect(aspectType.getName());
	}

	@Override
	public TypePattern getAspectScope(ResolvedType declaringType) {
		return xmlConfiguration.getScopeFor(declaringType.getName());
	}

	@Override
	public boolean hasUnsatisfiedDependency(ResolvedType aspectType) {
		String aspectName = aspectType.getName();

		if (aspectType.hasAnnotations()) {
			AnnotationAJ[] annos = aspectType.getAnnotations();
			for (AnnotationAJ anno: annos) {
				if (anno.getTypeName().equals("org.aspectj.lang.annotation.RequiredTypes")) {
					String values = anno.getStringFormOfValue("value"); // Example: "[A,org.foo.Bar]"
					if (values != null && values.length() > 2) {
						values = values.substring(1,values.length()-1);
						StringTokenizer tokenizer = new StringTokenizer(values,",");
						boolean anythingMissing = false;
						while (tokenizer.hasMoreElements()) {
							String requiredTypeName = tokenizer.nextToken();
							ResolvedType rt = resolve(UnresolvedType.forName(requiredTypeName));
							if (rt.isMissing()) {
								if (!getMessageHandler().isIgnoring(IMessage.INFO)) {
									getMessageHandler().handleMessage(
											MessageUtil.info("deactivating aspect '" + aspectName + "' as it requires type '"
													+ requiredTypeName + "' which cannot be found on the classpath"));
								}
								anythingMissing = true;
								if (aspectRequiredTypes == null) {
									aspectRequiredTypes = new HashMap<>();
								}
								// Record that it has an invalid type reference
								aspectRequiredTypes.put(aspectName,requiredTypeName);
							}
						}
						if (anythingMissing) {
							return true;
						}
						else {
							return false;
						}
					}
					else {
						// no value specified for annotation
						return false;
					}
				}
			}
		}
		if (aspectRequiredTypes == null) {
			// no aspects require anything, so there can be no unsatisfied dependencies
			return false;
		}
		if (!aspectRequiredTypesProcessed.contains(aspectName)) {
			String requiredTypeName = aspectRequiredTypes.get(aspectName);
			if (requiredTypeName==null) {
				aspectRequiredTypesProcessed.add(aspectName);
				return false;
			} else {
				ResolvedType rt = resolve(UnresolvedType.forName(requiredTypeName));
				if (!rt.isMissing()) {
					aspectRequiredTypesProcessed.add(aspectName);
					aspectRequiredTypes.remove(aspectName);
					return false;
				} else {
					if (!getMessageHandler().isIgnoring(IMessage.INFO)) {
						getMessageHandler().handleMessage(
								MessageUtil.info("deactivating aspect '" + aspectName + "' as it requires type '"
										+ requiredTypeName + "' which cannot be found on the classpath"));
					}
					aspectRequiredTypesProcessed.add(aspectName);
					return true;
				}
			}
		}
		return aspectRequiredTypes.containsKey(aspectName);
	}

	private List aspectRequiredTypesProcessed = new ArrayList<>();
	private Map aspectRequiredTypes = null;

	public void addAspectRequires(String aspectClassName, String requiredType) {
		if (aspectRequiredTypes == null) {
			aspectRequiredTypes = new HashMap<>();
		}
		aspectRequiredTypes.put(aspectClassName,requiredType);
	}

	/**
	 * A WeavingXmlConfig is initially a collection of definitions from XML files - once the world is ready and weaving is running
	 * it will initialize and transform those definitions into an optimized set of values (eg. resolve type patterns and string
	 * names to real entities). It can then answer questions quickly: (1) is this aspect included in the weaving? (2) Is there a
	 * scope specified for this aspect and does it include type X?
	 *
	 */
	static class WeavingXmlConfig {

		final static int MODE_COMPILE = 1;
		final static int MODE_LTW = 2;

		private int mode;

		private boolean initialized = false; // Lazily done
		private List definitions = new ArrayList<>();

		private List resolvedIncludedAspects = new ArrayList<>();
		private Map scopes = new HashMap<>();

		// these are not set for LTW mode (exclusion of these fast match patterns is handled before the weaver/world are used)
		private List includedFastMatchPatterns = Collections.emptyList();
		private List includedPatterns = Collections.emptyList();
		private List excludedFastMatchPatterns = Collections.emptyList();
		private List excludedPatterns = Collections.emptyList();

		private BcelWorld world;

		public WeavingXmlConfig(BcelWorld bcelWorld, int mode) {
			this.world = bcelWorld;
			this.mode = mode;
		}

		public void add(Definition d) {
			definitions.add(d);
		}

		public void addScopedAspect(String aspectName, String scope) {
			ensureInitialized();
			resolvedIncludedAspects.add(aspectName);
			try {
				TypePattern scopePattern = new PatternParser(scope).parseTypePattern();
				scopePattern.resolve(world);
				scopes.put(aspectName, scopePattern);
				if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) {
					world.getMessageHandler().handleMessage(
							MessageUtil.info("Aspect '" + aspectName + "' is scoped to apply against types matching pattern '"
									+ scopePattern + "'"));
				}
			} catch (Exception e) {
				world.getMessageHandler().handleMessage(
						MessageUtil.error("Unable to parse scope as type pattern.  Scope was '" + scope + "': " + e.getMessage()));
			}
		}

		public void ensureInitialized() {
			if (!initialized) {
				try {
					resolvedIncludedAspects = new ArrayList<>();
					// Process the definitions into something more optimal
					for (Definition definition : definitions) {
						List aspectNames = definition.getAspectClassNames();
						for (String name : aspectNames) {
							resolvedIncludedAspects.add(name);
							// TODO check for existence?
							// ResolvedType resolvedAspect = resolve(UnresolvedType.forName(name));
							// if (resolvedAspect.isMissing()) {
							// // ERROR
							// } else {
							// resolvedIncludedAspects.add(resolvedAspect);
							// }
							String scope = definition.getScopeForAspect(name);
							if (scope != null) {
								// Resolve the type pattern
								try {
									TypePattern scopePattern = new PatternParser(scope).parseTypePattern();
									scopePattern.resolve(world);
									scopes.put(name, scopePattern);
									if (!world.getMessageHandler().isIgnoring(IMessage.INFO)) {
										world.getMessageHandler().handleMessage(
												MessageUtil.info("Aspect '" + name
														+ "' is scoped to apply against types matching pattern '"
														+ scopePattern.toString() + "'"));
									}
								} catch (Exception e) {
									// TODO definitions should remember which file they came from, for inclusion in this message
									world.getMessageHandler().handleMessage(
											MessageUtil.error("Unable to parse scope as type pattern.  Scope was '" + scope + "': "
													+ e.getMessage()));
								}
							}
						}
						try {
							List includePatterns = definition.getIncludePatterns();
							if (includePatterns.size() > 0) {
								includedPatterns = new ArrayList<>();
								includedFastMatchPatterns = new ArrayList<>();
							}
							for (String includePattern : includePatterns) {
								if (includePattern.endsWith("..*")) {
									// from 'blah.blah.blah..*' leave the 'blah.blah.blah.'
									includedFastMatchPatterns.add(includePattern.substring(0, includePattern.length() - 2));
								} else {
									TypePattern includedPattern = new PatternParser(includePattern).parseTypePattern();
									includedPatterns.add(includedPattern);
								}
							}
							List excludePatterns = definition.getExcludePatterns();
							if (excludePatterns.size() > 0) {
								excludedPatterns = new ArrayList<>();
								excludedFastMatchPatterns = new ArrayList<>();
							}
							for (String excludePattern : excludePatterns) {
								if (excludePattern.endsWith("..*")) {
									// from 'blah.blah.blah..*' leave the 'blah.blah.blah.'
									excludedFastMatchPatterns.add(excludePattern.substring(0, excludePattern.length() - 2));
								} else {
									TypePattern excludedPattern = new PatternParser(excludePattern).parseTypePattern();
									excludedPatterns.add(excludedPattern);
								}
							}
						} catch (ParserException pe) {
							// TODO definitions should remember which file they came from, for inclusion in this message
							world.getMessageHandler().handleMessage(
									MessageUtil.error("Unable to parse type pattern: " + pe.getMessage()));

						}
					}
				} finally {
					initialized = true;
				}
			}
		}

		public boolean specifiesInclusionOfAspect(String name) {
			ensureInitialized();
			return resolvedIncludedAspects.contains(name);
		}

		public TypePattern getScopeFor(String name) {
			return scopes.get(name);
		}

		/**
		 * Checks if a given type is to be excluded from weaving.
		 * 

* For LTW, the development guide (docs/devguide/ltw.adoc) says: *

* "The set of types to be woven are those types matched by at least one weaver {@code include} element and not * matched by any weaver {@code exclude} element. If there are no weaver include statements, then all non-excluded * types are included." *

* In CTW mode, we cannot quite follow the same rules for exclusion as used for LTW: If the weaver is seeing it * during this kind of build, the type is implicitly included. So all we should check for is exclusion. * * @param type resolved type to be checked * * @return Always false in LTW mode. In CTW mode true for excluded types, false otherwise. */ public boolean excludesType(ResolvedType type) { if (mode == MODE_LTW) { return false; } String typename = type.getName(); boolean excluded = false; for (String excludedPattern : excludedFastMatchPatterns) { if (typename.startsWith(excludedPattern)) { excluded = true; break; } } if (!excluded) { for (TypePattern excludedPattern : excludedPatterns) { if (excludedPattern.matchesStatically(type)) { excluded = true; break; } } } return excluded; } } @Override public TypeMap getTypeMap() { return typeMap; } @Override public boolean isLoadtimeWeaving() { return false; } public void addTypeDelegateResolver(TypeDelegateResolver typeDelegateResolver) { if (typeDelegateResolvers == null) { typeDelegateResolvers = new ArrayList<>(); } typeDelegateResolvers.add(typeDelegateResolver); } @Override public void classWriteEvent(char[][] compoundName) { typeMap.classWriteEvent(new String(CharOperation.concatWith(compoundName, '.'))); } /** * Force demote a type. */ public void demote(ResolvedType type) { typeMap.demote(type); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy