Better unresolved type support.
Also fix bug where miranda methods were changing their declaring class
and thereby breaking their return type indices.
Add support for dumping stacks on abort.
Change-Id: I3782864736b12d1f81ab9aea4909213d3344ba13
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 0232ee3..f49f546 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1328,7 +1328,9 @@
return;
}
dst->SetAccessFlags(src.access_flags_);
- dst->SetReturnTypeIdx(dex_file.GetProtoId(method_id.proto_idx_).return_type_idx_);
+ uint32_t return_type_idx = dex_file.GetProtoId(method_id.proto_idx_).return_type_idx_;
+ DCHECK_LT(return_type_idx, dex_file.NumTypeIds());
+ dst->SetReturnTypeIdx(return_type_idx);
dst->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
@@ -1910,8 +1912,8 @@
bool ClassLinker::HasSameMethodDescriptorClasses(const Method* method,
const Class* klass1,
const Class* klass2) {
- if (method->IsMiranda()) {
- return true;
+ if (klass1 == klass2) {
+ return true;
}
const DexFile& dex_file = FindDexFile(method->GetDeclaringClass()->GetDexCache());
const DexFile::ProtoId& proto_id = dex_file.GetProtoId(method->GetProtoIdx());
@@ -1946,6 +1948,9 @@
CHECK(descriptor != NULL);
CHECK(klass1 != NULL);
CHECK(klass2 != NULL);
+ if (klass1 == klass2) {
+ return true;
+ }
Class* found1 = FindClass(descriptor, klass1->GetClassLoader());
// TODO: found1 == NULL
Class* found2 = FindClass(descriptor, klass2->GetClassLoader());
@@ -1953,13 +1958,10 @@
// TODO: lookup found1 in initiating loader list
if (found1 == NULL || found2 == NULL) {
Thread::Current()->ClearException();
- if (found1 == found2) {
- return true;
- } else {
- return false;
- }
+ return found1 == found2;
+ } else {
+ return true;
}
- return true;
}
bool ClassLinker::InitializeSuperClass(Class* klass, bool can_run_clinit) {
@@ -2393,7 +2395,7 @@
vtable = vtable->CopyOf(new_vtable_count);
for (size_t i = 0; i < miranda_list.size(); ++i) {
Method* method = miranda_list[i];
- method->SetDeclaringClass(klass.get());
+ // Leave the declaring class alone as type indices are relative to it
method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
klass->SetVirtualMethod(old_method_count + i, method);
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 6cb7104..85c4182 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -156,31 +156,30 @@
return true;
} else {
switch (GetType()) {
- case RegType::kRegTypeBoolean: return IsBooleanTypes();
- case RegType::kRegTypeByte: return IsByteTypes();
- case RegType::kRegTypeShort: return IsShortTypes();
- case RegType::kRegTypeChar: return IsCharTypes();
- case RegType::kRegTypeInteger: return IsIntegralTypes();
- case RegType::kRegTypeFloat: return IsFloatTypes();
- case RegType::kRegTypeLongLo: return IsLongTypes();
- case RegType::kRegTypeDoubleLo: return IsDoubleTypes();
+ case RegType::kRegTypeBoolean: return src.IsBooleanTypes();
+ case RegType::kRegTypeByte: return src.IsByteTypes();
+ case RegType::kRegTypeShort: return src.IsShortTypes();
+ case RegType::kRegTypeChar: return src.IsCharTypes();
+ case RegType::kRegTypeInteger: return src.IsIntegralTypes();
+ case RegType::kRegTypeFloat: return src.IsFloatTypes();
+ case RegType::kRegTypeLongLo: return src.IsLongTypes();
+ case RegType::kRegTypeDoubleLo: return src.IsDoubleTypes();
default:
if (!IsReferenceTypes()) {
LOG(FATAL) << "Unexpected register type in IsAssignableFrom: '" << src << "'";
}
if (src.IsZero()) {
- return true;
- } else if (IsUninitializedReference()) {
- return false; // Nonsensical to Join two uninitialized classes
- } else if (GetClass()->IsInterface()) {
+ return true; // all reference types can be assigned null
+ } else if (!src.IsReferenceTypes()) {
+ return false; // expect src to be a reference type
+ } else if (IsJavaLangObject()) {
+ return true; // all reference types can be assigned to Object
+ } else if (!IsUnresolvedTypes() && GetClass()->IsInterface()) {
return true; // We allow assignment to any interface, see comment in ClassJoin
- } else if (IsReference() && src.IsReference() &&
+ } else if (!IsUnresolvedTypes() && !src.IsUnresolvedTypes() &&
GetClass()->IsAssignableFrom(src.GetClass())) {
// We're assignable from the Class point-of-view
return true;
- } else if (src.IsUnresolvedReference() && IsReference() && GetClass()->IsObjectClass()) {
- // We're an object being assigned an unresolved reference, which is ok
- return true;
} else {
return false;
}
@@ -263,12 +262,14 @@
// float/long/double MERGE float/long/double_constant => float/long/double
return SelectNonConstant(*this, incoming_type);
} else if (IsReferenceTypes() && incoming_type.IsReferenceTypes()) {
- if (IsZero() | incoming_type.IsZero()) {
+ if (IsZero() || incoming_type.IsZero()) {
return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref
- } else if (IsUnresolvedReference() || IsUninitializedReference() ||
- IsUninitializedThisReference()) {
- // Can only merge an uninitialized or unresolved type with itself or 0, we've already checked
- // these so => Conflict
+ } else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) {
+ return reg_types->JavaLangObject(); // Object MERGE ref => Object
+ } else if (IsUninitializedTypes() || incoming_type.IsUninitializedTypes() ||
+ IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) {
+ // Can only merge an unresolved or uninitialized type with itself, 0 or Object, we've already
+ // checked these so => Conflict
return reg_types->Conflict();
} else { // Two reference types, compute Join
Class* c1 = GetClass();
@@ -1865,18 +1866,14 @@
/* return_type is the *expected* return type, not register value */
DCHECK(!return_type.IsZero());
DCHECK(!return_type.IsUninitializedReference());
- // Verify that the reference in vAA is an instance of the type in "return_type". The Zero
- // type is allowed here. If the method is declared to return an interface, then any
- // initialized reference is acceptable.
- // Note GetClassFromRegister fails if the register holds an uninitialized reference, so
- // we do not allow them to be returned.
- Class* decl_class = return_type.GetClass();
- Class* res_class = work_line_->GetClassFromRegister(dec_insn.vA_);
- if (res_class != NULL && failure_ == VERIFY_ERROR_NONE) {
- if (!decl_class->IsInterface() && !decl_class->IsAssignableFrom(res_class)) {
- Fail(VERIFY_ERROR_GENERIC) << "returning " << PrettyClassAndClassLoader(res_class)
- << ", declared " << PrettyClassAndClassLoader(res_class);
- }
+ const RegType& reg_type = work_line_->GetRegisterType(dec_insn.vA_);
+ // Disallow returning uninitialized values and verify that the reference in vAA is an
+ // instance of the "return_type"
+ if (reg_type.IsUninitializedTypes()) {
+ Fail(VERIFY_ERROR_GENERIC) << "returning uninitialized object '" << reg_type << "'";
+ } else if (!return_type.IsAssignableFrom(reg_type)) {
+ Fail(VERIFY_ERROR_GENERIC) << "returning '" << reg_type
+ << "', but expected from declaration '" << return_type << "'";
}
}
}
@@ -2332,7 +2329,9 @@
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 RegType& return_type = reg_types_.FromClass(called_method->GetReturnType());
+ const RegType& return_type =
+ reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
+ called_method->GetReturnTypeDescriptor());
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2390,7 +2389,9 @@
if (failure_ != VERIFY_ERROR_NONE)
break;
}
- const RegType& return_type = reg_types_.FromClass(called_method->GetReturnType());
+ const RegType& return_type =
+ reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
+ called_method->GetReturnTypeDescriptor());
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2401,7 +2402,9 @@
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 RegType& return_type = reg_types_.FromClass(called_method->GetReturnType());
+ const RegType& return_type =
+ reg_types_.FromDescriptor(called_method->GetDeclaringClass()->GetClassLoader(),
+ called_method->GetReturnTypeDescriptor());
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -2445,7 +2448,9 @@
* 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 RegType& return_type = reg_types_.FromClass(abs_method->GetReturnType());
+ const RegType& return_type =
+ reg_types_.FromDescriptor(abs_method->GetDeclaringClass()->GetClassLoader(),
+ abs_method->GetReturnTypeDescriptor());
work_line_->SetResultRegisterType(return_type);
just_set_result = true;
}
@@ -3057,11 +3062,10 @@
return NULL;
}
if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
- Class* actual_this_ref = actual_arg_type.GetClass();
- if (!res_method->GetDeclaringClass()->IsAssignableFrom(actual_this_ref)) {
- Fail(VERIFY_ERROR_GENERIC) << "'this' arg '"
- << PrettyDescriptor(actual_this_ref->GetDescriptor()) << "' not instance of '"
- << PrettyDescriptor(res_method->GetDeclaringClass()->GetDescriptor()) << "'";
+ const RegType& res_method_class = reg_types_.FromClass(res_method->GetDeclaringClass());
+ if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
+ Fail(VERIFY_ERROR_GENERIC) << "'this' arg '" << actual_arg_type << "' not instance of '"
+ << res_method_class << "'";
return NULL;
}
}
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index d12e14f..b1e6e29 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -92,7 +92,9 @@
return IsUninitializedReference() || IsUninitializedThisReference() ||
IsUnresolvedAndUninitializedReference();
}
-
+ bool IsUnresolvedTypes() const {
+ return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference();
+ }
bool IsLowHalf() const { return type_ == kRegTypeLongLo ||
type_ == kRegTypeDoubleLo ||
type_ == kRegTypeConstLo; }
@@ -191,6 +193,9 @@
return down_cast<Class*>(klass_or_descriptor_);
}
+ bool IsJavaLangObject() const {
+ return IsReference() && GetClass()->IsObjectClass();
+ }
String* GetDescriptor() const {
DCHECK(IsUnresolvedReference());
DCHECK(klass_or_descriptor_ != NULL);
@@ -1125,7 +1130,8 @@
* Returned references are assumed to be initialized. Returns kRegTypeUnknown for "void".
*/
const RegType& GetMethodReturnType() {
- return reg_types_.FromClass(method_->GetReturnType());
+ return reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(),
+ method_->GetReturnTypeDescriptor());
}
/*
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 81d070f..eccf977 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -78,7 +78,7 @@
if (public_only && !m->IsPublic()) {
return false;
}
- if (m->IsMiranda() || m->IsStatic()) {
+ if (m->IsStatic()) {
return false;
}
if (m->GetName()->CharAt(0) != '<') {
@@ -134,9 +134,6 @@
if (public_only && !m->IsPublic()) {
return false;
}
- if (m->IsMiranda()) {
- return false;
- }
if (m->GetName()->CharAt(0) == '<') {
return false;
}
diff --git a/src/object.cc b/src/object.cc
index 91328aa..78a1976 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -400,6 +400,16 @@
new_return_type_idx, false);
}
+const char* Method::GetReturnTypeDescriptor() const {
+ Class* declaring_class = GetDeclaringClass();
+ DexCache* dex_cache = declaring_class->GetDexCache();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+ const char* descriptor = dex_file.dexStringByTypeIdx(GetReturnTypeIdx());
+ DCHECK(descriptor != NULL);
+ return descriptor;
+}
+
Class* Method::GetReturnType() const {
DCHECK(GetDeclaringClass()->IsResolved() || GetDeclaringClass()->IsErroneous())
<< PrettyMethod(this);
@@ -1468,6 +1478,25 @@
return !InstanceOf(jlre);
}
+std::string Throwable::Dump() const {
+ Object* stack_state = GetStackState();
+ if (stack_state == NULL || !stack_state->IsObjectArray()) {
+ // missing or corrupt stack state
+ return "";
+ }
+ // Decode the internal stack trace into the depth and method trace
+ ObjectArray<Object>* method_trace = down_cast<ObjectArray<Object>*>(stack_state);
+ int32_t depth = method_trace->GetLength() - 1;
+ std::string result;
+ for (int32_t i = 0; i < depth; ++i) {
+ Method* method = down_cast<Method*>(method_trace->Get(i));
+ result += " at ";
+ result += PrettyMethod(method, true);
+ result += "\n";
+ }
+ return result;
+}
+
Class* StackTraceElement::java_lang_StackTraceElement_ = NULL;
void StackTraceElement::SetClass(Class* java_lang_StackTraceElement) {
diff --git a/src/object.h b/src/object.h
index cabaac2..58c6ba7 100644
--- a/src/object.h
+++ b/src/object.h
@@ -712,6 +712,8 @@
void SetReturnTypeIdx(uint32_t new_return_type_idx);
+ const char* GetReturnTypeDescriptor() const;
+
Class* GetReturnType() const;
bool IsReturnAReference() const;
@@ -2026,9 +2028,10 @@
// virtual methods defined in this class; invoked through vtable
ObjectArray<Method>* virtual_methods_;
- // Virtual method table (vtable), for use by "invoke-virtual". The
- // vtable from the superclass is copied in, and virtual methods from
- // our class either replace those from the super or are appended.
+ // Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass is
+ // copied in, and virtual methods from our class either replace those from the super or are
+ // appended. For abstract classes, methods may be created in the vtable that aren't in
+ // virtual_ methods_ for miranda methods.
ObjectArray<Method>* vtable_;
// access flags; low 16 bits are defined by VM spec
@@ -2207,23 +2210,24 @@
}
inline bool Method::IsReturnAReference() const {
- return !GetReturnType()->IsPrimitive();
+ char d = GetReturnTypeDescriptor()[0];
+ return d == 'L' || d == '[';
}
inline bool Method::IsReturnAFloat() const {
- return GetReturnType()->IsPrimitiveFloat();
+ return GetReturnTypeDescriptor()[0] == 'F';
}
inline bool Method::IsReturnADouble() const {
- return GetReturnType()->IsPrimitiveDouble();
+ return GetReturnTypeDescriptor()[0] == 'D';
}
inline bool Method::IsReturnALong() const {
- return GetReturnType()->IsPrimitiveLong();
+ return GetReturnTypeDescriptor()[0] == 'J';
}
inline bool Method::IsReturnVoid() const {
- return GetReturnType()->IsPrimitiveVoid();
+ return GetReturnTypeDescriptor()[0] == 'V';
}
inline size_t Array::SizeOf() const {
@@ -2628,9 +2632,14 @@
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_),
new_detail_message, false);
}
+ std::string Dump() const;
bool IsCheckedException() const;
private:
+ Object* GetStackState() const {
+ return GetFieldObject<Object*>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_), true);
+ }
+
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
Throwable* cause_;
String* detail_message_;
diff --git a/src/runtime.cc b/src/runtime.cc
index 9e8b28b..7aa4f51 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -82,7 +82,21 @@
// Many people have difficulty distinguish aborts from crashes,
// so be explicit.
- LogMessage(file, line, ERROR, -1).stream() << "Runtime aborting...";
+ {
+ LogMessage log(file, line, ERROR, -1);
+ log.stream() << "Runtime aborting..." << std::endl;
+ // Add Java stack trace if possible
+ Thread* thread = Thread::Current();
+ if (thread != NULL) {
+ log.stream() << "Java stack trace of aborting thread:" << std::endl;
+ thread->DumpStack(log.stream());
+ if (thread->IsExceptionPending()) {
+ Throwable* e = thread->GetException();
+ log.stream() << "Pending exception on thread: " << PrettyTypeOf(e) << std::endl;
+ log.stream() << e->Dump();
+ }
+ }
+ }
// Perform any platform-specific pre-abort actions.
PlatformAbort(file, line);
diff --git a/src/thread.cc b/src/thread.cc
index d9edd60..fc7fadb 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1081,11 +1081,10 @@
jobjectArray output_array, int* stack_depth) {
// Transition into runnable state to work on Object*/Array*
ScopedJniThreadState ts(env);
-
// Decode the internal stack trace into the depth, method trace and PC trace
ObjectArray<Object>* method_trace =
down_cast<ObjectArray<Object>*>(Decode<Object*>(ts.Env(), internal));
- int32_t depth = method_trace->GetLength()-1;
+ int32_t depth = method_trace->GetLength() - 1;
IntArray* pc_trace = down_cast<IntArray*>(method_trace->Get(depth));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();