Merge "Make 946 print stacktrace to System.out"
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 3d51fdd..99d7a49 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -223,13 +223,10 @@
}
bool IsObsolete() {
- // TODO Should maybe make this IsIntrinsic check not needed
- return !IsIntrinsic() && (GetAccessFlags() & kAccObsoleteMethod) != 0;
+ return (GetAccessFlags() & kAccObsoleteMethod) != 0;
}
void SetIsObsolete() {
- // TODO We should really support redefining intrinsic if possible.
- DCHECK(!IsIntrinsic());
AddAccessFlags(kAccObsoleteMethod);
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 46f1644..57fc909 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3798,14 +3798,10 @@
}
void ClassLinker::WriteBarrierForBootOatFileBssRoots(const OatFile* oat_file) {
- if (!kUseReadBarrier) {
- WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation();
- if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) {
- new_bss_roots_boot_oat_files_.push_back(oat_file);
- }
- } else {
- LOG(FATAL) << "UNREACHABLE";
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation();
+ if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) {
+ new_bss_roots_boot_oat_files_.push_back(oat_file);
}
}
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 47c6b51..355d7b3 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -32,12 +32,9 @@
namespace art {
static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) {
- // For non-CC AOT code, we need a write barrier for the class loader that holds the
- // GC roots in the .bss. For CC, we do not need to do anything because the roots
- // we're storing are all referencing to-space and do not need to be re-visited.
- // However, we do the DCHECK() for the registration of oat files with .bss sections.
- const DexFile* dex_file =
- (kUseReadBarrier && !kIsDebugBuild) ? nullptr : outer_method->GetDexFile();
+ // For AOT code, we need a write barrier for the class loader that holds the
+ // GC roots in the .bss.
+ const DexFile* dex_file = outer_method->GetDexFile();
if (dex_file != nullptr &&
dex_file->GetOatDexFile() != nullptr &&
!dex_file->GetOatDexFile()->GetOatFile()->GetBssGcRoots().empty()) {
@@ -50,15 +47,13 @@
<< "Oat file with .bss GC roots was not registered in class table: "
<< dex_file->GetOatDexFile()->GetOatFile()->GetLocation();
}
- if (!kUseReadBarrier) {
- if (class_loader != nullptr) {
- // Note that we emit the barrier before the compiled code stores the String or Class
- // as a GC root. This is OK as there is no suspend point point in between.
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
- } else {
- Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots(
- dex_file->GetOatDexFile()->GetOatFile());
- }
+ if (class_loader != nullptr) {
+ // Note that we emit the barrier before the compiled code stores the String or Class
+ // as a GC root. This is OK as there is no suspend point point in between.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+ } else {
+ Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots(
+ dex_file->GetOatDexFile()->GetOatFile());
}
}
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index f18ffb4..b939cbe 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -53,6 +53,8 @@
// Slow path mark stack size, increase this if the stack is getting full and it is causing
// performance problems.
static constexpr size_t kReadBarrierMarkStackSize = 512 * KB;
+// Verify that there are no missing card marks.
+static constexpr bool kVerifyNoMissingCardMarks = kIsDebugBuild;
ConcurrentCopying::ConcurrentCopying(Heap* heap,
const std::string& name_prefix,
@@ -318,6 +320,9 @@
TimingLogger::ScopedTiming split("(Paused)FlipCallback", cc->GetTimings());
// Note: self is not necessarily equal to thread since thread may be suspended.
Thread* self = Thread::Current();
+ if (kVerifyNoMissingCardMarks) {
+ cc->VerifyNoMissingCardMarks();
+ }
CHECK(thread == self);
Locks::mutator_lock_->AssertExclusiveHeld(self);
cc->region_space_->SetFromSpace(cc->rb_table_, cc->force_evacuate_all_);
@@ -428,6 +433,72 @@
}
}
+class ConcurrentCopying::VerifyNoMissingCardMarkVisitor {
+ public:
+ VerifyNoMissingCardMarkVisitor(ConcurrentCopying* cc, ObjPtr<mirror::Object> holder)
+ : cc_(cc),
+ holder_(holder) {}
+
+ void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
+ if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
+ CheckReference(obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(
+ offset), offset.Uint32Value());
+ }
+ }
+ void operator()(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
+ CHECK(klass->IsTypeOfReferenceClass());
+ this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ }
+
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CheckReference(root->AsMirrorPtr());
+ }
+
+ void CheckReference(mirror::Object* ref, uint32_t offset = 0) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(ref == nullptr || !cc_->region_space_->IsInNewlyAllocatedRegion(ref))
+ << holder_->PrettyTypeOf() << "(" << holder_.Ptr() << ") references object "
+ << ref->PrettyTypeOf() << "(" << ref << ") in newly allocated region at offset=" << offset;
+ }
+
+ private:
+ ConcurrentCopying* const cc_;
+ ObjPtr<mirror::Object> const holder_;
+};
+
+void ConcurrentCopying::VerifyNoMissingCardMarkCallback(mirror::Object* obj, void* arg) {
+ auto* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+ // Objects not on dirty cards should never have references to newly allocated regions.
+ if (!collector->heap_->GetCardTable()->IsDirty(obj)) {
+ VerifyNoMissingCardMarkVisitor visitor(collector, /*holder*/ obj);
+ obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
+ visitor,
+ visitor);
+ }
+}
+
+void ConcurrentCopying::VerifyNoMissingCardMarks() {
+ TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
+ region_space_->Walk(&VerifyNoMissingCardMarkCallback, this);
+ {
+ ReaderMutexLock rmu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ heap_->GetLiveBitmap()->Walk(&VerifyNoMissingCardMarkCallback, this);
+ }
+}
+
// Switch threads that from from-space to to-space refs. Forward/mark the thread roots.
void ConcurrentCopying::FlipThreadRoots() {
TimingLogger::ScopedTiming split("FlipThreadRoots", GetTimings());
@@ -2330,7 +2401,9 @@
MutexLock mu(self, mark_stack_lock_);
CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize);
}
- {
+ // kVerifyNoMissingCardMarks relies on the region space cards not being cleared to avoid false
+ // positives.
+ if (!kVerifyNoMissingCardMarks) {
TimingLogger::ScopedTiming split("ClearRegionSpaceCards", GetTimings());
// We do not currently use the region space cards at all, madvise them away to save ram.
heap_->GetCardTable()->ClearCardRange(region_space_->Begin(), region_space_->Limit());
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 844bb45..11060a8 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -162,6 +162,12 @@
void VerifyGrayImmuneObjects()
REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
+ static void VerifyNoMissingCardMarkCallback(mirror::Object* obj, void* arg)
+ REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_);
+ void VerifyNoMissingCardMarks()
+ REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_);
size_t ProcessThreadLocalMarkStacks(bool disable_weak_ref_access, Closure* checkpoint_callback)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
void RevokeThreadLocalMarkStacks(bool disable_weak_ref_access, Closure* checkpoint_callback)
@@ -329,6 +335,7 @@
class VerifyNoFromSpaceRefsFieldVisitor;
class VerifyNoFromSpaceRefsObjectVisitor;
class VerifyNoFromSpaceRefsVisitor;
+ class VerifyNoMissingCardMarkVisitor;
DISALLOW_IMPLICIT_CONSTRUCTORS(ConcurrentCopying);
};
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index f3b9595..feab9b0 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -176,6 +176,14 @@
return false;
}
+ bool IsInNewlyAllocatedRegion(mirror::Object* ref) {
+ if (HasAddress(ref)) {
+ Region* r = RefToRegionUnlocked(ref);
+ return r->IsNewlyAllocated();
+ }
+ return false;
+ }
+
bool IsInUnevacFromSpace(mirror::Object* ref) {
if (HasAddress(ref)) {
Region* r = RefToRegionUnlocked(ref);
@@ -351,6 +359,10 @@
return idx_;
}
+ bool IsNewlyAllocated() const {
+ return is_newly_allocated_;
+ }
+
bool IsInFromSpace() const {
return type_ == RegionType::kRegionTypeFromSpace;
}
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index adb7d8a..f6720bd 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -250,7 +250,7 @@
StackReference<mirror::Object> storage_[kNumReferences];
// Position new handles will be created.
- size_t pos_ = 0;
+ uint32_t pos_ = 0;
template<size_t kNumRefs> friend class StackHandleScope;
friend class VariableSizedHandleScope;
@@ -299,12 +299,20 @@
void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
private:
- static constexpr size_t kNumReferencesPerScope = 4;
+ static constexpr size_t kLocalScopeSize = 64u;
+ static constexpr size_t kSizeOfReferencesPerScope =
+ kLocalScopeSize
+ - /* BaseHandleScope::link_ */ sizeof(BaseHandleScope*)
+ - /* BaseHandleScope::number_of_references_ */ sizeof(int32_t)
+ - /* FixedSizeHandleScope<>::pos_ */ sizeof(uint32_t);
+ static constexpr size_t kNumReferencesPerScope =
+ kSizeOfReferencesPerScope / sizeof(StackReference<mirror::Object>);
Thread* const self_;
// Linked list of fixed size handle scopes.
using LocalScopeType = FixedSizeHandleScope<kNumReferencesPerScope>;
+ static_assert(sizeof(LocalScopeType) == kLocalScopeSize, "Unexpected size of LocalScopeType");
LocalScopeType* current_scope_;
DISALLOW_COPY_AND_ASSIGN(VariableSizedHandleScope);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index af0478c..80554c2 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1330,17 +1330,18 @@
result->SetC(string->CharAt(index));
}
-// This allows setting chars from the new style of String objects during compilation.
-void UnstartedRuntime::UnstartedStringSetCharAt(
- Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset) {
- jint index = shadow_frame->GetVReg(arg_offset + 1);
- jchar c = shadow_frame->GetVReg(arg_offset + 2);
- mirror::String* string = shadow_frame->GetVRegReference(arg_offset)->AsString();
+// This allows creating String objects with replaced characters during compilation.
+// String.doReplace(char, char) is called from String.replace(char, char) when there is a match.
+void UnstartedRuntime::UnstartedStringDoReplace(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ jchar old_c = shadow_frame->GetVReg(arg_offset + 1);
+ jchar new_c = shadow_frame->GetVReg(arg_offset + 2);
+ ObjPtr<mirror::String> string = shadow_frame->GetVRegReference(arg_offset)->AsString();
if (string == nullptr) {
- AbortTransactionOrFail(self, "String.setCharAt with null object");
+ AbortTransactionOrFail(self, "String.replaceWithMatch with null object");
return;
}
- string->SetCharAt(index, c);
+ result->SetL(string->DoReplace(self, old_c, new_c));
}
// This allows creating the new style of String objects during compilation.
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 6fc7989..e9435e4 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -63,7 +63,7 @@
V(RuntimeAvailableProcessors, "int java.lang.Runtime.availableProcessors()") \
V(StringGetCharsNoCheck, "void java.lang.String.getCharsNoCheck(int, int, char[], int)") \
V(StringCharAt, "char java.lang.String.charAt(int)") \
- V(StringSetCharAt, "void java.lang.String.setCharAt(int, char)") \
+ V(StringDoReplace, "java.lang.String java.lang.String.doReplace(char, char)") \
V(StringFactoryNewStringFromChars, "java.lang.String java.lang.StringFactory.newStringFromChars(int, int, char[])") \
V(StringFactoryNewStringFromString, "java.lang.String java.lang.StringFactory.newStringFromString(java.lang.String)") \
V(StringFastSubstring, "java.lang.String java.lang.String.fastSubstring(int, int)") \
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index c2407d7..57b20a1 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -36,7 +36,7 @@
namespace mirror {
inline uint32_t String::ClassSize(PointerSize pointer_size) {
- uint32_t vtable_entries = Object::kVTableLength + 57;
+ uint32_t vtable_entries = Object::kVTableLength + 56;
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size);
}
@@ -311,9 +311,7 @@
inline bool String::AllASCII(const MemoryType* chars, const int length) {
static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType");
for (int i = 0; i < length; ++i) {
- // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII
- // because it would complicate the detection of ASCII strings in Modified-UTF8.
- if ((chars[i] - 1u) >= 0x7fu) {
+ if (!IsASCII(chars[i])) {
return false;
}
}
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 0ab0bd6..884b88a 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -79,14 +79,55 @@
}
}
-void String::SetCharAt(int32_t index, uint16_t c) {
- DCHECK((index >= 0) && (index < GetLength()));
- if (IsCompressed()) {
- // TODO: Handle the case where String is compressed and c is non-ASCII
- GetValueCompressed()[index] = static_cast<uint8_t>(c);
- } else {
- GetValue()[index] = c;
+inline bool String::AllASCIIExcept(const uint16_t* chars, int32_t length, uint16_t non_ascii) {
+ DCHECK(!IsASCII(non_ascii));
+ for (int32_t i = 0; i < length; ++i) {
+ if (!IsASCII(chars[i]) && chars[i] != non_ascii) {
+ return false;
+ }
}
+ return true;
+}
+
+ObjPtr<String> String::DoReplace(Thread* self, uint16_t old_c, uint16_t new_c) {
+ DCHECK(IsCompressed() ? ContainsElement(ArrayRef<uint8_t>(value_compressed_, GetLength()), old_c)
+ : ContainsElement(ArrayRef<uint16_t>(value_, GetLength()), old_c));
+ int32_t length = GetLength();
+ bool compressible =
+ kUseStringCompression &&
+ IsASCII(new_c) &&
+ (IsCompressed() || (!IsASCII(old_c) && AllASCIIExcept(value_, length, old_c)));
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ const int32_t length_with_flag = String::GetFlaggedCount(GetLength(), compressible);
+ SetStringCountVisitor visitor(length_with_flag);
+ ObjPtr<String> string = Alloc<true>(self, length_with_flag, allocator_type, visitor);
+ if (UNLIKELY(string == nullptr)) {
+ return nullptr;
+ }
+ if (compressible) {
+ auto replace = [old_c, new_c](uint16_t c) {
+ return dchecked_integral_cast<uint8_t>((old_c != c) ? c : new_c);
+ };
+ uint8_t* out = string->value_compressed_;
+ if (LIKELY(IsCompressed())) { // LIKELY(compressible == IsCompressed())
+ std::transform(value_compressed_, value_compressed_ + length, out, replace);
+ } else {
+ std::transform(value_, value_ + length, out, replace);
+ }
+ DCHECK(kUseStringCompression && AllASCII(out, length));
+ } else {
+ auto replace = [old_c, new_c](uint16_t c) {
+ return (old_c != c) ? c : new_c;
+ };
+ uint16_t* out = string->value_;
+ if (UNLIKELY(IsCompressed())) { // LIKELY(compressible == IsCompressed())
+ std::transform(value_compressed_, value_compressed_ + length, out, replace);
+ } else {
+ std::transform(value_, value_ + length, out, replace);
+ }
+ DCHECK(!kUseStringCompression || !AllASCII(out, length));
+ }
+ return string;
}
String* String::AllocFromStrings(Thread* self, Handle<String> string, Handle<String> string2) {
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 38f6dd4..35ce98e 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -94,7 +94,10 @@
uint16_t CharAt(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_);
- void SetCharAt(int32_t index, uint16_t c) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Create a new string where all occurences of `old_c` are replaced with `new_c`.
+ // String.doReplace(char, char) is called from String.replace(char, char) when there is a match.
+ ObjPtr<String> DoReplace(Thread* self, uint16_t old_c, uint16_t new_c)
+ REQUIRES_SHARED(Locks::mutator_lock_);
ObjPtr<String> Intern() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -229,6 +232,14 @@
REQUIRES_SHARED(Locks::mutator_lock_);
private:
+ static constexpr bool IsASCII(uint16_t c) {
+ // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII
+ // because it would complicate the detection of ASCII strings in Modified-UTF8.
+ return (c - 1u) < 0x7fu;
+ }
+
+ static bool AllASCIIExcept(const uint16_t* chars, int32_t length, uint16_t non_ascii);
+
void SetHashCode(int32_t new_hash_code) REQUIRES_SHARED(Locks::mutator_lock_) {
// Hash code is invariant so use non-transactional mode. Also disable check as we may run inside
// a transaction.
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index ae6b31d..461f870 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -45,6 +45,9 @@
static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init>
static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
+// Set to indicate that the ArtMethod is obsolete and has a different DexCache + DexFile from its
+// declaring class. This flag may only be applied to methods.
+static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime)
// Used by a method to denote that its execution does not need to go through slow path interpreter.
static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (dex only)
// Used by a class to denote that the verifier has attempted to check it at least once.
@@ -67,10 +70,6 @@
// Set by the verifier for a method that could not be verified to follow structured locking.
static constexpr uint32_t kAccMustCountLocks = 0x02000000; // method (runtime)
-// Set to indicate that the ArtMethod is obsolete and has a different DexCache from its declaring
-// class.
-// TODO Might want to re-arrange some of these so that we can have obsolete + intrinsic methods.
-static constexpr uint32_t kAccObsoleteMethod = 0x04000000; // method (runtime)
// Set by the class linker for a method that has only one implementation for a
// virtual call.
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index f1d6ff5..1357338 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -99,9 +99,11 @@
return soa.AddLocalReference<jstring>(result);
}
-static void String_setCharAt(JNIEnv* env, jobject java_this, jint index, jchar c) {
+static jstring String_doReplace(JNIEnv* env, jobject java_this, jchar old_c, jchar new_c) {
ScopedFastNativeObjectAccess soa(env);
- soa.Decode<mirror::String>(java_this)->SetCharAt(index, c);
+ ObjPtr<mirror::String> result =
+ soa.Decode<mirror::String>(java_this)->DoReplace(soa.Self(), old_c, new_c);
+ return soa.AddLocalReference<jstring>(result);
}
static jcharArray String_toCharArray(JNIEnv* env, jobject java_this) {
@@ -114,11 +116,11 @@
NATIVE_METHOD(String, charAt, "!(I)C"),
NATIVE_METHOD(String, compareTo, "!(Ljava/lang/String;)I"),
NATIVE_METHOD(String, concat, "!(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(String, doReplace, "!(CC)Ljava/lang/String;"),
NATIVE_METHOD(String, fastIndexOf, "!(II)I"),
NATIVE_METHOD(String, fastSubstring, "!(II)Ljava/lang/String;"),
NATIVE_METHOD(String, getCharsNoCheck, "!(II[CI)V"),
NATIVE_METHOD(String, intern, "!()Ljava/lang/String;"),
- NATIVE_METHOD(String, setCharAt, "!(IC)V"),
NATIVE_METHOD(String, toCharArray, "!()[C"),
};
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index a815a60..77ca9ce 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1188,13 +1188,13 @@
ENSURE_NON_NULL(name_ptr);
switch (error) {
#define ERROR_CASE(e) case (JVMTI_ERROR_ ## e) : do { \
- jvmtiError res = CopyString(env, \
- "JVMTI_ERROR_"#e, \
- reinterpret_cast<unsigned char**>(name_ptr)); \
- if (res != OK) { \
+ jvmtiError res; \
+ JvmtiUniquePtr<char[]> copy = CopyString(env, "JVMTI_ERROR_"#e, &res); \
+ if (copy == nullptr) { \
*name_ptr = nullptr; \
return res; \
} else { \
+ *name_ptr = copy.release(); \
return OK; \
} \
} while (false)
@@ -1248,13 +1248,13 @@
ERROR_CASE(INVALID_ENVIRONMENT);
#undef ERROR_CASE
default: {
- jvmtiError res = CopyString(env,
- "JVMTI_ERROR_UNKNOWN",
- reinterpret_cast<unsigned char**>(name_ptr));
- if (res != OK) {
+ jvmtiError res;
+ JvmtiUniquePtr<char[]> copy = CopyString(env, "JVMTI_ERROR_UNKNOWN", &res);
+ if (copy == nullptr) {
*name_ptr = nullptr;
return res;
} else {
+ *name_ptr = copy.release();
return ERR(ILLEGAL_ARGUMENT);
}
}
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 106165c..99139a1 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -33,6 +33,7 @@
#define ART_RUNTIME_OPENJDKJVMTI_ART_JVMTI_H_
#include <memory>
+#include <type_traits>
#include <jni.h>
@@ -86,6 +87,7 @@
return ret_value;
}
+template <typename T>
class JvmtiDeleter {
public:
JvmtiDeleter() : env_(nullptr) {}
@@ -95,9 +97,9 @@
JvmtiDeleter(JvmtiDeleter&&) = default;
JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
- void operator()(unsigned char* ptr) const {
+ void operator()(T* ptr) const {
CHECK(env_ != nullptr);
- jvmtiError ret = env_->Deallocate(ptr);
+ jvmtiError ret = env_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
CHECK(ret == ERR(NONE));
}
@@ -105,12 +107,65 @@
mutable jvmtiEnv* env_;
};
-using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>;
+template <typename T>
+class JvmtiDeleter<T[]> {
+ public:
+ JvmtiDeleter() : env_(nullptr) {}
+ explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
+
+ JvmtiDeleter(JvmtiDeleter&) = default;
+ JvmtiDeleter(JvmtiDeleter&&) = default;
+ JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
+
+ template <typename U>
+ void operator()(U* ptr) const {
+ CHECK(env_ != nullptr);
+ jvmtiError ret = env_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
+ CHECK(ret == ERR(NONE));
+ }
+
+ private:
+ mutable jvmtiEnv* env_;
+};
+
+template <typename T>
+using JvmtiUniquePtr = std::unique_ptr<T, JvmtiDeleter<T>>;
template <typename T>
ALWAYS_INLINE
-static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
- return JvmtiUniquePtr(reinterpret_cast<unsigned char*>(mem), JvmtiDeleter(env));
+static inline JvmtiUniquePtr<T> MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
+ return JvmtiUniquePtr<T>(mem, JvmtiDeleter<T>(env));
+}
+
+template <typename T>
+ALWAYS_INLINE
+static inline JvmtiUniquePtr<T> MakeJvmtiUniquePtr(jvmtiEnv* env, unsigned char* mem) {
+ return JvmtiUniquePtr<T>(reinterpret_cast<T*>(mem), JvmtiDeleter<T>(env));
+}
+
+template <typename T>
+ALWAYS_INLINE
+static inline JvmtiUniquePtr<T> AllocJvmtiUniquePtr(jvmtiEnv* env, jvmtiError* error) {
+ unsigned char* tmp;
+ *error = env->Allocate(sizeof(T), &tmp);
+ if (*error != ERR(NONE)) {
+ return JvmtiUniquePtr<T>();
+ }
+ return JvmtiUniquePtr<T>(tmp, JvmtiDeleter<T>(env));
+}
+
+template <typename T>
+ALWAYS_INLINE
+static inline JvmtiUniquePtr<T> AllocJvmtiUniquePtr(jvmtiEnv* env,
+ size_t count,
+ jvmtiError* error) {
+ unsigned char* tmp;
+ *error = env->Allocate(sizeof(typename std::remove_extent<T>::type) * count, &tmp);
+ if (*error != ERR(NONE)) {
+ return JvmtiUniquePtr<T>();
+ }
+ return JvmtiUniquePtr<T>(reinterpret_cast<typename std::remove_extent<T>::type*>(tmp),
+ JvmtiDeleter<T>(env));
}
ALWAYS_INLINE
@@ -129,15 +184,12 @@
}
ALWAYS_INLINE
-static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) {
+static inline JvmtiUniquePtr<char[]> CopyString(jvmtiEnv* env, const char* src, jvmtiError* error) {
size_t len = strlen(src) + 1;
- unsigned char* buf;
- jvmtiError ret = env->Allocate(len, &buf);
- if (ret != ERR(NONE)) {
- return ret;
+ JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, len, error);
+ if (ret != nullptr) {
+ strcpy(ret.get(), src);
}
- strcpy(reinterpret_cast<char*>(buf), src);
- *copy = buf;
return ret;
}
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index a8a0ded..4282e38 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -673,18 +673,17 @@
return ERR(INVALID_CLASS);
}
- JvmtiUniquePtr sig_copy;
+ JvmtiUniquePtr<char[]> sig_copy;
if (signature_ptr != nullptr) {
std::string storage;
const char* descriptor = klass->GetDescriptor(&storage);
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, descriptor, &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ sig_copy = CopyString(env, descriptor, &ret);
+ if (sig_copy == nullptr) {
return ret;
}
- sig_copy = MakeJvmtiUniquePtr(env, tmp);
- *signature_ptr = reinterpret_cast<char*>(tmp);
+ *signature_ptr = sig_copy.get();
}
if (generic_ptr != nullptr) {
@@ -700,12 +699,12 @@
oss << str_array->Get(i)->ToModifiedUtf8();
}
std::string output_string = oss.str();
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, output_string.c_str(), &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ JvmtiUniquePtr<char[]> copy = CopyString(env, output_string.c_str(), &ret);
+ if (copy == nullptr) {
return ret;
}
- *generic_ptr = reinterpret_cast<char*>(tmp);
+ *generic_ptr = copy.release();
} else if (soa.Self()->IsExceptionPending()) {
// TODO: Should we report an error here?
soa.Self()->ClearException();
diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h
index dbe5da2..3c251d4 100644
--- a/runtime/openjdkjvmti/ti_class_definition.h
+++ b/runtime/openjdkjvmti/ti_class_definition.h
@@ -46,7 +46,7 @@
std::string name;
jobject protection_domain;
jint dex_len;
- JvmtiUniquePtr dex_data;
+ JvmtiUniquePtr<unsigned char> dex_data;
art::ArraySlice<const unsigned char> original_dex_file;
ArtClassDefinition() = default;
diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc
index 131e6c3..8c3f2ff 100644
--- a/runtime/openjdkjvmti/ti_field.cc
+++ b/runtime/openjdkjvmti/ti_field.cc
@@ -63,31 +63,29 @@
art::ScopedObjectAccess soa(art::Thread::Current());
art::ArtField* art_field = art::jni::DecodeArtField(field);
- JvmtiUniquePtr name_copy;
+ JvmtiUniquePtr<char[]> name_copy;
if (name_ptr != nullptr) {
const char* field_name = art_field->GetName();
if (field_name == nullptr) {
field_name = "<error>";
}
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, field_name, &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ name_copy = CopyString(env, field_name, &ret);
+ if (name_copy == nullptr) {
return ret;
}
- name_copy = MakeJvmtiUniquePtr(env, tmp);
- *name_ptr = reinterpret_cast<char*>(tmp);
+ *name_ptr = name_copy.get();
}
- JvmtiUniquePtr signature_copy;
+ JvmtiUniquePtr<char[]> signature_copy;
if (signature_ptr != nullptr) {
const char* sig = art_field->GetTypeDescriptor();
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, sig, &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ signature_copy = CopyString(env, sig, &ret);
+ if (signature_copy == nullptr) {
return ret;
}
- signature_copy = MakeJvmtiUniquePtr(env, tmp);
- *signature_ptr = reinterpret_cast<char*>(tmp);
+ *signature_ptr = signature_copy.get();
}
// TODO: Support generic signature.
@@ -102,12 +100,12 @@
oss << str_array->Get(i)->ToModifiedUtf8();
}
std::string output_string = oss.str();
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, output_string.c_str(), &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ JvmtiUniquePtr<char[]> copy = CopyString(env, output_string.c_str(), &ret);
+ if (copy == nullptr) {
return ret;
}
- *generic_ptr = reinterpret_cast<char*>(tmp);
+ *generic_ptr = copy.release();
} else if (soa.Self()->IsExceptionPending()) {
// TODO: Should we report an error here?
soa.Self()->ClearException();
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index a6cfcc1..bc73029 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -110,35 +110,32 @@
art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
art_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
- JvmtiUniquePtr name_copy;
+ JvmtiUniquePtr<char[]> name_copy;
if (name_ptr != nullptr) {
const char* method_name = art_method->GetName();
if (method_name == nullptr) {
method_name = "<error>";
}
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, method_name, &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ name_copy = CopyString(env, method_name, &ret);
+ if (name_copy == nullptr) {
return ret;
}
- name_copy = MakeJvmtiUniquePtr(env, tmp);
- *name_ptr = reinterpret_cast<char*>(tmp);
+ *name_ptr = name_copy.get();
}
- JvmtiUniquePtr signature_copy;
+ JvmtiUniquePtr<char[]> signature_copy;
if (signature_ptr != nullptr) {
const art::Signature sig = art_method->GetSignature();
std::string str = sig.ToString();
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, str.c_str(), &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ signature_copy = CopyString(env, str.c_str(), &ret);
+ if (signature_copy == nullptr) {
return ret;
}
- signature_copy = MakeJvmtiUniquePtr(env, tmp);
- *signature_ptr = reinterpret_cast<char*>(tmp);
+ *signature_ptr = signature_copy.get();
}
- // TODO: Support generic signature.
if (generic_ptr != nullptr) {
*generic_ptr = nullptr;
if (!art_method->GetDeclaringClass()->IsProxyClass()) {
@@ -150,12 +147,12 @@
oss << str_array->Get(i)->ToModifiedUtf8();
}
std::string output_string = oss.str();
- unsigned char* tmp;
- jvmtiError ret = CopyString(env, output_string.c_str(), &tmp);
- if (ret != ERR(NONE)) {
+ jvmtiError ret;
+ JvmtiUniquePtr<char[]> generic_copy = CopyString(env, output_string.c_str(), &ret);
+ if (generic_copy == nullptr) {
return ret;
}
- *generic_ptr = reinterpret_cast<char*>(tmp);
+ *generic_ptr = generic_copy.release();
} else if (soa.Self()->IsExceptionPending()) {
// TODO: Should we report an error here?
soa.Self()->ClearException();
diff --git a/runtime/openjdkjvmti/ti_properties.cc b/runtime/openjdkjvmti/ti_properties.cc
index 46b9e71..4f4f013 100644
--- a/runtime/openjdkjvmti/ti_properties.cc
+++ b/runtime/openjdkjvmti/ti_properties.cc
@@ -82,71 +82,69 @@
static constexpr const char* kPropertyLibraryPath = "java.library.path";
static constexpr const char* kPropertyClassPath = "java.class.path";
-static jvmtiError Copy(jvmtiEnv* env, const char* in, char** out) {
- unsigned char* data = nullptr;
- jvmtiError result = CopyString(env, in, &data);
- *out = reinterpret_cast<char*>(data);
- return result;
-}
-
jvmtiError PropertiesUtil::GetSystemProperties(jvmtiEnv* env,
jint* count_ptr,
char*** property_ptr) {
if (count_ptr == nullptr || property_ptr == nullptr) {
return ERR(NULL_POINTER);
}
- unsigned char* array_data;
- jvmtiError array_alloc_result = env->Allocate((kPropertiesSize + 2) * sizeof(char*), &array_data);
- if (array_alloc_result != ERR(NONE)) {
+ jvmtiError array_alloc_result;
+ JvmtiUniquePtr<char*[]> array_data_ptr = AllocJvmtiUniquePtr<char*[]>(env,
+ kPropertiesSize + 2,
+ &array_alloc_result);
+ if (array_data_ptr == nullptr) {
return array_alloc_result;
}
- JvmtiUniquePtr array_data_ptr = MakeJvmtiUniquePtr(env, array_data);
- char** array = reinterpret_cast<char**>(array_data);
- std::vector<JvmtiUniquePtr> property_copies;
+ std::vector<JvmtiUniquePtr<char[]>> property_copies;
{
- char* libpath_data;
- jvmtiError libpath_result = Copy(env, kPropertyLibraryPath, &libpath_data);
- if (libpath_result != ERR(NONE)) {
+ jvmtiError libpath_result;
+ JvmtiUniquePtr<char[]> libpath_data = CopyString(env, kPropertyLibraryPath, &libpath_result);
+ if (libpath_data == nullptr) {
return libpath_result;
}
- array[0] = libpath_data;
- property_copies.push_back(MakeJvmtiUniquePtr(env, libpath_data));
+ array_data_ptr.get()[0] = libpath_data.get();
+ property_copies.push_back(std::move(libpath_data));
}
{
- char* classpath_data;
- jvmtiError classpath_result = Copy(env, kPropertyClassPath, &classpath_data);
- if (classpath_result != ERR(NONE)) {
+ jvmtiError classpath_result;
+ JvmtiUniquePtr<char[]> classpath_data = CopyString(env, kPropertyClassPath, &classpath_result);
+ if (classpath_data == nullptr) {
return classpath_result;
}
- array[1] = classpath_data;
- property_copies.push_back(MakeJvmtiUniquePtr(env, classpath_data));
+ array_data_ptr.get()[1] = classpath_data.get();
+ property_copies.push_back(std::move(classpath_data));
}
for (size_t i = 0; i != kPropertiesSize; ++i) {
- char* data;
- jvmtiError data_result = Copy(env, kProperties[i][0], &data);
- if (data_result != ERR(NONE)) {
+ jvmtiError data_result;
+ JvmtiUniquePtr<char[]> data = CopyString(env, kProperties[i][0], &data_result);
+ if (data == nullptr) {
return data_result;
}
- array[i + 2] = data;
- property_copies.push_back(MakeJvmtiUniquePtr(env, data));
+ array_data_ptr.get()[i + 2] = data.get();
+ property_copies.push_back(std::move(data));
}
// Everything is OK, release the data.
- array_data_ptr.release();
+ *count_ptr = kPropertiesSize + 2;
+ *property_ptr = array_data_ptr.release();
for (auto& uptr : property_copies) {
uptr.release();
}
- *count_ptr = kPropertiesSize + 2;
- *property_ptr = array;
-
return ERR(NONE);
}
+static jvmtiError Copy(jvmtiEnv* env, const char* in, char** out) {
+ jvmtiError result;
+ JvmtiUniquePtr<char[]> data = CopyString(env, in, &result);
+ *out = data.release();
+ return result;
+}
+
jvmtiError PropertiesUtil::GetSystemProperty(jvmtiEnv* env,
const char* property,
char** value_ptr) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 8436045..16e93db 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -170,10 +170,6 @@
// We cannot ensure that the right dex file is used in inlined frames so we don't support
// redefining them.
DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition";
- // TODO We should really support intrinsic obsolete methods.
- // TODO We should really support redefining intrinsics.
- // We don't support intrinsics so check for them here.
- DCHECK(!old_method->IsIntrinsic());
art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method);
if (new_obsolete_method == nullptr) {
// Create a new Obsolete Method and put it in the list.
@@ -396,8 +392,8 @@
*error_msg_ = "Unable to get class signature!";
return ret;
}
- JvmtiUniquePtr generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused));
- JvmtiUniquePtr signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
+ JvmtiUniquePtr<char> generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused));
+ JvmtiUniquePtr<char> signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
def.dex_len,
def.dex_data.get(),
@@ -518,6 +514,11 @@
CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
// Add all the declared methods to the map
for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ if (m.IsIntrinsic()) {
+ LOG(WARNING) << "Redefining intrinsic method " << m.PrettyMethod() << ". This may cause the "
+ << "unexpected use of the original definition of " << m.PrettyMethod() << "in "
+ << "methods that have already been compiled.";
+ }
// It is possible to simply filter out some methods where they cannot really become obsolete,
// such as native methods and keep their original (possibly optimized) implementations. We don't
// do this, however, since we would need to mark these functions (still in the classes
@@ -526,8 +527,6 @@
// error checking from the interpreter which ensure we don't try to start executing obsolete
// methods.
ctx.obsolete_methods.insert(&m);
- // TODO Allow this or check in IsModifiableClass.
- DCHECK(!m.IsIntrinsic());
}
{
art::MutexLock mu(driver_->self_, *art::Locks::thread_list_lock_);
@@ -1143,6 +1142,10 @@
holder.GetOriginalDexFileBytes(counter));
counter++;
}
+ // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any
+ // are, force a full-world deoptimization before finishing redefinition. If we don't do this then
+ // methods that have been jitted prior to the current redefinition being applied might continue
+ // to use the old versions of the intrinsics!
// TODO Shrink the obsolete method maps if possible?
// TODO Put this into a scoped thing.
runtime_->GetThreadList()->ResumeAll();
@@ -1193,6 +1196,8 @@
linker->SetEntryPointsToInterpreter(&method);
method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size);
+ // Clear all the intrinsics related flags.
+ method.ClearAccessFlags(art::kAccIntrinsic | (~art::kAccFlagsNotUsedByIntrinsic));
// Notify the jit that this method is redefined.
art::jit::Jit* jit = driver_->runtime_->GetJit();
if (jit != nullptr) {
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index f8f8fa6..788ac30 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -186,17 +186,17 @@
return ERR(INVALID_THREAD);
}
- JvmtiUniquePtr name_uptr;
+ JvmtiUniquePtr<char[]> name_uptr;
if (self != nullptr) {
// Have a native thread object, this thread is alive.
std::string name;
self->GetThreadName(name);
- jvmtiError name_result = CopyString(
- env, name.c_str(), reinterpret_cast<unsigned char**>(&info_ptr->name));
- if (name_result != ERR(NONE)) {
+ jvmtiError name_result;
+ name_uptr = CopyString(env, name.c_str(), &name_result);
+ if (name_uptr == nullptr) {
return name_result;
}
- name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name);
+ info_ptr->name = name_uptr.get();
info_ptr->priority = self->GetNativePriority();
@@ -239,12 +239,12 @@
} else {
name_cstr = "";
}
- jvmtiError name_result = CopyString(
- env, name_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name));
- if (name_result != ERR(NONE)) {
+ jvmtiError name_result;
+ name_uptr = CopyString(env, name_cstr, &name_result);
+ if (name_uptr == nullptr) {
return name_result;
}
- name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name);
+ info_ptr->name = name_uptr.get();
}
// Priority.
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc
index 1423874..df14333 100644
--- a/runtime/openjdkjvmti/ti_threadgroup.cc
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -116,11 +116,12 @@
tmp_str = name_obj->ToModifiedUtf8();
tmp_cstr = tmp_str.c_str();
}
- jvmtiError result =
- CopyString(env, tmp_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name));
- if (result != ERR(NONE)) {
+ jvmtiError result;
+ JvmtiUniquePtr<char[]> copy = CopyString(env, tmp_cstr, &result);
+ if (copy == nullptr) {
return result;
}
+ info_ptr->name = copy.release();
}
// Parent.
@@ -239,45 +240,38 @@
std::vector<art::ObjPtr<art::mirror::Object>> thread_groups;
GetChildThreadGroups(thread_group, &thread_groups);
- jthread* thread_data = nullptr;
- JvmtiUniquePtr peers_uptr;
+ JvmtiUniquePtr<jthread[]> peers_uptr;
if (!thread_peers.empty()) {
- unsigned char* data;
- jvmtiError res = env->Allocate(sizeof(jthread) * thread_peers.size(), &data);
- if (res != ERR(NONE)) {
+ jvmtiError res;
+ peers_uptr = AllocJvmtiUniquePtr<jthread[]>(env, thread_peers.size(), &res);
+ if (peers_uptr == nullptr) {
return res;
}
- thread_data = reinterpret_cast<jthread*>(data);
- peers_uptr = MakeJvmtiUniquePtr(env, data);
}
- jthreadGroup* group_data = nullptr;
+ JvmtiUniquePtr<jthreadGroup[]> group_uptr;
if (!thread_groups.empty()) {
- unsigned char* data;
- jvmtiError res = env->Allocate(sizeof(jthreadGroup) * thread_groups.size(), &data);
- if (res != ERR(NONE)) {
+ jvmtiError res;
+ group_uptr = AllocJvmtiUniquePtr<jthreadGroup[]>(env, thread_groups.size(), &res);
+ if (group_uptr == nullptr) {
return res;
}
- group_data = reinterpret_cast<jthreadGroup*>(data);
}
// Can't fail anymore from here on.
// Copy data into out buffers.
for (size_t i = 0; i != thread_peers.size(); ++i) {
- thread_data[i] = soa.AddLocalReference<jthread>(thread_peers[i]);
+ peers_uptr[i] = soa.AddLocalReference<jthread>(thread_peers[i]);
}
for (size_t i = 0; i != thread_groups.size(); ++i) {
- group_data[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]);
+ group_uptr[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]);
}
*thread_count_ptr = static_cast<jint>(thread_peers.size());
- *threads_ptr = thread_data;
+ *threads_ptr = peers_uptr.release();
*group_count_ptr = static_cast<jint>(thread_groups.size());
- *groups_ptr = group_data;
-
- // Everything's fine.
- peers_uptr.release();
+ *groups_ptr = group_uptr.release();
return ERR(NONE);
}
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index dd89d64..e2a1001 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -33,7 +33,7 @@
14 (class java.lang.Short)
[java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)]
+[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
0
diff --git a/test/950-redefine-intrinsic/expected.txt b/test/950-redefine-intrinsic/expected.txt
new file mode 100644
index 0000000..1264c94
--- /dev/null
+++ b/test/950-redefine-intrinsic/expected.txt
@@ -0,0 +1 @@
+Finished!
diff --git a/test/950-redefine-intrinsic/info.txt b/test/950-redefine-intrinsic/info.txt
new file mode 100644
index 0000000..c19d2b4
--- /dev/null
+++ b/test/950-redefine-intrinsic/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that we are able to redefine intrinsic functions.
diff --git a/test/950-redefine-intrinsic/run b/test/950-redefine-intrinsic/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/950-redefine-intrinsic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+./default-run "$@" --jvmti
diff --git a/test/950-redefine-intrinsic/src/Main.java b/test/950-redefine-intrinsic/src/Main.java
new file mode 100644
index 0000000..30cd3ab
--- /dev/null
+++ b/test/950-redefine-intrinsic/src/Main.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+import java.util.Random;
+import java.util.function.*;
+import java.util.stream.*;
+
+public class Main {
+
+ // The bytes below define the following java program.
+ // package java.lang;
+ // import java.math.*;
+ // public final class Long extends Number implements Comparable<Long> {
+ // public static final long MIN_VALUE = 0;
+ // public static final long MAX_VALUE = 0;
+ // public static final Class<Long> TYPE = null;
+ // static { }
+ // // Used for Stream.count for some reason.
+ // public static long sum(long a, long b) {
+ // return a + b;
+ // }
+ // // Used in stream/lambda functions.
+ // public Long(long value) {
+ // this.value = value;
+ // }
+ // // Used in stream/lambda functions.
+ // public static Long valueOf(long l) {
+ // return new Long(l);
+ // }
+ // // Intrinsic! Do something cool. Return i + 1
+ // public static long highestOneBit(long i) {
+ // return i + 1;
+ // }
+ // // Intrinsic! Do something cool. Return i - 1
+ // public static long lowestOneBit(long i) {
+ // return i - 1;
+ // }
+ // // Intrinsic! Do something cool. Return i + i
+ // public static int numberOfLeadingZeros(long i) {
+ // return (int)(i + i);
+ // }
+ // // Intrinsic! Do something cool. Return i & (i >>> 1);
+ // public static int numberOfTrailingZeros(long i) {
+ // return (int)(i & (i >>> 1));
+ // }
+ // // Intrinsic! Do something cool. Return 5
+ // public static int bitCount(long i) {
+ // return 5;
+ // }
+ // // Intrinsic! Do something cool. Return i
+ // public static long rotateLeft(long i, int distance) {
+ // return i;
+ // }
+ // // Intrinsic! Do something cool. Return 10 * i
+ // public static long rotateRight(long i, int distance) {
+ // return 10 * i;
+ // }
+ // // Intrinsic! Do something cool. Return -i
+ // public static long reverse(long i) {
+ // return -i;
+ // }
+ // // Intrinsic! Do something cool. Return 0
+ // public static int signum(long i) {
+ // return 0;
+ // }
+ // // Intrinsic! Do something cool. Return 0
+ // public static long reverseBytes(long i) {
+ // return 0;
+ // }
+ // public String toString() {
+ // return "Redefined Long! value (as double)=" + ((double)value);
+ // }
+ // public static String toString(long i, int radix) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static String toUnsignedString(long i, int radix) {
+ // throw new Error("Method redefined away!");
+ // }
+ // private static BigInteger toUnsignedBigInteger(long i) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static String toHexString(long i) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static String toOctalString(long i) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static String toBinaryString(long i) {
+ // throw new Error("Method redefined away!");
+ // }
+ // static String toUnsignedString0(long val, int shift) {
+ // throw new Error("Method redefined away!");
+ // }
+ // static int formatUnsignedLong(long val, int shift, char[] buf, int offset, int len) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static String toString(long i) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static String toUnsignedString(long i) {
+ // throw new Error("Method redefined away!");
+ // }
+ // static void getChars(long i, int index, char[] buf) {
+ // throw new Error("Method redefined away!");
+ // }
+ // static int stringSize(long x) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static long parseLong(String s, int radix) throws NumberFormatException {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static long parseLong(String s) throws NumberFormatException {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static long parseUnsignedLong(String s, int radix) throws NumberFormatException {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static long parseUnsignedLong(String s) throws NumberFormatException {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static Long valueOf(String s, int radix) throws NumberFormatException {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static Long valueOf(String s) throws NumberFormatException {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static Long decode(String nm) throws NumberFormatException {
+ // throw new Error("Method redefined away!");
+ // }
+ // private final long value;
+ // public Long(String s) throws NumberFormatException {
+ // this(0);
+ // throw new Error("Method redefined away!");
+ // }
+ // public byte byteValue() {
+ // throw new Error("Method redefined away!");
+ // }
+ // public short shortValue() {
+ // throw new Error("Method redefined away!");
+ // }
+ // public int intValue() {
+ // throw new Error("Method redefined away!");
+ // }
+ // public long longValue() {
+ // return value;
+ // }
+ // public float floatValue() {
+ // throw new Error("Method redefined away!");
+ // }
+ // public double doubleValue() {
+ // throw new Error("Method redefined away!");
+ // }
+ // public int hashCode() {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static int hashCode(long value) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public boolean equals(Object obj) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static Long getLong(String nm) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static Long getLong(String nm, long val) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static Long getLong(String nm, Long val) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public int compareTo(Long anotherLong) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static int compare(long x, long y) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static int compareUnsigned(long x, long y) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static long divideUnsigned(long dividend, long divisor) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static long remainderUnsigned(long dividend, long divisor) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static final int SIZE = 64;
+ // public static final int BYTES = SIZE / Byte.SIZE;
+ // public static long max(long a, long b) {
+ // throw new Error("Method redefined away!");
+ // }
+ // public static long min(long a, long b) {
+ // throw new Error("Method redefined away!");
+ // }
+ // private static final long serialVersionUID = 0;
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAiQUAAAAAAAAACgcAdQoAAwB2CAB3CgADAHgJAA0AeQoAAwB6CgADAHsHAHwIAH0K" +
+ "AAoAfgcAfwoADQCACgASAHYKAA0AgQkADQCCBwCDBwCEAQAJTUlOX1ZBTFVFAQABSgEADUNvbnN0" +
+ "YW50VmFsdWUFAAAAAAAAAAABAAlNQVhfVkFMVUUBAARUWVBFAQARTGphdmEvbGFuZy9DbGFzczsB" +
+ "AAlTaWduYXR1cmUBACNMamF2YS9sYW5nL0NsYXNzPExqYXZhL2xhbmcvTG9uZzs+OwEABXZhbHVl" +
+ "AQAEU0laRQEAAUkDAAAAQAEABUJZVEVTAwAAAAgBABBzZXJpYWxWZXJzaW9uVUlEAQANaGlnaGVz" +
+ "dE9uZUJpdAEABChKKUoBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAMbG93ZXN0T25lQml0AQAU" +
+ "bnVtYmVyT2ZMZWFkaW5nWmVyb3MBAAQoSilJAQAVbnVtYmVyT2ZUcmFpbGluZ1plcm9zAQAIYml0" +
+ "Q291bnQBAApyb3RhdGVMZWZ0AQAFKEpJKUoBAAtyb3RhdGVSaWdodAEAB3JldmVyc2UBAAZzaWdu" +
+ "dW0BAAxyZXZlcnNlQnl0ZXMBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAWKEpJ" +
+ "KUxqYXZhL2xhbmcvU3RyaW5nOwEAEHRvVW5zaWduZWRTdHJpbmcBABR0b1Vuc2lnbmVkQmlnSW50" +
+ "ZWdlcgEAGShKKUxqYXZhL21hdGgvQmlnSW50ZWdlcjsBAAt0b0hleFN0cmluZwEAFShKKUxqYXZh" +
+ "L2xhbmcvU3RyaW5nOwEADXRvT2N0YWxTdHJpbmcBAA50b0JpbmFyeVN0cmluZwEAEXRvVW5zaWdu" +
+ "ZWRTdHJpbmcwAQASZm9ybWF0VW5zaWduZWRMb25nAQAJKEpJW0NJSSlJAQAIZ2V0Q2hhcnMBAAco" +
+ "SklbQylWAQAKc3RyaW5nU2l6ZQEACXBhcnNlTG9uZwEAFihMamF2YS9sYW5nL1N0cmluZztJKUoB" +
+ "AApFeGNlcHRpb25zBwCFAQAVKExqYXZhL2xhbmcvU3RyaW5nOylKAQARcGFyc2VVbnNpZ25lZExv" +
+ "bmcBAAd2YWx1ZU9mAQAlKExqYXZhL2xhbmcvU3RyaW5nO0kpTGphdmEvbGFuZy9Mb25nOwEAJChM" +
+ "amF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Mb25nOwEAEyhKKUxqYXZhL2xhbmcvTG9uZzsB" +
+ "AAZkZWNvZGUBAAY8aW5pdD4BAAQoSilWAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAJYnl0ZVZh" +
+ "bHVlAQADKClCAQAKc2hvcnRWYWx1ZQEAAygpUwEACGludFZhbHVlAQADKClJAQAJbG9uZ1ZhbHVl" +
+ "AQADKClKAQAKZmxvYXRWYWx1ZQEAAygpRgEAC2RvdWJsZVZhbHVlAQADKClEAQAIaGFzaENvZGUB" +
+ "AAZlcXVhbHMBABUoTGphdmEvbGFuZy9PYmplY3Q7KVoBAAdnZXRMb25nAQAlKExqYXZhL2xhbmcv" +
+ "U3RyaW5nO0opTGphdmEvbGFuZy9Mb25nOwEANChMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5n" +
+ "L0xvbmc7KUxqYXZhL2xhbmcvTG9uZzsBAAljb21wYXJlVG8BABMoTGphdmEvbGFuZy9Mb25nOylJ" +
+ "AQAHY29tcGFyZQEABShKSilJAQAPY29tcGFyZVVuc2lnbmVkAQAOZGl2aWRlVW5zaWduZWQBAAUo" +
+ "SkopSgEAEXJlbWFpbmRlclVuc2lnbmVkAQADc3VtAQADbWF4AQADbWluAQAVKExqYXZhL2xhbmcv" +
+ "T2JqZWN0OylJAQAIPGNsaW5pdD4BAAMoKVYBADpMamF2YS9sYW5nL051bWJlcjtMamF2YS9sYW5n" +
+ "L0NvbXBhcmFibGU8TGphdmEvbGFuZy9Mb25nOz47AQAKU291cmNlRmlsZQEACUxvbmcuamF2YQEA" +
+ "F2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDABPAHEBACJSZWRlZmluZWQgTG9uZyEgdmFsdWUgKGFz" +
+ "IGRvdWJsZSk9DACGAIcMAB4AFQwAhgCIDAA0ADUBAA9qYXZhL2xhbmcvRXJyb3IBABZNZXRob2Qg" +
+ "cmVkZWZpbmVkIGF3YXkhDABPAFEBAA5qYXZhL2xhbmcvTG9uZwwATwBQDABkAGUMABoAGwEAEGph" +
+ "dmEvbGFuZy9OdW1iZXIBABRqYXZhL2xhbmcvQ29tcGFyYWJsZQEAH2phdmEvbGFuZy9OdW1iZXJG" +
+ "b3JtYXRFeGNlcHRpb24BAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcv" +
+ "U3RyaW5nQnVpbGRlcjsBABwoRClMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ADEADQASAAEAEwAH" +
+ "ABkAFAAVAAEAFgAAAAIAFwAZABkAFQABABYAAAACABcAGQAaABsAAQAcAAAAAgAdABIAHgAVAAAA" +
+ "GQAfACAAAQAWAAAAAgAhABkAIgAgAAEAFgAAAAIAIwAaACQAFQABABYAAAACABcANwAJACUAJgAB" +
+ "ACcAAAAcAAQAAgAAAAQeCmGtAAAAAQAoAAAABgABAAAADgAJACkAJgABACcAAAAcAAQAAgAAAAQe" +
+ "CmWtAAAAAQAoAAAABgABAAAAEwAJACoAKwABACcAAAAdAAQAAgAAAAUeHmGIrAAAAAEAKAAAAAYA" +
+ "AQAAABgACQAsACsAAQAnAAAAHwAFAAIAAAAHHh4EfX+IrAAAAAEAKAAAAAYAAQAAAB0ACQAtACsA" +
+ "AQAnAAAAGgABAAIAAAACCKwAAAABACgAAAAGAAEAAAAiAAkALgAvAAEAJwAAABoAAgADAAAAAh6t" +
+ "AAAAAQAoAAAABgABAAAAJwAJADAALwABACcAAAAeAAQAAwAAAAYUAAEeaa0AAAABACgAAAAGAAEA" +
+ "AAAsAAkAMQAmAAEAJwAAABsAAgACAAAAAx51rQAAAAEAKAAAAAYAAQAAADEACQAyACsAAQAnAAAA" +
+ "GgABAAIAAAACA6wAAAABACgAAAAGAAEAAAA2AAkAMwAmAAEAJwAAABoAAgACAAAAAgmtAAAAAQAo" +
+ "AAAABgABAAAAOwABADQANQABACcAAAAwAAMAAQAAABi7AANZtwAEEgW2AAYqtAAHirYACLYACbAA" +
+ "AAABACgAAAAGAAEAAAA/AAkANAA2AAEAJwAAACIAAwADAAAACrsAClkSC7cADL8AAAABACgAAAAG" +
+ "AAEAAABDAAkANwA2AAEAJwAAACIAAwADAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABGAAoA" +
+ "OAA5AAEAJwAAACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABKAAkAOgA7AAEAJwAA" +
+ "ACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABNAAkAPAA7AAEAJwAAACIAAwACAAAA" +
+ "CrsAClkSC7cADL8AAAABACgAAAAGAAEAAABRAAkAPQA7AAEAJwAAACIAAwACAAAACrsAClkSC7cA" +
+ "DL8AAAABACgAAAAGAAEAAABVAAgAPgA2AAEAJwAAACIAAwADAAAACrsAClkSC7cADL8AAAABACgA" +
+ "AAAGAAEAAABZAAgAPwBAAAEAJwAAACIAAwAGAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABd" +
+ "AAkANAA7AAEAJwAAACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABhAAkANwA7AAEA" +
+ "JwAAACIAAwACAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABlAAgAQQBCAAEAJwAAACIAAwAE" +
+ "AAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAABpAAgAQwArAAEAJwAAACIAAwACAAAACrsAClkS" +
+ "C7cADL8AAAABACgAAAAGAAEAAABtAAkARABFAAIAJwAAACIAAwACAAAACrsAClkSC7cADL8AAAAB" +
+ "ACgAAAAGAAEAAABxAEYAAAAEAAEARwAJAEQASAACACcAAAAiAAMAAQAAAAq7AApZEgu3AAy/AAAA" +
+ "AQAoAAAABgABAAAAdQBGAAAABAABAEcACQBJAEUAAgAnAAAAIgADAAIAAAAKuwAKWRILtwAMvwAA" +
+ "AAEAKAAAAAYAAQAAAHkARgAAAAQAAQBHAAkASQBIAAIAJwAAACIAAwABAAAACrsAClkSC7cADL8A" +
+ "AAABACgAAAAGAAEAAAB9AEYAAAAEAAEARwAJAEoASwACACcAAAAiAAMAAgAAAAq7AApZEgu3AAy/" +
+ "AAAAAQAoAAAABgABAAAAgQBGAAAABAABAEcACQBKAEwAAgAnAAAAIgADAAEAAAAKuwAKWRILtwAM" +
+ "vwAAAAEAKAAAAAYAAQAAAIQARgAAAAQAAQBHAAkASgBNAAEAJwAAACEABAACAAAACbsADVketwAO" +
+ "sAAAAAEAKAAAAAYAAQAAAIcACQBOAEwAAgAnAAAAIgADAAEAAAAKuwAKWRILtwAMvwAAAAEAKAAA" +
+ "AAYAAQAAAIsARgAAAAQAAQBHAAEATwBQAAEAJwAAACoAAwADAAAACiq3AA8qH7UAB7EAAAABACgA" +
+ "AAAOAAMAAACQAAQAkQAJAJIAAQBPAFEAAgAnAAAAKwADAAIAAAAPKgm3AA67AApZEgu3AAy/AAAA" +
+ "AQAoAAAACgACAAAAlQAFAJYARgAAAAQAAQBHAAEAUgBTAAEAJwAAACIAAwABAAAACrsAClkSC7cA" +
+ "DL8AAAABACgAAAAGAAEAAACaAAEAVABVAAEAJwAAACIAAwABAAAACrsAClkSC7cADL8AAAABACgA" +
+ "AAAGAAEAAACeAAEAVgBXAAEAJwAAACIAAwABAAAACrsAClkSC7cADL8AAAABACgAAAAGAAEAAACi" +
+ "AAEAWABZAAEAJwAAAB0AAgABAAAABSq0AAetAAAAAQAoAAAABgABAAAApgABAFoAWwABACcAAAAi" +
+ "AAMAAQAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAqgABAFwAXQABACcAAAAiAAMAAQAAAAq7" +
+ "AApZEgu3AAy/AAAAAQAoAAAABgABAAAArgABAF4AVwABACcAAAAiAAMAAQAAAAq7AApZEgu3AAy/" +
+ "AAAAAQAoAAAABgABAAAAsgAJAF4AKwABACcAAAAiAAMAAgAAAAq7AApZEgu3AAy/AAAAAQAoAAAA" +
+ "BgABAAAAtgABAF8AYAABACcAAAAiAAMAAgAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAugAJ" +
+ "AGEATAABACcAAAAiAAMAAQAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAvgAJAGEAYgABACcA" +
+ "AAAiAAMAAwAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAwgAJAGEAYwABACcAAAAiAAMAAgAA" +
+ "AAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAAxgABAGQAZQABACcAAAAiAAMAAgAAAAq7AApZEgu3" +
+ "AAy/AAAAAQAoAAAABgABAAAAyQAJAGYAZwABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAo" +
+ "AAAABgABAAAAzQAJAGgAZwABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAA" +
+ "0QAJAGkAagABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAA1QAJAGsAagAB" +
+ "ACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAABgABAAAA2QAJAGwAagABACcAAAAcAAQA" +
+ "BAAAAAQeIGGtAAAAAQAoAAAABgABAAAA4AAJAG0AagABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/" +
+ "AAAAAQAoAAAABgABAAAA5AAJAG4AagABACcAAAAiAAMABAAAAAq7AApZEgu3AAy/AAAAAQAoAAAA" +
+ "BgABAAAA5xBBAGQAbwABACcAAAAhAAIAAgAAAAkqK8AADbYAEKwAAAABACgAAAAGAAEAAAAFAAgA" +
+ "cABxAAEAJwAAACEAAQAAAAAABQGzABGxAAAAAQAoAAAACgACAAAACAAEAAoAAgAcAAAAAgByAHMA" +
+ "AAACAHQ=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAFtMupmeDN6Ck5nxdemGsp43KmLNpYLrMYFgAAcAAAAHhWNBIAAAAAAAAAAEgVAABl" +
+ "AAAAcAAAABUAAAAEAgAAIAAAAFgCAAAHAAAA2AMAAD0AAAAQBAAAAQAAAPgFAAAAEAAAGAYAAB4O" +
+ "AAAhDgAAKw4AADMOAAA3DgAAOg4AAEEOAABEDgAARw4AAEoOAABODgAAVg4AAFsOAABfDgAAYg4A" +
+ "AGYOAABrDgAAcA4AAHQOAAB5DgAAfA4AAIAOAACEDgAAiQ4AAI0OAACSDgAAlw4AAJwOAAC7DgAA" +
+ "1w4AAOkOAAD8DgAAEw8AACsPAAA+DwAAUA8AAGQPAACHDwAAmw8AAK8PAADKDwAA4g8AAO0PAAD4" +
+ "DwAAAxAAABsQAAA/EAAAQhAAAEgQAABOEAAAURAAAFUQAABbEAAAXxAAAGIQAABmEAAAahAAAHIQ" +
+ "AAB8EAAAhxAAAJAQAACbEAAArBAAALQQAADEEAAA0RAAAOUQAADtEAAA+RAAAA0RAAAXEQAAIBEA" +
+ "ACoRAAA5EQAAQxEAAE4RAABcEQAAYREAAGYRAAB8EQAAkxEAAJ4RAACxEQAAxBEAAM0RAADbEQAA" +
+ "5xEAAPQRAAAGEgAAEhIAABoSAAAmEgAAKxIAADsSAABIEgAAVxIAAGESAAB3EgAAiRIAAJwSAACj" +
+ "EgAABAAAAAYAAAAHAAAACAAAAA0AAAAbAAAAHAAAAB4AAAAgAAAAIQAAACIAAAAjAAAAJAAAACUA" +
+ "AAAmAAAAJwAAACgAAAAuAAAAMQAAADUAAAA3AAAABAAAAAAAAAAAAAAABgAAAAEAAAAAAAAABwAA" +
+ "AAIAAAAAAAAACAAAAAMAAAAAAAAACQAAAAMAAAC0DQAACgAAAAMAAAC8DQAACwAAAAMAAADMDQAA" +
+ "DAAAAAMAAADUDQAADAAAAAMAAADcDQAADQAAAAQAAAAAAAAADgAAAAQAAAC0DQAADwAAAAQAAADk" +
+ "DQAAEAAAAAQAAADMDQAAEQAAAAQAAADsDQAAEgAAAAQAAAD0DQAAFQAAAAoAAAC0DQAAFwAAAAoA" +
+ "AADsDQAAGAAAAAoAAAD0DQAAGQAAAAoAAAD8DQAAGgAAAAoAAAAEDgAAEwAAAA4AAAAAAAAAFQAA" +
+ "AA4AAAC0DQAAFgAAAA4AAADkDQAAFAAAAA8AAAAMDgAAFwAAAA8AAADsDQAAFQAAABAAAAC0DQAA" +
+ "LgAAABEAAAAAAAAAMQAAABIAAAAAAAAAMgAAABIAAAC0DQAAMwAAABIAAAAUDgAANAAAABIAAADs" +
+ "DQAANgAAABMAAADcDQAACgADAAUAAAAKAAQAKgAAAAoABAArAAAACgADAC8AAAAKAAcAMAAAAAoA" +
+ "BABXAAAACgAEAGMAAAAJAB4AAgAAAAoAGwABAAAACgAcAAIAAAAKAB4AAgAAAAoABAA5AAAACgAA" +
+ "ADoAAAAKAAYAOwAAAAoABwA8AAAACgAIADwAAAAKAAYAPQAAAAoAEAA+AAAACgAMAD8AAAAKAAEA" +
+ "QAAAAAoAHwBCAAAACgACAEMAAAAKAAUARAAAAAoAHQBFAAAACgAQAEYAAAAKABIARgAAAAoAEwBG" +
+ "AAAACgADAEcAAAAKAAQARwAAAAoACgBIAAAACgADAEkAAAAKAAkASgAAAAoACgBLAAAACgAMAEwA" +
+ "AAAKAAwATQAAAAoABABOAAAACgAEAE8AAAAKAA0AUAAAAAoADgBQAAAACgANAFEAAAAKAA4AUQAA" +
+ "AAoADABSAAAACgAKAFMAAAAKAAoAVAAAAAoACwBVAAAACgALAFYAAAAKABoAWAAAAAoABABZAAAA" +
+ "CgAEAFoAAAAKAAwAWwAAAAoAFQBcAAAACgAVAF0AAAAKABUAXgAAAAoAFABfAAAACgAVAF8AAAAK" +
+ "ABYAXwAAAAoAGQBgAAAACgAVAGEAAAAKABYAYQAAAAoAFgBiAAAACgAPAGQAAAAKABAAZAAAAAoA" +
+ "EQBkAAAACwAbAAIAAAAPABsAAgAAAA8AFwA4AAAADwAYADgAAAAPABQAXwAAAAoAAAARAAAACwAA" +
+ "AKwNAAApAAAAVA0AAFEUAABIFAAAAQAAACIUAAABAAAAMhQAAAEAAABAFAAAAAAAAAAAAACsEgAA" +
+ "AQAAAA4AAAAEAAMAAQAAALESAAAGAAAAcBA4AAEAWhIGAA4ABAACAAMAAAC6EgAADgAAABYAAABw" +
+ "MAIAAgEiAAkAGwEsAAAAcCAAABAAJwADAAIAAAAAAMISAAACAAAAElAPAAYABAACAAAAyBIAAAkA" +
+ "AAAiAAkAGwEsAAAAcCAAABAAJwAAAAYABAACAAAA0BIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAA" +
+ "AAMAAQACAAAA2BIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAYABAACAAAA3xIAAAkAAAAiAAkA" +
+ "GwEsAAAAcCAAABAAJwAAAAgABgACAAAA5xIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAYABAAC" +
+ "AAAA8RIAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAMAAQACAAAA+RIAAAkAAAAiAAkAGwEsAAAA" +
+ "cCAAABAAJwAAAAUAAwACAAAAABMAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAQAAgACAAAACBMA" +
+ "AAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAQAAgACAAAAEBMAAAkAAAAiAAkAGwEsAAAAcCAAABAA" +
+ "JwAAAAQAAgAAAAAAFxMAAAQAAAAWAAEAuyAQAAQAAgAAAAAAHRMAAAUAAAAWAAEAnAACABAAAAAG" +
+ "AAQAAgAAACMTAAAJAAAAIgAJABsBLAAAAHAgAAAQACcAAAAGAAQAAgAAACsTAAAJAAAAIgAJABsB" +
+ "LAAAAHAgAAAQACcAAAAEAAIAAAAAADMTAAAEAAAAmwACAoQADwAEAAIAAAAAADkTAAAGAAAAEhCl" +
+ "AAIAwCCEAA8AAwABAAIAAAA/EwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIAAABFEwAA" +
+ "CQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAIAAABMEwAACQAAACIACQAbASwAAABwIAAAEAAn" +
+ "AAAABAACAAIAAABSEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABgAEAAIAAABZEwAACQAAACIA" +
+ "CQAbASwAAABwIAAAEAAnAAAABAACAAAAAABhEwAAAgAAAH0gEAAEAAIAAAAAAGcTAAADAAAAFgAA" +
+ "ABAAAAADAAMAAAAAAG0TAAABAAAAEAAAAAUAAwAAAAAAdBMAAAQAAAAWAAoAvSAQAAMAAgAAAAAA" +
+ "exMAAAIAAAASAA8ABAACAAIAAACBEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABgAEAAAAAACH" +
+ "EwAAAwAAAJsAAgQQAAAABAACAAIAAACPEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIA" +
+ "AACVEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIAAACbEwAACQAAACIACQAbASwAAABw" +
+ "IAAAEAAnAAAABAACAAIAAAChEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABQADAAIAAACnEwAA" +
+ "CQAAACIACQAbASwAAABwIAAAEAAnAAAABAACAAIAAACuEwAACQAAACIACQAbASwAAABwIAAAEAAn" +
+ "AAAABAACAAIAAAC0EwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABQADAAIAAAC6EwAACQAAACIA" +
+ "CQAbASwAAABwIAAAEAAnAAAABQADAAIAAADBEwAACQAAACIACQAbASwAAABwIAAAEAAnAAAABAAC" +
+ "AAMAAADIEwAABgAAACIACgBwMAIAIAMRAAMAAQACAAAAzxMAAAkAAAAiAAkAGwEsAAAAcCAAABAA" +
+ "JwAAAAQAAgACAAAA1hMAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAMAAQACAAAA3hMAAAkAAAAi" +
+ "AAkAGwEsAAAAcCAAABAAJwAAAAQAAgACAAAA5BMAAAkAAAAiAAkAGwEsAAAAcCAAABAAJwAAAAMA" +
+ "AgACAAAA6xMAAAcAAAAfAgoAbiAHACEACgAPAAAAAwABAAIAAADyEwAACQAAACIACQAbASwAAABw" +
+ "IAAAEAAnAAAABAACAAIAAAD4EwAACQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAIAAAD/EwAA" +
+ "CQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAIAAAAFFAAACQAAACIACQAbASwAAABwIAAAEAAn" +
+ "AAAAAwABAAIAAAALFAAACQAAACIACQAbASwAAABwIAAAEAAnAAAAAwABAAAAAAARFAAAAwAAAFMg" +
+ "BgAQAAAAAwABAAIAAAAXFAAACQAAACIACQAbASwAAABwIAAAEAAnAAAABQABAAMAAAAdFAAAGAAA" +
+ "ACIADwBwEDkAAAAbAS0AAABuIDsAEAAMAFNCBgCGIm4wOgAgAwwAbhA8AAAADAARABgGAAABAAAA" +
+ "CAAAAAAAAAAEAAAAIAYAAAMAAAAoBgAACgAAACgGAAAeAAAAKAYAAB8AAAAoBgAAIAAAACgGAAAh" +
+ "AAAAKAYAADYAAAAoBgAANwAAACgGAAABAAAACAAAAAEAAAAEAAAABQAAAAQAAwAUAAMAAwAAAAIA" +
+ "AAAEAAQAAQAAAAoAAAABAAAADQAAAAIAAAAEAAMAAQAAAA4AAAACAAAADgADAAIAAAAOAAQAAgAA" +
+ "AA4ACgABAAAAAQAAAAMAAAAEAAMAFAABPAAIPGNsaW5pdD4ABjxpbml0PgACPjsAAUIABUJZVEVT" +
+ "AAFEAAFGAAFJAAJJSgAGSUpJTElJAANJSkoAAklMAAFKAAJKSgADSkpJAANKSkoAAkpMAANKTEkA" +
+ "AUwAAkxEAAJMSgADTEpJAAJMTAADTExJAANMTEoAA0xMTAAdTGRhbHZpay9hbm5vdGF0aW9uL1Np" +
+ "Z25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABBMamF2YS9sYW5nL0NsYXNzABFM" +
+ "amF2YS9sYW5nL0NsYXNzOwAVTGphdmEvbGFuZy9Db21wYXJhYmxlABZMamF2YS9sYW5nL0NvbXBh" +
+ "cmFibGU7ABFMamF2YS9sYW5nL0Vycm9yOwAQTGphdmEvbGFuZy9Mb25nOwASTGphdmEvbGFuZy9O" +
+ "dW1iZXI7ACFMamF2YS9sYW5nL051bWJlckZvcm1hdEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2Jq" +
+ "ZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABZMamF2" +
+ "YS9tYXRoL0JpZ0ludGVnZXI7AAlMb25nLmphdmEACU1BWF9WQUxVRQAJTUlOX1ZBTFVFABZNZXRo" +
+ "b2QgcmVkZWZpbmVkIGF3YXkhACJSZWRlZmluZWQgTG9uZyEgdmFsdWUgKGFzIGRvdWJsZSk9AAFT" +
+ "AARTSVpFAARUWVBFAAFWAAJWSgAEVkpJTAACVkwAAVoAAlpMAAJbQwAGYXBwZW5kAAhiaXRDb3Vu" +
+ "dAAJYnl0ZVZhbHVlAAdjb21wYXJlAAljb21wYXJlVG8AD2NvbXBhcmVVbnNpZ25lZAAGZGVjb2Rl" +
+ "AA5kaXZpZGVVbnNpZ25lZAALZG91YmxlVmFsdWUAEmVtaXR0ZXI6IGphY2stNC4yNQAGZXF1YWxz" +
+ "AApmbG9hdFZhbHVlABJmb3JtYXRVbnNpZ25lZExvbmcACGdldENoYXJzAAdnZXRMb25nAAhoYXNo" +
+ "Q29kZQANaGlnaGVzdE9uZUJpdAAIaW50VmFsdWUACWxvbmdWYWx1ZQAMbG93ZXN0T25lQml0AANt" +
+ "YXgAA21pbgAUbnVtYmVyT2ZMZWFkaW5nWmVyb3MAFW51bWJlck9mVHJhaWxpbmdaZXJvcwAJcGFy" +
+ "c2VMb25nABFwYXJzZVVuc2lnbmVkTG9uZwARcmVtYWluZGVyVW5zaWduZWQAB3JldmVyc2UADHJl" +
+ "dmVyc2VCeXRlcwAKcm90YXRlTGVmdAALcm90YXRlUmlnaHQAEHNlcmlhbFZlcnNpb25VSUQACnNo" +
+ "b3J0VmFsdWUABnNpZ251bQAKc3RyaW5nU2l6ZQADc3VtAA50b0JpbmFyeVN0cmluZwALdG9IZXhT" +
+ "dHJpbmcADXRvT2N0YWxTdHJpbmcACHRvU3RyaW5nABR0b1Vuc2lnbmVkQmlnSW50ZWdlcgAQdG9V" +
+ "bnNpZ25lZFN0cmluZwARdG9VbnNpZ25lZFN0cmluZzAABXZhbHVlAAd2YWx1ZU9mAAUABw4AkAEB" +
+ "AAcOPC0AlQEBAAcOWgAiAQAHDgDNAQIAAAcOANEBAgAABw4AiwEBAAcOANUBAgAABw4AXQUAAAAA" +
+ "AAcOAGkDAAAABw4AvgEBAAcOAMIBAgAABw4AxgECAAAHDgC2AQEABw4ADgEABw4AEwEABw4A5AEC" +
+ "AAAHDgDnAQIAAAcOABgBAAcOAB0BAAcOAHUBAAcOAHECAAAHDgB9AQAHDgB5AgAABw4A2QECAAAH" +
+ "DgAxAQAHDgA7AQAHDgAnAgAABw4ALAIAAAcOADYBAAcOAG0BAAcOAOABAgAABw4AVQEABw4ATQEA" +
+ "Bw4AUQEABw4AYQEABw4AQwIAAAcOAEoBAAcOAGUBAAcOAEYCAAAHDgBZAgAABw4AhwEBAAcOAIQB" +
+ "AQAHDgCBAQIAAAcOAJoBAAcOAMkBAQAHDgDIAQEABw4ArgEABw4AugEBAAcOAKoBAAcOALIBAAcO" +
+ "AKIBAAcOAKYBAAcOAJ4BAAcOAD8ABw4AAgUBYxwFFyMXHxcAFyIXAwIFAWMcBBcdFwAXIhcDAgYB" +
+ "YxwBGAwEBAgGAAYABEAGASwLABkBGQEZARkBGQEaBhIBiIAEsAwBgYAExAwBgYAE4AwBCYwNAgmg" +
+ "DQMJxA0BCegNAQmMDgQIsA4BCNQOAQn4DgEJnA8BCcAPAgnkDwEJiBADCaAQAQm8EAEJ4BABCYQR" +
+ "AQmcEQEJuBEBCdwRAQmAEgEJpBIBCcgSAQnsEgEJgBMBCZgTAQmsEwIJxBMBCNgTAQn8EwEJlBQB" +
+ "CbgUAQncFAIJgBUBCaQVAQrIFQEJ7BUBCZAWAQi0FgEJ2BYBCfQWAQmYFwUBvBcCAeAXAcEghBgE" +
+ "AaQYAQHIGAEB7BgGAZAZAwG0GQEB2BkPAfAZBwGUGgAAEQAAAAAAAAABAAAAAAAAAAEAAABlAAAA" +
+ "cAAAAAIAAAAVAAAABAIAAAMAAAAgAAAAWAIAAAQAAAAHAAAA2AMAAAUAAAA9AAAAEAQAAAYAAAAB" +
+ "AAAA+AUAAAMQAAADAAAAGAYAAAEgAAA3AAAAMAYAAAYgAAABAAAAVA0AAAEQAAANAAAArA0AAAIg" +
+ "AABlAAAAHg4AAAMgAAA3AAAArBIAAAQgAAADAAAAIhQAAAUgAAABAAAASBQAAAAgAAABAAAAURQA" +
+ "AAAQAAABAAAASBUAAA==");
+
+ static class FuncCmp implements LongPredicate {
+ final String name;
+ final LongPredicate p;
+ public FuncCmp(String name, LongPredicate p) {
+ this.name = name;
+ this.p = p;
+ }
+
+ public boolean test(long l) {
+ return p.test(l);
+ }
+ }
+ static FuncCmp l2l(String name, final LongUnaryOperator a, final LongUnaryOperator b) {
+ return new FuncCmp(name, (v) -> a.applyAsLong(v) == b.applyAsLong(v));
+ }
+ static FuncCmp l2i(String name, final LongToIntFunction a, final LongToIntFunction b) {
+ return new FuncCmp(name, (v) -> a.applyAsInt(v) == b.applyAsInt(v));
+ }
+
+ /** Interface for a long, int -> long function. */
+ static interface LI2IFunction { public long applyToLongInt(long a, int b); }
+
+ static FuncCmp li2l(String name, final Random r, final LI2IFunction a, final LI2IFunction b) {
+ return new FuncCmp(name, new LongPredicate() {
+ public boolean test(long v) {
+ int i = r.nextInt();
+ return a.applyToLongInt(v, i) == b.applyToLongInt(v, i);
+ }
+ });
+ }
+
+ public static void main(String[] args) {
+ doTest(10000);
+ }
+
+ public static void doTest(int iters) {
+ // Just transform immediately.
+ doCommonClassRedefinition(Long.class, CLASS_BYTES, DEX_BYTES);
+ final Random r = new Random();
+ FuncCmp[] comps = new FuncCmp[] {
+ l2l("highestOneBit", Long::highestOneBit, RedefinedLongIntrinsics::highestOneBit),
+ l2l("lowestOneBit", Long::lowestOneBit, RedefinedLongIntrinsics::lowestOneBit),
+ l2i("numberOfLeadingZeros",
+ Long::numberOfLeadingZeros,
+ RedefinedLongIntrinsics::numberOfLeadingZeros),
+ l2i("numberOfTrailingZeros",
+ Long::numberOfTrailingZeros,
+ RedefinedLongIntrinsics::numberOfTrailingZeros),
+ l2i("bitCount", Long::bitCount, RedefinedLongIntrinsics::bitCount),
+ li2l("rotateLeft", r, Long::rotateLeft, RedefinedLongIntrinsics::rotateLeft),
+ li2l("rotateRight", r, Long::rotateRight, RedefinedLongIntrinsics::rotateRight),
+ l2l("reverse", Long::reverse, RedefinedLongIntrinsics::reverse),
+ l2i("signum", Long::signum, RedefinedLongIntrinsics::signum),
+ l2l("reverseBytes", Long::reverseBytes, RedefinedLongIntrinsics::reverseBytes),
+ };
+ for (FuncCmp f : comps) {
+ // Just actually use ints so we can cast them back int the tests to print them (since we
+ // deleted a bunch of the Long methods needed for printing longs)!
+ int failures = (int)r.ints(iters)
+ .mapToLong((v) -> (long)v)
+ .filter(f.negate()) // Get all the test cases that failed.
+ .count();
+ if (failures != 0) {
+ double percent = 100.0d*((double)failures/(double)iters);
+ System.out.println("for intrinsic " + f.name + " " + failures + "/" + iters
+ + " (" + Double.toString(percent) + "%) tests failed!");
+ }
+ }
+ System.out.println("Finished!");
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_file,
+ byte[] dex_file);
+}
diff --git a/test/950-redefine-intrinsic/src/RedefinedLongIntrinsics.java b/test/950-redefine-intrinsic/src/RedefinedLongIntrinsics.java
new file mode 100644
index 0000000..0ada4a6
--- /dev/null
+++ b/test/950-redefine-intrinsic/src/RedefinedLongIntrinsics.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * The methods that are intrinsified in Long and their expected redefined implementations.
+ */
+class RedefinedLongIntrinsics {
+ // Intrinsic! Do something cool. Return i + 1
+ public static long highestOneBit(long i) {
+ return i + 1;
+ }
+
+ // Intrinsic! Do something cool. Return i - 1
+ public static long lowestOneBit(long i) {
+ return i - 1;
+ }
+
+ // Intrinsic! Do something cool. Return i + i
+ public static int numberOfLeadingZeros(long i) {
+ return (int)(i + i);
+ }
+
+ // Intrinsic! Do something cool. Return i & (i >>> 1);
+ public static int numberOfTrailingZeros(long i) {
+ return (int)(i & (i >>> 1));
+ }
+
+ // Intrinsic! Do something cool. Return 5
+ public static int bitCount(long i) {
+ return 5;
+ }
+
+ // Intrinsic! Do something cool. Return i
+ public static long rotateLeft(long i, int distance) {
+ return i;
+ }
+
+ // Intrinsic! Do something cool. Return 10 * i
+ public static long rotateRight(long i, int distance) {
+ return 10 * i;
+ }
+
+ // Intrinsic! Do something cool. Return -i
+ public static long reverse(long i) {
+ return -i;
+ }
+
+ // Intrinsic! Do something cool. Return 0
+ public static int signum(long i) {
+ return 0;
+ }
+
+ // Intrinsic! Do something cool. Return 0
+ public static long reverseBytes(long i) {
+ return 0;
+ }
+}
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index f327974..4336d77 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -176,6 +176,8 @@
ART_TEST_WITH_STRACE = getEnvBoolean('ART_TEST_DEBUG_GC', False)
+EXTRA_DISABLED_TESTS = set(env.get("ART_TEST_RUN_TEST_SKIP", "").split())
+
TARGET_2ND_ARCH = get_build_var('TARGET_2ND_ARCH')
TARGET_ARCH = get_build_var('TARGET_ARCH')
if TARGET_2ND_ARCH:
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 748ec31..8c0b928 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -44,10 +44,10 @@
In the end, the script will print the failed and skipped tests if any.
"""
+import argparse
import fnmatch
import itertools
import json
-from optparse import OptionParser
import os
import re
import subprocess
@@ -596,6 +596,8 @@
"""
if dry_run:
return True
+ if test in env.EXTRA_DISABLED_TESTS:
+ return True
variants_list = DISABLED_TEST_CONTAINER.get(test, {})
for variants in variants_list:
variants_present = True
@@ -711,23 +713,26 @@
global gdb
global gdb_arg
- parser = OptionParser()
- parser.add_option('-t', '--test', dest='test', help='name of the test')
- parser.add_option('-j', type='int', dest='n_thread')
+ parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
+ parser.add_argument('-t', '--test', dest='test', help='name of the test')
+ parser.add_argument('-j', type=int, dest='n_thread')
for variant in TOTAL_VARIANTS_SET:
flag = '--' + variant
flag_dest = variant.replace('-', '_')
if variant == '32' or variant == '64':
flag_dest = 'n' + flag_dest
- parser.add_option(flag, action='store_true', dest=flag_dest)
- parser.add_option('--verbose', '-v', action='store_true', dest='verbose')
- parser.add_option('--dry-run', action='store_true', dest='dry_run')
- parser.add_option('-b', '--build-dependencies', action='store_true', dest='build')
- parser.add_option('--gdb', action='store_true', dest='gdb')
- parser.add_option('--gdb-arg', dest='gdb_arg')
+ parser.add_argument(flag, action='store_true', dest=flag_dest)
+ parser.add_argument('--verbose', '-v', action='store_true', dest='verbose')
+ parser.add_argument('--dry-run', action='store_true', dest='dry_run')
+ parser.add_argument("--skip", action="append", dest="skips", default=[],
+ help="Skip the given test in all circumstances.")
+ parser.add_argument('-b', '--build-dependencies', action='store_true', dest='build')
+ parser.add_argument('--gdb', action='store_true', dest='gdb')
+ parser.add_argument('--gdb-arg', dest='gdb_arg')
- options = parser.parse_args()[0]
+ options = parser.parse_args()
test = ''
+ env.EXTRA_DISABLED_TESTS.update(set(options.skips))
if options.test:
test = parse_test_name(options.test)
if options.pictest: