Bug 561819 - loadtime weaving using WeavingURLClassLoader does not use current url classloader when lookupClass in function ObjectType.referencesClass
Summary: loadtime weaving using WeavingURLClassLoader does not use current url classlo...
Status: NEW
Alias: None
Product: AspectJ
Classification: Tools
Component: LTWeaving (show other bugs)
Version: 1.9.5   Edit
Hardware: All All
: P3 critical (vote)
Target Milestone: ---   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-04-06 09:09 EDT by Shen Min CLA
Modified: 2020-04-22 14:03 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Shen Min CLA 2020-04-06 09:09:59 EDT

    
Comment 1 Shen Min CLA 2020-04-06 09:29:13 EDT
loadtime weaving using WeavingURLClassLoader does not use current url classloader when lookupClass in function ObjectType.referencesClass.
When weaving a class loaded by a WeavingURLClassLoader, it will call Utility.createConversion(getFactory(), BcelWorld.makeBcelType(returnType),
					callbackMethod.getReturnType(), world.isInJava5Mode()) in line 2922 in class BcelShadow, in this stack, ObjectType.referencesClass is invoked when judge a class is assignable from another class. But class lookup method is global static,  by default, it will load class from system classpath, rather then current  WeavingURLClassLoader, which will cause class not found. the classloader ref in LtwWorld is lost!

        public boolean referencesClass() {
		JavaClass jc = Repository.lookupClass(classname);
		if (jc == null) {
			return false;
		} else {
			return jc.isClass();
		}
	}


To fix this problem, we can define a ThreadLocalRepository as global repositry, and set a ClassloaderRepository instance to threadlocal when WeavingURLClassLoader.loadClass is invoked, code like this:

public class ThreadLocalRepository implements Repository {

    private static ThreadLocal<WeakReference<Repository>> threadLocal = new ThreadLocal<>();

    public static void setCurrentRepository(Repository repo) {

        threadLocal.set(new WeakReference<>(repo));
    }

    public static Repository getCurrentRepository() {

        WeakReference<Repository> ref = threadLocal.get();
        if (ref != null) {
            return ref.get();
        }
        return null;
    }

    @Override
    public void storeClass(JavaClass clazz) {

        Repository repository = getCurrentRepository();
        if (repository != null) {
            repositoryHit.incrementAndGet();
            repository.storeClass(clazz);
        } else {
            repositoryMissed.incrementAndGet();
            SyntheticRepository.getInstance().storeClass(clazz);
        }
    }

    @Override
    public void removeClass(JavaClass clazz) {

        Repository repository = getCurrentRepository();
        if (repository != null) {
            repository.removeClass(clazz);
        } else {
            SyntheticRepository.getInstance().removeClass(clazz);
        }
    }

    @Override
    public JavaClass findClass(String className) {

        Repository repository = getCurrentRepository();
        if (repository != null) {
            return repository.findClass(className);
        } else {
            return SyntheticRepository.getInstance().findClass(className);
        }
    }

    @Override
    public JavaClass loadClass(String className) throws ClassNotFoundException {

        Repository repository = getCurrentRepository();
        if (repository != null) {
            return repository.loadClass(className);
        } else {
            return SyntheticRepository.getInstance().loadClass(className);
        }
    }

    @Override
    public JavaClass loadClass(Class clazz) throws ClassNotFoundException {

        Repository repository = getCurrentRepository();
        if (repository != null) {
            return repository.loadClass(clazz);
        } else {
            return SyntheticRepository.getInstance().loadClass(clazz);
        }
    }

    @Override
    public void clear() {

        Repository repository = getCurrentRepository();
        if (repository != null) {
            repository.clear();
        } else {
            SyntheticRepository.getInstance().clear();
        }
    }
}


WeavingURLClassLoader class code may like this:

    static {
        ClassLoader.registerAsParallelCapable();
        // 修复aspectj bug
        Repository.setRepository(new ThreadLocalRepository());
    }

    private org.aspectj.apache.bcel.util.Repository repository;

    public WeavingURLClassLoader(URL[] urls) {
        super(urls);
        repository = new NonCachingClassLoaderRepository(this);
    }

    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            try {
                ThreadLocalRepository.setCurrentRepository(repository);
                // do load class
            } finally {
                ThreadLocalRepository.setCurrentRepository(null);
            }
    }
Comment 2 Andrew Clement CLA 2020-04-22 14:03:22 EDT
I created a ThreadLocalAwareRepository in anticipation of doing more here, but I'd really like a testcase or test program that shows the problem - if you have one? So we can be confident in the fix and that it doesn't get rebroken in the future.