java - 委派给抽象基类上的 setter 会导致加载时出现VerifyError

标签 java bytecode byte-buddy

这个问题与我之前的问题相关:How to create a default constructor with Byte Buddy

我正在创建一个子类,它首先在将方法调用委托(delegate)给某个实例之前设置一些上下文。这已经很有效了,还剩下一个问题。

加载动态创建的子类时出现以下错误。

    java.lang.VerifyError: Bad access to protected data in invokevirtual
Exception Details:
  Location:
    com/frequentis/ps/service/test/saga/ProxyTestSaga$ByteBuddy$Rm8DV3Lj.setTimeoutManager(Lcom/codebullets/sagalib/timeout/TimeoutManager;)V @3: invokevirtual
  Reason:
    Type 'com/frequentis/ps/service/test/saga/ProxyTestSaga' (current frame, stack[0]) is not assignable to 'com/frequentis/ps/service/test/saga/ProxyTestSaga$ByteBuddy$Rm8DV3Lj'
  Current Frame:
    bci: @3
    flags: { }
    locals: { 'com/frequentis/ps/service/test/saga/ProxyTestSaga$ByteBuddy$Rm8DV3Lj', 'com/codebullets/sagalib/timeout/TimeoutManager' }
    stack: { 'com/frequentis/ps/service/test/saga/ProxyTestSaga' }
  Bytecode:
    0x0000000: b200 0cb6 0010 57b1                    

    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:124)
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:200)
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:200)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.initialize(DynamicType.java:3497)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:3485)
    at com.frequentis.ps.service.test.saga.DynamicSagaTypeBuilder.buildAndLoad(DynamicSagaTypeBuilder.java:65)
    at com.frequentis.ps.service.test.saga.MoreAbstractSpaceSagaUnitTest.generateProxyClassForSagaUnderTest(MoreAbstractSpaceSagaUnitTest.java:274)
    at com.frequentis.ps.service.test.saga.AbstractSpaceSagaUnitTest.enhance(AbstractSpaceSagaUnitTest.java:105)
    at com.frequentis.ps.service.test.saga.ProxyTestSagaTest.before(ProxyTestSagaTest.java:27)

这是目前我的字节伙伴设置,它适用于几乎所有情况,除了导致显示错误的“setTimeoutManager”和“setState”方法。

// called within the unit test base class (as shown in the call stack above)
builder = new ByteBuddy()
    .subclass(sagaUnderTestClass, ConstructorStrategy.Default.IMITATE_SUPER_TYPE_PUBLIC);
    // define default ctor if necessary that passes "null" values to the super ctor
    builder.method(isAnnotatedWith(StartsSaga.class).or(isAnnotatedWith(EventHandler.class)))
           .intercept(MethodDelegation.to(new ForwardingContextSetupInterceptor<(sagaUnderTest, contextSetter))
                                      .appendParameterBinder(Pipe.Binder.install(Forwarder.class)))
           .method(isPublic()
                           .and(isDeclaredBy(sagaUnderTest.getClass()).or(isDeclaredBy(AbstractSaga.class)
                           .and(not(isAnnotatedWith(StartsSaga.class))).and(not(isAnnotatedWith(EventHandler.class))))
           .intercept(MethodDelegation.to(sagaUnderTest))))

我需要对 setter 进行不同的设置吗?
还是抽象类导致的?
我真的不明白为什么它说对 protected 数据的访问不良,这是否意味着私有(private)字段?

我的类型层次结构如下所示。声明 setter 的最顶层基类:

public abstract class AbstractSaga<SAGA_STATE extends SagaState> implements Saga<SAGA_STATE>, NeedTimeouts, NeedContext {
    private SAGA_STATE state;
    private boolean completed;
    private TimeoutManager timeoutManager;
    private ExecutionContext context;

    protected AbstractSaga() {
        completed = false;
    }

    // i have omitted some method for clarity

    protected ExecutionContext context() {
        return context;
    }

    @Override
    public SAGA_STATE state() {
        return state;
    }

    @Override
    public void setState(final SAGA_STATE state) {
        this.state = state;
    }

    @Override
    public boolean isFinished() {
        return completed;
    }

    protected void setFinished() {
        completed = true;
    }

    @Override
    public void setTimeoutManager(final TimeoutManager timeoutManager) {
        this.timeoutManager = timeoutManager;
    }
}

扩展者:

public abstract class AbstractSpaceSaga<SAGA_STATE extends SpaceSagaState, MESSAGE extends Message> 
    extends AbstractSaga<SAGA_STATE> {
}

最后再次扩展:

public class ProxyTestSaga 
    extends AbstractSpaceSaga<SpaceSagaState, TestRequest> {

    @StartsSaga
    public void handle(final TestRequest request) {
    }

    @EventHandler
    public void handle(final TestEvent event) {
    }
}

我希望代码在某种程度上是可以理解的,如果需要,我可以随时添加更多信息。

最佳答案

这绝对是 Byte Buddy 中的一个错误,因为它不允许您创建非法字节代码。然而,我对正在发生的事情感到困惑,因为 Byte Buddy 似乎正在选择一种与拦截器不兼容的方法。我只是查看了实现情况,这不应该发生。您使用的是最新版本(0.6.14)吗?

根据字节码,拦截器会生成以下代码:

GETSTATIC (interceptor of type ProxyTestSaga)
INVOKEVIRTUAL (some method without arguments)
POP (drop value of the invoked method)
RETURN

调用委托(delegate)方法时发生错误。调用为代理类型定义的方法。从 Byte Buddy 的代码来看,使用 MethodDelegation 时这是不可能的,所以我假设在某处拾取了另一个拦截器?

真正对我有帮助的是一个可重现错误的可运行示例。也许你可以分解你的代码,以便我可以运行它。或者,如果您的应用程序是开源的,也许您可​​以提供它的链接,以便我可以运行您的代码。或者,请与我联系,以便我可以私下访问您的代码。

最后,您可能需要研究 Forwarding 工具,而不是使用 MethodDelegation 进行第二次拦截。此外,如果您能帮助我修复这个错误,我将不胜感激。

关于java - 委派给抽象基类上的 setter 会导致加载时出现VerifyError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31626448/

相关文章:

java - JSch:从存储在 hdfs 上的私钥添加身份

java - 哪些 JVM 优化导致了这些性能结果?

byte-buddy - 用于构建上限通配符的 ByteBuddy 配方是什么?

java - 使用 ByteBuddy 装饰 ClassLoader 方法

java - 将 OSGI 包与 Maven 一起使用时如何隐藏嵌入式依赖项?

java - 在vaadin中调整CSS变化的选择方法是什么?

将项目(比如 Emacs)编译为 LLVM 字节码

Java字节码——每条语句一个标签

java - "No compatible attachment provider is available"是什么意思

java - 简单的跨平台 GUI 应用程序