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

NoClassDefFound -- new class cannot access the class whose classloader was used for injection #419

Open
koradir opened this issue Mar 14, 2018 · 7 comments
Assignees
Milestone

Comments

@koradir
Copy link

koradir commented Mar 14, 2018

In a nutshell, I have a class B$1 that I create from scatch at runtime and that has a non-static method calling a static method of class B$0 (there is no inheritance between the two classes, although they may share the parent, but even that's not a necessity).

Injection is done by

Class b0 = Class.forName("B$0");
ClassLoadingStrategy.Default.INJECTION.load(b0.getClassLoader(),
        Collections.singletonMap(newClass.getTypeDescription(), finishedBytecode));

but if I immediately after that injection test the method

try {
    Class c = Class.forName("B$1");
    Object instance = c.newInstance();
    c.getMethod("move", Object.class).invoke(instance, new Object());
} catch (Exception e) {
    e.printStackTrace();
}

I get a NoClassDefFoundError: B$0.

Why? And how do I resolve this?

(For more details about the use case, please see https://stackoverflow.com/questions/49232664/how-to-load-this-class-in-the-correct-way .)

Oh, and if at all relevant: using Java 1.7.0_65 and ByteBuddy 1.7.9

@raphw raphw self-assigned this Mar 15, 2018
@raphw raphw added the question label Mar 15, 2018
@raphw raphw added this to the 1.8.0 milestone Mar 15, 2018
@raphw
Copy link
Owner

raphw commented Mar 15, 2018

The NoClassDefFoundError might be misleading and point to an error in your instrumentation. It could be a erification error in disguise. Are you instrumenting your code using Byte Buddy? I am wondering why you do not operate on a dynamic type.

I would need to see more of your example to provide better help. I think the actual problem is in the byte code generation.

@koradir
Copy link
Author

koradir commented Mar 17, 2018

Looks like you were right about the problem's lying with the code generation.

Couldn't post more code because it's proprietary and frankly, too darn large. Didn't help that all the "minimal working examples" I tried to construct actually worked ...

The issue turned out to be disappointingly trivial: a string constant used dots instead of slashes to separate package names. Since that constant is used by ASM and looking for foo/bar/Baz is not the same as looking for foo.bar.Baz, it failed.

Yeah, really stupid.
Still, thanks.

As for why I'm not using a dynamic type, it's that we already have ASM visitors that do part of the transformation required for the new class.

The bytebuddy tutorial, however, says

if you really need to create byte code with jump instructions, make sure to add the correct stack map frames using ASM since Byte Buddy will not automatically include them for you.

and quite frankly, that's too much of a hassle (and in my case actually bars me from using the ASM visitors we already have because they rely on the fact that stack frames are computed).

It's much more convenient to write something like

public class MethodAdder extends ClassVisitor implements Opcodes {
    private MethodAdder(int api, ClassVisitor cv) {
        super(api, cv);
    }

    public static byte[] instrument(byte[] rawByteCode){
        ClassReader cr = new ClassReader(rawByteCode);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        FooTransformer ft = new FooTransformer(ASM5,cw);
        BarTransformer bt = new BarTransformer(ASM5,ft);
        MethodAdder ma = new MethodAdder(ASM5,bt);
        cr.accept(ma, ClassReader.EXPAND_FRAMES);
        return cw.toByteArray();
    }

    ...

}

and let ASM figure out the rest.

@raphw
Copy link
Owner

raphw commented Mar 17, 2018

You can still use ASM with Byte Buddy in this case, have a look at AsmVisitorWrapper.ForDeclaredMethods. you can register frame computation in there and still use ASM for the method body.

Glad you figured it out.

@koradir
Copy link
Author

koradir commented Mar 19, 2018

In that case, may I suggest adding this information to the tutorial? Because I read "ByteBuddy will not" as "ByteBuddy refuses to" and stopped considering ByteBuddy for transformations right then and there.

@raphw
Copy link
Owner

raphw commented Mar 19, 2018

Yes, please! Could you do a PR on the gh-pages branch with a suggestion?

@koradir
Copy link
Author

koradir commented Mar 22, 2018

Can do, but it will take a while.
I'll need to invest time into learning the "how-to" well enough to write anything useful. Time I currently just don't have. Should be able to tackle this in about two, three months or so.

@raphw
Copy link
Owner

raphw commented Mar 23, 2018

No worries, if I find time first, I will do it, otherwise, I can easily just merge your PR.

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

No branches or pull requests

2 participants