Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support delay ExceptionInInitializerError. #493

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

nieyuanhong
Copy link

When executing the CtBehavior.instrument method, if the replaced code uses the special variable $class, then after the CtBehavior.instrument method is executed, $class will actually compile to Desc. getClazz(className), and the getClazz method will ultimately call Class. forName (name, True, Thread. CurrentThread(). getContextClassLoad()) or Class. forName (name).
However, both of these calling ways will execute static code blocks after class loading. If the static code block throws an exception, the class will not be loaded. So if we choose to only load class and not execute static code blocks, using the following code Class.forName(name, false, Thread.currentThread().getContextClassLoader()), we can get the Class object without throwing exceptions, which helps us get the members and other information of the Class.

@nieyuanhong
Copy link
Author

There are some tests.

public class DescFixTest {
    ClassPool pool = ClassPool.getDefault();

    @Test
    @SneakyThrows
    public void replaceTest(){
        CtClass ctClass = pool.getCtClass("javassist.runtime.ForTest1");
        CtMethod ctMethod = ctClass.getDeclaredMethod("test");
        ctMethod.instrument(new MyExprEditor());
        Class<?> aClass = ctClass.toClass();
        ForTest1 forTest = (ForTest1) aClass.newInstance();
        forTest.test(); //will failed
        System.exit(0);
    }

    @Test
    @SneakyThrows
    public void fixTest(){
        fixDescGetClassObject();  

        CtClass ctClass = pool.getCtClass("javassist.runtime.ForTest2");
        CtMethod ctMethod = ctClass.getDeclaredMethod("test");
        ctMethod.instrument(new MyExprEditor());
        Class<?> aClass = ctClass.toClass();
        ForTest2 forTest = (ForTest2) aClass.newInstance();
        forTest.test(); //will success
        System.exit(0);
    }

    static class MyExprEditor extends ExprEditor {
        @Override
        public void edit(NewExpr e) throws CannotCompileException {
            e.replace("{$_ = javassist.runtime.DescFixTest#printAllFieldsAndReturnNull($class, $args);}");
        }
    }

    public static Object printAllFieldsAndReturnNull(Class<?> clazz, Object object){
        Field[] declaredClasses = clazz.getDeclaredFields();
        for (Field declaredClass : declaredClasses) {
            System.out.println(declaredClass);
        }
        return null;
    }

    @SneakyThrows
    private void fixDescGetClassObject() {
        CtClass ctClass = pool.getCtClass("javassist.runtime.Desc");
        CtBehavior ctBehavior = ctClass.getDeclaredMethod("getClassObject");
        ctBehavior.instrument(new ExprEditor(){
            @Override
            @SneakyThrows
            public void edit(MethodCall m) throws CannotCompileException {
                CtMethod callMethod = m.getMethod();
                if(callMethod.getDeclaringClass().getName().equals(Class.class.getName()) &&
                   callMethod.getName().equals("forName")){
                    int count = callMethod.getParameterTypes().length;
                    if(count == 3)
                        m.replace("{$_ = Class#forName($1, false, Thread#currentThread().getContextClassLoader());}");
                    else if(count == 1)
                        m.replace("{$_ = Class#forName($1, false, " + ctClass.getName() + ".class.getClassLoader());}");
                }
            }
        });
        ctClass.toClass();
    }
}

class ForTest1{
    public void test(){
        ErrorInStaticCodeBlock errorInStaticCodeBlock = new ErrorInStaticCodeBlock();
        System.out.println("test success!");
    }
}

class ForTest2{
    public void test(){
        ErrorInStaticCodeBlock errorInStaticCodeBlock = new ErrorInStaticCodeBlock();
        System.out.println("test success!");
    }
}

class ErrorInStaticCodeBlock{
    static int i = 0;

    static {
        i = 1/0;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant