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

Spock 2.4-M5 VerifyError: Stack map does not match the one at exception handler #2080

Open
leonard84 opened this issue Jan 9, 2025 · 3 comments
Labels

Comments

@leonard84
Copy link
Member

leonard84 commented Jan 9, 2025

Describe the bug

Groovy seems to write invalid class files in combination with Spock 2.4-M5 for certain tests.

Class file + disassembled bytecode: StackframeReproducer.zip

https://github.com/leonard84/spock/tree/stackframe-reproducer

https://github.com/leonard84/spock/actions/runs/12688993419 shows it fails with all Groovy 2.5, 3, 4 and Java 8, 11, 17, 21 versions

To Reproduce

import spock.lang.Specification

class StackframeReproducer extends Specification {

  def "reproducer"() {
    when:
    def a = ""

    then:
    nop()

    when:
    nop()

    then:
    nop()

    cleanup:
    nop()
  }

  void nop() {
  }
}

Expected behavior

Test can be executed

Actual behavior

org.gradle.api.internal.tasks.testing.TestSuiteExecutionException: Could not complete execution for Gradle Test Executor 38.
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:65)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92)
	at com.sun.proxy.$Proxy4.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: org.junit.platform.commons.JUnitException: TestEngine with ID 'spock' failed to discover tests
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:160)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:132)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:78)
	at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:99)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:85)
	at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63)
	... 18 more
Caused by: org.junit.platform.commons.JUnitException: ClassSelector [className = 'org.spockframework.smoke.issues.StackframeReproducer', classLoader = sun.misc.Launcher$AppClassLoader@73d16e93] resolution failed
	at org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener.selectorProcessed(AbortOnFailureLauncherDiscoveryListener.java:39)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:103)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:83)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:113)
	at org.spockframework.runtime.SpockEngine.discover(SpockEngine.java:28)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:152)
	... 28 more
Caused by: java.lang.VerifyError: Stack map does not match the one at exception handler 361
Exception Details:
  Location:
    org/spockframework/smoke/issues/StackframeReproducer.$spock_feature_0_0()V @361: astore
  Reason:
    Type 'java/lang/String' (current frame, locals[5]) is not assignable to null (stack map, locals[5])
  Current Frame:
    bci: @70
    flags: { }
    locals: { 'org/spockframework/smoke/issues/StackframeReproducer', top, '[Z', 'org/spockframework/runtime/ErrorCollector', 'org/spockframework/runtime/ValueRecorder', 'java/lang/String', null }
    stack: { 'java/lang/Throwable' }
  Stackmap Frame:
    bci: @361
    flags: { }
    locals: { 'org/spockframework/smoke/issues/StackframeReproducer', top, '[Z', 'org/spockframework/runtime/ErrorCollector', 'org/spockframework/runtime/ValueRecorder', null, null }
    stack: { 'java/lang/Throwable' }
  Bytecode:
    0x0000000: b800 f54d 1256 ba00 6300 00ba 0069 0000
    0x0000010: 4e2d 5712 6bba 006f 0000 ba00 7200 003a
    0x0000020: 0419 0457 2c10 0804 5401 3a05 1905 5701
    0x0000030: 3a06 1906 572c 1009 0454 2a03 b800 7801
    0x0000040: 5712 7a59 3a05 572a 03b8 007d 0157 2a04
    0x0000050: b800 7801 5700 0000 0000 0000 0000 0000
    0x0000060: 0000 0000 0000 0000 0000 0000 0000 0000
    0x0000070: 0000 0000 0000 0000 0000 0000 0000 0000
    0x0000080: 0000 0000 0000 0000 0000 2c10 0a04 54bf
    0x0000090: 002c 100b 0454 bf00 0000 0000 0000 0000
    0x00000a0: 0000 0000 0000 2c10 0c04 54bf 0000 2c10
    0x00000b0: 0d04 54bf 0000 2c10 0e04 54bf 002c 100f
    0x00000c0: 0454 bf00 002c 1010 0454 bf00 0000 0000
    0x00000d0: 0000 0000 0000 0000 2c10 1104 54bf 2aba
    0x00000e0: 00ad 0000 572a 05b8 007d 0157 2a06 b800
    0x00000f0: 7801 572c 1012 0454 2d19 04b6 0081 1283
    0x0000100: 1020 0801 2a19 0419 0403 b600 8712 88b6
    0x0000110: 008c ba00 8f00 0003 bd00 2519 0406 03b8
    0x0000120: 0095 b600 98b8 009e 0305 b800 a201 572c
    0x0000130: 1013 0454 a700 1e3a 092d 1904 1283 1020
    0x0000140: 0801 1909 b800 a601 5700 2c10 1404 54a7
    0x0000150: 0003 a700 0d3a 0a19 0a2c 1015 0454 bf2a
    0x0000160: 06b8 007d 0157 a700 1c3a 0b19 0b59 3a06
    0x0000170: 5719 0bc0 00a8 2c10 1604 54bf 2c10 1704
    0x0000180: 54bf 013a 0c19 0c57 2c10 1804 5419 0601
    0x0000190: b800 b19a 000b 2c10 1904 54a7 0018 2ab6
    0x00001a0: 00b7 ba00 ba00 00b6 00c0 593a 0c57 2c10
    0x00001b0: 1a04 542a 07b8 0078 0157 2c10 1b04 542a
    0x00001c0: ba00 ad00 0057 2a07 b800 7d01 572c 101c
    0x00001d0: 0454 a700 2e3a 0d19 0601 b800 b199 000f
    0x00001e0: 1906 190d b600 c401 57a7 000e 190d c000
    0x00001f0: a82c 101d 0454 bf00 2c10 1e04 54a7 0003
    0x0000200: 1906 01b8 00b1 9a00 0b2c 101f 0454 a700
    0x0000210: 182a b600 b7ba 00ba 0000 190c b600 c801
    0x0000220: 572c 1020 0454 a700 333a 0e19 0601 b800
    0x0000230: b19a 000b 2c10 2104 54a7 0018 2ab6 00b7
    0x0000240: ba00 ba00 0019 0cb6 00c8 0157 2c10 2204
    0x0000250: 5419 0e2c 1023 0454 bfa7 00df 3a0f 013a
    0x0000260: 1019 1057 2c10 2404 5419 0601 b800 b19a
    0x0000270: 000b 2c10 2504 54a7 0018 2ab6 00b7 ba00
    0x0000280: ba00 00b6 00c0 593a 1057 2c10 2604 542a
    0x0000290: 07b8 0078 0157 2aba 00ad 0000 572a 07b8
    0x00002a0: 007d 0157 2c10 2704 54a7 002e 3a11 1906
    0x00002b0: 01b8 00b1 9900 0f19 0619 11b6 00c4 0157
    0x00002c0: a700 0e19 11c0 00a8 2c10 2804 54bf 002c
    0x00002d0: 1029 0454 a700 0319 0601 b800 b19a 000b
    0x00002e0: 2c10 2a04 54a7 0018 2ab6 00b7 ba00 ba00
    0x00002f0: 0019 10b6 00c8 0157 2c10 2b04 54a7 0033
    0x0000300: 3a12 1906 01b8 00b1 9a00 0b2c 102c 0454
    0x0000310: a700 182a b600 b7ba 00ba 0000 1910 b600
    0x0000320: c801 572c 102d 0454 1912 2c10 2e04 54bf
    0x0000330: 190f 2c10 2f04 54bf 2ab6 00b7 ba00 ba00
    0x0000340: 00b6 00cc ba00 cf00 00b6 00d4 0157 2c10
    0x0000350: 3004 54b1                              
  Exception Handler Table:
    bci [243, 311] => handler: 311
    bci [243, 311] => handler: 341
    bci [311, 330] => handler: 341
    bci [53, 85] => handler: 361
    bci [222, 361] => handler: 361
    bci [53, 85] => handler: 604
    bci [222, 361] => handler: 604
    bci [361, 380] => handler: 604
    bci [392, 469] => handler: 469
    bci [392, 469] => handler: 553
    bci [469, 504] => handler: 553
    bci [612, 684] => handler: 684
    bci [612, 684] => handler: 768
    bci [684, 719] => handler: 768
  Stackmap Table:
    full_frame(@85,{Top,Top,Object[#247]},{Object[#168]})
    same_locals_1_stack_item_frame(@144,Object[#168])
    same_locals_1_stack_item_frame(@151,Object[#168])
    same_locals_1_stack_item_frame(@172,Object[#168])
    same_locals_1_stack_item_frame(@180,Object[#168])
    same_locals_1_stack_item_frame(@188,Object[#168])
    same_locals_1_stack_item_frame(@195,Object[#168])
    same_locals_1_stack_item_frame(@203,Object[#168])
    full_frame(@311,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Null},{Object[#168]})
    same_frame(@338)
    same_locals_1_stack_item_frame(@341,Object[#168])
    same_frame(@351)
    same_locals_1_stack_item_frame(@361,Object[#168])
    full_frame(@380,{Top,Top,Object[#247]},{Object[#168]})
    full_frame(@386,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Null},{})
    full_frame(@414,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Null,Top,Top,Top,Top,Top,Null},{})
    full_frame(@435,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Null,Top,Top,Top,Top,Top,Object[#228]},{})
    same_locals_1_stack_item_frame(@469,Object[#168])
    append_frame(@492,Object[#168])
    same_frame(@503)
    chop_frame(@512,1)
    same_frame(@529)
    same_frame(@550)
    same_locals_1_stack_item_frame(@553,Object[#168])
    append_frame(@572,Top,Object[#168])
    same_frame(@593)
    chop_frame(@601,2)
    full_frame(@604,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Object[#168]},{Object[#168]})
    full_frame(@634,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Object[#168],Top,Top,Top,Top,Top,Top,Top,Top,Object[#168],Null},{})
    full_frame(@655,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Object[#168],Top,Top,Top,Top,Top,Top,Top,Top,Object[#168],Object[#228]},{})
    same_locals_1_stack_item_frame(@684,Object[#168])
    append_frame(@707,Object[#168])
    same_frame(@718)
    chop_frame(@727,1)
    same_frame(@744)
    same_frame(@765)
    same_locals_1_stack_item_frame(@768,Object[#168])
    append_frame(@787,Top,Object[#168])
    same_frame(@808)
    chop_frame(@816,2)
    full_frame(@824,{Object[#2],Top,Object[#247],Object[#226],Object[#107],Null,Null,Top,Top,Top,Top,Top,Object[#228]},{})

	at java.lang.Class.getDeclaredFields0(Native Method)
	at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
	at java.lang.Class.getDeclaredFields(Class.java:1916)
	at org.spockframework.runtime.SpecInfoBuilder.buildFields(SpecInfoBuilder.java:88)
	at org.spockframework.runtime.SpecInfoBuilder.doBuild(SpecInfoBuilder.java:59)
	at org.spockframework.runtime.SpecInfoBuilder.build(SpecInfoBuilder.java:44)
	at org.spockframework.runtime.ClassSelectorResolver.lambda$resolveClass$1(ClassSelectorResolver.java:46)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$DefaultContext.createAndAdd(EngineDiscoveryRequestResolution.java:250)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution$DefaultContext.addToParent(EngineDiscoveryRequestResolution.java:213)
	at org.spockframework.runtime.ClassSelectorResolver.resolveClass(ClassSelectorResolver.java:45)
	at org.spockframework.runtime.ClassSelectorResolver.resolve(ClassSelectorResolver.java:28)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$2(EngineDiscoveryRequestResolution.java:135)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1361)
	at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
	at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:499)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:486)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:531)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:189)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:126)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:92)
	... 32 more

Java version

Java 8

Buildtool version


Gradle 8.12

Build time: 2024-12-20 15:46:53 UTC
Revision: a3cacb207fec727859be9354c1937da2e59004c1

Kotlin: 2.0.21
Groovy: 3.0.22
Ant: Apache Ant(TM) version 1.10.15 compiled on August 25 2024
Launcher JVM: 21.0.1 (BellSoft 21.0.1+12-LTS)
Daemon JVM: /Users//.asdf/installs/java/liberica-21.0.1+12 (no JDK specified, using current Java home)
OS: Mac OS X 15.2 aarch64

What operating system are you using

Mac OS X 15.2 aarch64

Dependencies

groovy4 = '4.0.24'

Additional context

No response

@leonard84 leonard84 added the bug label Jan 9, 2025
leonard84 added a commit to leonard84/spock that referenced this issue Jan 9, 2025
Run `./gradlew -Dvariant=4.0 --stacktrace :spock-specs:test --tests StackframeReproducer`
@leonard84 leonard84 changed the title Spock 2.4-M5 with Groovy 4.0 VerifyError: Stack map does not match the one at exception handler Spock 2.4-M5 VerifyError: Stack map does not match the one at exception handler Jan 9, 2025
@leonard84
Copy link
Member Author

This can be narrowed down to the changes made by the addBlockListener() call in visitCleanupBlock. If that is removed, the tests pass without error.

@AndreasTu
Copy link
Member

@leonard84 @paulk-asert
I have investigated the issue a bit further.
The generated code has nested try/catch blocks, like:

   try {
        org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
        a = ''
        org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
        org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1)
        try {

Whereas the instruction def a = "" gets an ASM Label for bytecode index 47 from source line 25.
The line def a = "" also has a Groovy StatementLabel when in the Statement.

When the ASM Label is generated, the class StatementWriter tries to create a ASM Label for the Groovy StatementLabel (writeStatementLabel() method) and caches the new created Label.

Now when the second when: block line 31 is processed it also has the same StatementLabel when, with bytecode index 163.
The StatementWriter searches for an existing ASM Label and returns the same instance as from bytecode index 47.

Now the ASM code Label.resolve() overrides the bytecodeOffset from 47 to 163, making the original following inner try basic block of the first then unreachable, and the method MethodWriter.computeAllFrames() fills up unreachable code with nops and athrows, which lead to the VerifyError.

To check all my reasoning I have added the following code into org.spockframework.compiler.SpecParser.addBlock()

  if (blockInfo == BlockParseInfo.WHEN) {
      stat.getStatementLabels().remove(BlockParseInfo.WHEN.toString());
  }

This removes the when statement labels from the Groovy AST.
After that, the feature method is generated fine.

So my proposal whould be to remove the consumed "special" Statement labels from the Groovy AST during the rewrite.

@Vampire
Copy link
Member

Vampire commented Jan 10, 2025

But this is clearly a Groovy bug, isn't it, and I'd say it should be fixed by Groovy.
I'm not sure whether the Groovy labels still have some use somewhere.
For example a later AST transformation could use it for some processing.
In the past I already several times had to add some AST transformation that runs after the Spock AST transformation to work-around some bugs in Spock or Groovy and there the information might have been very useful.

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

No branches or pull requests

3 participants