Tried bumping to 2.0.0-beta.1 and I am getting a s...
# arrow
s
Tried bumping to 2.0.0-beta.1 and I am getting a super weird crash, only on my tests. The (I think) important part of the exception is:
Copy code
Caused by: java.lang.IllegalAccessError: failed to access class arrow.core.raise.RaiseKt__RaiseAccumulateKt from class com.hedvig.android.feature.profile.myinfo.MyInfoViewModelKt (arrow.core.raise.RaiseKt__RaiseAccumulateKt and com.hedvig.android.feature.profile.myinfo.MyInfoViewModelKt are in unnamed module of loader 'app')
And it's just pointing to this part of my code. This is quite curious, I don't even know where to start figuring out what is going on here 😄
The code it points to is this:
Copy code
private fun MyInfoMember.validate(): Either<NonEmptyList<MyInfoMemberErrors>, ValidMyInfoMember> {
  return either {
    zipOrAccumulate(
      {
        val hasValidEmail = email.isNotBlank() && validateEmail(email).isSuccessful
        if (!hasValidEmail) raise(MyInfoMemberErrors.Email)
        email
      },
      {
        val phoneNumber = phoneNumber ?: raise(MyInfoMemberErrors.PhoneNumber)
        val hasValidPhoneNumber = phoneNumber.isNotBlank() == true && phoneNumberRegex.matches(phoneNumber)
        if (!hasValidPhoneNumber) raise(MyInfoMemberErrors.PhoneNumber)
        phoneNumber
      },
    ) { email: String, phoneNumber: String ->
      ValidMyInfoMember(email, phoneNumber)
    }
  }
}
And it points to the
zipOrAccumulate
there.
This is the stacktrace dump
I am depending on arrow directly here, no weird transitive dependencies I don't think at least.
Running
./gradlew app:dependencies
also seems to confirm that only arrow 2.0.0-beta.1 is on the classpath. Does anyone have even a slight hint of where I should be heading towards to figure this out? I am stumped 😄
d
Maybe proguard or multidex?
s
This is running tests locally, these shouldn't be involved in that afaik
a
I'm trying to check the cide, but unfortunately I cannot build it without some credentials 😕
s
Yeah sorry about that, I can prepare a branch which is buildable for anyone by removing those dependencies. I can get back to you a bit later in the day, would that be okay with you?
a
sure! I might not have time to look at this until the weekend anyway 😕
s
There's no time rush 😊 I'll get back to ya soon
Did a quick and dirty removal of all related code to that dependency. The app obviously no longer functions as it should, but this is enough for our purposes here 😄 If you: • clone from here https://github.com/HedvigInsurance/android/tree/arrow-error-repro • checkout the "arrow-error-repro" branch. • Sync the project • And then run
Copy code
./gradlew testDebugUnitTest --tests *
You should be getting 4 failing tests related to this:
Copy code
if error is received show error section
if save button is clicked it is not showing anymore
can recover from a network error
if phone and email are updated they are received in state
I hope this is simple enough that it works for you. I just tried doing this exact thing on my machine after cloning it from scratch as well, without having any of the credentials and so on.
thank you color 1
c
@Alejandro Serrano.Mena same problem as this one, no? https://github.com/arrow-kt/arrow/issues/3235
It was a problem with an inline function accessing stuff it shouldn't have had the ability to, IIRC Cc @Youssef Shoaib [MOD]
y
The last time this happened it was because
RaiseCancellationException
was marked as
private
, but was being used for a
catch
type in an inline function (and somehow the compiler didn't produce an error). Here I don't see something similar happening, but something else is fishy:
RaiseKt__RaiseAccumulateKt
is named that way because
RaiseAccumulate.kt
is declared as a JVM multifile class, but to my understanding, there shouldn't be such a special class for it then. I'll take a look at the decompiled bytecode to see where things are going wrong, but maybe this is a compiler bug?? The code for that file was also changed recently, so I need to familiarize myself with it, so sadly I might take some time
thank you color 1
Here's my diagnosis after decompilation: The decompiled code in the reproducer tries to call a method
RaiseKt__RaiseAccumulateKt.access$zipOrAccumulate$lambda$39$lambda-30
(and that last 0 ranges from 0 to 8). The
access
prefix suggests this is originally a private method, and so we use it through a generated synthetic public method. Decompiling
RaiseAccumulate.kt
, I found methods that look like:
Copy code
private static final <A, Error> A zipOrAccumulate$lambda$39$lambda-30$RaiseKt__RaiseAccumulateKt(RaiseAccumulate.Value<? extends A> $a$delegate) {
        return $a$delegate.getValue(null, $$delegatedProperties[0]);
    }
With the 0 again replaced with numbers from 0-8. So clearly the reproducer tries to call the getter for the local properties in
zipOrAccumulate
. That should be fine, except for the fact that the
access$
methods are not generated! The real kicker (and the reason why the error isn't
NoSuchMethodException
) is that
RaiseKt__RaiseAccumulateKt
, is declared package-private! Hence, the code can't access
RaiseKt__RaiseAccumulateKt
in the first place. Pretty sure that this is thus a compiler bug to do with inlining. An immediate workaround we can do in Arrow is to avoid using delegation in
zipOrAccumulate
.
🤯 2
a
WOW! thanks very much for your diagnostic, @Youssef Shoaib [MOD] I'll create a PR reverting the change in
zipOrAccumulate
and use the previous implementation
y
Not sure if doing that is necessary. I think it's as simple as not using delegated properties in those methods. Preparing a PR now to do that, and will test it locally first! Btw, we should find some way to test these "running in other package and module" scenarios. Perhaps Arrow's tests should be forced to run in a different module somehow? Food for thought.
👍 1
a
thanks, then I'll wait for your PR 🙂
y
https://github.com/arrow-kt/arrow/pull/3533 PR itself was simple. What took time was getting Gradle to use my local maven repo and compiling and such
Update on diagnosis: Turns out the
public static final java.lang.Object access$zipOrAccumulate$lambda$39$lambda-30(arrow.core.raise.RaiseAccumulate$Value);
methods do get generated, they just weren't shown by default by CFR (but
javap -p
showed them). Now I'm convinced the issue is simply that
RaiseKt__RaiseAccumulateKt
is package private. I've been able to even recreate the issue in Arrow's tests by making a new test in a different package.
🦠 1
Confirmed that the issue is in Kotlin and filed an issue with a reproducer!
a
thank you for your hard work!
s
So for steps forward now. Does https://github.com/arrow-kt/arrow/pull/3533 work around the bug? Can it be merged now that we're in beta? I see it changes the API of the library a bit. Will that be a problem if you later go back to using the delegate when the bug is fixed? Or will the bug being fixed mean that the API will also anyway be generated this way, so you could safely migrate back without doing a backwards incompatible change?
a
I've just merged it and started the release of
beta.2
, which hopefully should be out during the day The API that changes is in a new 2.x API, so there's no need to preserve compatibility. The 1.x API (zipOrAccumulate) are fully compatible.
thank you color 1
s
Tests pass using beta.2, no crash Thanks so much for the super quick help! And what a find this was, very good exploration of the bug Youssef!
❤️ 1
y
The API changes were me following the philosophy of "clean up any piece of code you touch". I needed to add something to those APIs, and since they're new and experimental, I just cleaned them up. The change back to delegates would be very simple and won't show up in the API at all.