java.lang.NoSuchFieldError: $MMB #298

Closed
Vampire opened this Issue Jun 20, 2016 · 4 comments

Projects

None yet

2 participants

@Vampire
Vampire commented Jun 20, 2016

I tried to prevent the need for using the -javaagent way when using JMockit on WildFly with its VFS.
For this I packaged the jmockit.jar as resource inside my own JAR and at runtime extract it into a temporary file called jmockit.jar, add this file to java.class.path system property and call mockit.internal.startup.Startup.initializeIfPossible(); inside a @Startup bean, so before any test classes are loaded.

This seemed to work fine at first, but there is a problem with the Mockups API.
Using Expectations works fine so far, but the MockUps, do not work.
When the code comes to the call of the mocked method, I get java.lang.NoSuchFieldError: $MMB.
If I enable the -javaagent option it works fine.

I tried to debug a bit and found out, that if I run with the -javaagent option, when NegativeArraySizeException.class.getName(); is done in MockingBridgeFields, the FieldAdditionTransformer is called like expected. But if I run without the option, thus using the Attach API, the FieldAdditionTransformer is not called and thus the fields not added. It seems to me that at this point the NegativeArraySizeException class was not loaded yet, as IntelliJ IDEA still shows the breakpoints as not set which is the case if the class was not yet loaded by the JVM.

After some more debugging I found out that the problem actually is, that the NegativeArraySizeException class was loaded before already. We use Hazelcast for distributed caching and it is initialized before JMockit gets initialized. In the Hazelcast initialization in the class ClientExceptionFactory all kinds of exceptions are loaded already, including the NegativeArraySizeException. This is the cause the synthetic field adding is not working like expected.

I looked around for an alternative and I think java.lang.annotation.IncompleteAnnotationException might be a good general replacement, as it seems to me that it should maybe not happen too often.

Maybe it would even be better to make the class that is used for this configurable by some way, so that one can choose the class that is manipulated.

Or have a list of all sorts of probable candidates, trying one after the other, always checking whether the fields were added, stopping after one class was successfully manipulated.

Actually, the most reliable and stable way would maybe be to not rely on some class not having been loaded before. Is there a reason it is done this way and not e. g. system properties are used to store the mocking bridges?

@rliesenfeld
Member

TLDR; How would I reproduce the problem?

@rliesenfeld
Member

And I thought that NegativeArraySizeException would never be used...

Ok, I will change the way a class is chosen for the "$MMB" field, so it's more robust. Use of system properties was attempted in the past, it doesn't work (only Strings can be added safely).

@rliesenfeld rliesenfeld self-assigned this Jun 20, 2016
@rliesenfeld rliesenfeld added the bug label Jun 20, 2016
@Vampire
Vampire commented Jun 20, 2016

Load NegativeArraySizeException class before initializing JMockit

@Vampire
Vampire commented Jun 20, 2016

Ah, right, Properties are strings only. Didn't think about it. But actually I supplied a patch that does it more robustly and can be made more robust by adding further classes that are unlikely to be loaded before JMockit initialization. :-)

@rliesenfeld rliesenfeld added a commit that closed this issue Jun 25, 2016
@rliesenfeld rliesenfeld Re-implemented the addition of $MMB, etc. fields to a JRE class, in a…
… way that is not dependent on any known JRE class(es); closes #298.
69e87cc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment