staticvoid AbortTransactionOrFail(Thread* self, constchar* fmt, ...) {
va_list args;
Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) {
va_start(args, fmt);
runtime->GetClassLinker()->AbortTransactionV(self, fmt, args);
va_end(args);
} else {
va_start(args, fmt);
std::string msg;
StringAppendV(&msg, fmt, args);
va_end(args);
LOG(FATAL) << "Trying to abort, but not in transaction mode: " << msg;
UNREACHABLE();
}
}
// Restricted support for character upper case / lower case. Only support ASCII, where // it's easy. Abort the transaction otherwise. staticvoid CharacterLowerUpper(Thread* self,
ShadowFrame* shadow_frame,
JValue* result,
size_t arg_offset, bool to_lower_case) REQUIRES_SHARED(Locks::mutator_lock_) {
int32_t int_value = shadow_frame->GetVReg(arg_offset);
// Only ASCII (7-bit). if (!isascii(int_value)) {
AbortTransactionOrFail(self, "Only support ASCII characters for toLowerCase/toUpperCase: %u",
int_value); return;
}
// Constructing a `std::locale("C")` is slow. Use an explicit calculation, compare in debug mode.
int32_t masked_value = int_value & ~0x20; // Clear bit distinguishing `A`..`Z` from `a`..`z`. bool is_ascii_letter = ('A' <= masked_value) && (masked_value <= 'Z');
int32_t result_value = is_ascii_letter ? (masked_value | (to_lower_case ? 0x20 : 0)) : int_value;
DCHECK_EQ(result_value,
to_lower_case
? std::tolower(dchecked_integral_cast<char>(int_value), std::locale("C"))
: std::toupper(dchecked_integral_cast<char>(int_value), std::locale("C")))
<< std::boolalpha << to_lower_case;
result->SetI(result_value);
}
// Common helper for class-loading cutouts in an unstarted runtime. We call Runtime methods that // rely on Java code to wrap errors in the correct exception class (i.e., NoClassDefFoundError into // ClassNotFoundException), so need to do the same. The only exception is if the exception is // actually the transaction abort exception. This must not be wrapped, as it signals an // initialization abort. staticvoid CheckExceptionGenerateClassNotFound(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) { if (self->IsExceptionPending()) {
Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { // The boot class path at run time may contain additional dex files with // the required class definition(s). We cannot throw a normal exception at // compile time because a class initializer could catch it and successfully // initialize a class differently than when executing at run time. // If we're not aborting the transaction yet, abort now. b/183691501 if (!runtime->GetClassLinker()->IsTransactionAborted()) {
DCHECK(!PendingExceptionHasAbortDescriptor(self));
runtime->GetClassLinker()->AbortTransactionF(self, "ClassNotFoundException");
} else {
DCHECK(PendingExceptionHasAbortDescriptor(self))
<< self->GetException()->GetClass()->PrettyDescriptor();
}
} else { // If not in a transaction, it cannot be the transaction abort exception. Wrap it.
DCHECK(!PendingExceptionHasAbortDescriptor(self));
self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;", "ClassNotFoundException");
}
}
}
// Check that it's not null. if (h_klass == nullptr) {
AbortTransactionOrFail(self, "Class reference is null for newInstance"); return;
}
// If we're in a transaction, class must not be finalizable (it or a superclass has a finalizer).
Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction() &&
runtime->GetClassLinker()->TransactionAllocationConstraint(self, h_klass.Get())) {
DCHECK(self->IsExceptionPending()); return;
}
// There are two situations in which we'll abort this run. // 1) If the class isn't yet initialized and initialization fails. // 2) If we can't find the default constructor. We'll postpone the exception to runtime. // Note that 2) could likely be handled here, but for safety abort the transaction. bool ok = false; auto* cl = runtime->GetClassLinker(); if (cl->EnsureInitialized(self, h_klass, true, true)) {
ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize()); if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) {
cons = nullptr;
} if (cons != nullptr) {
Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(self)));
CHECK(h_obj != nullptr); // We don't expect OOM at compile-time.
EnterInterpreterFromInvoke(self, cons, h_obj.Get(), nullptr, nullptr); if (!self->IsExceptionPending()) {
result->SetL(h_obj.Get());
ok = true;
}
} else {
self->ThrowNewExceptionF("Ljava/lang/InternalError;", "Could not find default constructor for '%s'",
h_klass->PrettyClass().c_str());
}
} if (!ok) {
AbortTransactionOrFail(self, "Failed in Class.newInstance for '%s' with %s",
h_klass->PrettyClass().c_str(),
mirror::Object::PrettyTypeOf(self->GetException()).c_str());
}
}
void UnstartedRuntime::UnstartedClassGetDeclaredField(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail // going the reflective Dex way.
ObjPtr<mirror::Class> klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
ObjPtr<mirror::String> name2 = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
ArtField* found = nullptr; for (ArtField& field : klass->GetFields()) { if (name2->Equals(field.GetName())) {
found = &field; break;
}
} if (found != nullptr && ShouldDenyAccessToMember(found, shadow_frame)) {
found = nullptr;
} if (found == nullptr) {
AbortTransactionOrFail(self, "Failed to find field in Class.getDeclaredField in un-started " " runtime. name=%s class=%s", name2->ToModifiedUtf8().c_str(),
klass->PrettyDescriptor().c_str()); return;
}
ObjPtr<mirror::Field> field = mirror::Field::CreateFromArtField(self, found, true);
result->SetL(field);
}
void UnstartedRuntime::UnstartedClassGetDeclaredFields(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail // going the reflective Dex way.
ObjPtr<mirror::Class> klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); auto object_array = klass->GetDeclaredFields(self, /*public_only=*/ false, /*force_resolve=*/ true); if (object_array != nullptr) {
result->SetL(object_array);
}
}
MemMap mem_map;
size_t map_size;
std::string last_error_msg; // Only store the last message (we could concatenate).
bool has_bcp_fds = !boot_class_path_files.empty(); for (size_t i = 0; i < boot_class_path.size(); ++i) { const std::string& jar_file = boot_class_path[i]; constint jar_fd = has_bcp_fds ? boot_class_path_files[i].Fd() : -1;
mem_map = FindAndExtractEntry(jar_file, jar_fd, resource_cstr, &map_size, &last_error_msg); if (mem_map.IsValid()) { break;
}
}
if (!mem_map.IsValid()) { // Didn't find it. There's a good chance this will be the same at runtime, but still // conservatively abort the transaction here.
AbortTransactionOrFail(self, "Could not find resource %s. Last error was %s.",
resource_name_str.c_str(),
last_error_msg.c_str()); return;
}
StackHandleScope<3> hs(self);
// Create byte array for content.
Handle<mirror::ByteArray> h_array(hs.NewHandle(mirror::ByteArray::Alloc(self, map_size))); if (h_array == nullptr) {
AbortTransactionOrFail(self, "Could not find/create byte array class"); return;
} // Copy in content.
memcpy(h_array->GetData(), mem_map.Begin(), map_size); // Be proactive releasing memory.
mem_map.Reset();
// Create a ByteArrayInputStream.
Handle<mirror::Class> h_class(hs.NewHandle(
runtime->GetClassLinker()->FindSystemClass(self, "Ljava/io/ByteArrayInputStream;"))); if (h_class == nullptr) {
AbortTransactionOrFail(self, "Could not find ByteArrayInputStream class"); return;
} if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
AbortTransactionOrFail(self, "Could not initialize ByteArrayInputStream class"); return;
}
Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self))); if (h_obj == nullptr) {
AbortTransactionOrFail(self, "Could not allocate ByteArrayInputStream object"); return;
}
void UnstartedRuntime::UnstartedConstructorNewInstance0(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // This is a cutdown version of java_lang_reflect_Constructor.cc's implementation.
StackHandleScope<4> hs(self);
Handle<mirror::Constructor> m = hs.NewHandle( reinterpret_cast<mirror::Constructor*>(shadow_frame->GetVRegReference(arg_offset)));
Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle( reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(
shadow_frame->GetVRegReference(arg_offset + 1)));
Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass())); if (UNLIKELY(c->IsAbstract())) {
AbortTransactionOrFail(self, "Cannot handle abstract classes"); return;
} // Verify that we can access the class. if (!m->IsAccessible() && !c->IsPublic()) { // Go 2 frames back, this method is always called from newInstance0, which is called from // Constructor.newInstance(Object... args).
ObjPtr<mirror::Class> caller = GetCallingClass(self, 2); // If caller is null, then we called from JNI, just avoid the check since JNI avoids most // access checks anyways. TODO: Investigate if this the correct behavior. if (caller != nullptr && !caller->CanAccess(c.Get())) {
AbortTransactionOrFail(self, "Cannot access class"); return;
}
} if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, c, true, true)) {
DCHECK(self->IsExceptionPending()); return;
} if (c->IsClassClass()) {
AbortTransactionOrFail(self, "new Class() is not supported"); return;
}
// String constructor is replaced by a StringFactory method in InvokeMethod. if (c->IsStringClass()) { // We don't support strings.
AbortTransactionOrFail(self, "String construction is not supported"); return;
}
Handle<mirror::Object> receiver = hs.NewHandle(c->AllocObject(self)); if (receiver == nullptr) {
AbortTransactionOrFail(self, "Could not allocate"); return;
}
// It's easier to use reflection to make the call, than create the uint32_t array.
{
ScopedObjectAccessUnchecked soa(self);
ScopedLocalRef<jobject> method_ref(self->GetJniEnv(),
soa.AddLocalReference<jobject>(m.Get()));
ScopedLocalRef<jobject> object_ref(self->GetJniEnv(),
soa.AddLocalReference<jobject>(receiver.Get()));
ScopedLocalRef<jobject> args_ref(self->GetJniEnv(),
soa.AddLocalReference<jobject>(args.Get()));
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); if (pointer_size == PointerSize::k64) {
InvokeMethod<PointerSize::k64>(soa, method_ref.get(), object_ref.get(), args_ref.get(), 2);
} else {
InvokeMethod<PointerSize::k32>(soa, method_ref.get(), object_ref.get(), args_ref.get(), 2);
}
} if (self->IsExceptionPending()) {
AbortTransactionOrFail(self, "Failed running constructor");
} else {
result->SetL(receiver.Get());
}
}
ObjPtr<mirror::Class> class_array_class = GetClassRoot<mirror::ObjectArray<mirror::Class>>();
Handle<mirror::ObjectArray<mirror::Class>> ptypes = hs.NewHandle(
mirror::ObjectArray<mirror::Class>::Alloc(soa.Self(), class_array_class, num_params)); if (ptypes.IsNull()) {
AbortTransactionOrFail(self, "Could not allocate array of mirror::Class"); return;
}
MutableHandle<mirror::Class> param(hs.NewHandle<mirror::Class>(nullptr)); for (uint32_t i = 0; i < num_params; ++i) { const dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
param.Assign(Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method)); if (param.Get() == nullptr) {
AbortTransactionOrFail(self, "Could not resolve type"); return;
}
ptypes->SetWithoutChecks<false>(i, param.Get());
}
result->SetL(ptypes.Get());
}
void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
ObjPtr<mirror::String> class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
ObjPtr<mirror::ClassLoader> class_loader =
ObjPtr<mirror::ClassLoader>::DownCast(shadow_frame->GetVRegReference(arg_offset));
StackHandleScope<2> hs(self);
Handle<mirror::String> h_class_name(hs.NewHandle(class_name));
Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader));
UnstartedRuntimeFindClass(self,
h_class_name,
h_class_loader,
result, /*initialize_class=*/ false); // This might have an error pending. But semantics are to just return null. if (self->IsExceptionPending()) {
Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { // If we're not aborting the transaction yet, abort now. b/183691501 // See CheckExceptionGenerateClassNotFound() for more detailed explanation. if (!runtime->GetClassLinker()->IsTransactionAborted()) {
DCHECK(!PendingExceptionHasAbortDescriptor(self));
runtime->GetClassLinker()->AbortTransactionF(self, "ClassNotFoundException");
} else {
DCHECK(PendingExceptionHasAbortDescriptor(self))
<< self->GetException()->GetClass()->PrettyDescriptor();
}
} else { // If not in a transaction, it cannot be the transaction abort exception. Clear it.
DCHECK(!PendingExceptionHasAbortDescriptor(self));
self->ClearException();
}
}
}
// Arraycopy emulation. // Note: we can't use any fast copy functions, as they are not available under transaction.
template <typename T> staticvoid PrimitiveArrayCopy(Thread* self,
ObjPtr<mirror::Array> src_array,
int32_t src_pos,
ObjPtr<mirror::Array> dst_array,
int32_t dst_pos,
int32_t length)
REQUIRES_SHARED(Locks::mutator_lock_) { if (src_array->GetClass()->GetComponentType() != dst_array->GetClass()->GetComponentType()) {
AbortTransactionOrFail(self, "Types mismatched in arraycopy: %s vs %s.",
mirror::Class::PrettyDescriptor(
src_array->GetClass()->GetComponentType()).c_str(),
mirror::Class::PrettyDescriptor(
dst_array->GetClass()->GetComponentType()).c_str()); return;
}
ObjPtr<mirror::PrimitiveArray<T>> src = ObjPtr<mirror::PrimitiveArray<T>>::DownCast(src_array);
ObjPtr<mirror::PrimitiveArray<T>> dst = ObjPtr<mirror::PrimitiveArray<T>>::DownCast(dst_array); constbool copy_forward = (dst_pos < src_pos) || (dst_pos - src_pos >= length); if (copy_forward) { for (int32_t i = 0; i < length; ++i) {
dst->Set(dst_pos + i, src->Get(src_pos + i));
}
} else { for (int32_t i = 1; i <= length; ++i) {
dst->Set(dst_pos + length - i, src->Get(src_pos + length - i));
}
}
}
mirror::Object* src_obj = shadow_frame->GetVRegReference(arg_offset);
mirror::Object* dst_obj = shadow_frame->GetVRegReference(arg_offset + 2); // Null checking. For simplicity, abort transaction. if (src_obj == nullptr) {
AbortTransactionOrFail(self, "src is null in arraycopy."); return;
} if (dst_obj == nullptr) {
AbortTransactionOrFail(self, "dst is null in arraycopy."); return;
} // Test for arrayness. Throw ArrayStoreException. if (!src_obj->IsArrayInstance() || !dst_obj->IsArrayInstance()) {
self->ThrowNewException("Ljava/lang/ArrayStoreException;", "src or trg is not an array"); return;
}
// Type checking.
ObjPtr<mirror::Class> src_type = shadow_frame->GetVRegReference(arg_offset)->GetClass()->
GetComponentType();
if (!src_type->IsPrimitive()) { // Check that the second type is not primitive.
ObjPtr<mirror::Class> trg_type = shadow_frame->GetVRegReference(arg_offset + 2)->GetClass()->
GetComponentType(); if (trg_type->IsPrimitiveInt()) {
AbortTransactionOrFail(self, "Type mismatch in arraycopy: %s vs %s",
mirror::Class::PrettyDescriptor(
src_array->GetClass()->GetComponentType()).c_str(),
mirror::Class::PrettyDescriptor(
dst_array->GetClass()->GetComponentType()).c_str()); return;
}
ObjPtr<mirror::ObjectArray<mirror::Object>> src = src_array->AsObjectArray<mirror::Object>();
ObjPtr<mirror::ObjectArray<mirror::Object>> dst = dst_array->AsObjectArray<mirror::Object>(); if (src == dst) { // Can overlap, but not have type mismatches. // We cannot use ObjectArray::MemMove here, as it doesn't support transactions. constbool copy_forward = (dst_pos < src_pos) || (dst_pos - src_pos >= length); if (copy_forward) { for (int32_t i = 0; i < length; ++i) {
dst->Set(dst_pos + i, src->Get(src_pos + i));
}
} else { for (int32_t i = 1; i <= length; ++i) {
dst->Set(dst_pos + length - i, src->Get(src_pos + length - i));
}
}
} else { // We're being lazy here. Optimally this could be a memcpy (if component types are // assignable), but the ObjectArray implementation doesn't support transactions. The // checking version, however, does. if (Runtime::Current()->IsActiveTransaction()) {
dst->AssignableCheckingMemcpy<true>(
dst_pos, src, src_pos, length, /* throw_exception= */ true);
} else {
dst->AssignableCheckingMemcpy<false>(
dst_pos, src, src_pos, length, /* throw_exception= */ true);
}
}
} elseif (src_type->IsPrimitiveByte()) {
PrimitiveArrayCopy<uint8_t>(self, src_array, src_pos, dst_array, dst_pos, length);
} elseif (src_type->IsPrimitiveChar()) {
PrimitiveArrayCopy<uint16_t>(self, src_array, src_pos, dst_array, dst_pos, length);
} elseif (src_type->IsPrimitiveInt()) {
PrimitiveArrayCopy<int32_t>(self, src_array, src_pos, dst_array, dst_pos, length);
} else {
AbortTransactionOrFail(self, "Unimplemented System.arraycopy for type '%s'",
src_type->PrettyDescriptor().c_str());
}
}
// This is overall inefficient, but reflecting the values here is not great, either. So // for simplicity, and with the assumption that the number of getProperty calls is not // too great, just iterate each time.
// Get the storage class.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> h_props_class(hs.NewHandle(
class_linker->FindSystemClass(self, "Ljava/lang/AndroidHardcodedSystemProperties;"))); if (h_props_class == nullptr) {
AbortTransactionOrFail(self, "Could not find AndroidHardcodedSystemProperties"); return;
} if (!class_linker->EnsureInitialized(self, h_props_class, true, true)) {
AbortTransactionOrFail(self, "Could not initialize AndroidHardcodedSystemProperties"); return;
}
// Get the storage array.
ArtField* static_properties =
h_props_class->FindDeclaredStaticField(kAndroidHardcodedSystemPropertiesFieldName, "[[Ljava/lang/String;"); if (static_properties == nullptr) {
AbortTransactionOrFail(self, "Could not find %s field",
kAndroidHardcodedSystemPropertiesFieldName); return;
}
ObjPtr<mirror::Object> props = static_properties->GetObject(h_props_class.Get());
Handle<mirror::ObjectArray<mirror::ObjectArray<mirror::String>>> h_2string_array(hs.NewHandle(
props->AsObjectArray<mirror::ObjectArray<mirror::String>>())); if (h_2string_array == nullptr) {
AbortTransactionOrFail(self, "Field %s is null", kAndroidHardcodedSystemPropertiesFieldName); return;
}
// Iterate over it. const int32_t prop_count = h_2string_array->GetLength(); // Use the third handle as mutable.
MutableHandle<mirror::ObjectArray<mirror::String>> h_string_array(
hs.NewHandle<mirror::ObjectArray<mirror::String>>(nullptr)); for (int32_t i = 0; i < prop_count; ++i) {
h_string_array.Assign(h_2string_array->Get(i)); if (h_string_array == nullptr ||
h_string_array->GetLength() != 2 ||
h_string_array->Get(0) == nullptr) {
AbortTransactionOrFail(self, "Unexpected content of %s",
kAndroidHardcodedSystemPropertiesFieldName); return;
} if (h_key->Equals(h_string_array->Get(0))) { // Found a value. if (h_string_array->Get(1) == nullptr && is_default_version) { // Null is being delegated to the default map, and then resolved to the given default value. // As there's no default map, return the given value.
result->SetL(shadow_frame->GetVRegReference(arg_offset + 1));
} else {
result->SetL(h_string_array->Get(1));
} return;
}
}
// Key is not supported.
AbortTransactionOrFail(self, "getProperty key %s not supported", h_key->ToModifiedUtf8().c_str());
}
void UnstartedRuntime::UnstartedSystemNanoTime(Thread* self, ShadowFrame*, JValue*, size_t) { // We don't want `System.nanoTime` to be called at compile time because `java.util.Random`'s // default constructor uses `nanoTime` to initialize seed and having it set during compile time // makes that `java.util.Random` instance deterministic for given system image.
AbortTransactionOrFail(self, "Should not be called by UnstartedRuntime");
}
static ObjPtr<mirror::Object> CreateInstanceOf(Thread* self, constchar* class_descriptor)
REQUIRES_SHARED(Locks::mutator_lock_) { // Find the requested class.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ObjPtr<mirror::Class> klass = class_linker->FindSystemClass(self, class_descriptor); if (klass == nullptr) {
AbortTransactionOrFail(self, "Could not load class %s", class_descriptor); return nullptr;
}
StackHandleScope<2> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(klass));
Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self))); if (h_obj != nullptr) {
ArtMethod* init_method = h_class->FindConstructor("()V", class_linker->GetImagePointerSize()); if (init_method == nullptr) {
AbortTransactionOrFail(self, "Could not find <init> for %s", class_descriptor); return nullptr;
} else {
JValue invoke_result;
EnterInterpreterFromInvoke(self, init_method, h_obj.Get(), nullptr, nullptr); if (!self->IsExceptionPending()) { return h_obj.Get();
}
AbortTransactionOrFail(self, "Could not run <init> for %s", class_descriptor);
}
}
AbortTransactionOrFail(self, "Could not allocate instance of %s", class_descriptor); return nullptr;
}
void UnstartedRuntime::UnstartedThreadLocalGet(Thread* self,
ShadowFrame* shadow_frame,
JValue* result,
[[maybe_unused]] size_t arg_offset) { if (CheckCallers(shadow_frame,
{ "jdk.internal.math.FloatingDecimal$BinaryToASCIIBuffer " "jdk.internal.math.FloatingDecimal.getBinaryToASCIIBuffer()" })) {
result->SetL(CreateInstanceOf(self, "Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;"));
} else {
AbortTransactionOrFail(self, "ThreadLocal.get() does not support %s",
GetImmediateCaller(shadow_frame).c_str());
}
}
void UnstartedRuntime::UnstartedThreadCurrentThread(Thread* self,
ShadowFrame* shadow_frame,
JValue* result,
[[maybe_unused]] size_t arg_offset) { if (CheckCallers(shadow_frame,
{ "void java.lang.Thread.<init>(java.lang.ThreadGroup, java.lang.Runnable, " "java.lang.String, long, java.security.AccessControlContext, boolean)", "void java.lang.Thread.<init>(java.lang.ThreadGroup, java.lang.Runnable, " "java.lang.String, long)", "void java.lang.Thread.<init>()", "void java.util.logging.LogManager$Cleaner.<init>(" "java.util.logging.LogManager)" })) { // Allow list LogManager$Cleaner, which is an unstarted Thread (for a shutdown hook). The // Thread constructor only asks for the current thread to set up defaults and add the // thread as unstarted to the ThreadGroup. A faked-up main thread peer is good enough for // these purposes.
Runtime::Current()->InitThreadGroups(self);
ObjPtr<mirror::Object> main_peer = self->CreateCompileTimePeer( "main", /*as_daemon=*/ false, Runtime::Current()->GetMainThreadGroup()); if (main_peer == nullptr) {
AbortTransactionOrFail(self, "Failed allocating peer"); return;
}
result->SetL(main_peer);
} else {
AbortTransactionOrFail(self, "Thread.currentThread() does not support %s",
GetImmediateCaller(shadow_frame).c_str());
}
}
void UnstartedRuntime::UnstartedThreadNativeGetStatus(Thread* self,
ShadowFrame* shadow_frame,
JValue* result,
[[maybe_unused]] size_t arg_offset) { if (CheckCallers(shadow_frame,
{"java.lang.Thread$State java.lang.Thread.threadState()", "java.lang.Thread$State java.lang.Thread.getState()", "java.lang.ThreadGroup java.lang.Thread.getThreadGroup()", "void java.lang.Thread.<init>(java.lang.ThreadGroup, java.lang.Runnable, " "java.lang.String, long, java.security.AccessControlContext, boolean)", "void java.lang.Thread.<init>(java.lang.ThreadGroup, java.lang.Runnable, " "java.lang.String, long)", "void java.lang.Thread.<init>()", "void java.util.logging.LogManager$Cleaner.<init>(" "java.util.logging.LogManager)"})) { // Allow list reading the state of the "main" thread when creating another (unstarted) thread // for LogManager. Report the thread as "new" (it really only counts that it isn't terminated).
constexpr int32_t kJavaRunnable = 1;
result->SetI(kJavaRunnable);
} else {
AbortTransactionOrFail(self, "Thread.nativeGetStatus() does not support %s",
GetImmediateCaller(shadow_frame).c_str());
}
}
void UnstartedRuntime::UnstartedThreadNicenessForPriority(Thread* self,
ShadowFrame* shadow_frame,
JValue* result,
size_t arg_offset) { if (CheckCallers(shadow_frame,
{"void java.lang.Thread.<init>(java.lang.ThreadGroup, java.lang.Runnable, " "java.lang.String, long, java.security.AccessControlContext, boolean)", "void java.lang.Thread.<init>(java.lang.ThreadGroup, java.lang.Runnable, " "java.lang.String, long)", "void java.lang.Thread.<init>()", "void java.util.logging.LogManager$Cleaner.<init>(" "java.util.logging.LogManager)"})) { // Allow niceness of newly created (unstarted) thread to be computed for LogManager. // Only used for a shutdown hook, so report as something reasonable without bothering with // the actual mapping.
int32_t pri = shadow_frame->GetVReg(arg_offset);
CHECK_GE(pri, kMinThreadPriority);
CHECK_LE(pri, kMaxThreadPriority);
result->SetI(10 - 2 * pri /* Posix normal niceness */);
} else {
AbortTransactionOrFail(self, "Thread.nicenessForPriority() does not support %s",
GetImmediateCaller(shadow_frame).c_str());
}
}
staticvoid UnstartedMemoryPeek(
Primitive::Type type, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
int64_t address = shadow_frame->GetVRegLong(arg_offset); // TODO: Check that this is in the heap somewhere. Otherwise we will segfault instead of // aborting the transaction.
switch (type) { case Primitive::kPrimByte: {
result->SetB(*reinterpret_cast<int8_t*>(static_cast<intptr_t>(address))); return;
}
case Primitive::kPrimShort: { using unaligned_short __attribute__((__aligned__(1))) = int16_t;
result->SetS(*reinterpret_cast<unaligned_short*>(static_cast<intptr_t>(address))); return;
}
case Primitive::kPrimInt: { using unaligned_int __attribute__((__aligned__(1))) = int32_t;
result->SetI(*reinterpret_cast<unaligned_int*>(static_cast<intptr_t>(address))); return;
}
case Primitive::kPrimLong: { using unaligned_long __attribute__((__aligned__(1))) = int64_t;
result->SetJ(*reinterpret_cast<unaligned_long*>(static_cast<intptr_t>(address))); return;
}
case Primitive::kPrimBoolean: case Primitive::kPrimChar: case Primitive::kPrimFloat: case Primitive::kPrimDouble: case Primitive::kPrimVoid: case Primitive::kPrimNot:
LOG(FATAL) << "Not in the Memory API: " << type;
UNREACHABLE();
}
LOG(FATAL) << "Should not reach here";
UNREACHABLE();
}
int offset = shadow_frame->GetVReg(arg_offset + 3); int count = shadow_frame->GetVReg(arg_offset + 4); if (offset < 0 || offset + count > array->GetLength()) {
Runtime::Current()->GetClassLinker()->AbortTransactionF(
self, "Array out of bounds in peekArray: %d/%d vs %d", offset, count, array->GetLength()); return;
}
switch (type) { case Primitive::kPrimByte: {
int8_t* address = reinterpret_cast<int8_t*>(static_cast<intptr_t>(address_long));
ObjPtr<mirror::ByteArray> byte_array = array->AsByteArray(); for (int32_t i = 0; i < count; ++i, ++address) {
byte_array->SetWithoutChecks<true>(i + offset, *address);
} return;
}
case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimLong:
LOG(FATAL) << "Type unimplemented for Memory Array API, should not reach here: " << type;
UNREACHABLE();
case Primitive::kPrimBoolean: case Primitive::kPrimChar: case Primitive::kPrimFloat: case Primitive::kPrimDouble: case Primitive::kPrimVoid: case Primitive::kPrimNot:
LOG(FATAL) << "Not in the Memory API: " << type;
UNREACHABLE();
}
LOG(FATAL) << "Should not reach here";
UNREACHABLE();
}
void UnstartedRuntime::UnstartedReferenceRefersTo(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // Use the naive implementation that may block and needlessly extend the lifetime // of the referenced object. const ObjPtr<mirror::Reference> ref = ObjPtr<mirror::Reference>::DownCast(
shadow_frame->GetVRegReference(arg_offset)); if (ref == nullptr) {
AbortTransactionOrFail(self, "Reference.refersTo() with null object"); return;
} const ObjPtr<mirror::Object> referent =
Runtime::Current()->GetHeap()->GetReferenceProcessor()->GetReferent(self, ref); const ObjPtr<mirror::Object> o = shadow_frame->GetVRegReference(arg_offset + 1);
result->SetZ(o == referent);
}
// This allows statically initializing ConcurrentHashMap and SynchronousQueue. We use a somewhat // conservative upper bound. We restrict the callers to SynchronousQueue and ConcurrentHashMap, // where we can predict the behavior (somewhat). // Note: this is required (instead of lazy initialization) as these classes are used in the static // initialization of other classes, so will *use* the value. void UnstartedRuntime::UnstartedRuntimeAvailableProcessors(Thread* self,
ShadowFrame* shadow_frame,
JValue* result,
[[maybe_unused]] size_t arg_offset) { if (CheckCallers(shadow_frame, { "void java.util.concurrent.SynchronousQueue.<clinit>()" })) { // SynchronousQueue really only separates between single- and multiprocessor case. Return // 8 as a conservative upper approximation.
result->SetI(8);
} elseif (CheckCallers(shadow_frame,
{"void java.util.concurrent.ConcurrentHashMap.runtimeSetup()", "void java.util.concurrent.ConcurrentHashMap.<clinit>()"})) { // ConcurrentHashMap uses it for striding. 8 still seems an OK general value, as it's likely // a good upper bound. // TODO: Consider resetting in the zygote?
result->SetI(8);
} else { // Not supported.
AbortTransactionOrFail(self, "Accessing availableProcessors not allowed");
}
}
// This allows accessing ConcurrentHashMap/SynchronousQueue.
// Must use non transactional mode. if (gUseReadBarrier) { // Need to make sure the reference stored in the field is a to-space one before attempting the // CAS or the CAS could fail incorrectly.
mirror::HeapReference<mirror::Object>* field_addr = reinterpret_cast<mirror::HeapReference<mirror::Object>*>( reinterpret_cast<uint8_t*>(obj) + static_cast<size_t>(offset));
ReadBarrier::Barrier<
mirror::Object, /* kIsVolatile= */ false,
kWithReadBarrier, /* kAlwaysUpdateField= */ true>(
obj,
MemberOffset(offset),
field_addr);
} bool success; // Check whether we're in a transaction, call accordingly.
Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) ||
runtime->GetClassLinker()->TransactionWriteValueConstraint(self, new_value)) {
DCHECK(self->IsExceptionPending()); return;
}
success = obj->CasFieldObject<true>(MemberOffset(offset),
expected_value,
new_value,
CASMode::kStrong,
std::memory_order_seq_cst);
} else {
success = obj->CasFieldObject<false>(MemberOffset(offset),
expected_value,
new_value,
CASMode::kStrong,
std::memory_order_seq_cst);
}
result->SetZ(success ? 1 : 0);
}
// A cutout for Integer.parseInt(String). Note: this code is conservative and will bail instead // of correctly handling the corner cases. void UnstartedRuntime::UnstartedIntegerParseInt(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
REQUIRES_SHARED(Locks::mutator_lock_) {
mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset); if (obj == nullptr) {
AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime."); return;
}
std::string string_value = obj->AsString()->ToModifiedUtf8(); if (string_value.empty()) {
AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime."); return;
}
constchar* c_str = string_value.c_str(); char *end; // Can we set errno to 0? Is this always a variable, and not a macro? // Worst case, we'll incorrectly fail a transaction. Seems OK.
int64_t l = strtol(c_str, &end, 10);
if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
(errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str); return;
} if (l == 0) { // Check whether the string wasn't exactly zero. if (string_value != "0") {
AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str); return;
}
} elseif (*end != '\0') {
AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str); return;
}
result->SetI(static_cast<int32_t>(l));
}
// A cutout for Long.parseLong. // // Note: for now use code equivalent to Integer.parseInt, as the full range may not be supported // well. void UnstartedRuntime::UnstartedLongParseLong(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
REQUIRES_SHARED(Locks::mutator_lock_) {
mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset); if (obj == nullptr) {
AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime."); return;
}
std::string string_value = obj->AsString()->ToModifiedUtf8(); if (string_value.empty()) {
AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime."); return;
}
constchar* c_str = string_value.c_str(); char *end; // Can we set errno to 0? Is this always a variable, and not a macro? // Worst case, we'll incorrectly fail a transaction. Seems OK.
int64_t l = strtol(c_str, &end, 10);
// Note: comparing against int32_t min/max is intentional here. if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
(errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str); return;
} if (l == 0) { // Check whether the string wasn't exactly zero. if (string_value != "0") {
AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str); return;
}
} elseif (*end != '\0') {
AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str); return;
}
// Conservatively flag all exceptions as transaction aborts. This way we don't need to unwrap // InvocationTargetExceptions. if (self->IsExceptionPending()) {
AbortTransactionOrFail(self, "Failed Method.invoke");
}
}
// Checks whether the runtime is s64-bit. This is needed for the clinit of // java.lang.invoke.VarHandle clinit. The clinit determines sets of // available VarHandle accessors and these differ based on machine // word size. void UnstartedRuntime::UnstartedJNIVMRuntimeIs64Bit([[maybe_unused]] Thread* self,
[[maybe_unused]] ArtMethod* method,
[[maybe_unused]] mirror::Object* receiver,
[[maybe_unused]] uint32_t* args,
JValue* result) {
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
jboolean is64bit = (pointer_size == PointerSize::k64) ? JNI_TRUE : JNI_FALSE;
result->SetZ(is64bit);
}
void UnstartedRuntime::UnstartedJNIThrowableNativeFillInStackTrace(
Thread* self,
[[maybe_unused]] ArtMethod* method,
[[maybe_unused]] mirror::Object* receiver,
[[maybe_unused]] uint32_t* args,
JValue* result) {
ScopedObjectAccessUnchecked soa(self);
Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { // Abort the transaction. // The stack trace contains pointers to methods which would be bogus when written // to the image. We would need to check if all classes owning these methods are // image classes and then we would need to fix up these pointers in `ImageWriter`.
DCHECK(runtime->IsAotCompiler());
runtime->GetClassLinker()->AbortTransactionF(self, "Stack trace not supported for dex2oat"); return;
}
result->SetL(self->CreateInternalStackTrace(soa));
}
// The actual value of `kMinLoadFactor` is irrelevant because the HashMap<>s below // are never resized, but we still need to pass a reasonable value to the constructor. static constexpr double kMinLoadFactor = 0.5; static constexpr double kMaxLoadFactor = 0.7;
constexpr size_t BufferSize(size_t size) { // Note: ceil() is not suitable for constexpr, so cast to size_t and adjust by 1 if needed. const size_t estimate = static_cast<size_t>(size / kMaxLoadFactor); returnstatic_cast<size_t>(estimate * kMaxLoadFactor) == size ? estimate : estimate + 1u;
}
// Note: HashSet::clear() abandons the pre-allocated storage which we need to keep. while (!invoke_handlers_.empty()) {
invoke_handlers_.erase(invoke_handlers_.begin());
} while (!jni_handlers_.empty()) {
jni_handlers_.erase(jni_handlers_.begin());
}
tables_initialized_ = false;
Initialize();
}
void UnstartedRuntime::Invoke(Thread* self, const CodeItemDataAccessor& accessor,
ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // In a runtime that's not started we intercept certain methods to avoid complicated dependency // problems in core libraries.
CHECK(tables_initialized_);
constauto& iter = invoke_handlers_.find(shadow_frame->GetMethod()); if (iter != invoke_handlers_.end()) { // Note: When we special case the method, we do not ensure initialization. // This has been the behavior since implementation of this feature.
// Clear out the result in case it's not zeroed out.
result->SetL(nullptr);
// Push the shadow frame. This is so the failing method can be seen in abort dumps.
ScopedShadowFrame pusher(self, shadow_frame);
(*iter->second)(self, shadow_frame, result, arg_offset);
} else { if (!EnsureInitialized(self, shadow_frame)) { return;
} // Not special, continue with regular interpreter execution.
ArtInterpreterToInterpreterBridge(self, accessor, shadow_frame, result);
}
}
// Hand select a number of methods to be run in a not yet started runtime without using JNI. void UnstartedRuntime::Jni(Thread* self, ArtMethod* method, mirror::Object* receiver,
uint32_t* args, JValue* result) { constauto& iter = jni_handlers_.find(method); if (iter != jni_handlers_.end()) { // Clear out the result in case it's not zeroed out.
result->SetL(nullptr);
(*iter->second)(self, method, receiver, args, result);
} else {
Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) {
runtime->GetClassLinker()->AbortTransactionF(
self, "Attempt to invoke native method in non-started runtime: %s",
ArtMethod::PrettyMethod(method).c_str());
} else {
LOG(FATAL) << "Calling native method " << ArtMethod::PrettyMethod(method)
<< " in an unstarted non-transactional runtime";
}
}
}
} // namespace interpreter
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.