diff options
-rw-r--r-- | runtime/dex_file_verifier.cc | 37 | ||||
-rw-r--r-- | runtime/hprof/hprof.cc | 48 | ||||
-rw-r--r-- | runtime/indenter.h | 71 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.cc | 55 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.h | 8 | ||||
-rw-r--r-- | test/496-checker-inlining-and-class-loader/expected.txt | 2 | ||||
-rw-r--r-- | test/496-checker-inlining-and-class-loader/src/FirstSeenByMyClassLoader.java | 26 | ||||
-rw-r--r-- | test/496-checker-inlining-and-class-loader/src/Main.java | 17 | ||||
-rwxr-xr-x | tools/run-libcore-tests.sh | 2 |
9 files changed, 181 insertions, 85 deletions
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 5fa58f754f..90b8fdbadc 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -694,25 +694,52 @@ bool DexFileVerifier::CheckIntraClassDataItem() { std::unordered_set<uint32_t> direct_method_indexes; // These calls use the raw access flags to check whether the whole dex field is valid. - + uint32_t prev_index = 0; for (; it.HasNextStaticField(); it.Next()) { - if (!CheckClassDataItemField(it.GetMemberIndex(), it.GetRawMemberAccessFlags(), true)) { + uint32_t curr_index = it.GetMemberIndex(); + if (curr_index < prev_index) { + ErrorStringPrintf("out-of-order static field indexes %d and %d", prev_index, curr_index); + return false; + } + prev_index = curr_index; + if (!CheckClassDataItemField(curr_index, it.GetRawMemberAccessFlags(), true)) { return false; } } + prev_index = 0; for (; it.HasNextInstanceField(); it.Next()) { - if (!CheckClassDataItemField(it.GetMemberIndex(), it.GetRawMemberAccessFlags(), false)) { + uint32_t curr_index = it.GetMemberIndex(); + if (curr_index < prev_index) { + ErrorStringPrintf("out-of-order instance field indexes %d and %d", prev_index, curr_index); + return false; + } + prev_index = curr_index; + if (!CheckClassDataItemField(curr_index, it.GetRawMemberAccessFlags(), false)) { return false; } } + prev_index = 0; for (; it.HasNextDirectMethod(); it.Next()) { - if (!CheckClassDataItemMethod(it.GetMemberIndex(), it.GetRawMemberAccessFlags(), + uint32_t curr_index = it.GetMemberIndex(); + if (curr_index < prev_index) { + ErrorStringPrintf("out-of-order direct method indexes %d and %d", prev_index, curr_index); + return false; + } + prev_index = curr_index; + if (!CheckClassDataItemMethod(curr_index, it.GetRawMemberAccessFlags(), it.GetMethodCodeItemOffset(), direct_method_indexes, true)) { return false; } } + prev_index = 0; for (; it.HasNextVirtualMethod(); it.Next()) { - if (!CheckClassDataItemMethod(it.GetMemberIndex(), it.GetRawMemberAccessFlags(), + uint32_t curr_index = it.GetMemberIndex(); + if (curr_index < prev_index) { + ErrorStringPrintf("out-of-order virtual method indexes %d and %d", prev_index, curr_index); + return false; + } + prev_index = curr_index; + if (!CheckClassDataItemMethod(curr_index, it.GetRawMemberAccessFlags(), it.GetMethodCodeItemOffset(), direct_method_indexes, false)) { return false; } diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index f32d5a1b81..8ba6172395 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -1255,15 +1255,16 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) { size_t size_patch_offset = output_->Length(); __ AddU4(0x77777777); - // Write the instance data; fields for this class, followed by super class fields, - // and so on. Don't write the klass or monitor fields of Object.class. - mirror::Class* orig_klass = klass; + // What we will use for the string value if the object is a string. + mirror::Object* string_value = nullptr; + + // Write the instance data; fields for this class, followed by super class fields, and so on. do { - int ifieldCount = klass->NumInstanceFields(); - for (int i = 0; i < ifieldCount; ++i) { + const size_t instance_fields = klass->NumInstanceFields(); + for (size_t i = 0; i < instance_fields; ++i) { ArtField* f = klass->GetInstanceField(i); size_t size; - auto t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size); + HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size); switch (t) { case hprof_basic_byte: __ AddU1(f->GetByte(obj)); @@ -1288,34 +1289,35 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) { break; } } + // Add value field for String if necessary. + if (klass->IsStringClass()) { + mirror::String* s = obj->AsString(); + if (s->GetLength() == 0) { + // If string is empty, use an object-aligned address within the string for the value. + string_value = reinterpret_cast<mirror::Object*>( + reinterpret_cast<uintptr_t>(s) + kObjectAlignment); + } else { + string_value = reinterpret_cast<mirror::Object*>(s->GetValue()); + } + __ AddObjectId(string_value); + } klass = klass->GetSuperClass(); } while (klass != nullptr); + // Patch the instance field length. + __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4)); + // Output native value character array for strings. - if (orig_klass->IsStringClass()) { + CHECK_EQ(obj->IsString(), string_value != nullptr); + if (string_value != nullptr) { mirror::String* s = obj->AsString(); - mirror::Object* value; - if (s->GetLength() == 0) { - // If string is empty, use an object-aligned address within the string for the value. - value = reinterpret_cast<mirror::Object*>(reinterpret_cast<uintptr_t>(s) + kObjectAlignment); - } else { - value = reinterpret_cast<mirror::Object*>(s->GetValue()); - } - __ AddObjectId(value); - - // Patch the instance field length. - __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4)); - __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP); - __ AddObjectId(value); + __ AddObjectId(string_value); __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj)); __ AddU4(s->GetLength()); __ AddU1(hprof_basic_char); __ AddU2List(s->GetValue(), s->GetLength()); - } else { - // Patch the instance field length. - __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4)); } } diff --git a/runtime/indenter.h b/runtime/indenter.h index d055d4e3f4..38b398de4a 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -27,45 +27,76 @@ const size_t kIndentBy1Count = 2; class Indenter : public std::streambuf { public: Indenter(std::streambuf* out, char text, size_t count) - : indent_next_(true), out_sbuf_(out), text_(text), count_(count) {} + : indent_next_(true), out_sbuf_(out), + text_{text, text, text, text, text, text, text, text}, // NOLINT(whitespace/braces) + count_(count) {} private: - int_type overflow(int_type c) { + std::streamsize xsputn(const char* s, std::streamsize n) OVERRIDE { + std::streamsize result = n; // Aborts on failure. + const char* eol = static_cast<const char*>(memchr(s, '\n', n)); + while (eol != nullptr) { + size_t to_write = eol + 1 - s; + Write(s, to_write); + s += to_write; + n -= to_write; + indent_next_ = true; + eol = static_cast<const char*>(memchr(s, '\n', n)); + } + if (n != 0u) { + Write(s, n); + } + return result; + } + + int_type overflow(int_type c) OVERRIDE { if (UNLIKELY(c == std::char_traits<char>::eof())) { out_sbuf_->pubsync(); return c; } - if (indent_next_) { - for (size_t i = 0; i < count_; ++i) { - int_type r = out_sbuf_->sputc(text_); - if (UNLIKELY(r != text_)) { - out_sbuf_->pubsync(); - r = out_sbuf_->sputc(text_); - CHECK_EQ(r, text_) << "Error writing to buffer. Disk full?"; - } - } - } + char data[1] = { static_cast<char>(c) }; + Write(data, 1u); indent_next_ = (c == '\n'); - int_type r = out_sbuf_->sputc(c); - if (UNLIKELY(r != c)) { - out_sbuf_->pubsync(); - r = out_sbuf_->sputc(c); - CHECK_EQ(r, c) << "Error writing to buffer. Disk full?"; - } - return r; + return c; } int sync() { return out_sbuf_->pubsync(); } + void Write(const char* s, std::streamsize n) { + if (indent_next_) { + size_t remaining = count_; + while (remaining != 0u) { + size_t to_write = std::min(remaining, sizeof(text_)); + RawWrite(text_, to_write); + remaining -= to_write; + } + indent_next_ = false; + } + RawWrite(s, n); + } + + void RawWrite(const char* s, std::streamsize n) { + size_t written = out_sbuf_->sputn(s, n); + s += written; + n -= written; + while (n != 0u) { + out_sbuf_->pubsync(); + written = out_sbuf_->sputn(s, n); + CHECK_NE(written, 0u) << "Error writing to buffer. Disk full?"; + s += written; + n -= written; + } + } + bool indent_next_; // Buffer to write output to. std::streambuf* const out_sbuf_; // Text output as indent. - const char text_; + const char text_[8]; // Number of times text is output. const size_t count_; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index ddd1caa69e..5d685da542 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2402,9 +2402,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { StackHandleScope<1> hs(self_); mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { - return_type = ®_types_.FromClass(called_method->GetReturnTypeDescriptor(), - return_type_class, - return_type_class->CannotBeAssignedFromOtherTypes()); + return_type = &FromClass(called_method->GetReturnTypeDescriptor(), + return_type_class, + return_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); self_->ClearException(); @@ -2444,9 +2444,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { StackHandleScope<1> hs(self_); mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { - return_type = ®_types_.FromClass(return_type_descriptor, - return_type_class, - return_type_class->CannotBeAssignedFromOtherTypes()); + return_type = &FromClass(return_type_descriptor, + return_type_class, + return_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); self_->ClearException(); @@ -3195,7 +3195,7 @@ const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { const RegType& referrer = GetDeclaringClass(); mirror::Class* klass = dex_cache_->GetResolvedType(class_idx); const RegType& result = klass != nullptr ? - reg_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) : + FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) : reg_types_.FromDescriptor(GetClassLoader(), descriptor, false); if (result.IsConflict()) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor @@ -3414,8 +3414,8 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( if (res_method != nullptr && !res_method->IsMiranda()) { mirror::Class* klass = res_method->GetDeclaringClass(); std::string temp; - res_method_class = ®_types_.FromClass(klass->GetDescriptor(&temp), klass, - klass->CannotBeAssignedFromOtherTypes()); + res_method_class = &FromClass(klass->GetDescriptor(&temp), klass, + klass->CannotBeAssignedFromOtherTypes()); } else { const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_; @@ -3672,8 +3672,7 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, mirror::Class* klass = res_method->GetDeclaringClass(); std::string temp; const RegType& res_method_class = - reg_types_.FromClass(klass->GetDescriptor(&temp), klass, - klass->CannotBeAssignedFromOtherTypes()); + FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes()); if (!res_method_class.IsAssignableFrom(actual_arg_type)) { Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type @@ -3983,8 +3982,8 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id } else { mirror::Class* klass = field->GetDeclaringClass(); const RegType& field_klass = - reg_types_.FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id), - klass, klass->CannotBeAssignedFromOtherTypes()); + FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id), + klass, klass->CannotBeAssignedFromOtherTypes()); if (obj_type.IsUninitializedTypes() && (!IsConstructor() || GetDeclaringClass().Equals(obj_type) || !field_klass.Equals(GetDeclaringClass()))) { @@ -4034,8 +4033,8 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& mirror::Class* field_type_class = can_load_classes_ ? field->GetType<true>() : field->GetType<false>(); if (field_type_class != nullptr) { - field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class, - field_type_class->CannotBeAssignedFromOtherTypes()); + field_type = &FromClass(field->GetTypeDescriptor(), field_type_class, + field_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); self_->ClearException(); @@ -4146,8 +4145,8 @@ void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegTy field->GetType<false>(); if (field_type_class != nullptr) { - field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class, - field_type_class->CannotBeAssignedFromOtherTypes()); + field_type = &FromClass(field->GetTypeDescriptor(), field_type_class, + field_type_class->CannotBeAssignedFromOtherTypes()); } else { Thread* self = Thread::Current(); DCHECK(!can_load_classes_ || self->IsExceptionPending()); @@ -4339,9 +4338,9 @@ const RegType& MethodVerifier::GetMethodReturnType() { if (mirror_method_ != nullptr) { mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { - return_type_ = ®_types_.FromClass(mirror_method_->GetReturnTypeDescriptor(), - return_type_class, - return_type_class->CannotBeAssignedFromOtherTypes()); + return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(), + return_type_class, + return_type_class->CannotBeAssignedFromOtherTypes()); } else { DCHECK(!can_load_classes_ || self_->IsExceptionPending()); self_->ClearException(); @@ -4365,8 +4364,8 @@ const RegType& MethodVerifier::GetDeclaringClass() { = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); if (mirror_method_ != nullptr) { mirror::Class* klass = mirror_method_->GetDeclaringClass(); - declaring_class_ = ®_types_.FromClass(descriptor, klass, - klass->CannotBeAssignedFromOtherTypes()); + declaring_class_ = &FromClass(descriptor, klass, + klass->CannotBeAssignedFromOtherTypes()); } else { declaring_class_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false); } @@ -4466,5 +4465,17 @@ void MethodVerifier::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) reg_types_.VisitRoots(visitor, root_info); } +const RegType& MethodVerifier::FromClass(const char* descriptor, + mirror::Class* klass, + bool precise) { + DCHECK(klass != nullptr); + if (precise && !klass->IsInstantiable() && !klass->IsPrimitive()) { + Fail(VerifyError::VERIFY_ERROR_NO_CLASS) << "Could not create precise reference for " + << "non-instantiable klass " << descriptor; + precise = false; + } + return reg_types_.FromClass(descriptor, klass, precise); +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 824daf6fd4..994616f6ce 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -670,6 +670,14 @@ class MethodVerifier { const RegType& DetermineCat1Constant(int32_t value, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Try to create a register type from the given class. In case a precise type is requested, but + // the class is not instantiable, a soft error (of type NO_CLASS) will be enqueued and a + // non-precise reference will be returned. + // Note: we reuse NO_CLASS as this will throw an exception at runtime, when the failing class is + // actually touched. + const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // The thread we're verifying on. Thread* const self_; diff --git a/test/496-checker-inlining-and-class-loader/expected.txt b/test/496-checker-inlining-and-class-loader/expected.txt index c6fcb51ecf..312c28f8b0 100644 --- a/test/496-checker-inlining-and-class-loader/expected.txt +++ b/test/496-checker-inlining-and-class-loader/expected.txt @@ -1,4 +1,4 @@ Request for LoadedByMyClassLoader -Request for Main +Request for FirstSeenByMyClassLoader In between the two calls. In $noinline$bar diff --git a/test/496-checker-inlining-and-class-loader/src/FirstSeenByMyClassLoader.java b/test/496-checker-inlining-and-class-loader/src/FirstSeenByMyClassLoader.java new file mode 100644 index 0000000000..e97b4e3391 --- /dev/null +++ b/test/496-checker-inlining-and-class-loader/src/FirstSeenByMyClassLoader.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 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 class FirstSeenByMyClassLoader { + public static void $inline$bar() { + } + + public static void $noinline$bar() { + try { + System.out.println("In $noinline$bar"); + } catch (Throwable t) { /* Ignore */ } + } +} diff --git a/test/496-checker-inlining-and-class-loader/src/Main.java b/test/496-checker-inlining-and-class-loader/src/Main.java index 4f23eec4a0..39c031a6bc 100644 --- a/test/496-checker-inlining-and-class-loader/src/Main.java +++ b/test/496-checker-inlining-and-class-loader/src/Main.java @@ -82,7 +82,7 @@ class LoadedByMyClassLoader { /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after) /// CHECK: LoadClass /// CHECK-NEXT: ClinitCheck - /* We inlined Main.$inline$bar */ + /* We inlined FirstSeenByMyClassLoader.$inline$bar */ /// CHECK-NEXT: LoadClass /// CHECK-NEXT: ClinitCheck /// CHECK-NEXT: StaticFieldGet @@ -91,7 +91,7 @@ class LoadedByMyClassLoader { /// CHECK-NEXT: InvokeVirtual /// CHECK-START: void LoadedByMyClassLoader.bar() register (before) - /* Load and initialize Main */ + /* Load and initialize FirstSeenByMyClassLoader */ /// CHECK: LoadClass gen_clinit_check:true /* Load and initialize System */ /// CHECK-NEXT: LoadClass gen_clinit_check:true @@ -100,9 +100,9 @@ class LoadedByMyClassLoader { /// CHECK-NEXT: NullCheck /// CHECK-NEXT: InvokeVirtual public static void bar() { - Main.$inline$bar(); + FirstSeenByMyClassLoader.$inline$bar(); System.out.println("In between the two calls."); - Main.$noinline$bar(); + FirstSeenByMyClassLoader.$noinline$bar(); } } @@ -113,13 +113,4 @@ public class Main { Method m = foo.getDeclaredMethod("bar"); m.invoke(null); } - - public static void $inline$bar() { - } - - public static void $noinline$bar() { - try { - System.out.println("In $noinline$bar"); - } catch (Throwable t) { /* Ignore */ } - } } diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 1b8748bd99..e28de09317 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -84,7 +84,7 @@ while true; do # Increase the timeout, as vogar cannot set individual test # timeout when being asked to run packages, and some tests go above # the default timeout. - vogar_args="$vogar_args --timeout 180" + vogar_args="$vogar_args --timeout 240" shift elif [[ "$1" == "" ]]; then break |