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/libdex/DexFile.c b/libdex/DexFile.c
index a8b5345..99b38c9 100644
--- a/libdex/DexFile.c
+++ b/libdex/DexFile.c
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Access the contents of a .dex file.
*/
#include "DexFile.h"
#include "DexProto.h"
+#include "DexCatch.h"
#include "Leb128.h"
#include "sha1.h"
#include "ZipArchive.h"
@@ -887,6 +889,43 @@
/*
+ * Compute the size, in bytes, of a DexCode.
+ */
+size_t dexGetDexCodeSize(const DexCode* pCode)
+{
+ /*
+ * The catch handler data is the last entry. It has a variable number
+ * of variable-size pieces, so we need to create an iterator.
+ */
+ u4 handlersSize;
+ u4 offset;
+ u4 ui;
+
+ if (pCode->triesSize != 0) {
+ handlersSize = dexGetHandlersSize(pCode);
+ offset = dexGetFirstHandlerOffset(pCode);
+ } else {
+ handlersSize = 0;
+ offset = 0;
+ }
+
+ for (ui = 0; ui < handlersSize; ui++) {
+ DexCatchIterator iterator;
+ dexCatchIteratorInit(&iterator, pCode, offset);
+ offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+ }
+
+ const u1* handlerData = dexGetCatchHandlerData(pCode);
+
+ //LOGD("+++ pCode=%p handlerData=%p last offset=%d\n",
+ // pCode, handlerData, offset);
+
+ /* return the size of the catch handler + everything before it */
+ return (handlerData - (u1*) pCode) + offset;
+}
+
+
+/*
* ===========================================================================
* Debug info
* ===========================================================================
@@ -1185,3 +1224,4 @@
free(methodDescriptor);
}
}
+
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
index 4d8d151..4b5fe7c 100644
--- a/libdex/DexFile.h
+++ b/libdex/DexFile.h
@@ -735,6 +735,9 @@
return dexStringById(pDexFile, pClassDef->sourceFileIdx);
}
+/* get the size, in bytes, of a DexCode */
+size_t dexGetDexCodeSize(const DexCode* pCode);
+
/* Get the list of "tries" for the given DexCode. */
DEX_INLINE const DexTry* dexGetTries(const DexCode* pCode) {
const u2* insnsEnd = &pCode->insns[pCode->insnsSize];
@@ -753,6 +756,7 @@
return (const u1*) &pTries[pCode->triesSize];
}
+/* get a pointer to the start of the debugging data */
DEX_INLINE const u1* dexGetDebugInfoStream(const DexFile* pDexFile,
const DexCode* pCode)
{
diff --git a/libdex/InstrUtils.c b/libdex/InstrUtils.c
index f9ce4de..33c7e7d 100644
--- a/libdex/InstrUtils.c
+++ b/libdex/InstrUtils.c
@@ -604,7 +604,7 @@
flags = kInstrCanSwitch | kInstrCanContinue;
break;
- /* optimizer-generated instructions */
+ /* verifier/optimizer-generated instructions */
case OP_THROW_VERIFICATION_ERROR:
flags = kInstrCanThrow;
break;
diff --git a/tests/003-omnibus-opcodes/expected.txt b/tests/003-omnibus-opcodes/expected.txt
index 81a8879..25ed38b 100644
--- a/tests/003-omnibus-opcodes/expected.txt
+++ b/tests/003-omnibus-opcodes/expected.txt
@@ -64,8 +64,9 @@
Throw.twoA
Throw.twoN
Throw.rethrow
-Caught: java.lang.VerifyError: UnresTest1
-Caught (retry): java.lang.VerifyError: UnresTest1
-Caught: java.lang.VerifyError: UnresTest2
+UnresTest1...
+UnresTest1...
+UnresTest2...
+UnresTest2 done
InternedString.run
Done!
diff --git a/tests/003-omnibus-opcodes/src/UnresTest2.java b/tests/003-omnibus-opcodes/src/UnresTest2.java
index 43a92ac..b458bfe 100644
--- a/tests/003-omnibus-opcodes/src/UnresTest2.java
+++ b/tests/003-omnibus-opcodes/src/UnresTest2.java
@@ -44,6 +44,7 @@
}
checkCasts(stuff);
+ System.out.println("UnresTest2 done");
}
}
diff --git a/tests/075-verification-error/expected.txt b/tests/075-verification-error/expected.txt
new file mode 100644
index 0000000..3e8dbd0
--- /dev/null
+++ b/tests/075-verification-error/expected.txt
@@ -0,0 +1,11 @@
+Got expected InstantationError
+Got expected NoSuchFieldError
+Got expected NoSuchFieldError
+Got expected NoSuchMethodError
+Got expected NoSuchMethodError
+Got expected IllegalAccessError (ifield)
+Got expected IllegalAccessError (sfield)
+Got expected IllegalAccessError (method)
+Got expected IllegalAccessError (smethod)
+Got expected IllegalAccessError (meth-class)
+Got expected IllegalAccessError (meth-meth)
diff --git a/tests/075-verification-error/info.txt b/tests/075-verification-error/info.txt
new file mode 100644
index 0000000..be688ff
--- /dev/null
+++ b/tests/075-verification-error/info.txt
@@ -0,0 +1 @@
+Exercise deferred verification error reporting.
diff --git a/tests/075-verification-error/src/Main.java b/tests/075-verification-error/src/Main.java
new file mode 100644
index 0000000..8a0dd15
--- /dev/null
+++ b/tests/075-verification-error/src/Main.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import other.Mutant;
+import other.InaccessibleClass;
+import other.InaccessibleMethod;
+
+/**
+ * Test some problematic situations that the verifier detects.
+ */
+public class Main {
+ public static final boolean VERBOSE = false;
+
+ public static void main(String[] args) {
+ testClassNewInstance();
+ testMissingStuff();
+ testBadAccess();
+ }
+
+ /**
+ * Try to create a new instance of an abstract class.
+ */
+ static void testClassNewInstance() {
+ try {
+ MaybeAbstract ma = new MaybeAbstract();
+ System.err.println("ERROR: MaybeAbstract succeeded unexpectedly");
+ } catch (InstantiationError ie) {
+ System.out.println("Got expected InstantationError");
+ if (VERBOSE) System.out.println("--- " + ie);
+ } catch (Exception ex) {
+ System.err.println("Got unexpected MaybeAbstract failure");
+ }
+ }
+
+ /**
+ * Test stuff that disappears.
+ */
+ static void testMissingStuff() {
+ Mutant mutant = new Mutant();
+
+ try {
+ int x = mutant.disappearingField;
+ } catch (NoSuchFieldError nsfe) {
+ System.out.println("Got expected NoSuchFieldError");
+ if (VERBOSE) System.out.println("--- " + nsfe);
+ }
+
+ try {
+ int y = Mutant.disappearingStaticField;
+ } catch (NoSuchFieldError nsfe) {
+ System.out.println("Got expected NoSuchFieldError");
+ if (VERBOSE) System.out.println("--- " + nsfe);
+ }
+
+ try {
+ mutant.disappearingMethod();
+ } catch (NoSuchMethodError nsme) {
+ System.out.println("Got expected NoSuchMethodError");
+ if (VERBOSE) System.out.println("--- " + nsme);
+ }
+
+ try {
+ Mutant.disappearingStaticMethod();
+ } catch (NoSuchMethodError nsme) {
+ System.out.println("Got expected NoSuchMethodError");
+ if (VERBOSE) System.out.println("--- " + nsme);
+ }
+ }
+
+ /**
+ * Test stuff that becomes inaccessible.
+ */
+ static void testBadAccess() {
+ Mutant mutant = new Mutant();
+
+ try {
+ int x = mutant.inaccessibleField;
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (ifield)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ int y = Mutant.inaccessibleStaticField;
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (sfield)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ mutant.inaccessibleMethod();
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (method)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ Mutant.inaccessibleStaticMethod();
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (smethod)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ /* accessible static method in an inaccessible class */
+ InaccessibleClass.test();
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (meth-class)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ /* inaccessible static method in an accessible class */
+ InaccessibleMethod.test();
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (meth-meth)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+ }
+}
+
diff --git a/tests/075-verification-error/src/MaybeAbstract.java b/tests/075-verification-error/src/MaybeAbstract.java
new file mode 100644
index 0000000..43c002b
--- /dev/null
+++ b/tests/075-verification-error/src/MaybeAbstract.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public /*abstract*/ class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
+
diff --git a/tests/075-verification-error/src/other/InaccessibleClass.java b/tests/075-verification-error/src/other/InaccessibleClass.java
new file mode 100644
index 0000000..79ad335
--- /dev/null
+++ b/tests/075-verification-error/src/other/InaccessibleClass.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+public class InaccessibleClass {
+ public static void test() {}
+}
+
diff --git a/tests/075-verification-error/src/other/InaccessibleMethod.java b/tests/075-verification-error/src/other/InaccessibleMethod.java
new file mode 100644
index 0000000..49a0b29
--- /dev/null
+++ b/tests/075-verification-error/src/other/InaccessibleMethod.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+public class InaccessibleMethod {
+ public static void test() {}
+}
+
diff --git a/tests/075-verification-error/src/other/Mutant.java b/tests/075-verification-error/src/other/Mutant.java
new file mode 100644
index 0000000..6c869c0
--- /dev/null
+++ b/tests/075-verification-error/src/other/Mutant.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+/**
+ * Parts of this class will disappear or change form.
+ */
+public class Mutant {
+ public int disappearingField = 3;
+ public static int disappearingStaticField = 4;
+
+ public void disappearingMethod() {
+ System.out.println("bye");
+ }
+ public static void disappearingStaticMethod() {
+ System.out.println("kthxbai");
+ }
+
+ public int inaccessibleField = 5;
+ public static int inaccessibleStaticField = 6;
+
+ public void inaccessibleMethod() {
+ System.out.println("no");
+ }
+
+ public static void inaccessibleStaticMethod() {
+ System.out.println("nay");
+ }
+}
+
diff --git a/tests/075-verification-error/src2/MaybeAbstract.java b/tests/075-verification-error/src2/MaybeAbstract.java
new file mode 100644
index 0000000..bfbfd45
--- /dev/null
+++ b/tests/075-verification-error/src2/MaybeAbstract.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public abstract class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
+
diff --git a/tests/075-verification-error/src2/other/InaccessibleClass.java b/tests/075-verification-error/src2/other/InaccessibleClass.java
new file mode 100644
index 0000000..c598910
--- /dev/null
+++ b/tests/075-verification-error/src2/other/InaccessibleClass.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+/*package*/ class InaccessibleClass {
+ public static void test() {}
+}
+
diff --git a/tests/075-verification-error/src2/other/InaccessibleMethod.java b/tests/075-verification-error/src2/other/InaccessibleMethod.java
new file mode 100644
index 0000000..6e2738e
--- /dev/null
+++ b/tests/075-verification-error/src2/other/InaccessibleMethod.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+public class InaccessibleMethod {
+ /*package*/ static void test() {}
+}
+
diff --git a/tests/075-verification-error/src2/other/Mutant.java b/tests/075-verification-error/src2/other/Mutant.java
new file mode 100644
index 0000000..220fda0
--- /dev/null
+++ b/tests/075-verification-error/src2/other/Mutant.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+/**
+ * Parts of this class will disappear or change form.
+ */
+public class Mutant {
+ //public int disappearingField = 3;
+ //public static int disappearingStaticField = 4;
+
+ //public static void disappearingMethod() {
+ // System.out.println("bye");
+ //}
+ //public static void disappearingStaticMethod() {
+ // System.out.println("kthxbai");
+ //}
+
+ protected int inaccessibleField = 5;
+ protected static int inaccessibleStaticField = 6;
+
+ protected void inaccessibleMethod() {
+ System.out.println("no");
+ }
+
+ protected static void inaccessibleStaticMethod() {
+ System.out.println("nay");
+ }
+}
+
+
diff --git a/tests/etc/local-run-test-jar b/tests/etc/local-run-test-jar
index 641306d..ee3f856 100755
--- a/tests/etc/local-run-test-jar
+++ b/tests/etc/local-run-test-jar
@@ -100,7 +100,7 @@
if [ "$VALGRIND" = "y" ]; then
msg "Running with valgrind"
valgrind_cmd="valgrind"
- #valgrind_cmd="$valgrind_cmd --leak-check=full"
+ #valgrind_cmd="valgrind --leak-check=full"
else
valgrind_cmd=""
fi
diff --git a/vm/LinearAlloc.c b/vm/LinearAlloc.c
index 73d9235..84bb103 100644
--- a/vm/LinearAlloc.c
+++ b/vm/LinearAlloc.c
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Linear memory allocation, tied to class loaders.
*/
@@ -148,7 +149,7 @@
LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
strerror(errno));
free(pHdr);
- close(fd);
+ close(fd);
return NULL;
}
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 4125742..b37b9a9 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -120,9 +120,9 @@
VerifyError* pFailure);
static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount,\
u4 vsrc, RegType checkType, VerifyError* pFailure);
-static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,\
+static bool doCodeVerification(Method* meth, InsnFlags* insnFlags,\
RegisterTable* regTable, UninitInstanceMap* uninitMap);
-static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,\
+static bool verifyInstruction(Method* meth, InsnFlags* insnFlags,\
RegisterTable* regTable, RegType* workRegs, int insnIdx,
UninitInstanceMap* uninitMap, int* pStartGuess);
static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2);
@@ -1055,7 +1055,7 @@
//char* curMethodDesc =
// dexProtoCopyMethodDescriptor(&meth->prototype);
- LOGE("Could not find method %s.%s, referenced from "
+ LOGI("Could not find method %s.%s, referenced from "
"method %s.%s\n",
dotMissingClass, methodName/*, methodDesc*/,
dotMethClass, meth->name/*, curMethodDesc*/);
@@ -1069,7 +1069,8 @@
dvmMethodTypeStr(methodType), pDecInsn->vB,
classDescriptor, methodName, methodDesc);
free(methodDesc);
- *pFailure = VERIFY_ERROR_NO_METHOD;
+ if (VERIFY_OK(*pFailure)) /* not set for interface resolve */
+ *pFailure = VERIFY_ERROR_NO_METHOD;
goto fail;
}
@@ -2457,7 +2458,7 @@
if (meth->clazz != field->clazz) {
LOG_VFY_METH(meth, "VFY: can't modify final field %s.%s\n",
field->clazz->descriptor, field->name);
- *pFailure = VERIFY_ERROR_ACCESS;
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
return;
}
@@ -2793,6 +2794,121 @@
/*
+ * Replace an instruction with "throw-verification-error". This allows us to
+ * defer error reporting until the code path is first used.
+ *
+ * The throw-verification-error instruction requires two code units. Some
+ * of the replaced instructions require three; the third code unit will
+ * receive a "nop". The instruction's length will be left unchanged
+ * in "insnFlags".
+ *
+ * IMPORTANT: this may replace meth->insns with a pointer to a new copy of
+ * the instructions.
+ *
+ * Returns "true" on success.
+ */
+static bool replaceFailingInstruction(Method* meth, InsnFlags* insnFlags,
+ int insnIdx, VerifyError failure)
+{
+ const u2* oldInsns = meth->insns + insnIdx;
+ u2 oldInsn = *oldInsns;
+ bool result = false;
+
+ dvmMakeCodeReadWrite(meth);
+
+ //LOGD(" was 0x%04x\n", oldInsn);
+ u2* newInsns = (u2*) meth->insns + insnIdx;
+
+ /*
+ * Generate the new instruction out of the old.
+ *
+ * First, make sure this is an instruction we're expecting to stomp on.
+ */
+ switch (oldInsn & 0xff) {
+ case OP_CONST_CLASS: // insn[1] == class ref, 2 bytes
+ case OP_CHECK_CAST:
+ case OP_INSTANCE_OF:
+ case OP_NEW_INSTANCE:
+ case OP_NEW_ARRAY:
+
+ case OP_FILLED_NEW_ARRAY: // insn[1] == class ref, 3 bytes
+ case OP_FILLED_NEW_ARRAY_RANGE:
+
+ case OP_IGET: // insn[1] == field ref, 2 bytes
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IPUT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_SGET:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SPUT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+
+ case OP_INVOKE_VIRTUAL: // insn[1] == method ref, 3 bytes
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ break;
+ default:
+ /* could handle this in a generic way, but this is probably safer */
+ LOG_VFY("GLITCH: verifier asked to replace opcode 0x%02x\n",
+ oldInsn & 0xff);
+ goto bail;
+ }
+
+ /* write a NOP over the third code unit, if necessary */
+ int width = dvmInsnGetWidth(insnFlags, insnIdx);
+ switch (width) {
+ case 2:
+ /* nothing to do */
+ break;
+ case 3:
+ newInsns[2] = OP_NOP;
+ break;
+ default:
+ /* whoops */
+ LOGE("ERROR: stomped a %d-unit instruction with a verifier error\n",
+ width);
+ dvmAbort();
+ }
+
+ /* encode the opcode, with the failure code in the high byte */
+ newInsns[0] = OP_THROW_VERIFICATION_ERROR | (failure << 8);
+
+ result = true;
+
+bail:
+ dvmMakeCodeReadOnly(meth);
+ return result;
+}
+
+
+/*
* ===========================================================================
* Entry point and driver loop
* ===========================================================================
@@ -2801,7 +2917,7 @@
/*
* Entry point for the detailed code-flow analysis.
*/
-bool dvmVerifyCodeFlow(const Method* meth, InsnFlags* insnFlags,
+bool dvmVerifyCodeFlow(Method* meth, InsnFlags* insnFlags,
UninitInstanceMap* uninitMap)
{
bool result = false;
@@ -2951,7 +3067,7 @@
* instruction if a register contains an uninitialized instance created
* by that same instrutcion.
*/
-static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,
+static bool doCodeVerification(Method* meth, InsnFlags* insnFlags,
RegisterTable* regTable, UninitInstanceMap* uninitMap)
{
const int insnsSize = dvmGetMethodInsnsSize(meth);
@@ -3109,10 +3225,14 @@
dvmInsnSetChanged(insnFlags, insnIdx, false);
}
- if (DEAD_CODE_SCAN) {
+ if (DEAD_CODE_SCAN && !IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
/*
- * Scan for dead code. There's nothing "evil" about dead code, but it
- * indicates a flaw somewhere down the line, possibly in the verifier.
+ * Scan for dead code. There's nothing "evil" about dead code
+ * (besides the wasted space), but it indicates a flaw somewhere
+ * down the line, possibly in the verifier.
+ *
+ * If we've rewritten "always throw" instructions into the stream,
+ * we are almost certainly going to have some dead code.
*/
int deadStart = -1;
for (insnIdx = 0; insnIdx < insnsSize;
@@ -3178,8 +3298,11 @@
* if execution at that point needs to be (re-)evaluated. Register changes
* are merged into "regTypes" at the target addresses. Does not set or
* clear any other flags in "insnFlags".
+ *
+ * This may alter meth->insns if we need to replace an instruction with
+ * throw-verification-error.
*/
-static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,
+static bool verifyInstruction(Method* meth, InsnFlags* insnFlags,
RegisterTable* regTable, RegType* workRegs, int insnIdx,
UninitInstanceMap* uninitMap, int* pStartGuess)
{
@@ -3223,7 +3346,7 @@
#endif
dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn);
- const int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
+ int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
/*
* Make a copy of the previous register state. If the instruction
@@ -3539,15 +3662,6 @@
break;
case OP_NEW_INSTANCE:
- /*
- * We can check for interface and abstract classes here, but we
- * can't reject them. We can ask the optimizer to replace the
- * instructions with a magic "always throw InstantiationError"
- * instruction. (Not enough bytes to sub in a method call.)
- *
- * TODO (xyz): check for abstract/interface, cause an
- * InstantiationError to be thrown.
- */
resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
if (resClass == NULL) {
const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
@@ -3558,6 +3672,14 @@
} else {
RegType uninitType;
+ /* can't create an instance of an interface or abstract class */
+ if (dvmIsAbstractClass(resClass) || dvmIsInterfaceClass(resClass)) {
+ LOG_VFY("VFY: new-instance on interface or abstract class %s\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_INSTANTIATION;
+ break;
+ }
+
/* add resolved class to uninit map if not already there */
int uidx = dvmSetUninitInstance(uninitMap, insnIdx, resClass);
assert(uidx >= 0);
@@ -5081,6 +5203,13 @@
kRegTypeInteger, kRegTypeInteger, true, &failure);
break;
+ /*
+ * This falls into the general category of "optimized" instructions,
+ * which don't generally appear during verification. Because it's
+ * inserted in the course of verification, we can expect to see it here.
+ */
+ case OP_THROW_VERIFICATION_ERROR:
+ break;
/*
* Verifying "quickened" instructions is tricky, because we have
@@ -5114,7 +5243,6 @@
* If the object reference was null, the field-get returns the "wildcard"
* type, which is acceptable for any operation.
*/
- case OP_THROW_VERIFICATION_ERROR:
case OP_EXECUTE_INLINE:
case OP_INVOKE_DIRECT_EMPTY:
case OP_IGET_QUICK:
@@ -5166,9 +5294,27 @@
}
if (!VERIFY_OK(failure)) {
- LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n",
- decInsn.opCode, insnIdx);
- goto bail;
+ if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing) {
+ /* immediate failure, reject class */
+ LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n",
+ decInsn.opCode, insnIdx);
+ goto bail;
+ } else {
+ /* replace opcode and continue on */
+ LOGD("VFY: replacing opcode 0x%02x at 0x%04x\n",
+ decInsn.opCode, insnIdx);
+ if (!replaceFailingInstruction(meth, insnFlags, insnIdx, failure)) {
+ LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n",
+ decInsn.opCode, insnIdx);
+ goto bail;
+ }
+ /* IMPORTANT: meth->insns may have been changed */
+ insns = meth->insns + insnIdx;
+
+ /* continue on as if we just handled a throw-verification-error */
+ failure = VERIFY_ERROR_NONE;
+ nextFlags = kInstrCanThrow;
+ }
}
/*
@@ -5335,6 +5481,7 @@
return result;
}
+
/*
* callback function used in dumpRegTypes to print local vars
* valid at a given address.
diff --git a/vm/analysis/CodeVerify.h b/vm/analysis/CodeVerify.h
index 55fe41c..1b93655 100644
--- a/vm/analysis/CodeVerify.h
+++ b/vm/analysis/CodeVerify.h
@@ -259,7 +259,7 @@
* Verify bytecode in "meth". "insnFlags" should be populated with
* instruction widths and "in try" flags.
*/
-bool dvmVerifyCodeFlow(const Method* meth, InsnFlags* insnFlags,
+bool dvmVerifyCodeFlow(Method* meth, InsnFlags* insnFlags,
UninitInstanceMap* uninitMap);
#endif /*_DALVIK_CODEVERIFY*/
diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c
index d420043..162ba9a 100644
--- a/vm/analysis/DexOptimize.c
+++ b/vm/analysis/DexOptimize.c
@@ -611,8 +611,8 @@
}
}
- updateChecksum(dexAddr, dexLength,
- (DexHeader*) pDvmDex->pHeader);
+ DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
+ updateChecksum(dexAddr, dexLength, pHeader);
dvmDexFileFree(pDvmDex);
}
@@ -1713,7 +1713,7 @@
LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
referrer->descriptor, resClass->descriptor);
if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS;
+ *pFailure = VERIFY_ERROR_ACCESS_CLASS;
return NULL;
}
@@ -1745,8 +1745,7 @@
if (resClass == NULL) {
//dvmClearOptException(dvmThreadSelf());
assert(!dvmCheckException(dvmThreadSelf()));
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_NO_FIELD;
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
return NULL;
}
@@ -1777,7 +1776,7 @@
referrer->descriptor, resField->field.clazz->descriptor,
resField->field.name);
if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS;
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
return NULL;
}
@@ -1811,8 +1810,7 @@
if (resClass == NULL) {
//dvmClearOptException(dvmThreadSelf());
assert(!dvmCheckException(dvmThreadSelf()));
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_NO_FIELD;
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
return NULL;
}
@@ -1846,7 +1844,7 @@
referrer->descriptor, resField->field.clazz->descriptor,
resField->field.name);
if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS;
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
return NULL;
}
@@ -1919,11 +1917,13 @@
resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
if (resClass == NULL) {
- /* can't find the class that the method is a part of */
+ /*
+ * Can't find the class that the method is a part of, or don't
+ * have permission to access the class.
+ */
LOGV("DexOpt: can't find called method's class (?.%s)\n",
dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
- if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_NO_METHOD;
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
return NULL;
}
if (dvmIsInterfaceClass(resClass)) {
@@ -1999,7 +1999,7 @@
free(desc);
}
if (pFailure != NULL)
- *pFailure = VERIFY_ERROR_ACCESS;
+ *pFailure = VERIFY_ERROR_ACCESS_METHOD;
return NULL;
}
diff --git a/vm/analysis/DexOptimize.h b/vm/analysis/DexOptimize.h
index c07dea1..8ae2af5 100644
--- a/vm/analysis/DexOptimize.h
+++ b/vm/analysis/DexOptimize.h
@@ -43,11 +43,14 @@
VERIFY_ERROR_NONE = 0, /* no error; must be zero */
VERIFY_ERROR_GENERIC, /* VerifyError */
- VERIFY_ERROR_NO_CLASS, /* NoClassDefFoundError */
- VERIFY_ERROR_NO_METHOD, /* NoSuchMethodError */
- VERIFY_ERROR_NO_FIELD, /* NoSuchFieldError */
- VERIFY_ERROR_CLASS_CHANGE, /* IncompatibleClassChangeError */
- VERIFY_ERROR_ACCESS, /* IllegalAccessError */
+ VERIFY_ERROR_NO_CLASS, /* NoClassDefFoundError (ref=class) */
+ VERIFY_ERROR_NO_FIELD, /* NoSuchFieldError (ref=field) */
+ VERIFY_ERROR_NO_METHOD, /* NoSuchMethodError (ref=method) */
+ VERIFY_ERROR_ACCESS_CLASS, /* IllegalAccessError (ref=class) */
+ VERIFY_ERROR_ACCESS_FIELD, /* IllegalAccessError (ref=field) */
+ VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError (ref=method) */
+ VERIFY_ERROR_CLASS_CHANGE, /* IncompatibleClassChangeError (ref=class) */
+ VERIFY_ERROR_INSTANTIATION, /* InstantiationError (ref=class) */
} VerifyError;
#define VERIFY_OK(_failure) ((_failure) == VERIFY_ERROR_NONE)
diff --git a/vm/interp/Interp.c b/vm/interp/Interp.c
index 25a2145..de3d676 100644
--- a/vm/interp/Interp.c
+++ b/vm/interp/Interp.c
@@ -635,6 +635,95 @@
}
+
+/*
+ * Helpers for dvmThrowVerificationError().
+ *
+ * Each returns a newly-allocated string.
+ */
+#define kThrowShow_accessFromClass 1
+static char* classNameFromIndex(const Method* method, int ref, int flags)
+{
+ static const int kBufLen = 256;
+ const DvmDex* pDvmDex = method->clazz->pDvmDex;
+ const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, ref);
+ char* dotClassName = dvmDescriptorToDot(className);
+ if (flags == 0)
+ return dotClassName;
+
+ char* result = (char*) malloc(kBufLen);
+
+ if ((flags & kThrowShow_accessFromClass) != 0) {
+ char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+ snprintf(result, kBufLen, "tried to access class %s from class %s",
+ dotClassName, dotFromName);
+ free(dotFromName);
+ } else {
+ assert(false); // should've been caught above
+ result[0] = '\0';
+ }
+
+ free(dotClassName);
+ return result;
+}
+static char* fieldNameFromIndex(const Method* method, int ref, int flags)
+{
+ static const int kBufLen = 256;
+ const DvmDex* pDvmDex = method->clazz->pDvmDex;
+ const DexFieldId* pFieldId;
+ const char* className;
+ const char* fieldName;
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+ className = dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->classIdx);
+ fieldName = dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);
+
+ char* dotName = dvmDescriptorToDot(className);
+ char* result = (char*) malloc(kBufLen);
+
+ if ((flags & kThrowShow_accessFromClass) != 0) {
+ char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+ snprintf(result, kBufLen, "tried to access field %s.%s from class %s",
+ dotName, fieldName, dotFromName);
+ free(dotFromName);
+ } else {
+ snprintf(result, kBufLen, "%s.%s", dotName, fieldName);
+ }
+
+ free(dotName);
+ return result;
+}
+static char* methodNameFromIndex(const Method* method, int ref, int flags)
+{
+ static const int kBufLen = 384;
+ const DvmDex* pDvmDex = method->clazz->pDvmDex;
+ const DexMethodId* pMethodId;
+ const char* className;
+ const char* methodName;
+
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+ className = dexStringByTypeIdx(pDvmDex->pDexFile, pMethodId->classIdx);
+ methodName = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+ char* dotName = dvmDescriptorToDot(className);
+ char* result = (char*) malloc(kBufLen);
+
+ if ((flags & kThrowShow_accessFromClass) != 0) {
+ char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ snprintf(result, kBufLen,
+ "tried to access method %s.%s:%s from class %s",
+ dotName, methodName, desc, dotFromName);
+ free(dotFromName);
+ free(desc);
+ } else {
+ snprintf(result, kBufLen, "%s.%s", dotName, methodName);
+ }
+
+ free(dotName);
+ return result;
+}
+
/*
* Throw an exception for a problem identified by the verifier.
*
@@ -645,10 +734,59 @@
* meaning of "ref" is kind-specific; it's usually an index to a
* class, field, or method reference.
*/
-void dvmThrowVerificationError(const DvmDex* pDvmDex, int kind, int ref)
+void dvmThrowVerificationError(const Method* method, int kind, int ref)
{
- // TODO
- dvmThrowException("Ljava/lang/VerifyError;", NULL);
+ const char* exceptionName = "Ljava/lang/VerifyError;";
+ char* msg = NULL;
+
+ switch ((VerifyError) kind) {
+ case VERIFY_ERROR_NO_CLASS:
+ exceptionName = "Ljava/lang/NoClassDefFoundError;";
+ msg = classNameFromIndex(method, ref, 0);
+ break;
+ case VERIFY_ERROR_NO_FIELD:
+ exceptionName = "Ljava/lang/NoSuchFieldError;";
+ msg = fieldNameFromIndex(method, ref, 0);
+ break;
+ case VERIFY_ERROR_NO_METHOD:
+ exceptionName = "Ljava/lang/NoSuchMethodError;";
+ msg = methodNameFromIndex(method, ref, 0);
+ break;
+ case VERIFY_ERROR_ACCESS_CLASS:
+ exceptionName = "Ljava/lang/IllegalAccessError;";
+ msg = classNameFromIndex(method, ref, kThrowShow_accessFromClass);
+ break;
+ case VERIFY_ERROR_ACCESS_FIELD:
+ exceptionName = "Ljava/lang/IllegalAccessError;";
+ msg = fieldNameFromIndex(method, ref, kThrowShow_accessFromClass);
+ break;
+ case VERIFY_ERROR_ACCESS_METHOD:
+ exceptionName = "Ljava/lang/IllegalAccessError;";
+ msg = methodNameFromIndex(method, ref, kThrowShow_accessFromClass);
+ break;
+ case VERIFY_ERROR_CLASS_CHANGE:
+ exceptionName = "Ljava/lang/IncompatibleClassChangeError;";
+ msg = classNameFromIndex(method, ref, 0);
+ break;
+ case VERIFY_ERROR_INSTANTIATION:
+ exceptionName = "Ljava/lang/InstantiationError;";
+ msg = classNameFromIndex(method, ref, 0);
+ break;
+
+ case VERIFY_ERROR_GENERIC:
+ /* generic VerifyError; use default exception, no message */
+ break;
+ case VERIFY_ERROR_NONE:
+ /* should never happen; use default exception */
+ assert(false);
+ msg = strdup("weird - no error specified");
+ break;
+
+ /* no default clause -- want warning if enum updated */
+ }
+
+ dvmThrowException(exceptionName, msg);
+ free(msg);
}
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
index 9997bcb..cd4c7ec 100644
--- a/vm/interp/Interp.h
+++ b/vm/interp/Interp.h
@@ -27,8 +27,11 @@
/*
* Throw an exception for a problem detected by the verifier.
+ *
+ * This is called from the handler for the throw-verification-error
+ * instruction. "method" is the method currently being executed.
*/
-void dvmThrowVerificationError(const DvmDex* pDvmDex, int kind, int ref);
+void dvmThrowVerificationError(const Method* method, int kind, int ref);
/*
* Breakpoint optimization table.
diff --git a/vm/mterp/armv5te/OP_NEW_INSTANCE.S b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
index d1d2df6..639d9c6 100644
--- a/vm/mterp/armv5te/OP_NEW_INSTANCE.S
+++ b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
@@ -22,16 +22,13 @@
cmp r1, #CLASS_INITIALIZED @ has class been initialized?
bne .L${opcode}_needinit @ no, init class now
.L${opcode}_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 .L${opcode}_finish @ concrete class, continue
- b .L${opcode}_abstract @ fail
+ bl dvmAllocObject @ r0<- new object
+ b .L${opcode}_finish @ continue
%break
.balign 32 @ minimize cache lines
-.L${opcode}_finish: @ r0=class
- bl dvmAllocObject @ r0<- new object
+.L${opcode}_finish: @ r0=new object
mov r3, rINST, lsr #8 @ r3<- AA
cmp r0, #0 @ failed?
beq common_exceptionThrown @ yes, handle the exception
@@ -67,18 +64,6 @@
bne .L${opcode}_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
- */
-.L${opcode}_abstract:
- ldr r1, [r0, #offClassObject_descriptor]
- ldr r0, .LstrInstantiationErrorPtr
- bl dvmThrowExceptionWithClassMessage
- b common_exceptionThrown
-
.LstrInstantiationErrorPtr:
.word .LstrInstantiationError
diff --git a/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
index cf26a28..0ed928b 100644
--- a/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
+++ b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
@@ -5,8 +5,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
diff --git a/vm/mterp/c/OP_NEW_INSTANCE.c b/vm/mterp/c/OP_NEW_INSTANCE.c
index 8096579..ce04286 100644
--- a/vm/mterp/c/OP_NEW_INSTANCE.c
+++ b/vm/mterp/c/OP_NEW_INSTANCE.c
@@ -19,21 +19,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();
diff --git a/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c
index 58b0f4a..85cc8fb 100644
--- a/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c
+++ b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c
@@ -1,6 +1,7 @@
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/config-x86 b/vm/mterp/config-x86
index b7139ca..d315a1c 100644
--- a/vm/mterp/config-x86
+++ b/vm/mterp/config-x86
@@ -33,6 +33,7 @@
# opcode list; argument to op-start is default directory
op-start x86
+ op OP_THROW_VERIFICATION_ERROR c
op-end
# arch-specific entry point to interpreter
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.
diff --git a/vm/mterp/x86/OP_NEW_INSTANCE.S b/vm/mterp/x86/OP_NEW_INSTANCE.S
index da951f7..d56d55c 100644
--- a/vm/mterp/x86/OP_NEW_INSTANCE.S
+++ b/vm/mterp/x86/OP_NEW_INSTANCE.S
@@ -27,6 +27,7 @@
%break
.L${opcode}_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 .L${opcode}_abstract
@@ -77,6 +78,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/oo/Class.c b/vm/oo/Class.c
index 59a6365..6369d9b 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -1962,9 +1962,11 @@
int directMethodCount = clazz->directMethodCount;
clazz->directMethods = NULL;
clazz->directMethodCount = -1;
+ dvmLinearReadWrite(clazz->classLoader, directMethods);
for (i = 0; i < directMethodCount; i++) {
freeMethodInnards(&directMethods[i]);
}
+ dvmLinearReadOnly(clazz->classLoader, directMethods);
dvmLinearFree(clazz->classLoader, directMethods);
}
if (clazz->virtualMethods != NULL) {
@@ -1972,9 +1974,11 @@
int virtualMethodCount = clazz->virtualMethodCount;
clazz->virtualMethodCount = -1;
clazz->virtualMethods = NULL;
+ dvmLinearReadWrite(clazz->classLoader, virtualMethods);
for (i = 0; i < virtualMethodCount; i++) {
freeMethodInnards(&virtualMethods[i]);
}
+ dvmLinearReadOnly(clazz->classLoader, virtualMethods);
dvmLinearFree(clazz->classLoader, virtualMethods);
}
@@ -2023,6 +2027,14 @@
dvmFreeRegisterMap((RegisterMap*) pMap);
meth->registerMap = NULL;
}
+
+ /*
+ * We may have copied the instructions.
+ */
+ if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+ DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+ dvmLinearFree(meth->clazz->classLoader, methodDexCode);
+ }
}
/*
@@ -2099,6 +2111,55 @@
}
/*
+ * We usually map bytecode directly out of the DEX file, which is mapped
+ * shared read-only. If we want to be able to modify it, we have to make
+ * a new copy.
+ *
+ * Once copied, the code will be in the LinearAlloc region, which may be
+ * marked read-only.
+ *
+ * The bytecode instructions are embedded inside a DexCode structure, so we
+ * need to copy all of that. (The dvmGetMethodCode function backs up the
+ * instruction pointer to find the start of the DexCode.)
+ */
+void dvmMakeCodeReadWrite(Method* meth)
+{
+ DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+
+ if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+ dvmLinearReadWrite(meth->clazz->classLoader, methodDexCode);
+ return;
+ }
+
+ assert(!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth));
+
+ size_t dexCodeSize = dexGetDexCodeSize(methodDexCode);
+ LOGD("Making a copy of %s.%s code (%d bytes)\n",
+ meth->clazz->descriptor, meth->name, dexCodeSize);
+
+ DexCode* newCode =
+ (DexCode*) dvmLinearAlloc(meth->clazz->classLoader, dexCodeSize);
+ memcpy(newCode, methodDexCode, dexCodeSize);
+
+ meth->insns = newCode->insns;
+ SET_METHOD_FLAG(meth, METHOD_ISWRITABLE);
+}
+
+/*
+ * Mark the bytecode read-only.
+ *
+ * If the contents of the DexCode haven't actually changed, we could revert
+ * to the original shared page.
+ */
+void dvmMakeCodeReadOnly(Method* meth)
+{
+ DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+ LOGV("+++ marking %p read-only\n", methodDexCode);
+ dvmLinearReadOnly(meth->clazz->classLoader, methodDexCode);
+}
+
+
+/*
* jniArgInfo (32-bit int) layout:
* SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
*
diff --git a/vm/oo/Class.h b/vm/oo/Class.h
index 349df3f..59e0da4 100644
--- a/vm/oo/Class.h
+++ b/vm/oo/Class.h
@@ -149,8 +149,19 @@
*/
void dvmSetRegisterMap(Method* method, const RegisterMap* pMap);
-/* during DEX optimizing, add an extra DEX to the bootstrap class path */
-INLINE void dvmSetBootPathExtraDex(DvmDex* pDvmDex);
+/*
+ * Make a method's DexCode (which includes the bytecode) read-write or
+ * read-only. The conversion to read-write may involve making a new copy
+ * of the DexCode, and in normal operation the read-only state is not
+ * actually enforced.
+ */
+void dvmMakeCodeReadWrite(Method* meth);
+void dvmMakeCodeReadOnly(Method* meth);
+
+/*
+ * During DEX optimizing, add an extra DEX to the bootstrap class path.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex);
/*
* Debugging.
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
index 18fbb36..0bf2728 100644
--- a/vm/oo/Object.h
+++ b/vm/oo/Object.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Declaration of the fundamental Object type and refinements thereof, plus
* some functions for manipulating them.
@@ -93,6 +94,44 @@
#define EXPECTED_FILE_FLAGS \
(ACC_CLASS_MASK | CLASS_ISPREVERIFIED | CLASS_ISOPTIMIZED)
+/*
+ * Get/set class flags.
+ */
+#define SET_CLASS_FLAG(clazz, flag) \
+ do { (clazz)->accessFlags |= (flag); } while (0)
+
+#define CLEAR_CLASS_FLAG(clazz, flag) \
+ do { (clazz)->accessFlags &= ~(flag); } while (0)
+
+#define IS_CLASS_FLAG_SET(clazz, flag) \
+ (((clazz)->accessFlags & (flag)) != 0)
+
+#define GET_CLASS_FLAG_GROUP(clazz, flags) \
+ ((u4)((clazz)->accessFlags & (flags)))
+
+/*
+ * Use the top 16 bits of the access flags field for other method flags.
+ * Code should use the *METHOD_FLAG*() macros to set/get these flags.
+ */
+typedef enum MethodFlags {
+ METHOD_ISWRITABLE = (1<<31), // the method's code is writable
+} MethodFlags;
+
+/*
+ * Get/set method flags.
+ */
+#define SET_METHOD_FLAG(method, flag) \
+ do { (method)->accessFlags |= (flag); } while (0)
+
+#define CLEAR_METHOD_FLAG(method, flag) \
+ do { (method)->accessFlags &= ~(flag); } while (0)
+
+#define IS_METHOD_FLAG_SET(method, flag) \
+ (((method)->accessFlags & (flag)) != 0)
+
+#define GET_METHOD_FLAG_GROUP(method, flags) \
+ ((u4)((method)->accessFlags & (flags)))
+
/* current state of the class, increasing as we progress */
typedef enum ClassStatus {
CLASS_ERROR = -1,
@@ -134,14 +173,6 @@
#define PRIM_TYPE_TO_LETTER "ZCFDBSIJV" /* must match order in enum */
/*
- * This defines the amount of space we leave for field slots in the
- * java.lang.Class definition. If we alter the class to have more than
- * this many fields, the VM will abort at startup.
- */
-#define CLASS_FIELD_SLOTS 4
-
-
-/*
* Used for iftable in ClassObject.
*/
typedef struct InterfaceEntry {
@@ -185,21 +216,6 @@
do { (obj)->clazz = (clazz_); DVM_LOCK_INIT(&(obj)->lock); } while (0)
/*
- * Get/set class flags.
- */
-#define SET_CLASS_FLAG(clazz, flag) \
- do { (clazz)->accessFlags |= (flag); } while (0)
-
-#define CLEAR_CLASS_FLAG(clazz, flag) \
- do { (clazz)->accessFlags &= ~(flag); } while (0)
-
-#define IS_CLASS_FLAG_SET(clazz, flag) \
- (((clazz)->accessFlags & (flag)) != 0)
-
-#define GET_CLASS_FLAG_GROUP(clazz, flags) \
- ((u4)((clazz)->accessFlags & (flags)))
-
-/*
* Data objects have an Object header followed by their instance data.
*/
struct DataObject {
@@ -262,6 +278,13 @@
};
/*
+ * This defines the amount of space we leave for field slots in the
+ * java.lang.Class definition. If we alter the class to have more than
+ * this many fields, the VM will abort at startup.
+ */
+#define CLASS_FIELD_SLOTS 4
+
+/*
* Class objects have many additional fields. This is used for both
* classes and interfaces, including synthesized classes (arrays and
* primitive types).