Skip to content

Conversation

sgerke-1L
Copy link
Contributor

@sgerke-1L sgerke-1L commented Apr 11, 2025

Based on the examples provided in #1375, this MR contains a reproducer for jacoco coverage collection that fails due to and after #1366.

It also contains a proposed fix: The fix is to revert to using redefine if static mockking is required. This is an obvious proposal that satisfies all our test-cases, but it does not answer the bigger question whether decorate is the right approach anyway (or whether we should use redefine in all cases, and omit visibility bridges for default methods).

So basically the two approaches boil down to changing the instrumentation code to

if (spec.shouldDoStaticIntercept) {
    redefine(classBeingRedefined, of(classBeingRedefined.name, classfileBuffer))
} else {
    decorate(classBeingRedefined, of(classBeingRedefined.name, classfileBuffer))
}

or

.with(VisibilityBridgeStrategy { not(isDefaultMethod()).matches(it) })
.redefine(classBeingRedefined, of(classBeingRedefined.name, classfileBuffer))

Both fix the reproducer I've extracted.

TODOs:

  • Agree on approach
  • Review jacoco integration

@sgerke-1L sgerke-1L changed the title Use redefine if mockkStatic is used (proposed fix for #1375) Use redefine if mockkStatic is used (fix candidate for #1375) Apr 11, 2025
@marcelstoer
Copy link
Contributor

it does not answer the bigger question whether decorate is the right approach anyway

None of us involved here (you, Mattia, myself) is really sure about this; an uncomfortable situation to be in, sigh.

if (spec.shouldDoStaticIntercept) {

I would totally not do this. I feel it makes our code brittle and less predictable. Also, I am quite convinced that this condition will grow more complicated over time. First exclude this, then that and later something else.

I propose we go back to your initial fix for the previous issue: .with(VisibilityBridgeStrategy { not(isDefaultMethod()).matches(it) }).

@Raibaz
Copy link
Collaborator

Raibaz commented Apr 11, 2025

I agree with @marcelstoer.

Wondering if we're able to get @raphw's opinion on this.

@sgerke-1L
Copy link
Contributor Author

Thanks for your input @marcelstoer and @Raibaz. Another argument for giving the solution with the VisibilityBridgeStrategy a try is that that would keep using redefine, and that minimizes the change to what we had before.

I've updated this MR according to your input in the meantime.

Let me know if you have further comments. I am also looking for feedback regarding the integration of jacoco. I think we might currently be running jacoco and kover both for the client-tests. Let me know if you have suggestions here.

@raphw
Copy link

raphw commented Apr 12, 2025

If you only ever want to change code but never add fields or methods, why not always decorate? No need for a condition and some general performance improvement.

As for visibility bridges: that Byte Buddy fixes up the oversight of javac is more of an accident as Byte Buddy just tries to stick to the spec by default. I'd disable in general for your case, not just for default methods. If they are already there, Byte Buddy won't touch them.

@sgerke-1L
Copy link
Contributor Author

First of all, thanks for your helpful and sound advice in this matter, @raphw

The answer to both of your questions is basically Hyrum's Law.

If you only ever want to change code but never add fields or methods, why not always decorate? No need for a condition and some general performance improvement.
Apparently, jacoco is depending on some implementation detail of redefine (for static mocks), and breaks when we use decorate.

As for visibility bridges: that Byte Buddy fixes up the oversight of javac is more of an accident as Byte Buddy just tries to stick to the spec by default. I'd disable in general for your case, not just for default methods. If they are already there, Byte Buddy won't touch them.
Again, Hyrum's law. The change as I'm proposing it here is the smallest I know to fix the original issue #1118. By disabling all bridge methods, we'd introduce another change from the state from before, so let's just not do this.

I'd suggest trying the change as proposed here next and see if we learn about more issues. Since we want to provide a non-major release, we should minimize breakage. We got reports that the previous fix (although probably technically sound) introduced regressions in projects that also apply instrumentation, because there the surface for interaction may include properties of how the instrumentation is done in detail.

Looking forward to hearing your thoughts!

@raphw
Copy link

raphw commented Apr 14, 2025

If you retransform previously loaded classes, you will not be able to add fields or methods. Decoration implies this limitation, so I would just go for it as this only reflects a well-tested performance improvement.

@Raibaz
Copy link
Collaborator

Raibaz commented Apr 18, 2025

I'm fine with trying this change out in a minor release and seeing what comes out of it.

I'll merge this and publish the release next week if there are no objections inbetween :)

@marcelstoer
Copy link
Contributor

Thanks @Raibaz, looking forward to the new release.

@Raibaz Raibaz merged commit ea2d003 into mockk:master Apr 28, 2025
22 checks passed
@Raibaz
Copy link
Collaborator

Raibaz commented Apr 29, 2025

Just published v1.14.2 with this change: https://github.com/mockk/mockk/releases/tag/1.14.2

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

Successfully merging this pull request may close these issues.

4 participants