VerifyError: Bad access to protected data in invokevirtual
Hi,
Similarly as in https://github.com/raphw/byte-buddy/issues/539, we are seeing <subj> when trying to use ByteBuddy in our code base. More specifically, this exception (note that the bytecode is different to the one in that issue):
java.lang.VerifyError: Bad access to protected data in invokevirtual
Exception Details:
Location:
fi/hibox/centre/domain/service/PackageService$ByteBuddy$XKOo4HgE.clone()Ljava/lang/Object; @3: invokevirtual
Reason:
Type 'fi/hibox/centre/domain/service/PackageService' (current frame, stack[0]) is not assignable to 'fi/hibox/centre/domain/service/PackageService$ByteBuddy$XKOo4HgE'
Current Frame:
bci: @3
flags: { }
locals: { 'fi/hibox/centre/domain/service/PackageService$ByteBuddy$XKOo4HgE' }
stack: { 'fi/hibox/centre/domain/service/PackageService' }
Bytecode:
0x0000000: b200 0cb6 000e b0
at java.lang.Class.getDeclaredFields0(Native Method)
at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
at java.lang.Class.getDeclaredField(Class.java:2068)
at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:163)
at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:234)
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:103)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6325)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6313)
at fi.hibox.centre.domain.immutable.AbstractIdentifiableEntityBuilder.buildTemporaryInstance(AbstractIdentifiableEntityBuilder.java:121)
at fi.hibox.centre.server.internalbilling.billingrules.NextIntervalSubscriptionBill.calculateBill(NextIntervalSubscriptionBill.java:59)
at fi.hibox.centre.server.internalbilling.billingrules.AbstractPriceSourceAwareBillingRule.calculateBill(AbstractPriceSourceAwareBillingRule.java:47)
at fi.hibox.centre.server.internalbilling.billingrules.AbstractBillingRule.createServiceBill(AbstractBillingRule.java:34)
at fi.hibox.centre.server.internalbilling.DefaultBillingRuleSet.createServiceBill(DefaultBillingRuleSet.java:78)
at fi.hibox.centre.server.commerce.ServiceBillTestUtil.createServiceBill(ServiceBillTestUtil.java:61)
at fi.hibox.centre.client.services.RegistrationServiceTest$1.answer(RegistrationServiceTest.java:168)
at fi.hibox.centre.client.services.RegistrationServiceTest$1.answer(RegistrationServiceTest.java:165)
at org.mockito.internal.stubbing.StubbedInvocationMatcher.answer(StubbedInvocationMatcher.java:42)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:103)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:34)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:56)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:161)
at fi.hibox.centre.protocol.CentreServer$MockitoMock$nfiFyNVm.getCalculatedServiceBill(Unknown Source)
at fi.hibox.centre.client.services.RegistrationService.getAccountSubscriptionServiceBill(RegistrationService.java:292)
at fi.hibox.centre.client.services.RegistrationService.applyAccountSubscriptionVoucher(RegistrationService.java:460)
at fi.hibox.centre.client.services.RegistrationServiceTest.testApplyAccountSubscriptionVoucher(RegistrationServiceTest.java:216)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.util.ArrayList.forEach(ArrayList.java:1259)
at java.util.ArrayList.forEach(ArrayList.java:1259)
The calling code (where we try to create the ByteBuddy-based instance) looks like this. It's the load( getClass().getClassLoader() ) call that fails.
public final T buildTemporaryInstance() {
T instance = create();
try {
return (T)new ByteBuddy()
.subclass( this.instance.getClass() )
.method( named( "builder" ) ).intercept( throwing( IllegalStateException.class, "can't create builder from temporary " + instance.getClass().getSimpleName() + " instance" ) )
.method( any() ).intercept( MethodCall.invokeSelf().on( instance ).withAllArguments() )
.make()
.load( getClass().getClassLoader() )
.getLoaded()
.newInstance();
}
catch ( InstantiationException | IllegalAccessException e ) {
throw new RuntimeException( e );
}
}
I looked at this in the debugger and I get the feeling that ByteBuddy somehow fails to get the fields from the type it has defined itself. :thinking: But I am clearly not an expert on the ByteBuddy internals so this may clearly be an incorrect understanding of the problem at hand.
I realize that this might be hard to say much about without a MCVE, but do you have any obvious ideas of what could go wrong here, or where we could/should look next? If so, much appreciated. :bow:
(ByteBuddy version: 1.14.11. JDK versions: reproduced on JDK 8, 17 and 21)
#539 gave some good hints of what the problem was: the superclass constructor was package-protected but needs to be at least protected for this to work (with the default class loading strategy).
Yep, thanks for noting this down for the sake of other people. :bow: I'll leave this open for now in case @raphw or anyone else wants to improve the UX here, to make it easier for people running into this to know how they need to modify their code.
That's a tricky thing because the class loading will actually implicate a visibility here. This cannot be decided during compilation, and might be legal when loading, this is why it is not failing the class creation.