diff options
author | Andreas Gampe <agampe@google.com> | 2015-02-27 20:08:34 -0800 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2015-03-02 10:16:43 -0800 |
commit | f0e128a41154642f470eae0c7c57476efb527cd1 (patch) | |
tree | 163083fe9eac8e79c94da4d00707aeddc9f66411 /runtime/interpreter | |
parent | f90316e06e1c7b7b4a4ebea8de0a1b4d72228fd6 (diff) | |
download | art-f0e128a41154642f470eae0c7c57476efb527cd1.tar.gz art-f0e128a41154642f470eae0c7c57476efb527cd1.tar.bz2 art-f0e128a41154642f470eae0c7c57476efb527cd1.zip |
ART: Allow more operations in unstarted Runtime
To compile-time initialize more classes, have more dedicated code
for special methods.
Bug: 19542228
Bug: 19548084
Change-Id: Iad37c1c58302b04fa3a5a904a923da388a079cd7
Diffstat (limited to 'runtime/interpreter')
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 150 |
1 files changed, 135 insertions, 15 deletions
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index a29558e3d8..3ab7f3036e 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -16,6 +16,8 @@ #include "interpreter_common.h" +#include <cmath> + #include "mirror/array-inl.h" namespace art { @@ -839,6 +841,23 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> class result->SetL(found); } +// 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 InternalError. This must not be wrapped, as it signals an initialization abort. +static void CheckExceptionGenerateClassNotFound(Thread* self) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (self->IsExceptionPending()) { + // If it is not an InternalError, wrap it. + std::string type(PrettyTypeOf(self->GetException(nullptr))); + if (type != "java.lang.InternalError") { + self->ThrowNewWrappedException(self->GetCurrentLocationForThrow(), + "Ljava/lang/ClassNotFoundException;", + "ClassNotFoundException"); + } + } +} + static void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { @@ -846,18 +865,34 @@ static void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_ // problems in core libraries. std::string name(PrettyMethod(shadow_frame->GetMethod())); if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { - // TODO: Support for the other variants that take more arguments should also be added. mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString(); StackHandleScope<1> hs(self); Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); UnstartedRuntimeFindClass(self, h_class_name, NullHandle<mirror::ClassLoader>(), result, name, - true, true); - } else if (name == "java.lang.Class java.lang.VMClassLoader.loadClass(java.lang.String, boolean)") { + true, false); + CheckExceptionGenerateClassNotFound(self); + } else if (name == "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)") { mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString(); - StackHandleScope<1> hs(self); + bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; + mirror::ClassLoader* class_loader = + down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); + StackHandleScope<2> hs(self); Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); - UnstartedRuntimeFindClass(self, h_class_name, NullHandle<mirror::ClassLoader>(), result, name, - false, true); + Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); + UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, name, initialize_class, + false); + CheckExceptionGenerateClassNotFound(self); + } else if (name == "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") { + mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString(); + bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; + mirror::ClassLoader* class_loader = + down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); + 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, name, initialize_class, + false); + CheckExceptionGenerateClassNotFound(self); } else if (name == "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") { mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); mirror::ClassLoader* class_loader = @@ -866,17 +901,47 @@ static void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_ 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, name, false, false); + // This might have an error pending. But semantics are to just return null. + if (self->IsExceptionPending()) { + // If it is an InternalError, keep it. See CheckExceptionGenerateClassNotFound. + std::string type(PrettyTypeOf(self->GetException(nullptr))); + if (type != "java.lang.InternalError") { + self->ClearException(); + } + } } else if (name == "java.lang.Class java.lang.Void.lookupType()") { result->SetL(Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V')); } else if (name == "java.lang.Object java.lang.Class.newInstance()") { + StackHandleScope<2> hs(self); Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); - ArtMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V"); - CHECK(c != NULL); - StackHandleScope<1> hs(self); - Handle<Object> obj(hs.NewHandle(klass->AllocObject(self))); - CHECK(obj.Get() != NULL); - EnterInterpreterFromInvoke(self, c, obj.Get(), NULL, NULL); - result->SetL(obj.Get()); + Handle<Class> h_klass(hs.NewHandle(klass)); + // 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; + if (Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) { + ArtMethod* c = h_klass->FindDeclaredDirectMethod("<init>", "()V"); + if (c != nullptr) { + Handle<Object> obj(hs.NewHandle(klass->AllocObject(self))); + CHECK(obj.Get() != nullptr); // We don't expect OOM at compile-time. + EnterInterpreterFromInvoke(self, c, obj.Get(), nullptr, nullptr); + result->SetL(obj.Get()); + ok = true; + } else { + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;", + "Could not find default constructor for '%s'", + PrettyClass(h_klass.Get()).c_str()); + } + } + if (!ok) { + std::string error_msg = StringPrintf("Failed in Class.newInstance for '%s' with %s", + PrettyClass(h_klass.Get()).c_str(), + PrettyTypeOf(self->GetException(nullptr)).c_str()); + self->ThrowNewWrappedException(self->GetCurrentLocationForThrow(), + "Ljava/lang/InternalError;", + error_msg.c_str()); + } } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") { // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail // going the reflective Dex way. @@ -949,12 +1014,67 @@ static void UnstartedRuntimeInvoke(Thread* self, const DexFile::CodeItem* code_ "Unimplemented System.arraycopy for type '%s'", PrettyDescriptor(ctype).c_str()); } - } else if (name == "java.lang.Object java.lang.ThreadLocal.get()") { + } else if (name == "long java.lang.Double.doubleToRawLongBits(double)") { + double in = shadow_frame->GetVRegDouble(arg_offset); + result->SetJ(bit_cast<int64_t>(in)); + } else if (name == "double java.lang.Math.ceil(double)") { + double in = shadow_frame->GetVRegDouble(arg_offset); + double out; + // Special cases: + // 1) NaN, infinity, +0, -0 -> out := in. All are guaranteed by cmath. + // -1 < in < 0 -> out := -0. + if (-1.0 < in && in < 0) { + out = -0.0; + } else { + out = ceil(in); + } + result->SetD(out); + } else if (name == "java.lang.Object java.lang.ThreadLocal.get()") { std::string caller(PrettyMethod(shadow_frame->GetLink()->GetMethod())); + bool ok = false; if (caller == "java.lang.String java.lang.IntegralToString.convertInt(java.lang.AbstractStringBuilder, int)") { // Allocate non-threadlocal buffer. result->SetL(mirror::CharArray::Alloc(self, 11)); - } else { + ok = true; + } else if (caller == "java.lang.RealToString java.lang.RealToString.getInstance()") { + // Note: RealToString is implemented and used in a different fashion than IntegralToString. + // Conversion is done over an actual object of RealToString (the conversion method is an + // instance method). This means it is not as clear whether it is correct to return a new + // object each time. The caller needs to be inspected by hand to see whether it (incorrectly) + // stores the object for later use. + // See also b/19548084 for a possible rewrite and bringing it in line with IntegralToString. + if (shadow_frame->GetLink()->GetLink() != nullptr) { + std::string caller2(PrettyMethod(shadow_frame->GetLink()->GetLink()->GetMethod())); + if (caller2 == "java.lang.String java.lang.Double.toString(double)") { + // Allocate new object. + mirror::Class* real_to_string_class = + shadow_frame->GetLink()->GetMethod()->GetDeclaringClass(); + mirror::Object* real_to_string_obj = real_to_string_class->AllocObject(self); + if (real_to_string_obj != nullptr) { + mirror::ArtMethod* init_method = + real_to_string_class->FindDirectMethod("<init>", "()V"); + if (init_method == nullptr) { + real_to_string_class->DumpClass(LOG(FATAL), mirror::Class::kDumpClassFullDetail); + } + JValue invoke_result; + // One arg, this. + uint32_t args = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(real_to_string_obj)); + init_method->Invoke(self, &args, 4, &invoke_result, init_method->GetShorty()); + if (!self->IsExceptionPending()) { + result->SetL(real_to_string_obj); + ok = true; + } + } + + if (!ok) { + // We'll abort, so clear exception. + self->ClearException(); + } + } + } + } + + if (!ok) { self->ThrowNewException(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;", "Unimplemented ThreadLocal.get"); } |