diff options
| -rw-r--r-- | compiler/optimizing/instruction_builder.cc | 15 | ||||
| -rw-r--r-- | test/174-escaping-instance-of-bad-class/expected.txt | 2 | ||||
| -rw-r--r-- | test/174-escaping-instance-of-bad-class/src/Main.java | 21 | ||||
| -rw-r--r-- | test/551-checker-clinit/src/Main.java | 22 |
4 files changed, 54 insertions, 6 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index bd94789144..63b2705b43 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1304,15 +1304,18 @@ bool HInstructionBuilder::IsInitialized(ScopedObjectAccess& soa, Handle<mirror:: } } - // We can avoid the class initialization check for `cls` in static methods in the - // very same class. Instance methods of the same class can run on an escaped instance + // We can avoid the class initialization check for `cls` in static methods and constructors + // in the very same class; invoking a static method involves a class initialization check + // and so does the instance allocation that must be executed before invoking a constructor. + // Other instance methods of the same class can run on an escaped instance // of an erroneous class. Even a superclass may need to be checked as the subclass // can be completely initialized while the superclass is initializing and the subclass // remains initialized when the superclass initializer throws afterwards. b/62478025 // Note: The HClinitCheck+HInvokeStaticOrDirect merging can still apply. ObjPtr<mirror::Class> outermost_cls = ResolveOutermostCompilingClass(soa); - bool is_static = (dex_compilation_unit_->GetAccessFlags() & kAccStatic) != 0u; - if (is_static && outermost_cls == cls.Get()) { + bool is_outer_static_or_constructor = + (outer_compilation_unit_->GetAccessFlags() & (kAccStatic | kAccConstructor)) != 0u; + if (is_outer_static_or_constructor && outermost_cls == cls.Get()) { return true; } // Remember if the compiled class is a subclass of `cls`. By the time this is used @@ -1325,7 +1328,9 @@ bool HInstructionBuilder::IsInitialized(ScopedObjectAccess& soa, Handle<mirror:: // TODO: We should walk over the entire inlined method chain, but we don't pass that // information to the builder. ObjPtr<mirror::Class> innermost_cls = ResolveCompilingClass(soa); - if (is_static && innermost_cls == cls.Get()) { + bool is_inner_static_or_constructor = + (dex_compilation_unit_->GetAccessFlags() & (kAccStatic | kAccConstructor)) != 0u; + if (is_inner_static_or_constructor && innermost_cls == cls.Get()) { return true; } is_subclass = is_subclass || IsSubClass(innermost_cls, cls.Get()); diff --git a/test/174-escaping-instance-of-bad-class/expected.txt b/test/174-escaping-instance-of-bad-class/expected.txt index e287759d44..611d698f1a 100644 --- a/test/174-escaping-instance-of-bad-class/expected.txt +++ b/test/174-escaping-instance-of-bad-class/expected.txt @@ -1,6 +1,8 @@ Bad.foo() Bad.instanceValue = 33 Caught NoClassDefFoundError. +Bad.bar() +Caught NoClassDefFoundError. BadSuper.foo() BadSuper.superInstanceValue = 1 Caught NoClassDefFoundError. diff --git a/test/174-escaping-instance-of-bad-class/src/Main.java b/test/174-escaping-instance-of-bad-class/src/Main.java index 4346152f6c..4f66a31837 100644 --- a/test/174-escaping-instance-of-bad-class/src/Main.java +++ b/test/174-escaping-instance-of-bad-class/src/Main.java @@ -39,6 +39,17 @@ public class Main { ncdfe.printStackTrace(); } } + // Call bar() on the escaped instance of Bad. + try { + bad.bar(); + } catch (NoClassDefFoundError ncdfe) { + // On RI, the NCDFE has no cause. On ART, the badClinit is the cause. + if (ncdfe.getCause() == badClinit || ncdfe.getCause() == null) { + System.out.println("Caught NoClassDefFoundError."); + } else { + ncdfe.printStackTrace(); + } + } } public static void hierarchyTest() { @@ -117,9 +128,19 @@ class Bad { System.out.println("Bad.instanceValue = " + instanceValue); System.out.println("Bad.staticValue = " + staticValue); } + public void bar() { + System.out.println("Bad.bar()"); + System.out.println("Bad.staticValue [indirect] = " + Helper.$inline$getBadStaticValue()); + } public Bad(int iv) { instanceValue = iv; } public int instanceValue; public static int staticValue; + + public static class Helper { + public static int $inline$getBadStaticValue() { + return Bad.staticValue; + } + } } class BadSuper { diff --git a/test/551-checker-clinit/src/Main.java b/test/551-checker-clinit/src/Main.java index ab92cd03fd..0eea8009a6 100644 --- a/test/551-checker-clinit/src/Main.java +++ b/test/551-checker-clinit/src/Main.java @@ -57,7 +57,7 @@ class Sub extends Main { } /// CHECK-START: void Sub.invokeSubClass() builder (after) - /// CHECK: ClinitCheck + /// CHECK: ClinitCheck public void invokeSubClass() { int a = SubSub.foo; } @@ -71,3 +71,23 @@ class SubSub { } public static int foo = 42; } + +class NonTrivial { + public static int staticFoo = 42; + public int instanceFoo; + + static { + System.out.println("NonTrivial.<clinit>"); + } + + /// CHECK-START: void NonTrivial.<init>() builder (after) + /// CHECK-NOT: ClinitCheck + + /// CHECK-START: void NonTrivial.<init>() builder (after) + /// CHECK: StaticFieldGet + public NonTrivial() { + // ClinitCheck is eliminated because this is a constructor and therefore the + // corresponding new-instance in the caller must have performed the check. + instanceFoo = staticFoo; + } +} |