diff options
author | 2015-06-09 18:33:02 +0100 | |
---|---|---|
committer | 2015-06-10 13:09:32 +0100 | |
commit | 222862ceaeed48528020412ef4f7b1cdaecf8789 (patch) | |
tree | e5890d54817d80319580044d01f8bc3c65c43729 | |
parent | 864a2d955aa85ab989c86d7f1eeacbe0b11f8b0f (diff) |
Add optimizations for instanceof/checkcast.
The optimizations try to statically determine the outcome of the
type tests, replacing/removing the instructions when possible.
This required to fix the is_exact flag for ReferenceTypePropagation.
Change-Id: I6cea29b6c351d118b62060e8420333085e9383fb
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 81 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 3 | ||||
-rw-r--r-- | compiler/optimizing/reference_type_propagation.cc | 18 | ||||
-rw-r--r-- | compiler/optimizing/reference_type_propagation.h | 7 | ||||
-rw-r--r-- | test/494-checker-instanceof-tests/expected.txt | 0 | ||||
-rw-r--r-- | test/494-checker-instanceof-tests/info.txt | 1 | ||||
-rw-r--r-- | test/494-checker-instanceof-tests/src/Main.java | 203 | ||||
-rw-r--r-- | test/495-checker-checkcast-tests/expected.txt | 0 | ||||
-rw-r--r-- | test/495-checker-checkcast-tests/info.txt | 1 | ||||
-rw-r--r-- | test/495-checker-checkcast-tests/src/Main.java | 204 |
10 files changed, 498 insertions, 20 deletions
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index fcb3471821..98a5841f80 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -186,33 +186,92 @@ bool InstructionSimplifierVisitor::IsDominatedByInputNullCheck(HInstruction* ins return false; } -void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { - HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); - if (!check_cast->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(check_cast)) { - check_cast->ClearMustDoNullCheck(); - } - - if (!load_class->IsResolved()) { +// Returns whether doing a type test between the class of `object` against `klass` has +// a statically known outcome. The result of the test is stored in `outcome`. +static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) { + if (!klass->IsResolved()) { // If the class couldn't be resolve it's not safe to compare against it. It's // default type would be Top which might be wider that the actual class type // and thus producing wrong results. - return; + return false; } - ReferenceTypeInfo obj_rti = check_cast->InputAt(0)->GetReferenceTypeInfo(); - ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); + + ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo(); + ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI(); ScopedObjectAccess soa(Thread::Current()); if (class_rti.IsSupertypeOf(obj_rti)) { + *outcome = true; + return true; + } else if (obj_rti.IsExact()) { + // The test failed at compile time so will also fail at runtime. + *outcome = false; + return true; + } else if (!class_rti.IsInterface() && !obj_rti.IsSupertypeOf(class_rti)) { + // Different type hierarchy. The test will fail. + *outcome = false; + return true; + } + return false; +} + +void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { + HInstruction* object = check_cast->InputAt(0); + if (!object->CanBeNull() || IsDominatedByInputNullCheck(check_cast)) { + check_cast->ClearMustDoNullCheck(); + } + + if (object->IsNullConstant()) { check_cast->GetBlock()->RemoveInstruction(check_cast); if (stats_ != nullptr) { stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast); } + return; + } + + bool outcome; + if (TypeCheckHasKnownOutcome(check_cast->InputAt(1)->AsLoadClass(), object, &outcome)) { + if (outcome) { + check_cast->GetBlock()->RemoveInstruction(check_cast); + if (stats_ != nullptr) { + stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast); + } + } else { + // Don't do anything for exceptional cases for now. Ideally we should remove + // all instructions and blocks this instruction dominates. + } } } void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { - if (!instruction->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(instruction)) { + HInstruction* object = instruction->InputAt(0); + bool can_be_null = true; + if (!object->CanBeNull() || IsDominatedByInputNullCheck(instruction)) { + can_be_null = false; instruction->ClearMustDoNullCheck(); } + + HGraph* graph = GetGraph(); + if (object->IsNullConstant()) { + instruction->ReplaceWith(graph->GetIntConstant(0)); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + return; + } + + bool outcome; + if (TypeCheckHasKnownOutcome(instruction->InputAt(1)->AsLoadClass(), object, &outcome)) { + if (outcome && can_be_null) { + // Type test will succeed, we just need a null test. + HNotEqual* test = new (graph->GetArena()) HNotEqual(graph->GetNullConstant(), object); + instruction->GetBlock()->InsertInstructionBefore(test, instruction); + instruction->ReplaceWith(test); + } else { + // We've statically determined the result of the instanceof. + instruction->ReplaceWith(graph->GetIntConstant(outcome)); + } + RecordSimplification(); + instruction->GetBlock()->RemoveInstruction(instruction); + } } void InstructionSimplifierVisitor::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index f87775e195..c5fc5637a0 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1281,6 +1281,9 @@ class ReferenceTypeInfo : ValueObject { bool IsExact() const { return is_exact_; } bool IsTop() const { return is_top_; } + bool IsInterface() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return !IsTop() && GetTypeHandle()->IsInterface(); + } Handle<mirror::Class> GetTypeHandle() const { return type_handle_; } diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 4f1f45769d..3d81c20a16 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -166,31 +166,35 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { } } -void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass) { +void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr, + mirror::Class* klass, + bool is_exact) { if (klass != nullptr) { ScopedObjectAccess soa(Thread::Current()); MutableHandle<mirror::Class> handle = handles_->NewHandle(klass); - instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, true)); + is_exact = is_exact || klass->IsFinal(); + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact)); } } void ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr, uint16_t type_idx, - const DexFile& dex_file) { + const DexFile& dex_file, + bool is_exact) { DCHECK_EQ(instr->GetType(), Primitive::kPrimNot); ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); // Get type from dex cache assuming it was populated by the verifier. - SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx)); + SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) { - UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile()); + UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } void ReferenceTypePropagation::VisitNewArray(HNewArray* instr) { - UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile()); + UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } void ReferenceTypePropagation::UpdateFieldAccessTypeInfo(HInstruction* instr, @@ -206,7 +210,7 @@ void ReferenceTypePropagation::UpdateFieldAccessTypeInfo(HInstruction* instr, ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache); DCHECK(field != nullptr); mirror::Class* klass = field->GetType<false>(); - SetClassAsTypeInfo(instr, klass); + SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } void ReferenceTypePropagation::VisitInstanceFieldGet(HInstanceFieldGet* instr) { diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 74e425fb3e..0a1d4c496e 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -46,14 +46,17 @@ class ReferenceTypePropagation : public HOptimization { void VisitPhi(HPhi* phi); void VisitBasicBlock(HBasicBlock* block); void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info); - void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass); + void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact); void UpdateBoundType(HBoundType* bound_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void UpdatePhi(HPhi* phi) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void BoundTypeForIfNotNull(HBasicBlock* block); void BoundTypeForIfInstanceOf(HBasicBlock* block); - void UpdateReferenceTypeInfo(HInstruction* instr, uint16_t type_idx, const DexFile& dex_file); + void UpdateReferenceTypeInfo(HInstruction* instr, + uint16_t type_idx, + const DexFile& dex_file, + bool is_exact); void VisitInstanceFieldGet(HInstanceFieldGet* instr); void VisitStaticFieldGet(HStaticFieldGet* instr); diff --git a/test/494-checker-instanceof-tests/expected.txt b/test/494-checker-instanceof-tests/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/494-checker-instanceof-tests/expected.txt diff --git a/test/494-checker-instanceof-tests/info.txt b/test/494-checker-instanceof-tests/info.txt new file mode 100644 index 0000000000..59e20bd6a9 --- /dev/null +++ b/test/494-checker-instanceof-tests/info.txt @@ -0,0 +1 @@ +Checker test for optimizations on instanceof. diff --git a/test/494-checker-instanceof-tests/src/Main.java b/test/494-checker-instanceof-tests/src/Main.java new file mode 100644 index 0000000000..bff9c72ded --- /dev/null +++ b/test/494-checker-instanceof-tests/src/Main.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2015 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. + */ + +public class Main { + public static boolean $inline$classTypeTest(Object o) { + return o instanceof SubMain; + } + + public static boolean $inline$interfaceTypeTest(Object o) { + return o instanceof Itf; + } + + public static SubMain subMain; + public static Main mainField; + public static Unrelated unrelatedField; + public static FinalUnrelated finalUnrelatedField; + + /// CHECK-START: boolean Main.classTypeTestNull() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestNull() { + return $inline$classTypeTest(null); + } + + /// CHECK-START: boolean Main.classTypeTestExactMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestExactMain() { + return $inline$classTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 1 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestExactSubMain() { + return $inline$classTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Value>>] + public static boolean classTypeTestSubMainOrNull() { + return $inline$classTypeTest(subMain); + } + + /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> InstanceOf + /// CHECK-DAG: Return [<<Value>>] + public static boolean classTypeTestMainOrNull() { + return $inline$classTypeTest(mainField); + } + + /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestUnrelated() { + return $inline$classTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean classTypeTestFinalUnrelated() { + return $inline$classTypeTest(finalUnrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestNull() { + return $inline$interfaceTypeTest(null); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestExactMain() { + return $inline$interfaceTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 1 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestExactSubMain() { + return $inline$interfaceTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Value>>] + public static boolean interfaceTypeTestSubMainOrNull() { + return $inline$interfaceTypeTest(subMain); + } + + /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after) + /// CHECK-DAG: <<Value:z\d+>> InstanceOf + /// CHECK-DAG: Return [<<Value>>] + public static boolean interfaceTypeTestMainOrNull() { + return $inline$interfaceTypeTest(mainField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after) + /// CHECK-DAG: <<Value:z\d+>> InstanceOf + /// CHECK-DAG: Return [<<Value>>] + public static boolean interfaceTypeTestUnrelated() { + // This method is the main difference between doing an instanceof on an interface + // or a class. We have to keep the instanceof in case a subclass of Unrelated + // implements the interface. + return $inline$interfaceTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after) + /// CHECK-DAG: <<Const:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Const>>] + public static boolean interfaceTypeTestFinalUnrelated() { + return $inline$interfaceTypeTest(finalUnrelatedField); + } + + public static void expect(boolean expected, boolean actual) { + if (expected != actual) { + throw new Error("Unexpected result"); + } + } + + public static void main(String[] args) { + expect(false, classTypeTestNull()); + expect(false, classTypeTestExactMain()); + expect(true, classTypeTestExactSubMain()); + + subMain = null; + expect(false, classTypeTestSubMainOrNull()); + subMain = new SubMain(); + expect(true, classTypeTestSubMainOrNull()); + + mainField = null; + expect(false, classTypeTestMainOrNull()); + mainField = new Main(); + expect(false, classTypeTestMainOrNull()); + mainField = new SubMain(); + expect(true, classTypeTestMainOrNull()); + + unrelatedField = null; + expect(false, classTypeTestUnrelated()); + unrelatedField = new Unrelated(); + expect(false, classTypeTestUnrelated()); + + finalUnrelatedField = null; + expect(false, classTypeTestFinalUnrelated()); + finalUnrelatedField = new FinalUnrelated(); + expect(false, classTypeTestFinalUnrelated()); + + expect(false, interfaceTypeTestNull()); + expect(false, interfaceTypeTestExactMain()); + expect(true, interfaceTypeTestExactSubMain()); + + subMain = null; + expect(false, interfaceTypeTestSubMainOrNull()); + subMain = new SubMain(); + expect(true, interfaceTypeTestSubMainOrNull()); + + mainField = null; + expect(false, interfaceTypeTestMainOrNull()); + mainField = new Main(); + expect(false, interfaceTypeTestMainOrNull()); + mainField = new SubMain(); + expect(true, interfaceTypeTestMainOrNull()); + + unrelatedField = null; + expect(false, interfaceTypeTestUnrelated()); + unrelatedField = new Unrelated(); + expect(false, interfaceTypeTestUnrelated()); + + finalUnrelatedField = null; + expect(false, interfaceTypeTestFinalUnrelated()); + finalUnrelatedField = new FinalUnrelated(); + expect(false, interfaceTypeTestFinalUnrelated()); + } +} + +interface Itf { +} + +class SubMain extends Main implements Itf { +} + +class Unrelated { +} + +final class FinalUnrelated { +} diff --git a/test/495-checker-checkcast-tests/expected.txt b/test/495-checker-checkcast-tests/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/495-checker-checkcast-tests/expected.txt diff --git a/test/495-checker-checkcast-tests/info.txt b/test/495-checker-checkcast-tests/info.txt new file mode 100644 index 0000000000..4517b22c69 --- /dev/null +++ b/test/495-checker-checkcast-tests/info.txt @@ -0,0 +1 @@ +Checker tests for optimizations on checkcast. diff --git a/test/495-checker-checkcast-tests/src/Main.java b/test/495-checker-checkcast-tests/src/Main.java new file mode 100644 index 0000000000..aa6d5a75f7 --- /dev/null +++ b/test/495-checker-checkcast-tests/src/Main.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2015 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. + */ + +public class Main { + public static boolean $inline$classTypeTest(Object o) { + return ((SubMain)o) == o; + } + + public static boolean $inline$interfaceTypeTest(Object o) { + return ((Itf)o) == o; + } + + public static SubMain subMain; + public static Main mainField; + public static Unrelated unrelatedField; + public static FinalUnrelated finalUnrelatedField; + + /// CHECK-START: boolean Main.classTypeTestNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean classTypeTestNull() { + return $inline$classTypeTest(null); + } + + /// CHECK-START: boolean Main.classTypeTestExactMain() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestExactMain() { + return $inline$classTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after) + /// CHECK-NOT: CheckCast + public static boolean classTypeTestExactSubMain() { + return $inline$classTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean classTypeTestSubMainOrNull() { + return $inline$classTypeTest(subMain); + } + + /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestMainOrNull() { + return $inline$classTypeTest(mainField); + } + + /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestUnrelated() { + return $inline$classTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after) + /// CHECK: CheckCast + public static boolean classTypeTestFinalUnrelated() { + return $inline$classTypeTest(finalUnrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean interfaceTypeTestNull() { + return $inline$interfaceTypeTest(null); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestExactMain() { + return $inline$interfaceTypeTest(new Main()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after) + /// CHECK-NOT: CheckCast + public static boolean interfaceTypeTestExactSubMain() { + return $inline$interfaceTypeTest(new SubMain()); + } + + /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after) + /// CHECK-NOT: CheckCast + public static boolean interfaceTypeTestSubMainOrNull() { + return $inline$interfaceTypeTest(subMain); + } + + /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestMainOrNull() { + return $inline$interfaceTypeTest(mainField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestUnrelated() { + return $inline$interfaceTypeTest(unrelatedField); + } + + /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after) + /// CHECK: CheckCast + public static boolean interfaceTypeTestFinalUnrelated() { + return $inline$interfaceTypeTest(finalUnrelatedField); + } + + public static void main(String[] args) { + classTypeTestNull(); + try { + classTypeTestExactMain(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + classTypeTestExactSubMain(); + + subMain = null; + classTypeTestSubMainOrNull(); + subMain = new SubMain(); + classTypeTestSubMainOrNull(); + + mainField = null; + classTypeTestMainOrNull(); + mainField = new Main(); + try { + classTypeTestMainOrNull(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + mainField = new SubMain(); + classTypeTestMainOrNull(); + + unrelatedField = null; + classTypeTestUnrelated(); + unrelatedField = new Unrelated(); + try { + classTypeTestUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + + finalUnrelatedField = null; + classTypeTestFinalUnrelated(); + finalUnrelatedField = new FinalUnrelated(); + try { + classTypeTestFinalUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + + interfaceTypeTestNull(); + try { + interfaceTypeTestExactMain(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + interfaceTypeTestExactSubMain(); + + subMain = null; + interfaceTypeTestSubMainOrNull(); + subMain = new SubMain(); + interfaceTypeTestSubMainOrNull(); + + mainField = null; + interfaceTypeTestMainOrNull(); + mainField = new Main(); + try { + interfaceTypeTestMainOrNull(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + mainField = new SubMain(); + interfaceTypeTestMainOrNull(); + + unrelatedField = null; + interfaceTypeTestUnrelated(); + unrelatedField = new Unrelated(); + try { + interfaceTypeTestUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + + finalUnrelatedField = null; + interfaceTypeTestFinalUnrelated(); + finalUnrelatedField = new FinalUnrelated(); + try { + interfaceTypeTestFinalUnrelated(); + throw new Error("ClassCastException expected"); + } catch (ClassCastException e) {} + } +} + +interface Itf { +} + +class SubMain extends Main implements Itf { +} + +class Unrelated { +} + +final class FinalUnrelated { +} |