Defer reporting of certain verifier failures.
The verifier currently reports all failures immediately. Certain failures,
such as the failure to resolve a method, or the determination that access
to a field is not allowed, are supposed to deferred until the first time
that executing code does something that could cause the resolution.
With this change, several kinds of verification failures are deferred.
This is done by making a writable copy of the bytecode and replacing the
failing instruction with an "always throw" opcode.
Gory details:
- Added throw-verification-error instruction. Implemented in "portable"
and ARM interpreters. x86 uses portable form through stub.
- Added a function that creates a copy of a DexCode area and makes the
bytecodes writable.
- Added code that replaces a single instruction with an "always throw".
- Replaced runtime check for abstract/interface in new-instance with a
check at verification time.
- Added a test to exercise the deferred error mechanism.
- Minor cleanups (replaced tab, bad valgrind command, ...).
diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S
index af725de..b231a30 100644
--- a/vm/mterp/out/InterpAsm-armv4t.S
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -966,11 +966,9 @@
cmp r1, #CLASS_INITIALIZED @ has class been initialized?
bne .LOP_NEW_INSTANCE_needinit @ no, init class now
.LOP_NEW_INSTANCE_initialized: @ r0=class
- ldr r3, [r0, #offClassObject_accessFlags] @ r3<- clazz->accessFlags
- tst r3, #(ACC_INTERFACE|ACC_ABSTRACT) @ abstract or interface?
mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
- beq .LOP_NEW_INSTANCE_finish @ concrete class, continue
- b .LOP_NEW_INSTANCE_abstract @ fail
+ bl dvmAllocObject @ r0<- new object
+ b .LOP_NEW_INSTANCE_finish @ continue
/* ------------------------------ */
.balign 64
@@ -7453,8 +7451,9 @@
* exception is indicated by AA, with some detail provided by BBBB.
*/
/* op AA, ref@BBBB */
- ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- glue->methodClassDex
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
mov r1, rINST, lsr #8 @ r1<- AA
bl dvmThrowVerificationError @ always throws
b common_exceptionThrown @ handle exception
@@ -7971,8 +7970,7 @@
/* continuation for OP_NEW_INSTANCE */
.balign 32 @ minimize cache lines
-.LOP_NEW_INSTANCE_finish: @ r0=class
- bl dvmAllocObject @ r0<- new object
+.LOP_NEW_INSTANCE_finish: @ r0=new object
mov r3, rINST, lsr #8 @ r3<- AA
cmp r0, #0 @ failed?
beq common_exceptionThrown @ yes, handle the exception
@@ -8008,18 +8006,6 @@
bne .LOP_NEW_INSTANCE_resolved @ no, continue
b common_exceptionThrown @ yes, handle exception
- /*
- * We can't instantiate an abstract class or interface, so throw an
- * InstantiationError with the class descriptor as the message.
- *
- * r0 holds class object
- */
-.LOP_NEW_INSTANCE_abstract:
- ldr r1, [r0, #offClassObject_descriptor]
- ldr r0, .LstrInstantiationErrorPtr
- bl dvmThrowExceptionWithClassMessage
- b common_exceptionThrown
-
.LstrInstantiationErrorPtr:
.word .LstrInstantiationError
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
index 3475fbf..e7efe25 100644
--- a/vm/mterp/out/InterpAsm-armv5te.S
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -966,11 +966,9 @@
cmp r1, #CLASS_INITIALIZED @ has class been initialized?
bne .LOP_NEW_INSTANCE_needinit @ no, init class now
.LOP_NEW_INSTANCE_initialized: @ r0=class
- ldr r3, [r0, #offClassObject_accessFlags] @ r3<- clazz->accessFlags
- tst r3, #(ACC_INTERFACE|ACC_ABSTRACT) @ abstract or interface?
mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
- beq .LOP_NEW_INSTANCE_finish @ concrete class, continue
- b .LOP_NEW_INSTANCE_abstract @ fail
+ bl dvmAllocObject @ r0<- new object
+ b .LOP_NEW_INSTANCE_finish @ continue
/* ------------------------------ */
.balign 64
@@ -7453,8 +7451,9 @@
* exception is indicated by AA, with some detail provided by BBBB.
*/
/* op AA, ref@BBBB */
- ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- glue->methodClassDex
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
mov r1, rINST, lsr #8 @ r1<- AA
bl dvmThrowVerificationError @ always throws
b common_exceptionThrown @ handle exception
@@ -7969,8 +7968,7 @@
/* continuation for OP_NEW_INSTANCE */
.balign 32 @ minimize cache lines
-.LOP_NEW_INSTANCE_finish: @ r0=class
- bl dvmAllocObject @ r0<- new object
+.LOP_NEW_INSTANCE_finish: @ r0=new object
mov r3, rINST, lsr #8 @ r3<- AA
cmp r0, #0 @ failed?
beq common_exceptionThrown @ yes, handle the exception
@@ -8006,18 +8004,6 @@
bne .LOP_NEW_INSTANCE_resolved @ no, continue
b common_exceptionThrown @ yes, handle exception
- /*
- * We can't instantiate an abstract class or interface, so throw an
- * InstantiationError with the class descriptor as the message.
- *
- * r0 holds class object
- */
-.LOP_NEW_INSTANCE_abstract:
- ldr r1, [r0, #offClassObject_descriptor]
- ldr r0, .LstrInstantiationErrorPtr
- bl dvmThrowExceptionWithClassMessage
- b common_exceptionThrown
-
.LstrInstantiationErrorPtr:
.word .LstrInstantiationError
diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S
index a6f88b4..2df6c20 100644
--- a/vm/mterp/out/InterpAsm-x86.S
+++ b/vm/mterp/out/InterpAsm-x86.S
@@ -5806,11 +5806,17 @@
/* ------------------------------ */
.balign 64
.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
-/* File: x86/OP_THROW_VERIFICATION_ERROR.S */
- /* TODO */
- call dvmAbort
-
-
+ /* (stub) */
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx) # only need to export these two
+ SAVE_FP_TO_GLUE(%ecx) # only need to export these two
+ movl %ecx,OUT_ARG0(%esp) # glue is first arg to function
+ call dvmMterp_OP_THROW_VERIFICATION_ERROR # do the real work
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values
+ LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values
+ FETCH_INST()
+ GOTO_NEXT
/* ------------------------------ */
.balign 64
.L_OP_EXECUTE_INLINE: /* 0xee */
@@ -6391,6 +6397,7 @@
/* continuation for OP_NEW_INSTANCE */
.LOP_NEW_INSTANCE_initialized: # on entry, ecx<- class
+ /* TODO: remove test for interface/abstract, now done in verifier */
testl $(ACC_INTERFACE|ACC_ABSTRACT),offClassObject_accessFlags(%ecx)
movl $ALLOC_DONT_TRACK,OUT_ARG1(%esp)
jne .LOP_NEW_INSTANCE_abstract
@@ -6441,6 +6448,7 @@
jmp common_exceptionThrown # no, handle exception
/*
+ * TODO: remove this
* We can't instantiate an abstract class or interface, so throw an
* InstantiationError with the class descriptor as the message.
*
diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c
index a37893a..420873e 100644
--- a/vm/mterp/out/InterpC-allstubs.c
+++ b/vm/mterp/out/InterpC-allstubs.c
@@ -1693,21 +1693,13 @@
GOTO_exceptionThrown();
/*
- * Note: the verifier can ensure that this never happens, allowing us
- * to remove the check. However, the spec requires we throw the
- * exception at runtime, not verify time, so the verifier would
- * need to replace the new-instance call with a magic "throw
- * InstantiationError" instruction.
- *
- * Since this relies on the verifier, which is optional, we would
- * also need a "new-instance-quick" instruction to identify instances
- * that don't require the check.
+ * Verifier now tests for interface/abstract class.
*/
- if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
- dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
- clazz->descriptor);
- GOTO_exceptionThrown();
- }
+ //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+ // dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+ // clazz->descriptor);
+ // GOTO_exceptionThrown();
+ //}
newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
if (newObj == NULL)
GOTO_exceptionThrown();
@@ -2805,9 +2797,10 @@
/* File: c/OP_THROW_VERIFICATION_ERROR.c */
HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
vsrc1 = INST_AA(inst);
ref = FETCH(1); /* class/field/method ref */
- dvmThrowVerificationError(methodClassDex, vsrc1, ref);
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
GOTO_exceptionThrown();
OP_END
diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c
index 60a146f..188639c 100644
--- a/vm/mterp/out/InterpC-portdbg.c
+++ b/vm/mterp/out/InterpC-portdbg.c
@@ -2037,21 +2037,13 @@
GOTO_exceptionThrown();
/*
- * Note: the verifier can ensure that this never happens, allowing us
- * to remove the check. However, the spec requires we throw the
- * exception at runtime, not verify time, so the verifier would
- * need to replace the new-instance call with a magic "throw
- * InstantiationError" instruction.
- *
- * Since this relies on the verifier, which is optional, we would
- * also need a "new-instance-quick" instruction to identify instances
- * that don't require the check.
+ * Verifier now tests for interface/abstract class.
*/
- if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
- dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
- clazz->descriptor);
- GOTO_exceptionThrown();
- }
+ //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+ // dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+ // clazz->descriptor);
+ // GOTO_exceptionThrown();
+ //}
newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
if (newObj == NULL)
GOTO_exceptionThrown();
@@ -3149,9 +3141,10 @@
/* File: c/OP_THROW_VERIFICATION_ERROR.c */
HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
vsrc1 = INST_AA(inst);
ref = FETCH(1); /* class/field/method ref */
- dvmThrowVerificationError(methodClassDex, vsrc1, ref);
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
GOTO_exceptionThrown();
OP_END
diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c
index 60f85b9..90d4ab4 100644
--- a/vm/mterp/out/InterpC-portstd.c
+++ b/vm/mterp/out/InterpC-portstd.c
@@ -1757,21 +1757,13 @@
GOTO_exceptionThrown();
/*
- * Note: the verifier can ensure that this never happens, allowing us
- * to remove the check. However, the spec requires we throw the
- * exception at runtime, not verify time, so the verifier would
- * need to replace the new-instance call with a magic "throw
- * InstantiationError" instruction.
- *
- * Since this relies on the verifier, which is optional, we would
- * also need a "new-instance-quick" instruction to identify instances
- * that don't require the check.
+ * Verifier now tests for interface/abstract class.
*/
- if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
- dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
- clazz->descriptor);
- GOTO_exceptionThrown();
- }
+ //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+ // dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+ // clazz->descriptor);
+ // GOTO_exceptionThrown();
+ //}
newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
if (newObj == NULL)
GOTO_exceptionThrown();
@@ -2869,9 +2861,10 @@
/* File: c/OP_THROW_VERIFICATION_ERROR.c */
HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
vsrc1 = INST_AA(inst);
ref = FETCH(1); /* class/field/method ref */
- dvmThrowVerificationError(methodClassDex, vsrc1, ref);
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
GOTO_exceptionThrown();
OP_END
diff --git a/vm/mterp/out/InterpC-x86.c b/vm/mterp/out/InterpC-x86.c
index ac524f4..469b690 100644
--- a/vm/mterp/out/InterpC-x86.c
+++ b/vm/mterp/out/InterpC-x86.c
@@ -1193,6 +1193,15 @@
FINISH(2);
+/* File: c/OP_THROW_VERIFICATION_ERROR.c */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class/field/method ref */
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
+ GOTO_exceptionThrown();
+OP_END
+
/* File: c/gotoTargets.c */
/*
* C footer. This has some common code shared by the various targets.