Support for unresolved types in new-instance during verification.
Also, ensure that classes that don't load are erroneous, warn early
about exceptions left on a thread by the verifier/compiler, factor out
slowpath checks for the compiler and fix the slowpath selector for
const-class.
This change causes more dex cache misses at runtime (more slowpath
execution). It also requires a "mm clean-oat".
Change-Id: I014b49ebdd7d8f7dd2e39cc0958fc0b708d58c4c
diff --git a/src/class_linker.cc b/src/class_linker.cc
index f49f546..8f4029a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1039,6 +1039,7 @@
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
CHECK(self->IsExceptionPending());
+ klass->SetStatus(Class::kStatusError);
lock.NotifyAll();
return NULL;
}
@@ -1048,6 +1049,7 @@
if (!LinkClass(klass)) {
// Linking failed.
CHECK(self->IsExceptionPending());
+ klass->SetStatus(Class::kStatusError);
lock.NotifyAll();
return NULL;
}
diff --git a/src/compiler.cc b/src/compiler.cc
index cbfd35c..ba61b4d 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -333,6 +333,9 @@
// TODO: this fails if we have an abstract method defined in more than one input dex file.
CHECK(compiled_invoke_stubs_.find(method) == compiled_invoke_stubs_.end()) << PrettyMethod(method);
compiled_invoke_stubs_[method] = compiled_invoke_stub;
+
+ Thread* self = Thread::Current();
+ CHECK(!self->IsExceptionPending()) << PrettyMethod(method);
}
const CompiledMethod* Compiler::GetCompiledMethod(const Method* method) const {
diff --git a/src/compiler.h b/src/compiler.h
index 0fc68df..55bab59 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -55,6 +55,21 @@
const CompiledMethod* GetCompiledMethod(const Method* method) const;
const CompiledInvokeStub* GetCompiledInvokeStub(const Method* method) const;
+ // Callbacks from OAT/ART compiler to see what runtime checks must be generated
+ bool CanAssumeTypeIsPresentInDexCache(const Method* referrer, uint32_t type_idx) const {
+ return IsImage() && referrer->GetDexCacheResolvedTypes()->Get(type_idx) != NULL;
+ }
+ bool CanAssumeStringIsPresentInDexCache(const Method* referrer, uint32_t string_idx) const {
+ return IsImage() && referrer->GetDexCacheStrings()->Get(string_idx) != NULL;
+ }
+ bool CanAccessTypeWithoutChecks(const Method* referrer, uint32_t type_idx) const {
+ Class* resolved_class = referrer->GetDexCacheResolvedTypes()->Get(type_idx);
+ // We should never ask whether a type needs access checks to raise a verification error,
+ // all other cases where this following test could fail should have been rewritten by the
+ // verifier to verification errors.
+ DCHECK(resolved_class == NULL || referrer->GetDeclaringClass()->CanAccess(resolved_class));
+ return resolved_class != NULL;
+ }
private:
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 7ea7d69..4326a9e 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -46,10 +46,19 @@
RegLocation rlSrc)
{
oatFlushAllRegs(cUnit); /* Everything to home location */
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pAllocArrayFromCode), rLR);
+ uint32_t type_idx = mir->dalvikInsn.vC;
+ if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
+ loadWordDisp(cUnit, rSELF,
+ OFFSETOF_MEMBER(Thread, pAllocArrayFromCode), rLR);
+ } else {
+ UNIMPLEMENTED(WARNING) << "Need to check access of '"
+ << PrettyMethod(cUnit->method)
+ << "' to unresolved type " << type_idx;
+ loadWordDisp(cUnit, rSELF,
+ OFFSETOF_MEMBER(Thread, pAllocArrayFromCode), rLR);
+ }
loadCurrMethodDirect(cUnit, r1); // arg1 <- Method*
- loadConstant(cUnit, r0, mir->dalvikInsn.vC); // arg0 <- type_id
+ loadConstant(cUnit, r0, type_idx); // arg0 <- type_id
loadValueDirectFixed(cUnit, rlSrc, r2); // arg2 <- count
callRuntimeHelper(cUnit, rLR);
RegLocation rlResult = oatGetReturn(cUnit);
@@ -70,6 +79,10 @@
oatFlushAllRegs(cUnit); /* Everything to home location */
loadWordDisp(cUnit, rSELF,
OFFSETOF_MEMBER(Thread, pCheckAndAllocArrayFromCode), rLR);
+ if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, typeId)) {
+ UNIMPLEMENTED(WARNING) << "Need to check access of '" << PrettyMethod(cUnit->method)
+ << "' to unresolved type " << typeId;
+ }
loadCurrMethodDirect(cUnit, r1); // arg1 <- Method*
loadConstant(cUnit, r0, typeId); // arg0 <- type_id
loadConstant(cUnit, r2, elems); // arg2 <- count
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index 7098188..4e3888d 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -442,7 +442,7 @@
loadWordDisp(cUnit, rSELF,
OFFSETOF_MEMBER(Thread, pFindInstanceFieldFromCode), rLR);
loadConstant(cUnit, r0, fieldIdx);
- callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
+ callRuntimeHelper(cUnit, rLR); // FindInstanceFieldFromCoderesolveTypeFromCode(idx, method)
ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
target->defMask = ENCODE_ALL;
if (!EXERCISE_SLOWEST_FIELD_PATH) {
@@ -606,42 +606,46 @@
STATIC void genConstClass(CompilationUnit* cUnit, MIR* mir,
RegLocation rlDest, RegLocation rlSrc)
{
- art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
- Get(mir->dalvikInsn.vB);
+ uint32_t type_idx = mir->dalvikInsn.vB;
int mReg = loadCurrMethod(cUnit);
int resReg = oatAllocTemp(cUnit);
RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
- loadWordDisp(cUnit, mReg, Method::DexCacheResolvedTypesOffset().Int32Value(),
- resReg);
- loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
- (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
- if (SLOW_TYPE_PATH || (classPtr == NULL)) {
- // Fast path, we're done - just store result
- storeValue(cUnit, rlDest, rlResult);
+ if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
+ // Check we have access to type_idx and if not throw IllegalAccessError
+ UNIMPLEMENTED(FATAL);
} else {
- // Slow path. Must test at runtime
- oatFlushAllRegs(cUnit);
- ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg,
- 0);
- // Resolved, store and hop over following code
- storeValue(cUnit, rlDest, rlResult);
- ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
- // TUNING: move slow path to end & remove unconditional branch
- ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
- target1->defMask = ENCODE_ALL;
- // Call out to helper, which will return resolved type in r0
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
- genRegCopy(cUnit, r1, mReg);
- loadConstant(cUnit, r0, mir->dalvikInsn.vB);
- callRuntimeHelper(cUnit, rLR);
- RegLocation rlResult = oatGetReturn(cUnit);
- storeValue(cUnit, rlDest, rlResult);
- // Rejoin code paths
- ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
- target2->defMask = ENCODE_ALL;
- branch1->generic.target = (LIR*)target1;
- branch2->generic.target = (LIR*)target2;
+ // We're don't need access checks, load type from dex cache
+ int32_t dex_cache_offset = Method::DexCacheResolvedTypesOffset().Int32Value();
+ loadWordDisp(cUnit, mReg, dex_cache_offset, resReg);
+ int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
+ loadWordDisp(cUnit, resReg, offset_of_type, rlResult.lowReg);
+ if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->method, type_idx) ||
+ SLOW_TYPE_PATH) {
+ // Slow path, at runtime test if the type is null and if so initialize
+ oatFlushAllRegs(cUnit);
+ ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, rlResult.lowReg, 0);
+ // Resolved, store and hop over following code
+ storeValue(cUnit, rlDest, rlResult);
+ ArmLIR* branch2 = genUnconditionalBranch(cUnit,0);
+ // TUNING: move slow path to end & remove unconditional branch
+ ArmLIR* target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target1->defMask = ENCODE_ALL;
+ // Call out to helper, which will return resolved type in r0
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
+ genRegCopy(cUnit, r1, mReg);
+ loadConstant(cUnit, r0, type_idx);
+ callRuntimeHelper(cUnit, rLR);
+ RegLocation rlResult = oatGetReturn(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ // Rejoin code paths
+ ArmLIR* target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target2->defMask = ENCODE_ALL;
+ branch1->generic.target = (LIR*)target1;
+ branch2->generic.target = (LIR*)target2;
+ } else {
+ // Fast path, we're done - just store result
+ storeValue(cUnit, rlDest, rlResult);
+ }
}
}
@@ -649,20 +653,19 @@
RegLocation rlDest, RegLocation rlSrc)
{
/* NOTE: Most strings should be available at compile time */
- const art::String* str = cUnit->method->GetDexCacheStrings()->
- Get(mir->dalvikInsn.vB);
- if (SLOW_STRING_PATH || (str == NULL) || !cUnit->compiler->IsImage()) {
+ uint32_t string_idx = mir->dalvikInsn.vB;
+ int32_t offset_of_string = Array::DataOffset().Int32Value() + (sizeof(String*) * string_idx);
+ if (!cUnit->compiler->CanAssumeStringIsPresentInDexCache(cUnit->method, string_idx) ||
+ SLOW_STRING_PATH) {
+ // slow path, resolve string if not in dex cache
oatFlushAllRegs(cUnit);
oatLockCallTemps(cUnit); // Using explicit registers
loadCurrMethodDirect(cUnit, r2);
- loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(),
- r0);
+ loadWordDisp(cUnit, r2, Method::DexCacheStringsOffset().Int32Value(), r0);
// Might call out to helper, which will return resolved string in r0
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR);
- loadWordDisp(cUnit, r0, Array::DataOffset().Int32Value() +
- (sizeof(String*) * mir->dalvikInsn.vB), r0);
- loadConstant(cUnit, r1, mir->dalvikInsn.vB);
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pResolveStringFromCode), rLR);
+ loadWordDisp(cUnit, r0, offset_of_string, r0);
+ loadConstant(cUnit, r1, string_idx);
opRegImm(cUnit, kOpCmp, r0, 0); // Is resolved?
genBarrier(cUnit);
// For testing, always force through helper
@@ -677,10 +680,8 @@
int mReg = loadCurrMethod(cUnit);
int resReg = oatAllocTemp(cUnit);
RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
- loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(),
- resReg);
- loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
- (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
+ loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(), resReg);
+ loadWordDisp(cUnit, resReg, offset_of_string, rlResult.lowReg);
storeValue(cUnit, rlDest, rlResult);
}
}
@@ -693,13 +694,17 @@
RegLocation rlDest)
{
oatFlushAllRegs(cUnit); /* Everything to home location */
- art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
- Get(mir->dalvikInsn.vB);
- loadWordDisp(cUnit, rSELF, (classPtr != NULL)
- ? OFFSETOF_MEMBER(Thread, pAllocObjectFromCode)
- : OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeSlowPath), rLR);
- loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
- loadConstant(cUnit, r0, mir->dalvikInsn.vB); // arg0 <- type_id
+ uint32_t type_idx = mir->dalvikInsn.vB;
+ // alloc will always check for resolution, do we also need to verify access because the
+ // verifier was unable to?
+ if (cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pAllocObjectFromCode), rLR);
+ } else {
+ loadWordDisp(cUnit, rSELF,
+ OFFSETOF_MEMBER(Thread, pAllocObjectFromCodeWithAccessCheck), rLR);
+ }
+ loadCurrMethodDirect(cUnit, r1); // arg1 <= Method*
+ loadConstant(cUnit, r0, type_idx); // arg0 <- type_idx
callRuntimeHelper(cUnit, rLR);
RegLocation rlResult = oatGetReturn(cUnit);
storeValue(cUnit, rlDest, rlResult);
@@ -708,8 +713,7 @@
void genThrow(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
{
oatFlushAllRegs(cUnit);
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pDeliverException), rLR);
loadValueDirectFixed(cUnit, rlSrc, r0); // Get exception object
callRuntimeHelper(cUnit, rLR); // art_deliver_exception(exception);
}
@@ -720,30 +724,33 @@
oatFlushAllRegs(cUnit);
// May generate a call - use explicit registers
oatLockCallTemps(cUnit);
- art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
- Get(mir->dalvikInsn.vC);
- int classReg = r2; // Fixed usage
+ uint32_t type_idx = mir->dalvikInsn.vC;
loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
- loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
- loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
- classReg);
- loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
- (sizeof(String*) * mir->dalvikInsn.vC), classReg);
- if (classPtr == NULL) {
- // Generate a runtime test
- ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
- // Not resolved
- // Call out to helper, which will return resolved type in r0
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
- loadConstant(cUnit, r0, mir->dalvikInsn.vC);
- callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
- genRegCopy(cUnit, r2, r0); // Align usage with fast path
- loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
- // Rejoin code paths
- ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
- hopTarget->defMask = ENCODE_ALL;
- hopBranch->generic.target = (LIR*)hopTarget;
+ loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
+ int classReg = r2; // r2 will hold the Class*
+ if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
+ // Check we have access to type_idx and if not throw IllegalAccessError
+ UNIMPLEMENTED(FATAL);
+ } else {
+ // Load dex cache entry into classReg (r2)
+ loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
+ int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
+ loadWordDisp(cUnit, classReg, offset_of_type, classReg);
+ if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->method, type_idx)) {
+ // Need to test presence of type in dex cache at runtime
+ ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
+ // Not resolved
+ // Call out to helper, which will return resolved type in r0
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
+ loadConstant(cUnit, r0, type_idx);
+ callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
+ genRegCopy(cUnit, r2, r0); // Align usage with fast path
+ loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
+ // Rejoin code paths
+ ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+ hopTarget->defMask = ENCODE_ALL;
+ hopBranch->generic.target = (LIR*)hopTarget;
+ }
}
/* r0 is ref, r2 is class. If ref==null, use directly as bool result */
ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
@@ -751,8 +758,7 @@
DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
/* r0 is ref, r1 is ref->clazz, r2 is class */
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
opRegReg(cUnit, kOpCmp, r1, r2); // Same?
genBarrier(cUnit);
genIT(cUnit, kArmCondEq, "EE"); // if-convert the test
@@ -774,39 +780,41 @@
oatFlushAllRegs(cUnit);
// May generate a call - use explicit registers
oatLockCallTemps(cUnit);
- art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
- Get(mir->dalvikInsn.vB);
- int classReg = r2; // Fixed usage
+ uint32_t type_idx = mir->dalvikInsn.vB;
loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
- loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
- classReg);
- loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
- (sizeof(String*) * mir->dalvikInsn.vB), classReg);
- if (classPtr == NULL) {
- // Generate a runtime test
- ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
- // Not resolved
- // Call out to helper, which will return resolved type in r0
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
- loadConstant(cUnit, r0, mir->dalvikInsn.vB);
- callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
- genRegCopy(cUnit, r2, r0); // Align usage with fast path
- // Rejoin code paths
- ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
- hopTarget->defMask = ENCODE_ALL;
- hopBranch->generic.target = (LIR*)hopTarget;
+ int classReg = r2; // r2 will hold the Class*
+ if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
+ // Check we have access to type_idx and if not throw IllegalAccessError
+ UNIMPLEMENTED(FATAL);
+ } else {
+ // Load dex cache entry into classReg (r2)
+ loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
+ int32_t offset_of_type = Array::DataOffset().Int32Value() + (sizeof(Class*) * type_idx);
+ loadWordDisp(cUnit, classReg, offset_of_type, classReg);
+ if (!cUnit->compiler->CanAssumeTypeIsPresentInDexCache(cUnit->method, type_idx)) {
+ // Need to test presence of type in dex cache at runtime
+ ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
+ // Not resolved
+ // Call out to helper, which will return resolved type in r0
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
+ loadConstant(cUnit, r0, type_idx);
+ callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
+ genRegCopy(cUnit, r2, r0); // Align usage with fast path
+ // Rejoin code paths
+ ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+ hopTarget->defMask = ENCODE_ALL;
+ hopBranch->generic.target = (LIR*)hopTarget;
+ }
}
- // At this point, r2 has class
- loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
+ // At this point, classReg (r2) has class
+ loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
/* Null is OK - continue */
ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
/* load object->clazz */
DCHECK_EQ(Object::ClassOffset().Int32Value(), 0);
loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
/* r1 now contains object->clazz */
- loadWordDisp(cUnit, rSELF,
- OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
+ loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
opRegReg(cUnit, kOpCmp, r1, r2);
ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
genRegCopy(cUnit, r0, r1);
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 79a455e..79f90e7 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -43,7 +43,7 @@
"Unresolved Reference",
"Uninitialized Reference",
"Uninitialized This Reference",
- "Unresolved And Uninitialized This Reference",
+ "Unresolved And Uninitialized Reference",
"Reference",
};
@@ -65,7 +65,7 @@
result = type_strings[type_];
if (IsReferenceTypes()) {
result += ": ";
- if (IsUnresolvedReference()) {
+ if (IsUnresolvedTypes()) {
result += PrettyDescriptor(GetDescriptor());
} else {
result += PrettyDescriptor(GetClass()->GetDescriptor());
@@ -370,15 +370,23 @@
entries_.push_back(entry);
return *entry;
} else {
+ // TODO: we assume unresolved, but we may be able to do better by validating whether the
+ // descriptor string is valid
// Unable to resolve so create unresolved register type
DCHECK(Thread::Current()->IsExceptionPending());
Thread::Current()->ClearException();
- String* string_descriptor =
- Runtime::Current()->GetInternTable()->InternStrong(descriptor.c_str());
- RegType* entry = new RegType(RegType::kRegTypeUnresolvedReference, string_descriptor, 0,
- entries_.size());
- entries_.push_back(entry);
- return *entry;
+ if (IsValidDescriptor(descriptor.c_str())) {
+ String* string_descriptor =
+ Runtime::Current()->GetInternTable()->InternStrong(descriptor.c_str());
+ RegType* entry = new RegType(RegType::kRegTypeUnresolvedReference, string_descriptor, 0,
+ entries_.size());
+ entries_.push_back(entry);
+ return *entry;
+ } else {
+ // The descriptor is broken return the unknown type as there's nothing sensible that
+ // could be done at runtime
+ return Unknown();
+ }
}
}
}
@@ -407,15 +415,58 @@
}
}
-const RegType& RegTypeCache::Uninitialized(Class* klass, uint32_t allocation_pc) {
- for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
- if (cur_entry->IsUninitializedReference() && cur_entry->GetAllocationPc() == allocation_pc &&
- cur_entry->GetClass() == klass) {
- return *cur_entry;
+const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
+ RegType* entry;
+ if (type.IsUnresolvedTypes()) {
+ String* descriptor = type.GetDescriptor();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsUnresolvedAndUninitializedReference() &&
+ cur_entry->GetAllocationPc() == allocation_pc &&
+ cur_entry->GetDescriptor() == descriptor) {
+ return *cur_entry;
+ }
}
+ entry = new RegType(RegType::kRegTypeUnresolvedAndUninitializedReference,
+ descriptor, allocation_pc, entries_.size());
+ } else {
+ Class* klass = type.GetClass();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsUninitializedReference() &&
+ cur_entry->GetAllocationPc() == allocation_pc &&
+ cur_entry->GetClass() == klass) {
+ return *cur_entry;
+ }
+ }
+ entry = new RegType(RegType::kRegTypeUninitializedReference,
+ klass, allocation_pc, entries_.size());
}
- RegType* entry = new RegType(RegType::kRegTypeUninitializedReference, klass, allocation_pc, entries_.size());
+ entries_.push_back(entry);
+ return *entry;
+}
+
+const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) {
+ RegType* entry;
+ if (uninit_type.IsUnresolvedTypes()) {
+ String* descriptor = uninit_type.GetDescriptor();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsUnresolvedReference() && cur_entry->GetDescriptor() == descriptor) {
+ return *cur_entry;
+ }
+ }
+ entry = new RegType(RegType::kRegTypeUnresolvedReference, descriptor, 0, entries_.size());
+ } else {
+ Class* klass = uninit_type.GetClass();
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
+ return *cur_entry;
+ }
+ }
+ entry = new RegType(RegType::kRegTypeReference, klass, 0, entries_.size());
+ }
entries_.push_back(entry);
return *entry;
}
@@ -462,6 +513,18 @@
return *entry;
}
+const RegType& RegTypeCache::GetComponentType(const RegType& array, const ClassLoader* loader) {
+ CHECK(array.IsArrayClass());
+ if (array.IsUnresolvedTypes()) {
+ std::string descriptor = array.GetDescriptor()->ToModifiedUtf8();
+ std::string component = descriptor.substr(1, descriptor.size() - 1);
+ return FromDescriptor(loader, component);
+ } else {
+ return FromClass(array.GetClass()->GetComponentType());
+ }
+}
+
+
bool RegisterLine::CheckConstructorReturn() const {
for (size_t i = 0; i < num_regs_; i++) {
if (GetRegisterType(i).IsUninitializedThisReference()) {
@@ -561,20 +624,16 @@
}
void RegisterLine::MarkRefsAsInitialized(const RegType& uninit_type) {
- Class* klass = uninit_type.GetClass();
- if (klass == NULL) {
- verifier_->Fail(VERIFY_ERROR_GENERIC) << "Unable to find type=" << uninit_type;
- } else {
- const RegType& init_type = verifier_->GetRegTypeCache()->FromClass(klass);
- size_t changed = 0;
- for (size_t i = 0; i < num_regs_; i++) {
- if (GetRegisterType(i).Equals(uninit_type)) {
- line_[i] = init_type.GetId();
- changed++;
- }
+ DCHECK(uninit_type.IsUninitializedTypes());
+ const RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
+ size_t changed = 0;
+ for (size_t i = 0; i < num_regs_; i++) {
+ if (GetRegisterType(i).Equals(uninit_type)) {
+ line_[i] = init_type.GetId();
+ changed++;
}
- DCHECK_GT(changed, 0u);
}
+ DCHECK_GT(changed, 0u);
}
void RegisterLine::MarkUninitRefsAsInvalid(const RegType& uninit_type) {
@@ -895,8 +954,9 @@
<< verifier.info_messages_.str() << Dumpable<DexVerifier>(verifier);
}
-DexVerifier::DexVerifier(Method* method) : java_lang_throwable_(NULL), work_insn_idx_(-1),
- method_(method), failure_(VERIFY_ERROR_NONE),
+DexVerifier::DexVerifier(Method* method) : work_insn_idx_(-1), method_(method),
+ failure_(VERIFY_ERROR_NONE),
+
new_instance_count_(0), monitor_enter_count_(0) {
const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -994,15 +1054,26 @@
/* Iterate over each of the handlers to verify target addresses. */
const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item_, 0);
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
for (uint32_t idx = 0; idx < handlers_size; idx++) {
DexFile::CatchHandlerIterator iterator(handlers_ptr);
for (; !iterator.HasNext(); iterator.Next()) {
- uint32_t dex_pc= iterator.Get().address_;
+ const DexFile::CatchHandlerItem& handler = iterator.Get();
+ uint32_t dex_pc= handler.address_;
if (!insn_flags_[dex_pc].IsOpcode()) {
Fail(VERIFY_ERROR_GENERIC) << "exception handler starts at bad address (" << dex_pc << ")";
return false;
}
insn_flags_[dex_pc].SetBranchTarget();
+ // Ensure exception types are resolved so that they don't need resolution to be delivered,
+ // unresolved exception types will be ignored by exception delivery
+ if (handler.type_idx_ != DexFile::kDexNoIndex) {
+ Class* exception_type = linker->ResolveType(handler.type_idx_, method_);
+ if (exception_type == NULL) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ Thread::Current()->ClearException();
+ }
+ }
}
handlers_ptr = iterator.GetData();
}
@@ -1814,12 +1885,8 @@
* all exception handlers need to have one of these). We verify that as part of extracting the
* exception type from the catch block list.
*/
- Class* res_class = GetCaughtExceptionType();
- if (res_class == NULL) {
- DCHECK(failure_ != VERIFY_ERROR_NONE);
- } else {
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class));
- }
+ const RegType& res_type = GetCaughtExceptionType();
+ work_line_->SetRegisterType(dec_insn.vA_, res_type);
break;
}
case Instruction::RETURN_VOID:
@@ -1911,17 +1978,12 @@
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.JavaLangString());
break;
case Instruction::CONST_CLASS: {
- /* make sure we can resolve the class; access check is important */
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve const-class " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else {
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.JavaLangClass());
- }
+ // Get type from instruction if unresolved then we need an access check
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
+ // Register holds class, ie its type is class, but on error we keep it Unknown
+ work_line_->SetRegisterType(dec_insn.vA_,
+ res_type.IsUnknown() ? res_type : reg_types_.JavaLangClass());
break;
}
case Instruction::MONITOR_ENTER:
@@ -1952,58 +2014,39 @@
work_line_->PopMonitor(dec_insn.vA_);
break;
- case Instruction::CHECK_CAST: {
- /*
- * If this instruction succeeds, we will promote register vA to
- * the type in vB. (This could be a demotion -- not expected, so
- * we don't try to address it.)
- *
- * If it fails, an exception is thrown, which we deal with later
- * by ignoring the update to dec_insn.vA_ when branching to a handler.
- */
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve check-cast " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else {
- const RegType& orig_type = work_line_->GetRegisterType(dec_insn.vA_);
- if (!orig_type.IsReferenceTypes()) {
- Fail(VERIFY_ERROR_GENERIC) << "check-cast on non-reference in v" << dec_insn.vA_;
- } else {
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class));
- }
- }
- break;
- }
+ case Instruction::CHECK_CAST:
case Instruction::INSTANCE_OF: {
- /* make sure we're checking a reference type */
- const RegType& tmp_type = work_line_->GetRegisterType(dec_insn.vB_);
- if (!tmp_type.IsReferenceTypes()) {
- Fail(VERIFY_ERROR_GENERIC) << "vB not a reference (" << tmp_type << ")";
+ /*
+ * If this instruction succeeds, we will "downcast" register vA to the type in vB. (This
+ * could be a "upcast" -- not expected, so we don't try to address it.)
+ *
+ * If it fails, an exception is thrown, which we deal with later by ignoring the update to
+ * dec_insn.vA_ when branching to a handler.
+ */
+ bool is_checkcast = dec_insn.opcode_ == Instruction::CHECK_CAST;
+ const RegType& res_type =
+ ResolveClassAndCheckAccess(is_checkcast ? dec_insn.vB_ : dec_insn.vC_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ const RegType& orig_type =
+ work_line_->GetRegisterType(is_checkcast ? dec_insn.vA_ : dec_insn.vB_);
+ if (!res_type.IsNonZeroReferenceTypes()) {
+ Fail(VERIFY_ERROR_GENERIC) << "check-cast on unexpected class " << res_type;
+ } else if (!orig_type.IsReferenceTypes()) {
+ Fail(VERIFY_ERROR_GENERIC) << "check-cast on non-reference in v" << dec_insn.vA_;
} else {
- /* make sure we can resolve the class; access check is important */
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vC_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vC_);
- fail_messages_ << "unable to resolve instance of " << dec_insn.vC_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
+ if (is_checkcast) {
+ work_line_->SetRegisterType(dec_insn.vA_, res_type);
} else {
- /* result is boolean */
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Boolean());
}
}
break;
}
case Instruction::ARRAY_LENGTH: {
- Class* res_class = work_line_->GetClassFromRegister(dec_insn.vB_);
- if (failure_ == VERIFY_ERROR_NONE) {
- if (res_class != NULL && !res_class->IsArrayClass()) {
- Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array";
+ const RegType& res_type = work_line_->GetRegisterType(dec_insn.vB_);
+ if (res_type.IsReferenceTypes()) {
+ if (!res_type.IsArrayClass()) {
+ Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array " << res_type;
} else {
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Integer());
}
@@ -2011,66 +2054,47 @@
break;
}
case Instruction::NEW_INSTANCE: {
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve new-instance " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
+ const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ // can't create an instance of an interface or abstract class */
+ if (!res_type.IsInstantiableTypes()) {
+ Fail(VERIFY_ERROR_INSTANTIATION)
+ << "new-instance on primitive, interface or abstract class" << res_type;
} else {
- /* can't create an instance of an interface or abstract class */
- if (res_class->IsPrimitive() || res_class->IsAbstract() || res_class->IsInterface()) {
- Fail(VERIFY_ERROR_INSTANTIATION)
- << "new-instance on primitive, interface or abstract class"
- << PrettyDescriptor(res_class->GetDescriptor());
- } else {
- const RegType& uninit_type = reg_types_.Uninitialized(res_class, work_insn_idx_);
- // Any registers holding previous allocations from this address that have not yet been
- // initialized must be marked invalid.
- work_line_->MarkUninitRefsAsInvalid(uninit_type);
-
- /* add the new uninitialized reference to the register state */
- work_line_->SetRegisterType(dec_insn.vA_, uninit_type);
- }
+ const RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_);
+ // Any registers holding previous allocations from this address that have not yet been
+ // initialized must be marked invalid.
+ work_line_->MarkUninitRefsAsInvalid(uninit_type);
+ // add the new uninitialized reference to the register state
+ work_line_->SetRegisterType(dec_insn.vA_, uninit_type);
}
break;
}
case Instruction::NEW_ARRAY: {
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vC_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vC_);
- fail_messages_ << "unable to resolve new-array " << dec_insn.vC_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else if (!res_class->IsArrayClass()) {
- Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class";
+ const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vC_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ if (!res_type.IsArrayClass()) {
+ Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type;
} else {
/* make sure "size" register is valid type */
work_line_->VerifyRegisterType(dec_insn.vB_, reg_types_.Integer());
/* set register type to array class */
- work_line_->SetRegisterType(dec_insn.vA_, reg_types_.FromClass(res_class));
+ work_line_->SetRegisterType(dec_insn.vA_, res_type);
}
break;
}
case Instruction::FILLED_NEW_ARRAY:
case Instruction::FILLED_NEW_ARRAY_RANGE: {
- Class* res_class = ResolveClassAndCheckAccess(dec_insn.vB_);
- if (res_class == NULL) {
- const char* bad_class_desc = dex_file_->dexStringByTypeIdx(dec_insn.vB_);
- fail_messages_ << "unable to resolve filled-array " << dec_insn.vB_
- << " (" << bad_class_desc << ") in "
- << PrettyDescriptor(method_->GetDeclaringClass()->GetDescriptor());
- DCHECK(failure_ != VERIFY_ERROR_GENERIC);
- } else if (!res_class->IsArrayClass()) {
+ const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ if (!res_type.IsArrayClass()) {
Fail(VERIFY_ERROR_GENERIC) << "filled-new-array on non-array class";
} else {
bool is_range = (dec_insn.opcode_ == Instruction::FILLED_NEW_ARRAY_RANGE);
/* check the arguments to the instruction */
- VerifyFilledNewArrayRegs(dec_insn, res_class, is_range);
+ VerifyFilledNewArrayRegs(dec_insn, res_type, is_range);
/* filled-array result goes into "result" register */
- work_line_->SetResultRegisterType(reg_types_.FromClass(res_class));
+ work_line_->SetResultRegisterType(res_type);
just_set_result = true;
}
break;
@@ -2093,12 +2117,9 @@
work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Integer());
break;
case Instruction::THROW: {
- Class* res_class = work_line_->GetClassFromRegister(dec_insn.vA_);
- if (failure_ == VERIFY_ERROR_NONE && res_class != NULL) {
- if (!JavaLangThrowable()->IsAssignableFrom(res_class)) {
- Fail(VERIFY_ERROR_GENERIC) << "thrown class "
- << PrettyDescriptor(res_class->GetDescriptor()) << " not instanceof Throwable";
- }
+ const RegType& res_type = work_line_->GetRegisterType(dec_insn.vA_);
+ if (!reg_types_.JavaLangThrowable().IsAssignableFrom(res_type)) {
+ Fail(VERIFY_ERROR_GENERIC) << "thrown class " << res_type << " not instanceof Throwable";
}
break;
}
@@ -2338,9 +2359,17 @@
dec_insn.opcode_ == Instruction::INVOKE_SUPER_RANGE);
Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_VIRTUAL, is_range, is_super);
if (failure_ == VERIFY_ERROR_NONE) {
+ const char* descriptor;
+ if (called_method == NULL) {
+ uint32_t method_idx = dec_insn.vB_;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+ descriptor = dex_file_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = called_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
- called_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2358,7 +2387,16 @@
* allowing the latter only if the "this" argument is the same as the "this" argument to
* this method (which implies that we're in a constructor ourselves).
*/
- if (called_method->IsConstructor()) {
+ bool is_constructor;
+ if (called_method != NULL) {
+ is_constructor = called_method->IsConstructor();
+ } else {
+ uint32_t method_idx = dec_insn.vB_;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ const char* name = dex_file_->GetMethodName(method_id);
+ is_constructor = strcmp(name, "<init>") == 0;
+ }
+ if (is_constructor) {
const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
if (failure_ != VERIFY_ERROR_NONE)
break;
@@ -2368,19 +2406,20 @@
Fail(VERIFY_ERROR_GENERIC) << "unable to initialize null ref";
break;
}
- Class* this_class = this_type.GetClass();
- DCHECK(this_class != NULL);
-
- /* must be in same class or in superclass */
- if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) {
- if (this_class != method_->GetDeclaringClass()) {
- Fail(VERIFY_ERROR_GENERIC)
- << "invoke-direct <init> on super only allowed for 'this' in <init>";
+ if (called_method != NULL) {
+ Class* this_class = this_type.GetClass();
+ DCHECK(this_class != NULL);
+ /* must be in same class or in superclass */
+ if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) {
+ if (this_class != method_->GetDeclaringClass()) {
+ Fail(VERIFY_ERROR_GENERIC)
+ << "invoke-direct <init> on super only allowed for 'this' in <init>";
+ break;
+ }
+ } else if (called_method->GetDeclaringClass() != this_class) {
+ Fail(VERIFY_ERROR_GENERIC) << "invoke-direct <init> must be on current class or super";
break;
}
- } else if (called_method->GetDeclaringClass() != this_class) {
- Fail(VERIFY_ERROR_GENERIC) << "invoke-direct <init> must be on current class or super";
- break;
}
/* arg must be an uninitialized reference */
@@ -2398,9 +2437,17 @@
if (failure_ != VERIFY_ERROR_NONE)
break;
}
+ const char* descriptor;
+ if (called_method == NULL) {
+ uint32_t method_idx = dec_insn.vB_;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+ descriptor = dex_file_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = called_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
- called_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2411,9 +2458,17 @@
bool is_range = (dec_insn.opcode_ == Instruction::INVOKE_STATIC_RANGE);
Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false);
if (failure_ == VERIFY_ERROR_NONE) {
+ const char* descriptor;
+ if (called_method == NULL) {
+ uint32_t method_idx = dec_insn.vB_;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+ descriptor = dex_file_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = called_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
- called_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2424,42 +2479,52 @@
bool is_range = (dec_insn.opcode_ == Instruction::INVOKE_INTERFACE_RANGE);
Method* abs_method = VerifyInvocationArgs(dec_insn, METHOD_INTERFACE, is_range, false);
if (failure_ == VERIFY_ERROR_NONE) {
- Class* called_interface = abs_method->GetDeclaringClass();
- if (!called_interface->IsInterface()) {
- Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
- << PrettyMethod(abs_method) << "'";
- break;
- } else {
- /* Get the type of the "this" arg, which should either be a sub-interface of called
- * interface or Object (see comments in RegType::JoinClass).
- */
- const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
- if (failure_ == VERIFY_ERROR_NONE) {
- if (this_type.IsZero()) {
- /* null pointer always passes (and always fails at runtime) */
- } else {
- if (this_type.IsUninitializedTypes()) {
- Fail(VERIFY_ERROR_GENERIC) << "interface call on uninitialized object "
- << this_type;
- break;
- }
- // In the past we have tried to assert that "called_interface" is assignable
- // from "this_type.GetClass()", however, as we do an imprecise Join
- // (RegType::JoinClass) we don't have full information on what interfaces are
- // implemented by "this_type". For example, two classes may implement the same
- // interfaces and have a common parent that doesn't implement the interface. The
- // join will set "this_type" to the parent class and a test that this implements
- // the interface will incorrectly fail.
+ if (abs_method != NULL) {
+ Class* called_interface = abs_method->GetDeclaringClass();
+ if (!called_interface->IsInterface()) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
+ << PrettyMethod(abs_method) << "'";
+ break;
+ }
+ }
+ /* Get the type of the "this" arg, which should either be a sub-interface of called
+ * interface or Object (see comments in RegType::JoinClass).
+ */
+ const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
+ if (failure_ == VERIFY_ERROR_NONE) {
+ if (this_type.IsZero()) {
+ /* null pointer always passes (and always fails at runtime) */
+ } else {
+ if (this_type.IsUninitializedTypes()) {
+ Fail(VERIFY_ERROR_GENERIC) << "interface call on uninitialized object "
+ << this_type;
+ break;
}
+ // In the past we have tried to assert that "called_interface" is assignable
+ // from "this_type.GetClass()", however, as we do an imprecise Join
+ // (RegType::JoinClass) we don't have full information on what interfaces are
+ // implemented by "this_type". For example, two classes may implement the same
+ // interfaces and have a common parent that doesn't implement the interface. The
+ // join will set "this_type" to the parent class and a test that this implements
+ // the interface will incorrectly fail.
}
}
/*
* We don't have an object instance, so we can't find the concrete method. However, all of
* the type information is in the abstract method, so we're good.
*/
+ const char* descriptor;
+ if (abs_method == NULL) {
+ uint32_t method_idx = dec_insn.vB_;
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+ uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+ descriptor = dex_file_->dexStringByTypeIdx(return_type_idx);
+ } else {
+ descriptor = abs_method->GetReturnTypeDescriptor();
+ }
const RegType& return_type =
- reg_types_.FromDescriptor(abs_method->GetDeclaringClass()->GetClassLoader(),
- abs_method->GetReturnTypeDescriptor());
+ reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+ work_line_->SetResultRegisterType(return_type);
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2887,24 +2952,30 @@
return true;
}
-Class* DexVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
- const Class* referrer = method_->GetDeclaringClass();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Class* res_class = class_linker->ResolveType(*dex_file_, class_idx, referrer);
-
- if (res_class == NULL) {
- Thread::Current()->ClearException();
- Fail(VERIFY_ERROR_NO_CLASS) << "can't find class with index " << (void*) class_idx;
- } else if (!referrer->CanAccess(res_class)) { /* Check if access is allowed. */
- Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: "
- << referrer->GetDescriptor()->ToModifiedUtf8() << " -> "
- << res_class->GetDescriptor()->ToModifiedUtf8();
+const RegType& DexVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
+ const char* descriptor = dex_file_->dexStringByTypeIdx(class_idx);
+ Class* referrer = method_->GetDeclaringClass();
+ Class* klass = method_->GetDexCacheResolvedTypes()->Get(class_idx);
+ const RegType& result =
+ klass != NULL ? reg_types_.FromClass(klass)
+ : reg_types_.FromDescriptor(referrer->GetClassLoader(), descriptor);
+ if (klass == NULL && !result.IsUnresolvedTypes()) {
+ method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass());
}
- return res_class;
+ // Check if access is allowed. Unresolved types use AllocObjectFromCodeWithAccessCheck to
+ // check at runtime if access is allowed and so pass here.
+ if (!result.IsUnresolvedTypes() && !referrer->CanAccess(result.GetClass())) {
+ Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
+ << PrettyDescriptor(referrer->GetDescriptor()) << "' -> '"
+ << result << "'";
+ return reg_types_.Unknown();
+ } else {
+ return result;
+ }
}
-Class* DexVerifier::GetCaughtExceptionType() {
- Class* common_super = NULL;
+const RegType& DexVerifier::GetCaughtExceptionType() {
+ const RegType* common_super = NULL;
if (code_item_->tries_size_ != 0) {
const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item_, 0);
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
@@ -2914,25 +2985,23 @@
DexFile::CatchHandlerItem handler = iterator.Get();
if (handler.address_ == (uint32_t) work_insn_idx_) {
if (handler.type_idx_ == DexFile::kDexNoIndex) {
- common_super = JavaLangThrowable();
+ common_super = ®_types_.JavaLangThrowable();
} else {
- Class* klass = ResolveClassAndCheckAccess(handler.type_idx_);
+ const RegType& exception = ResolveClassAndCheckAccess(handler.type_idx_);
/* TODO: on error do we want to keep going? If we don't fail this we run the risk of
* having a non-Throwable introduced at runtime. However, that won't pass an instanceof
* test, so is essentially harmless.
*/
- if (klass == NULL) {
- Fail(VERIFY_ERROR_GENERIC) << "unable to resolve exception class "
- << handler.type_idx_ << " ("
- << dex_file_->dexStringByTypeIdx(handler.type_idx_) << ")";
- return NULL;
- } else if(!JavaLangThrowable()->IsAssignableFrom(klass)) {
- Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << PrettyClass(klass);
- return NULL;
+ if(!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) {
+ Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << exception;
+ return reg_types_.Unknown();
} else if (common_super == NULL) {
- common_super = klass;
+ common_super = &exception;
+ } else if (common_super->Equals(exception)) {
+ // nothing to do
} else {
- common_super = RegType::ClassJoin(common_super, klass);
+ common_super = &common_super->Merge(exception, ®_types_);
+ CHECK(reg_types_.JavaLangThrowable().IsAssignableFrom(*common_super));
}
}
}
@@ -2944,7 +3013,7 @@
/* no catch blocks, or no catches with classes we can find */
Fail(VERIFY_ERROR_GENERIC) << "unable to find exception handler";
}
- return common_super;
+ return *common_super;
}
Method* DexVerifier::ResolveMethodAndCheckAccess(uint32_t method_idx, bool is_direct) {
@@ -2953,11 +3022,11 @@
Method* res_method = dex_cache->GetResolvedMethod(method_idx);
if (res_method == NULL) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- Class* klass = ResolveClassAndCheckAccess(method_id.class_idx_);
- if (klass == NULL) {
- DCHECK(failure_ != VERIFY_ERROR_NONE);
- return NULL;
+ const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
+ if(klass_type.IsUnresolvedTypes()) {
+ return NULL; // Can't resolve Class so no more to do here
}
+ Class* klass = klass_type.GetClass();
const char* name = dex_file_->GetMethodName(method_id);
std::string signature(dex_file_->CreateMethodDescriptor(method_id.proto_idx_, NULL));
if (is_direct) {
@@ -2991,14 +3060,7 @@
// we're making.
Method* res_method = ResolveMethodAndCheckAccess(dec_insn.vB_,
(method_type == METHOD_DIRECT || method_type == METHOD_STATIC));
- if (res_method == NULL) {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(dec_insn.vB_);
- const char* method_name = dex_file_->GetMethodName(method_id);
- std::string method_signature = dex_file_->GetMethodSignature(method_id);
- const char* class_descriptor = dex_file_->GetMethodDeclaringClassDescriptor(method_id);
- DCHECK_NE(failure_, VERIFY_ERROR_NONE);
- fail_messages_ << "unable to resolve method " << dec_insn.vB_ << ": "
- << class_descriptor << "." << method_name << " " << method_signature;
+ if (res_method == NULL) { // error or class is unresolved
return NULL;
}
// Make sure calls to constructors are "direct". There are additional restrictions but we don't
@@ -3410,13 +3472,14 @@
}
void DexVerifier::VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn,
- Class* res_class, bool is_range) {
- DCHECK(res_class->IsArrayClass()) << PrettyClass(res_class); // Checked before calling.
+ const RegType& res_type, bool is_range) {
+ DCHECK(res_type.IsArrayClass()) << res_type; // Checked before calling.
/*
* Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of the
* list and fail. It's legal, if silly, for arg_count to be zero.
*/
- const RegType& expected_type = reg_types_.FromClass(res_class->GetComponentType());
+ const RegType& expected_type = reg_types_.GetComponentType(res_type,
+ method_->GetDeclaringClass()->GetClassLoader());
uint32_t arg_count = dec_insn.vA_;
for (size_t ui = 0; ui < arg_count; ui++) {
uint32_t get_reg;
@@ -3663,15 +3726,6 @@
}
}
-Class* DexVerifier::JavaLangThrowable() {
- if (java_lang_throwable_ == NULL) {
- java_lang_throwable_ =
- Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Throwable;");
- DCHECK(java_lang_throwable_ != NULL);
- }
- return java_lang_throwable_;
-}
-
const uint8_t* PcToReferenceMap::FindBitMap(uint16_t dex_pc, bool error_if_not_present) const {
size_t num_entries = NumEntries();
// Do linear or binary search?
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index 9041c95..dbe92f6 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -143,7 +143,7 @@
bool IsReferenceTypes() const {
return IsReference() || IsUnresolvedReference() || IsUninitializedReference() ||
- IsUninitializedThisReference() || IsZero();
+ IsUninitializedThisReference() || IsUnresolvedAndUninitializedReference() || IsZero();
}
bool IsNonZeroReferenceTypes() const {
return IsReference() || IsUnresolvedReference() || IsUninitializedReference() ||
@@ -182,7 +182,7 @@
}
uint32_t GetAllocationPc() const {
- DCHECK(IsUninitializedReference());
+ DCHECK(IsUninitializedTypes());
return allocation_pc_or_constant_;
}
@@ -196,12 +196,24 @@
bool IsJavaLangObject() const {
return IsReference() && GetClass()->IsObjectClass();
}
+ bool IsInstantiableTypes() const {
+ return IsUnresolvedTypes() || (IsNonZeroReferenceTypes() && GetClass()->IsInstantiable());
+ }
String* GetDescriptor() const {
- DCHECK(IsUnresolvedReference());
+ DCHECK(IsUnresolvedTypes());
DCHECK(klass_or_descriptor_ != NULL);
DCHECK(klass_or_descriptor_->IsString());
return down_cast<String*>(klass_or_descriptor_);
}
+ bool IsArrayClass() const {
+ if (IsUnresolvedTypes()) {
+ return GetDescriptor()->CharAt(0) == '[';
+ } else if (!IsConstant()) {
+ return GetClass()->IsArrayClass();
+ } else {
+ return false;
+ }
+ }
uint16_t GetId() const {
return cache_id_;
@@ -239,12 +251,12 @@
RegType(Type type, Object* klass_or_descriptor, uint32_t allocation_pc_or_constant, uint16_t cache_id) :
type_(type), klass_or_descriptor_(klass_or_descriptor), allocation_pc_or_constant_(allocation_pc_or_constant),
cache_id_(cache_id) {
- DCHECK(IsConstant() || IsUninitializedReference() || allocation_pc_or_constant == 0);
+ DCHECK(IsConstant() || IsUninitializedTypes() || allocation_pc_or_constant == 0);
if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUnknown() &&
!IsConflict()) {
DCHECK(klass_or_descriptor != NULL);
- DCHECK(IsUnresolvedReference() || klass_or_descriptor_->IsClass());
- DCHECK(!IsUnresolvedReference() || klass_or_descriptor_->IsString());
+ DCHECK(IsUnresolvedTypes() || klass_or_descriptor_->IsClass());
+ DCHECK(!IsUnresolvedTypes() || klass_or_descriptor_->IsString());
}
}
@@ -300,14 +312,16 @@
const RegType& JavaLangClass() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Class;"); }
const RegType& JavaLangObject() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Object;"); }
const RegType& JavaLangString() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/String;"); }
+ const RegType& JavaLangThrowable() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Throwable;"); }
const RegType& Unknown() { return FromType(RegType::kRegTypeUnknown); }
const RegType& Conflict() { return FromType(RegType::kRegTypeConflict); }
const RegType& ConstLo() { return FromType(RegType::kRegTypeConstLo); }
const RegType& Zero() { return FromCat1Const(0); }
- const RegType& Uninitialized(Class* klass, uint32_t allocation_pc);
+ const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc);
const RegType& UninitializedThisArgument(Class* klass);
+ const RegType& FromUninitialized(const RegType& uninit_type);
// Representatives of various constant types. When merging constants we can't infer a type,
// (an int may later be used as a float) so we select these representative values meaning future
@@ -315,6 +329,8 @@
const RegType& ByteConstant() { return FromCat1Const(std::numeric_limits<jbyte>::min()); }
const RegType& ShortConstant() { return FromCat1Const(std::numeric_limits<jshort>::min()); }
const RegType& IntConstant() { return FromCat1Const(std::numeric_limits<jint>::max()); }
+
+ const RegType& GetComponentType(const RegType& array, const ClassLoader* loader);
private:
// The allocated entries
std::vector<RegType*> entries_;
@@ -1073,17 +1089,12 @@
bool is_primitive, bool is_static);
// Verify that the arguments in a filled-new-array instruction are valid.
- // "res_class" is the class refered to by dec_insn->vB_.
- void VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn, Class* res_class,
- bool is_range);
+ void VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn,
+ const RegType& res_type, bool is_range);
- /*
- * Resolves a class based on an index and performs access checks to ensure the referrer can
- * access the resolved class.
- * Exceptions caused by failures are cleared before returning.
- * Sets "*failure" on failure.
- */
- Class* ResolveClassAndCheckAccess(uint32_t class_idx);
+ // Resolves a class based on an index and performs access checks to ensure the referrer can
+ // access the resolved class.
+ const RegType& ResolveClassAndCheckAccess(uint32_t class_idx);
/*
* For the "move-exception" instruction at "work_insn_idx_", which must be at an exception handler
@@ -1091,7 +1102,7 @@
* Returns NULL if no matching exception handler can be found, or if the exception is not a
* subclass of Throwable.
*/
- Class* GetCaughtExceptionType();
+ const RegType& GetCaughtExceptionType();
/*
* Resolves a method based on an index and performs access checks to ensure
@@ -1169,8 +1180,6 @@
// Compute sizes for GC map data
void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
- Class* JavaLangThrowable();
-
InsnFlags CurrentInsnFlags() {
return insn_flags_[work_insn_idx_];
}
@@ -1182,9 +1191,6 @@
// Storage for the register status we're currently working on.
UniquePtr<RegisterLine> work_line_;
- // Lazily initialized reference to java.lang.Class<java.lang.Throwable>
- Class* java_lang_throwable_;
-
// The address of the instruction we're currently working on, note that this is in 2 byte
// quantities
uint32_t work_insn_idx_;
diff --git a/src/object.cc b/src/object.cc
index e59e79a..45fc781 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -668,7 +668,6 @@
uint32_t Method::FindCatchBlock(Class* exception_type, uint32_t dex_pc) const {
DexCache* dex_cache = GetDeclaringClass()->GetDexCache();
- const ClassLoader* class_loader = GetDeclaringClass()->GetClassLoader();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
const DexFile::CodeItem* code_item = dex_file.GetCodeItem(GetCodeItemOffset());
@@ -681,9 +680,12 @@
return iter.Get().address_;
}
// Does this catch exception type apply?
- Class* iter_exception_type =
- class_linker->ResolveType(dex_file, iter_type_idx, dex_cache, class_loader);
- if (iter_exception_type->IsAssignableFrom(exception_type)) {
+ Class* iter_exception_type = dex_cache->GetResolvedType(iter_type_idx);
+ if (iter_exception_type == NULL) {
+ // The verifier should take care of resolving all exception classes early
+ LOG(WARNING) << "Unresolved exception class when finding catch block: "
+ << dex_file.GetTypeDescriptor(dex_file.GetTypeId(iter_type_idx));
+ } else if (iter_exception_type->IsAssignableFrom(exception_type)) {
return iter.Get().address_;
}
}
@@ -764,10 +766,8 @@
}
Object* Class::AllocObject() {
- DCHECK(!IsAbstract()) << PrettyClass(this);
- DCHECK(!IsInterface()) << PrettyClass(this);
- DCHECK(!IsPrimitive()) << PrettyClass(this);
DCHECK(!IsArrayClass()) << PrettyClass(this);
+ DCHECK(IsInstantiable()) << PrettyClass(this);
DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this);
DCHECK_GE(this->object_size_, sizeof(Object));
return Heap::AllocObject(this, this->object_size_);
diff --git a/src/object.h b/src/object.h
index 58c6ba7..1051985 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1463,6 +1463,9 @@
bool IsObjectClass() const {
return !IsPrimitive() && GetSuperClass() == NULL;
}
+ bool IsInstantiable() const {
+ return !IsPrimitive() && !IsInterface() && !IsAbstract();
+ }
// Creates a raw object instance but does not invoke the default constructor.
Object* AllocObject();
@@ -2022,8 +2025,7 @@
Class* super_class_;
// If class verify fails, we must return same error on subsequent tries.
- // Update with SetVerifyErrorClass to ensure a write barrier is used.
- const Class* verify_error_class_;
+ Class* verify_error_class_;
// virtual methods defined in this class; invoked through vtable
ObjectArray<Method>* virtual_methods_;
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 6773338..8df2abe 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -425,30 +425,6 @@
return code;
}
-// TODO: placeholder. Helper function to type
-Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) {
- /*
- * Should initialize & fix up method->dex_cache_resolved_types_[].
- * Returns initialized type. Does not return normally if an exception
- * is thrown, but instead initiates the catch. Should be similar to
- * ClassLinker::InitializeStaticStorageFromCode.
- */
- UNIMPLEMENTED(FATAL);
- return NULL;
-}
-
-// TODO: placeholder. Helper function to resolve virtual method
-void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
- /*
- * Slow-path handler on invoke virtual method path in which
- * base method is unresolved at compile-time. Doesn't need to
- * return anything - just either ensure that
- * method->dex_cache_resolved_methods_(method_idx) != NULL or
- * throw and unwind. The caller will restart call sequence
- * from the beginning.
- */
-}
-
// Fast path field resolution that can't throw exceptions
static Field* FindFieldFast(uint32_t field_idx, const Method* referrer) {
Field* resolved_field = referrer->GetDexCacheResolvedFields()->Get(field_idx);
@@ -651,11 +627,30 @@
return klass->AllocObject();
}
-extern "C" Object* artAllocObjectFromCodeSlowPath(uint32_t type_idx,
- Method* method,
- Thread* self, Method** sp) {
- //TODO: Add delayed verification checks here
- return artAllocObjectFromCode(type_idx, method, self, sp);
+extern "C" Object* artAllocObjectFromCodeWithAccessCheck(uint32_t type_idx, Method* method,
+ Thread* self, Method** sp) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(klass == NULL)) {
+ klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
+ if (klass == NULL) {
+ DCHECK(self->IsExceptionPending());
+ return NULL; // Failure
+ }
+ }
+ Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", "illegal class access: '%s' -> '%s'",
+ PrettyDescriptor(referrer->GetDescriptor()).c_str(),
+ PrettyDescriptor(klass->GetDescriptor()).c_str());
+ return NULL; // Failure
+ }
+ if (!runtime->GetClassLinker()->EnsureInitialized(klass, true)) {
+ DCHECK(self->IsExceptionPending());
+ return NULL; // Failure
+ }
+ return klass->AllocObject();
}
Array* CheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
@@ -789,6 +784,25 @@
return InitializeStaticStorage(type_idx, referrer, self);
}
+extern "C" Class* artInitializeTypeFromCode(uint32_t type_idx, const Method* referrer, Thread* self,
+ Method** sp) {
+ // Called when method->dex_cache_resolved_types_[] misses
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return InitializeStaticStorage(type_idx, referrer, self);
+}
+
+// TODO: placeholder. Helper function to resolve virtual method
+void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
+ /*
+ * Slow-path handler on invoke virtual method path in which
+ * base method is unresolved at compile-time. Doesn't need to
+ * return anything - just either ensure that
+ * method->dex_cache_resolved_methods_(method_idx) != NULL or
+ * throw and unwind. The caller will restart call sequence
+ * from the beginning.
+ */
+}
+
String* ResolveStringFromCode(const Method* referrer, uint32_t string_idx) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
return class_linker->ResolveString(string_idx, referrer);
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 4ed6455..90fcf2c 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -59,12 +59,13 @@
extern "C" void art_unlock_object_from_code(void*);
extern "C" void* art_alloc_array_from_code(uint32_t, void*, int32_t);
extern "C" void* art_alloc_object_from_code(uint32_t type_idx, void* method);
- extern "C" void* art_alloc_object_from_code_slow_path(uint32_t type_idx, void* method);
+ extern "C" void* art_alloc_object_from_code_with_access_check(uint32_t type_idx, void* method);
extern "C" void* art_check_and_alloc_array_from_code(uint32_t, void*, int32_t);
extern "C" void* art_find_instance_field_from_code(uint32_t, void*);
extern "C" void* art_find_static_field_from_code(uint32_t, void*);
extern "C" void* art_get_obj_static_from_code(uint32_t, void*);
extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*);
+ extern "C" void* art_initialize_type_from_code(uint32_t, void*);
extern "C" void* art_resolve_string_from_code(void*, uint32_t);
/* Conversions */
diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S
index b6d8928..112a114 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -280,6 +280,22 @@
bxne lr @ return on success
DELIVER_PENDING_EXCEPTION
+ .global art_initialize_type_from_code
+ .extern artInitializeTypeFromCode
+ /*
+ * Entry from managed code when dex cache misses for a type_idx
+ */
+art_initialize_type_from_code:
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, SP)
+ bl artInitializeTypeFromCode
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ cmp r0, #0 @ success if result is non-null
+ bxne lr @ return on success
+ DELIVER_PENDING_EXCEPTION
+
.global art_find_instance_field_from_code
.extern artFindInstanceFieldFromCode
/*
@@ -441,16 +457,17 @@
bxne lr @ return on success
DELIVER_PENDING_EXCEPTION
- .global art_alloc_object_from_code_slow_path
- .extern artAllocObjectFromCodeSlowPath
+ .global art_alloc_object_from_code_with_access_check
+ .extern artAllocObjectFromCodeWithAccessCheck
/*
- * Called by managed code to allocate an object
+ * Called by managed code to allocate an object when the caller doesn't know whether it has
+ * access to the created type
*/
-art_alloc_object_from_code_slow_path:
+art_alloc_object_from_code_with_access_check:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
mov r2, r9 @ pass Thread::Current
mov r3, sp @ pass SP
- bl artAllocObjectFromCodeSlowPath @ (uint32_t type_idx, Method* method, Thread*, SP)
+ bl artAllocObjectFromCodeWithAccessCheck @ (uint32_t type_idx, Method* method, Thread*, SP)
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
cmp r0, #0 @ success if result is non-null
bxne lr @ return on success
diff --git a/src/thread.cc b/src/thread.cc
index 841ea9b..a8c58bf 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -91,7 +91,7 @@
pLmul = __aeabi_lmul;
pAllocArrayFromCode = art_alloc_array_from_code;
pAllocObjectFromCode = art_alloc_object_from_code;
- pAllocObjectFromCodeSlowPath = art_alloc_object_from_code_slow_path;
+ pAllocObjectFromCodeWithAccessCheck = art_alloc_object_from_code_with_access_check;
pCanPutArrayElementFromCode = art_can_put_array_element_from_code;
pCheckAndAllocArrayFromCode = art_check_and_alloc_array_from_code;
pCheckCastFromCode = art_check_cast_from_code;
@@ -101,6 +101,7 @@
pGetObjStatic = art_get_obj_static_from_code;
pHandleFillArrayDataFromCode = art_handle_fill_data_from_code;
pInitializeStaticStorage = art_initialize_static_storage_from_code;
+ pInitializeTypeFromCode = art_initialize_type_from_code;
pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
pLockObjectFromCode = art_lock_object_from_code;
pObjectInit = art_object_init_from_code;
@@ -126,7 +127,6 @@
pDecodeJObjectInThread = DecodeJObjectInThread;
pDeliverException = art_deliver_exception_from_code;
pFindNativeMethod = FindNativeMethod;
- pInitializeTypeFromCode = InitializeTypeFromCode;
pInstanceofNonTrivialFromCode = IsAssignableFromCode;
pResolveMethodFromCode = ResolveMethodFromCode;
pThrowAbstractMethodErrorFromCode = ThrowAbstractMethodErrorFromCode;
@@ -482,7 +482,8 @@
struct StackDumpVisitor : public Thread::StackVisitor {
StackDumpVisitor(std::ostream& os, const Thread* thread)
- : os(os), thread(thread), frame_count(0) {
+ : last_method(NULL), last_line_number(0), repetition_count(0), os(os), thread(thread),
+ frame_count(0) {
}
virtual ~StackDumpVisitor() {
@@ -492,28 +493,41 @@
if (!frame.HasMethod()) {
return;
}
-
+ const int kMaxRepetition = 3;
Method* m = frame.GetMethod();
Class* c = m->GetDeclaringClass();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const DexFile& dex_file = class_linker->FindDexFile(c->GetDexCache());
-
- os << " at " << PrettyMethod(m, false);
- if (m->IsNative()) {
- os << "(Native method)";
+ int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(pc));
+ if (line_number == last_line_number && last_method == m) {
+ repetition_count++;
} else {
- int line_number = dex_file.GetLineNumFromPC(m, m->ToDexPC(pc));
- SirtRef<String> source_file(c->GetSourceFile());
- os << "(" << (source_file.get() != NULL ? source_file->ToModifiedUtf8() : "unavailable")
- << ":" << line_number << ")";
+ if (repetition_count >= kMaxRepetition) {
+ os << " ... repeated " << (repetition_count - kMaxRepetition) << " times\n";
+ }
+ repetition_count = 0;
+ last_line_number = line_number;
+ last_method = m;
}
- os << "\n";
+ if (repetition_count < kMaxRepetition) {
+ os << " at " << PrettyMethod(m, false);
+ if (m->IsNative()) {
+ os << "(Native method)";
+ } else {
+ SirtRef<String> source_file(c->GetSourceFile());
+ os << "(" << (source_file.get() != NULL ? source_file->ToModifiedUtf8() : "unavailable")
+ << ":" << line_number << ")";
+ }
+ os << "\n";
+ }
if (frame_count++ == 0) {
Monitor::DescribeWait(os, thread);
}
}
-
+ Method* last_method;
+ int last_line_number;
+ int repetition_count;
std::ostream& os;
const Thread* thread;
int frame_count;
@@ -1316,13 +1330,35 @@
};
void Thread::DeliverException() {
- Throwable *exception = GetException(); // Set exception on thread
+ const bool kDebugExceptionDelivery = false;
+ Throwable *exception = GetException(); // Get exception from thread
CHECK(exception != NULL);
+ // Don't leave exception visible while we try to find the handler, which may cause class
+ // resolution
+ ClearException();
+ if (kDebugExceptionDelivery) {
+ DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) << std::endl);
+ }
Context* long_jump_context = GetLongJumpContext();
CatchBlockStackVisitor catch_finder(exception->GetClass(), long_jump_context);
WalkStackUntilUpCall(&catch_finder, true);
+ if (kDebugExceptionDelivery) {
+ Method* handler_method = catch_finder.handler_frame_.GetMethod();
+ if (handler_method == NULL) {
+ LOG(INFO) << "Handler is upcall";
+ } else {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const DexFile& dex_file =
+ class_linker->FindDexFile(handler_method->GetDeclaringClass()->GetDexCache());
+ int line_number = dex_file.GetLineNumFromPC(handler_method,
+ handler_method->ToDexPC(catch_finder.handler_pc_));
+ LOG(INFO) << "Handler: " << PrettyMethod(handler_method)
+ << " (line: " << line_number << ")";
+ }
+ }
+ SetException(exception);
long_jump_context->SetSP(reinterpret_cast<intptr_t>(catch_finder.handler_frame_.GetSP()));
long_jump_context->SetPC(catch_finder.handler_pc_);
long_jump_context->DoLongJump();
diff --git a/src/thread.h b/src/thread.h
index ac97ebe..eec9df4 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -120,7 +120,7 @@
void (*pCheckSuspendFromCode)(Thread*); // Stub that is called when the suspend count is non-zero
void (*pTestSuspendFromCode)(); // Stub that is periodically called to test the suspend count
void* (*pAllocObjectFromCode)(uint32_t, void*);
- void* (*pAllocObjectFromCodeSlowPath)(uint32_t, void*);
+ void* (*pAllocObjectFromCodeWithAccessCheck)(uint32_t, void*);
void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t);
void (*pCanPutArrayElementFromCode)(void*, void*);
void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t);
@@ -137,7 +137,7 @@
void* (*pInitializeStaticStorage)(uint32_t, void*);
uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
- Class* (*pInitializeTypeFromCode)(uint32_t, Method*);
+ void* (*pInitializeTypeFromCode)(uint32_t, void*);
void (*pLockObjectFromCode)(void*);
void (*pObjectInit)(void*);
void (*pResolveMethodFromCode)(Method*, uint32_t);