diff options
-rw-r--r-- | compiler/driver/compiler_driver.cc | 28 | ||||
-rw-r--r-- | runtime/aot_class_linker.cc | 22 | ||||
-rw-r--r-- | runtime/aot_class_linker.h | 7 | ||||
-rw-r--r-- | runtime/class_linker.cc | 6 | ||||
-rw-r--r-- | runtime/class_linker.h | 6 | ||||
-rw-r--r-- | test/660-clinit/expected.txt | 1 | ||||
-rw-r--r-- | test/660-clinit/profile | 5 | ||||
-rw-r--r-- | test/660-clinit/src/Main.java | 63 | ||||
-rw-r--r-- | test/906-iterate-heap/expected.txt | 2 | ||||
-rw-r--r-- | test/906-iterate-heap/src/art/Test906.java | 29 | ||||
-rw-r--r-- | test/913-heaps/expected.txt | 2 | ||||
-rw-r--r-- | test/913-heaps/src/art/Test913.java | 29 | ||||
-rw-r--r-- | test/knownfailures.json | 6 |
13 files changed, 173 insertions, 33 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index bd530ac6a6..5eee7e003b 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2251,6 +2251,7 @@ class InitializeClassVisitor : public CompilationVisitor { const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_); ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<3> hs(soa.Self()); + ClassLinker *class_linker = manager_->GetClassLinker(); const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage(); const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage(); @@ -2264,7 +2265,7 @@ class InitializeClassVisitor : public CompilationVisitor { if (klass->IsVerified()) { // Attempt to initialize the class but bail if we either need to initialize the super-class // or static fields. - manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false); + class_linker->EnsureInitialized(soa.Self(), klass, false, false); old_status = klass->GetStatus(); if (!klass->IsInitialized()) { // We don't want non-trivial class initialization occurring on multiple threads due to @@ -2283,7 +2284,7 @@ class InitializeClassVisitor : public CompilationVisitor { bool is_superclass_initialized = !is_app_image ? true : InitializeDependencies(klass, class_loader, soa.Self()); if (!is_app_image || (is_app_image && is_superclass_initialized)) { - manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true); + class_linker->EnsureInitialized(soa.Self(), klass, false, true); } // Otherwise it's in app image but superclasses can't be initialized, no need to proceed. old_status = klass->GetStatus(); @@ -2309,10 +2310,13 @@ class InitializeClassVisitor : public CompilationVisitor { CHECK(is_app_image); // The boot image case doesn't need to recursively initialize the dependencies with // special logic since the class linker already does this. + // Optimization will be disabled in debuggable build, because in debuggable mode we + // want the <clinit> behavior to be observable for the debugger, so we don't do the + // <clinit> at compile time. can_init_static_fields = + !manager_->GetCompiler()->GetCompilerOptions().GetDebuggable() && !soa.Self()->IsExceptionPending() && - is_superclass_initialized && - NoClinitInDependency(klass, soa.Self(), &class_loader); + is_superclass_initialized; // TODO The checking for clinit can be removed since it's already // checked when init superclass. Currently keep it because it contains // processing of intern strings. Will be removed later when intern strings @@ -2326,6 +2330,18 @@ class InitializeClassVisitor : public CompilationVisitor { // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity // checks in Thread::AssertThreadSuspensionIsAllowable. Runtime* const runtime = Runtime::Current(); + // Resolve and initialize the exception type before enabling the transaction in case + // the transaction aborts and cannot resolve the type. + // TransactionAbortError is not initialized ant not in boot image, needed only by + // compiler and will be pruned by ImageWriter. + Handle<mirror::Class> exception_class = + hs.NewHandle(class_linker->FindClass(Thread::Current(), + Transaction::kAbortExceptionSignature, + class_loader)); + bool exception_initialized = + class_linker->EnsureInitialized(soa.Self(), exception_class, true, true); + DCHECK(exception_initialized); + // Run the class initializer in transaction mode. runtime->EnterTransactionMode(is_app_image, klass.Get()); bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true, @@ -2363,10 +2379,12 @@ class InitializeClassVisitor : public CompilationVisitor { } } - if (!success) { + if (!success && is_boot_image) { // On failure, still intern strings of static fields and seen in <clinit>, as these // will be created in the zygote. This is separated from the transaction code just // above as we will allocate strings, so must be allowed to suspend. + // We only need to intern strings for boot image because classes that failed to be + // initialized will not appear in app image. if (&klass->GetDexFile() == manager_->GetDexFile()) { InternStrings(klass, class_loader); } else { diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc index b1bc3f8f2e..d8f9e22962 100644 --- a/runtime/aot_class_linker.cc +++ b/runtime/aot_class_linker.cc @@ -27,6 +27,16 @@ AotClassLinker::AotClassLinker(InternTable *intern_table) : ClassLinker(intern_t AotClassLinker::~AotClassLinker() {} +bool AotClassLinker::CanAllocClass() { + // AllocClass doesn't work under transaction, so we abort. + if (Runtime::Current()->IsActiveTransaction()) { + Runtime::Current()->AbortTransactionAndThrowAbortError(Thread::Current(), "Can't resolve this " + "type within a transaction."); + return false; + } + return ClassLinker::CanAllocClass(); +} + // Wrap the original InitializeClass with creation of transaction when in strict mode. bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, bool can_init_statics, bool can_init_parents) { @@ -38,6 +48,13 @@ bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents); } + // When in strict_mode, don't initialize a class if it belongs to boot but not initialized. + if (strict_mode_ && klass->IsBootStrapClassLoaded()) { + runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve " + + klass->PrettyTypeOf() + " because it is an uninitialized boot class."); + return false; + } + // Don't initialize klass if it's superclass is not initialized, because superclass might abort // the transaction and rolled back after klass's change is commited. if (strict_mode_ && !klass->IsInterface() && klass->HasSuperClass()) { @@ -58,9 +75,8 @@ bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, // Exit Transaction if success. runtime->ExitTransactionMode(); } else { - // If not successfully initialized, the last transaction must abort. Don't rollback - // immediately, leave the cleanup to compiler driver which needs abort message and exception. - DCHECK(runtime->IsTransactionAborted()); + // If not successfully initialized, don't rollback immediately, leave the cleanup to compiler + // driver which needs abort message and exception. DCHECK(self->IsExceptionPending()); } } diff --git a/runtime/aot_class_linker.h b/runtime/aot_class_linker.h index 11bea86fc4..e9a96fae20 100644 --- a/runtime/aot_class_linker.h +++ b/runtime/aot_class_linker.h @@ -27,6 +27,13 @@ class AotClassLinker : public ClassLinker { explicit AotClassLinker(InternTable *intern_table); ~AotClassLinker(); + // Override AllocClass because aot compiler will need to perform a transaction check to determine + // can we allocate class from heap. + bool CanAllocClass() + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + bool InitializeClass(Thread *self, Handle<mirror::Class> klass, bool can_run_clinit, diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3ac87c5137..46b0113cdd 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2854,7 +2854,11 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, // Interface object should get the right size here. Regular class will // figure out the right size later and be replaced with one of the right // size when the class becomes resolved. - klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); + if (CanAllocClass()) { + klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); + } else { + return nullptr; + } } if (UNLIKELY(klass == nullptr)) { self->AssertPendingOOMException(); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index bf14aebb52..584bd1d5ce 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -710,6 +710,12 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); + virtual bool CanAllocClass() + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_) { + return true; + } + private: class LinkInterfaceMethodsHelper; diff --git a/test/660-clinit/expected.txt b/test/660-clinit/expected.txt index 9eb4941276..ee1b47929e 100644 --- a/test/660-clinit/expected.txt +++ b/test/660-clinit/expected.txt @@ -1,4 +1,5 @@ JNI_OnLoad called +hello world A.a: 5 A.a: 10 B.b: 10 diff --git a/test/660-clinit/profile b/test/660-clinit/profile index 0239f22039..9eb4924ab1 100644 --- a/test/660-clinit/profile +++ b/test/660-clinit/profile @@ -4,7 +4,10 @@ LDay; LA; LB; LC; +LE; LG; LGs; LObjectRef; - +LInvokeStatic; +LClinitE; +LPrint; diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java index f9b068e110..51b4d604f5 100644 --- a/test/660-clinit/src/Main.java +++ b/test/660-clinit/src/Main.java @@ -24,19 +24,28 @@ public class Main { if (!checkAppImageLoaded()) { System.out.println("AppImage not loaded."); } + if (!checkAppImageContains(ClInit.class)) { + System.out.println("ClInit class is not in app image!"); + } - expectNotPreInit(Day.class); - expectNotPreInit(ClInit.class); // should pass - expectNotPreInit(A.class); // should pass - expectNotPreInit(B.class); // should fail - expectNotPreInit(C.class); // should fail - expectNotPreInit(G.class); // should fail - expectNotPreInit(Gs.class); // should fail - expectNotPreInit(Gss.class); // should fail + expectPreInit(ClInit.class); + expectPreInit(A.class); + expectPreInit(E.class); + expectNotPreInit(B.class); + expectNotPreInit(C.class); + expectNotPreInit(G.class); + expectNotPreInit(Gs.class); + expectNotPreInit(Gss.class); + expectPreInit(InvokeStatic.class); + expectNotPreInit(ClinitE.class); expectNotPreInit(Add.class); expectNotPreInit(Mul.class); expectNotPreInit(ObjectRef.class); + expectNotPreInit(Print.class); + + Print p = new Print(); + Gs gs = new Gs(); A x = new A(); System.out.println("A.a: " + A.a); @@ -62,6 +71,10 @@ public class Main { System.out.println("a != 101"); } + try { + ClinitE e = new ClinitE(); + } catch (Error err) { } + return; } @@ -154,6 +167,13 @@ class C { } } +class E { + public static final int e; + static { + e = 100; + } +} + class G { static G g; static int i; @@ -182,9 +202,36 @@ class Add { } } +// test of INVOKE_STATIC instruction +class InvokeStatic { + static int a; + static int b; + static { + a = Add.exec(10, 20); + b = Mul.exec(10, 20); + } +} + // non-image class Mul { static int exec(int a, int b) { return a * b; } } + +class ClinitE { + static { + if (Math.sin(3) < 0.5) { + // throw anyway, can't initialized + throw new ExceptionInInitializerError("Can't initialize this class!"); + } + } +} + +// fail because JNI +class Print { + static { + System.out.println("hello world"); + } +} + diff --git a/test/906-iterate-heap/expected.txt b/test/906-iterate-heap/expected.txt index 73b7129bba..85391fa25a 100644 --- a/test/906-iterate-heap/expected.txt +++ b/test/906-iterate-heap/expected.txt @@ -19,9 +19,7 @@ 1@0 (32, 2xD '0000000000000000000000000000f03f') 2 doTestPrimitiveFieldsClasses -10000@0 (static, int, index=3) 0000000000000000 10001 -10000@0 (static, int, index=11) 0000000000000000 10001 10001 10001 diff --git a/test/906-iterate-heap/src/art/Test906.java b/test/906-iterate-heap/src/art/Test906.java index 65c2c8c560..1878687e26 100644 --- a/test/906-iterate-heap/src/art/Test906.java +++ b/test/906-iterate-heap/src/art/Test906.java @@ -143,19 +143,40 @@ public class Test906 { private static void doTestPrimitiveFieldsClasses() { System.out.println("doTestPrimitiveFieldsClasses"); + boolean correctHeapValue = false; + setTag(IntObject.class, 10000); - System.out.println(iterateThroughHeapPrimitiveFields(10000)); + String heapTrace = iterateThroughHeapPrimitiveFields(10000); + + if (!checkInitialized(IntObject.class)) { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000000"); + } else { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000005"); + } + + if (!correctHeapValue) + System.out.println("Heap Trace for IntObject is not as expected:\n" + heapTrace); + System.out.println(getTag(IntObject.class)); setTag(IntObject.class, 0); setTag(FloatObject.class, 10000); - System.out.println(iterateThroughHeapPrimitiveFields(10000)); + heapTrace = iterateThroughHeapPrimitiveFields(10000); + + if (!checkInitialized(FloatObject.class)) { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000000"); + } else { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000006"); + } + + if (!correctHeapValue) + System.out.println("Heap Trace for FloatObject is not as expected:\n" + heapTrace); + System.out.println(getTag(FloatObject.class)); setTag(FloatObject.class, 0); - boolean correctHeapValue = false; setTag(Inf1.class, 10000); - String heapTrace = iterateThroughHeapPrimitiveFields(10000); + heapTrace = iterateThroughHeapPrimitiveFields(10000); if (!checkInitialized(Inf1.class)) { correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000000"); diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index 6144881a55..844afe8172 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -136,9 +136,7 @@ root@root --(thread)--> 3000@0 [size=136, length=-1] 4@0 (18, 3xS '010002000300') 1@0 (14, 2xZ '0001') 23456789 -10000@0 (static, int, index=3) 0000000000000000 10001 -10000@0 (static, int, index=11) 0000000000000000 10001 10001 10001 diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java index b9990010ff..28f9546d95 100644 --- a/test/913-heaps/src/art/Test913.java +++ b/test/913-heaps/src/art/Test913.java @@ -185,19 +185,40 @@ public class Test913 { } private static void doTestPrimitiveFieldsClasses() { + boolean correctHeapValue = false; + setTag(IntObject.class, 10000); - System.out.println(followReferencesPrimitiveFields(IntObject.class)); + String heapTrace = followReferencesPrimitiveFields(IntObject.class); + + if (!checkInitialized(IntObject.class)) { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000000"); + } else { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000005"); + } + + if (!correctHeapValue) + System.out.println("Heap Trace for IntObject is not as expected:\n" + heapTrace); + System.out.println(getTag(IntObject.class)); setTag(IntObject.class, 0); setTag(FloatObject.class, 10000); - System.out.println(followReferencesPrimitiveFields(FloatObject.class)); + heapTrace = followReferencesPrimitiveFields(FloatObject.class); + + if (!checkInitialized(FloatObject.class)) { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000000"); + } else { + correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000006"); + } + + if (!correctHeapValue) + System.out.println("Heap Trace for FloatObject is not as expected:\n" + heapTrace); + System.out.println(getTag(FloatObject.class)); setTag(FloatObject.class, 0); - boolean correctHeapValue = false; setTag(Inf1.class, 10000); - String heapTrace = followReferencesPrimitiveFields(Inf1.class); + heapTrace = followReferencesPrimitiveFields(Inf1.class); if (!checkInitialized(Inf1.class)) { correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000000"); diff --git a/test/knownfailures.json b/test/knownfailures.json index 5a67fbcc45..04de7a1a74 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -708,8 +708,8 @@ }, { "tests": "660-clinit", - "variant": "no-image | no-dex2oat | no-prebuild", - "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild and", - "--no-dex2oat do not create"] + "variant": "no-image | no-dex2oat | no-prebuild | interp-ac", + "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild, interp-ac", + "and --no-dex2oat do not create"] } ] |