java - 如何解决 java.lang.verifyError

标签 java instrumentation

我正在尝试使用检测代码生成一个随机数。为此,我添加了以下表达式来生成随机数。但它抛出一个验证错误,如下所示。

method.addLocalVariable("instMethod_correlationId", CtClass.longType);
beforeBuilder.append("instMethod_correlationId = Long.valueOf(String.valueOf(System.nanoTime()) + String.valueOf(Math.round(Math.random())));");

我添加了一个长变量并分配了上面生成的值。但我在运行时遇到以下异常。但是,如果我在正常项目中运行相同的数字生成,它不会抛出任何异常。

java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    org/h2/jdbc/JdbcConnection.prepareStatement(Ljava/lang/String;)Ljava/sql/PreparedStatement; @38: i2l
  Reason:
    Type 'java/lang/Long' (current frame, stack[0]) is not assignable to integer
  Current Frame:
    bci: @38
    flags: { }
    locals: { 'org/h2/jdbc/JdbcConnection', 'java/lang/String', top, long, long_2nd }
    stack: { 'java/lang/Long' }
  Bytecode:
    0000000: b803 6c42 bb03 6e59 b703 6fb8 036c b803
    0000010: 73b6 0376 b803 7cb8 0380 b803 73b6 0376
    0000020: b603 81b8 0386 8537 05b8 038b b803 8e16
    0000030: 0513 0390 b603 94bb 0396 59b7 0397 3a07
    0000040: 1907 1303 992b b903 9d03 0057 b803 8bb8
    0000050: 038e 1605 1303 9f19 07b6 03a2 2a06 b600
    0000060: 113d 2ab6 002a 9900 272a 1237 061c bb00
    0000070: 1659 b700 1712 38b6 0019 2a2b b600 1db6
    0000080: 0019 1234 b600 19b6 0020 b600 2d2a b600
    0000090: 2e2a 2bb7 0039 4cbb 003a 592a 2b1c 1103
    00000a0: ebb2 0030 03b7 003b a700 0a4d 2a2c b600
    00000b0: 28bf 3a09 bb03 9659 b703 973a 0a19 0a13
    00000c0: 03a4 1303 a6b9 039d 0300 57b8 038b b803
    00000d0: 8e16 05bb 036e 59b7 036f 1303 a8b6 0376
    00000e0: b803 6c21 65b8 0373 b603 76b6 0381 190a
    00000f0: b603 a219 09b0                         
  Exception Handler Table:
    bci [55, 168] => handler: 171
  Stackmap Table:
    full_frame(@141,{Object[#320],Object[#331],Integer,Long,Long,Object[#918]},{})
    full_frame(@171,{Object[#320],Object[#331],Top,Long,Long},{Object[#322]})
    full_frame(@178,{Object[#320],Object[#331],Integer,Long,Long,Object[#918]},{Object[#58]})

    at org.h2.Driver.connect(Driver.java:73)
    at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:278)
    at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:182)
    at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:701)
    at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:635)
    at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:188)
    at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:128)
    at org.wso2.carbon.user.core.claim.dao.ClaimDAO.getDialectCount(ClaimDAO.java:158)
    at org.wso2.carbon.user.core.common.DefaultRealm.populateProfileAndClaimMaps(DefaultRealm.java:429)
    at org.wso2.carbon.user.core.common.DefaultRealm.init(DefaultRealm.java:105)
    at org.wso2.carbon.user.core.common.DefaultRealmService.initializeRealm(DefaultRealmService.java:262)
    at org.user.core.common.DefaultRealmService.<init>(DefaultRealmService.java:99)
    at org.user.core.common.DefaultRealmService.<init>(DefaultRealmService.java:112)
    at org.user.core.internal.Activator.startDeploy(Activator.java:68)
    at org.user.core.internal.BundleCheckActivator.start(BundleCheckActivator.java:61)
    at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:711)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:702)
    at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:683)
    at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:381)
    at org.eclipse.osgi.framework.internal.core.AbstractBundle.resume(AbstractBundle.java:390)
    at org.eclipse.osgi.framework.internal.core.Framework.resumeBundle(Framework.java:1176)
    at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:559)
    at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:544)
    at org.eclipse.osgi.framework.internal.core.StartLevelManager.incFWSL(StartLevelManager.java:457)
    at org.eclipse.osgi.framework.internal.core.StartLevelManager.doSetStartLevel(StartLevelManager.java:243)
    at org.eclipse.osgi.framework.internal.core.StartLevelManager.dispatchEvent(StartLevelManager.java:438)
    at org.eclipse.osgi.framework.internal.core.StartLevelManager.dispatchEvent(StartLevelManager.java:1)
    at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
    at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:340)

我应该怎样做才能克服这个问题?我在这里做错了什么?

最佳答案

您似乎遇到了 Javassist 字节码生成器中的错误。以下是从 VerifyError 报告的反汇编代码。请注意,由于异常消息中缺少常量池,实际的目标方法是从源代码中猜测的(但看起来似乎合理):

  0 invokestatic    [876]
  3 lstore_3
  4 new     [878]          guess: new StringBuilder
  7 dup
  8 invokespecial   [879]  guess: StringBuilder.<init>
 11 invokestatic    [876]  guess: System.nanoTime()
 14 invokestatic    [883]  guess: String.valueOf(long)
 17 invokevirtual   [886]  guess: StringBuilder.append(String)
 20 invokestatic    [892]  guess: Math.random()
 23 invokestatic    [896]  guess: Math.round(double)
 26 invokestatic    [883]  guess: String.valueOf(long)
 29 invokevirtual   [886]  guess: StringBuilder.append(String)
 32 invokevirtual   [897]  guess: StringBuilder.toString() 
 35 invokestatic    [902]  guess: Long.valueOf(String)
 38 i2l
 39 lstore  <5>
… rest omitted

请注意,方法调用与您的源代码片段相匹配(按预期为 static 或虚拟,并且在应该相同的地方是相同的引用),而位置 38< 处的指令,被验证者拒绝的指令,是一个虚假的i2l指令(从intlong的转换)。在这里,应该发生从 Longlong 的拆箱转换。由于以下 lstore 指令会将 long 值存储到局部变量中,因此 Javassist 似乎正确地声明了局部变量。

请注意,前两条指令表明正在发生另一个检测;由于它调用与位置 11 处的指令相同的方法,该位置应该是 System.nanoTime,因此其他检测似乎旨在测量该方法的总体执行时间。但这不应该影响您的检测代码。

您可以联系 Javassist 的作者,了解 Long 拆箱是否可行。同时,您可以通过使用 Long.parseLong 来解决该问题。而不是 Long.valueOf 以避免首先装箱/拆箱。您还可以使用 String.concat 而不是 + 运算符,从生成代码的角度简化操作,因为这样就无需处理 StringBuilder > 内部:

替换

method.addLocalVariable("instMethod_correlationId", CtClass.longType);
beforeBuilder.append("instMethod_correlationId = Long.valueOf(String.valueOf(System.nanoTime()) + String.valueOf(Math.round(Math.random())));");

method.addLocalVariable("instMethod_correlationId", CtClass.longType);
beforeBuilder.append("instMethod_correlationId = Long.parseLong(String.valueOf(System.nanoTime()).concat(String.valueOf(Math.round(Math.random()))));");

这应该会降低对 Javassist 编译能力的要求,并解决原始代码片段触发的错误。

<小时/>

更新:对于那些有兴趣将 VerifyError 的十六进制转储解码为可读指令序列的人:Online Decoder using tio.run

关于java - 如何解决 java.lang.verifyError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34223471/

相关文章:

java - 收到 404 running spring boot

java - 调用通过 ByteBuddy 检测的 java 代理时出现异常

java - 如何获取 Json 对象中的详细信息?

java - 使用我的 sql 和 java swing 作为前端的 wamp 服务器

java - FailedToCreateRouteException 中的 Camel 路由单元测试结果

node.js - 如何确定 Node.js 发送 HTTP 响应正文所花费的时间?

java - 是否可以在 JUnit 测试中使用 java.lang.instrument.Instrumentation?

java - android findViewById 在 LinearLayout 中为 null

android - 在 Dalvik 字节码上添加新寄存器

ruby-on-rails - 如何检测 Rails 应用程序并识别内存/性能瓶颈?