java.lang.VerifyError IllformedLocaleException java.lang.VerifyError IllformedLocaleException android android

java.lang.VerifyError IllformedLocaleException


Resolved the issue by removing the IllformedLocaleException class from the catch argument. This will still allow you to check for IllformedLocaleException.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)public int setVoice21 (@NonNull final String language, @NonNull final String region) {    try {        // try some API 21 stuff        ...    } catch (final Exception e) {        e.printStackTrace();        if (e instanceof IllformedLocaleException) {            ...        }    }    ...}


TL;DR: Exceptions are exceptional. Can't catch an exception whose type is not known.

The following is most speculation based on my limited knowledge about Java/Dalvik and common sense. Take it with a grain of salt.I found the method that spits out the failing log line and confirmed most of my mentioned speculations, see added links below.

Your problem seems to be that the classes are loaded at once, either the whole class is loaded or none of it.Verification is done first I guess to prevent some runtime checks (remember Android is resource constrained).

I used the following code:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)public int setVoice21(@NonNull final String language, @NonNull final String region) {    try {        // try some API 21 stuff        new Locale.Builder().build().getDisplayVariant();    } catch (final IllformedLocaleException ex) {        ex.printStackTrace();    }    return 0;}

When the system was trying to create an instance of the class containing this method the following happened:

E/dalvikvm: Could not find class 'java.util.Locale$Builder', referenced from method com.test.TestFragment.setVoice21

Loading the Locale.Builder class would be a ClassNotFoundException.

W/dalvikvm: VFY: unable to resolve new-instance 5241 (Ljava/util/Locale$Builder;) in Lcom/test/TestFragment;
D/dalvikvm: VFY: replacing opcode 0x22 at 0x0000

Then on that non-existent class it would try to call the <init> method which is prevented by replacing the OP_NEW_INSTANCE with a OP_NOP. I think this would have been survivable, as I see these all the time when using the support library. I think the assumption here is that if the class is not found then it must have been guarded with an SDK_INT check. Also if it went through dexing/proguard and other stuff it must've been intentional and a ClassNotFoundException is acceptable at runtime.

W/dalvikvm: VFY: unable to resolve exception class 5234 (Ljava/util/IllformedLocaleException;)

Another problematic class, notice this time it's an "exception class" which must be special. If you check the Java bytecode for this method via:

javap -verbose -l -private -c -s TestFragment.class > TestFragment.dispublic int setVoice21(java.lang.String, java.lang.String);    ...    Exception table:       from    to  target type           0    14    17   Class java/util/IllformedLocaleException    LocalVariableTable:      Start  Length  Slot  Name   Signature         18      11     3    ex   Ljava/util/IllformedLocaleException;          0      31     0  this   Lcom/test/TestFragment;          0      31     1 language   Ljava/lang/String;          0      31     2 region   Ljava/lang/String;    StackMapTable: number_of_entries = 2      frame_type = 81 /* same_locals_1_stack_item */        stack = [ class java/util/IllformedLocaleException ]      frame_type = 11 /* same */

You can indeed see that the Exception table and StackMapTable and LocalVariableTable all contain the problematic class, but not Locale$Builder. This may be because the builder is not stored in a variable, but the point to take from here is that exceptions are handled specially and get more scrutiny than normal lines of code.

Using BakSmali on the APK via:

apktool.bat d -r -f -o .\disassembled "app-debug.apk".method public setVoice21(Ljava/lang/String;Ljava/lang/String;)I.prologue:try_start_0new-instance v1, Ljava/util/Locale$Builder;invoke-direct {v1}, Ljava/util/Locale$Builder;-><init>()V...:try_end_0.catch Ljava/util/IllformedLocaleException; {:try_start_0 .. :try_end_0} :catch_0...:catch_0move-exception v0.local v0, "ex":Ljava/util/IllformedLocaleException;invoke-virtual {v0}, Ljava/util/IllformedLocaleException;->printStackTrace()V

seems to reveal a similar pattern, here we can actually see the op-codes mentioned in the log. Notice that .catch seems to be a special instruction, not an operation because it's preceded by a dot. I think this reinforces the scrutiny mentioned above: it's not a runtime operation, but it is required for the class to load the code contained within the methods.

W/dalvikvm: VFY: unable to find exception handler at addr 0xe
W/dalvikvm: VFY: rejected Lcom/test/TestFragment;.setVoice21 (Ljava/lang/String;Ljava/lang/String;)I

I guess this means that it was not able to reconstruct when to call which catch block from the Exception table and StackMapTable because it couldn't find the class to determine the parent classes. This is confirmed in getCaughtExceptionType where "unable to resolve exception class" directly leads to "unable to find exception handler" because it finds no common super-class for a non-existent exception, something like } catch (? ex) { so it doesn't know what to catch.

W/dalvikvm: VFY: rejecting opcode 0x0d at 0x000e
W/dalvikvm: VFY: rejected Lcom/test/TestFragment;.setVoice21 (Ljava/lang/String;Ljava/lang/String;)I

I think at this point the verifier just gave up because it couldn't make sense of the OP_MOVE_EXCEPTION. This is confirmed as that the getCaughtExceptionType method is only used in one place, a switch. Breaking out of that we get "rejecting opcode" then it goto bails up the call stack to "rejected class". After bailing the error code was VERIFY_ERROR_GENERIC which is mapped to VerifyError. Couldn't find where the actual JNI Exception is thrown if it even works that way.

W/dalvikvm: Verifier rejected class Lcom/test/TestFragment;

Multiple rejections were filed against the setVoice21 method and hence the whole class must be rejected (this seems harsh to me, it's possible ART is different in this regard).

W/dalvikvm: Class init failed in newInstance call (Lcom/test/TestFragment;)
D/AndroidRuntime: Shutting down VM
W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x41869da0)
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bumptech.glide.supportapp.v3, PID: 27649
java.lang.VerifyError: com/test/TestFragment

I guess this is similar to an ExceptionInInitializerError in desktop Java which is thrown when a static { } in class body or an static field initializer throws a RuntimeException/Error.

Why instanceof works

Using razzledazzle's workaround changes those tables to include java/lang/Exception, and moves the dependency to IllformedLocaleException into the code to be executed at runtime:

     0    14    17   Class java/lang/Exception19: instanceof    #34                 // class java/util/IllformedLocaleException

and similarly the Smali:

.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0instance-of v1, v0, Ljava/util/IllformedLocaleException;

E/dalvikvm: Could not find class 'java.util.IllformedLocaleException', referenced from method com.test.TestFragment.setVoice21
W/dalvikvm: VFY: unable to resolve instanceof 5234 (Ljava/util/IllformedLocaleException;) in Lcom/test/TestFragment;

Now, it's the same complaint as for Locale$Builder above

D/dalvikvm: VFY: replacing opcode 0x20 at 0x000f

Replacing OP_INSTANCE_OF with ?something?, it doesn't say :)

Another possible workaround

If you look at android.support.v4.view.ViewCompat* classes you will notice that not all those classes are used on all versions. The correct one is chosen at runtime (search for static final ViewCompatImpl IMPL in ViewCompat.java) and only that is loaded. This ensures that even at class load time there won't be any weirdness due to missing classes and is performant. You can do a similar architecture to prevent that method from loading on earlier API levels.