org.aspectj.weaver.ltw.LTWWorld Maven / Gradle / Ivy
/* *******************************************************************
* Copyright (c) 2005 Contributors.
* 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:
* Ron Bodkin Initial implementation
* ******************************************************************/
package org.aspectj.weaver.ltw;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.weaver.Dump.IVisitor;
import org.aspectj.weaver.ICrossReferenceHandler;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ReferenceTypeDelegate;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.loadtime.IWeavingContext;
import org.aspectj.weaver.reflect.AnnotationFinder;
import org.aspectj.weaver.reflect.IReflectionWorld;
import org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegateFactory;
import org.aspectj.weaver.reflect.ReflectionWorld;
/**
* @author adrian
* @author Ron Bodkin
*
* For use in LT weaving
*
* Backed by both a BcelWorld and a ReflectionWorld
*
* Needs a callback when a woven class is defined This is the trigger for us to ditch the class from Bcel and cache it in
* the reflective world instead.
*
* Create by passing in a classloader, message handler
*/
public class LTWWorld extends BcelWorld implements IReflectionWorld {
private AnnotationFinder annotationFinder;
private IWeavingContext weavingContext;
private String classLoaderString;
private String classLoaderParentString;
protected final static Class concurrentMapClass;
private static final boolean ShareBootstrapTypes = false;
protected static Map/* > */bootstrapTypes;
static {
if (ShareBootstrapTypes) {
concurrentMapClass = makeConcurrentMapClass();
bootstrapTypes = makeConcurrentMap();
} else {
concurrentMapClass = null;
}
}
/**
* Build a World from a ClassLoader, for LTW support
*/
public LTWWorld(ClassLoader loader, IWeavingContext weavingContext, IMessageHandler handler, ICrossReferenceHandler xrefHandler) {
super(loader, handler, xrefHandler);
this.weavingContext = weavingContext;
try {
classLoaderString = loader.toString();
} catch (Throwable t) {
// Possibly some state in the loader isn't initialized but is used in the toString()
classLoaderString = loader.getClass().getName()+":"+Integer.toString(System.identityHashCode(loader));
}
classLoaderParentString = (loader.getParent() == null ? "" : loader.getParent().toString());
setBehaveInJava5Way(true);
annotationFinder = ReflectionWorld.makeAnnotationFinderIfAny(loader, this);
}
public ClassLoader getClassLoader() {
return weavingContext.getClassLoader();
}
// TEST
// this is probably easier: just mark anything loaded while loading aspects as not
// expendible... it also fixes a possible bug whereby non-rewoven aspects are deemed expendible
//
// protected boolean isExpendable(ResolvedType type) {
// return ((type != null) && !loadingAspects && !type.isAspect() && (!type
// .isPrimitiveType()));
// }
/**
* Override
*/
@Override
protected ReferenceTypeDelegate resolveDelegate(ReferenceType ty) {
// use reflection delegates for all bootstrap types
ReferenceTypeDelegate bootstrapLoaderDelegate = resolveIfBootstrapDelegate(ty);
if (bootstrapLoaderDelegate != null) {
return bootstrapLoaderDelegate;
}
return super.resolveDelegate(ty);
}
protected ReferenceTypeDelegate resolveIfBootstrapDelegate(ReferenceType ty) {
// first check for anything available in the bootstrap loader: these types are just defined from that without allowing
// nondelegation
// if (!ShareBootstrapTypes) return null;
// String name = ty.getName();
// Reference bootRef = (Reference) bootstrapTypes.get(name);
// if (bootRef != null) {
// ReferenceTypeDelegate rtd = (ReferenceTypeDelegate) bootRef.get();
// if (rtd != null) {
// return rtd;
// }
// }
//
// char fc = name.charAt(0);
// if (fc == 'j' || fc == 'c' || fc == 'o' || fc == 's') { // cheaper than imminent string startsWith tests
// if (name.startsWith("java") || name.startsWith("com.sun.") || name.startsWith("org.w3c") ||
// name.startsWith("sun.") || name.startsWith("org.omg")) {
// ReferenceTypeDelegate bootstrapLoaderDelegate = resolveReflectionTypeDelegate(ty, null);
// if (bootstrapLoaderDelegate != null) {
// // it's always fine to load these bytes: there's no weaving into them
// // and since the class isn't initialized, all we are doing at this point is loading the bytes
// // processedRefTypes.put(ty, this); // has no effect - and probably too aggressive if we did store
// // these in the type map
//
// // should we share these, like we do the BCEL delegates?
// bootstrapTypes.put(ty.getName(), new WeakReference(bootstrapLoaderDelegate));
// }
// return bootstrapLoaderDelegate;
// }
// }
return null;
}
/**
* Helper method to resolve the delegate from the reflection delegate factory.
*/
private ReferenceTypeDelegate resolveReflectionTypeDelegate(ReferenceType ty, ClassLoader resolutionLoader) {
ReferenceTypeDelegate res = ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty, this, resolutionLoader);
return res;
}
/**
* Remove this class from the typeMap. Call back to be made from a publishing class loader The class loader should, ideally,
* make this call on each not yet working
*
* @param clazz
*/
public void loadedClass(Class clazz) {
}
private static final long serialVersionUID = 1;
public AnnotationFinder getAnnotationFinder() {
return this.annotationFinder;
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.reflect.IReflectionWorld#resolve(java.lang.Class)
*/
public ResolvedType resolve(Class aClass) {
return ReflectionWorld.resolve(this, aClass);
}
private static Map, ?> makeConcurrentMap() {
if (concurrentMapClass != null) {
try {
return (Map) concurrentMapClass.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ignored) {}
// fall through if exceptions
}
return Collections.synchronizedMap(new HashMap<>());
}
private static Class> makeConcurrentMapClass() {
String betterChoices[] = { "java.util.concurrent.ConcurrentHashMap",
"edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap",
"EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap" };
for (String betterChoice : betterChoices) {
try {
return Class.forName(betterChoice);
} catch (ClassNotFoundException cnfe) {
// try the next one
} catch (SecurityException se) {
// you get one of these if you dare to try to load an undefined class in a
// package starting with java like java.util.concurrent
}
}
return null;
}
@Override
public boolean isRunMinimalMemory() {
if (isRunMinimalMemorySet()) {
return super.isRunMinimalMemory();
}
return false;
}
// One type is completed at a time, if multiple need doing then they
// are queued up
private boolean typeCompletionInProgress = false;
private List typesForCompletion = new ArrayList<>();
@Override
protected void completeBinaryType(ResolvedType ret) {
if (isLocallyDefined(ret.getName())) {
if (typeCompletionInProgress) {
typesForCompletion.add(ret);
} else {
try {
typeCompletionInProgress = true;
completeHierarchyForType(ret);
} finally {
typeCompletionInProgress = false;
}
while (typesForCompletion.size() != 0) {
ResolvedType rt = typesForCompletion.get(0);
completeHierarchyForType(rt);
typesForCompletion.remove(0);
}
}
} else {
if (!ret.needsModifiableDelegate()) {
ret = completeNonLocalType(ret);
}
}
}
private void completeHierarchyForType(ResolvedType ret) {
getLint().typeNotExposedToWeaver.setSuppressed(true);
weaveInterTypeDeclarations(ret);
getLint().typeNotExposedToWeaver.setSuppressed(false);
}
protected boolean needsCompletion() {
return true;
}
@Override
public boolean isLocallyDefined(String classname) {
return weavingContext.isLocallyDefined(classname);
}
protected ResolvedType completeNonLocalType(ResolvedType ret) {
if (ret.isMissing()) {
return ret; // who knows ?!?
}
ResolvedType toResolve = ret;
if (ret.isParameterizedType() || ret.isGenericType()) {
toResolve = toResolve.getGenericType();
}
ReferenceTypeDelegate rtd = resolveReflectionTypeDelegate((ReferenceType) toResolve, getClassLoader());
((ReferenceType) ret).setDelegate(rtd);
return ret;
}
@Override
public void storeClass(JavaClass clazz) {
ensureRepositorySetup();
delegate.storeClass(clazz);
}
@Override
public void accept(IVisitor visitor) {
visitor.visitObject("Class loader:");
visitor.visitObject(classLoaderString);
visitor.visitObject("Class loader parent:");
visitor.visitObject(classLoaderParentString);
super.accept(visitor);
}
public boolean isLoadtimeWeaving() {
return true;
}
}