OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // This clang plugin checks various invariants of the Blink garbage | 5 // This clang plugin checks various invariants of the Blink garbage |
6 // collection infrastructure. | 6 // collection infrastructure. |
7 // | 7 // |
8 // Errors are described at: | 8 // Errors are described at: |
9 // http://www.chromium.org/developers/blink-gc-plugin-errors | 9 // http://www.chromium.org/developers/blink-gc-plugin-errors |
10 | 10 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 " which is not stack allocated."; | 122 " which is not stack allocated."; |
123 | 123 |
124 const char kClassOverridesNew[] = | 124 const char kClassOverridesNew[] = |
125 "[blink-gc] Garbage collected class %0" | 125 "[blink-gc] Garbage collected class %0" |
126 " is not permitted to override its new operator."; | 126 " is not permitted to override its new operator."; |
127 | 127 |
128 const char kClassDeclaresPureVirtualTrace[] = | 128 const char kClassDeclaresPureVirtualTrace[] = |
129 "[blink-gc] Garbage collected class %0" | 129 "[blink-gc] Garbage collected class %0" |
130 " is not permitted to declare a pure-virtual trace method."; | 130 " is not permitted to declare a pure-virtual trace method."; |
131 | 131 |
| 132 const char kLeftMostBaseMustBePolymorphic[] = |
| 133 "[blink-gc] Left-most base class %0 of derived class %1" |
| 134 " must be polymorphic."; |
| 135 |
| 136 const char kBaseClassMustDeclareVirtualTrace[] = |
| 137 "[blink-gc] Left-most base class %0 of derived class %1" |
| 138 " must define a virtual trace method."; |
| 139 |
132 struct BlinkGCPluginOptions { | 140 struct BlinkGCPluginOptions { |
133 BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {} | 141 BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {} |
134 bool enable_oilpan; | 142 bool enable_oilpan; |
135 bool dump_graph; | 143 bool dump_graph; |
136 std::set<std::string> ignored_classes; | 144 std::set<std::string> ignored_classes; |
137 std::set<std::string> checked_namespaces; | 145 std::set<std::string> checked_namespaces; |
138 std::vector<std::string> ignored_directories; | 146 std::vector<std::string> ignored_directories; |
139 }; | 147 }; |
140 | 148 |
141 typedef std::vector<CXXRecordDecl*> RecordVector; | 149 typedef std::vector<CXXRecordDecl*> RecordVector; |
(...skipping 622 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
764 diag_missing_trace_dispatch_ = | 772 diag_missing_trace_dispatch_ = |
765 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch); | 773 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch); |
766 diag_missing_finalize_dispatch_ = | 774 diag_missing_finalize_dispatch_ = |
767 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch); | 775 diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch); |
768 diag_derives_non_stack_allocated_ = | 776 diag_derives_non_stack_allocated_ = |
769 diagnostic_.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated); | 777 diagnostic_.getCustomDiagID(getErrorLevel(), kDerivesNonStackAllocated); |
770 diag_class_overrides_new_ = | 778 diag_class_overrides_new_ = |
771 diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew); | 779 diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew); |
772 diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID( | 780 diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID( |
773 getErrorLevel(), kClassDeclaresPureVirtualTrace); | 781 getErrorLevel(), kClassDeclaresPureVirtualTrace); |
| 782 diag_left_most_base_must_be_polymorphic_ = diagnostic_.getCustomDiagID( |
| 783 getErrorLevel(), kLeftMostBaseMustBePolymorphic); |
| 784 diag_base_class_must_declare_virtual_trace_ = diagnostic_.getCustomDiagID( |
| 785 getErrorLevel(), kBaseClassMustDeclareVirtualTrace); |
774 | 786 |
775 // Register note messages. | 787 // Register note messages. |
776 diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID( | 788 diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID( |
777 DiagnosticsEngine::Note, kBaseRequiresTracingNote); | 789 DiagnosticsEngine::Note, kBaseRequiresTracingNote); |
778 diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID( | 790 diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID( |
779 DiagnosticsEngine::Note, kFieldRequiresTracingNote); | 791 DiagnosticsEngine::Note, kFieldRequiresTracingNote); |
780 diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( | 792 diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( |
781 DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote); | 793 DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote); |
782 diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( | 794 diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( |
783 DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote); | 795 DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote); |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
893 it != info->GetBases().end(); | 905 it != info->GetBases().end(); |
894 ++it) { | 906 ++it) { |
895 if (!it->second.info()->IsStackAllocated()) | 907 if (!it->second.info()->IsStackAllocated()) |
896 ReportDerivesNonStackAllocated(info, &it->second); | 908 ReportDerivesNonStackAllocated(info, &it->second); |
897 } | 909 } |
898 } | 910 } |
899 | 911 |
900 if (CXXMethodDecl* trace = info->GetTraceMethod()) { | 912 if (CXXMethodDecl* trace = info->GetTraceMethod()) { |
901 if (trace->isPure()) | 913 if (trace->isPure()) |
902 ReportClassDeclaresPureVirtualTrace(info, trace); | 914 ReportClassDeclaresPureVirtualTrace(info, trace); |
| 915 if (info->record()->isPolymorphic()) |
| 916 CheckPolymorphicClass(info, trace); |
903 } else if (info->RequiresTraceMethod()) { | 917 } else if (info->RequiresTraceMethod()) { |
904 ReportClassRequiresTraceMethod(info); | 918 ReportClassRequiresTraceMethod(info); |
905 } | 919 } |
906 | 920 |
907 { | 921 { |
908 CheckFieldsVisitor visitor(options_); | 922 CheckFieldsVisitor visitor(options_); |
909 if (visitor.ContainsInvalidFields(info)) | 923 if (visitor.ContainsInvalidFields(info)) |
910 ReportClassContainsInvalidFields(info, &visitor.invalid_fields()); | 924 ReportClassContainsInvalidFields(info, &visitor.invalid_fields()); |
911 } | 925 } |
912 | 926 |
(...skipping 12 matching lines...) Expand all Loading... |
925 ReportClassContainsGCRoots(info, &visitor.gc_roots()); | 939 ReportClassContainsGCRoots(info, &visitor.gc_roots()); |
926 } | 940 } |
927 | 941 |
928 if (info->NeedsFinalization()) | 942 if (info->NeedsFinalization()) |
929 CheckFinalization(info); | 943 CheckFinalization(info); |
930 } | 944 } |
931 | 945 |
932 DumpClass(info); | 946 DumpClass(info); |
933 } | 947 } |
934 | 948 |
| 949 CXXRecordDecl* GetDependentTemplatedDecl(const Type& type) { |
| 950 const TemplateSpecializationType* tmpl_type = |
| 951 type.getAs<TemplateSpecializationType>(); |
| 952 if (!tmpl_type) |
| 953 return 0; |
| 954 |
| 955 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); |
| 956 if (!tmpl_decl) |
| 957 return 0; |
| 958 |
| 959 return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); |
| 960 } |
| 961 |
| 962 // The GC infrastructure assumes that if the vtable of a polymorphic |
| 963 // base-class is not initialized for a given object (ie, it is partially |
| 964 // initialized) then the object does not need to be traced. Thus, we must |
| 965 // ensure that any polymorphic class with a trace method does not have any |
| 966 // tractable fields that are initialized before we are sure that the vtable |
| 967 // and the trace method are both defined. There are two cases that need to |
| 968 // hold to satisfy that assumption: |
| 969 // |
| 970 // 1. If trace is virtual, then it must be defined in the left-most base. |
| 971 // This ensures that if the vtable is initialized and it contains a pointer to |
| 972 // the trace method. |
| 973 // |
| 974 // 2. If trace is non-virtual, then the trace method is defined and we must |
| 975 // ensure that the left-most base defines a vtable. This ensures that the |
| 976 // first thing to be initialized when constructing the object is the vtable |
| 977 // itself. |
| 978 void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) { |
| 979 CXXRecordDecl* left_most = info->record(); |
| 980 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
| 981 CXXRecordDecl* left_most_base = 0; |
| 982 while (it != left_most->bases_end()) { |
| 983 left_most_base = it->getType()->getAsCXXRecordDecl(); |
| 984 if (!left_most_base && it->getType()->isDependentType()) |
| 985 left_most_base = GetDependentTemplatedDecl(*it->getType()); |
| 986 |
| 987 // TODO: Find a way to correctly check actual instantiations |
| 988 // for dependent types. The escape below will be hit, eg, when |
| 989 // we have a primary template with no definition and |
| 990 // specializations for each case (such as SupplementBase) in |
| 991 // which case we don't succeed in checking the required |
| 992 // properties. |
| 993 if (!left_most_base || !left_most_base->hasDefinition()) |
| 994 return; |
| 995 |
| 996 StringRef name = left_most_base->getName(); |
| 997 // We know GCMixin base defines virtual trace. |
| 998 if (Config::IsGCMixinBase(name)) |
| 999 return; |
| 1000 |
| 1001 // Stop with the left-most prior to a safe polymorphic base (a safe base |
| 1002 // is non-polymorphic and contains no fields that need tracing). |
| 1003 if (Config::IsSafePolymorphicBase(name)) |
| 1004 break; |
| 1005 |
| 1006 left_most = left_most_base; |
| 1007 it = left_most->bases_begin(); |
| 1008 } |
| 1009 |
| 1010 if (RecordInfo* left_most_info = cache_.Lookup(left_most)) { |
| 1011 |
| 1012 // Check condition (1): |
| 1013 if (trace->isVirtual()) { |
| 1014 if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) { |
| 1015 if (trace->isVirtual()) |
| 1016 return; |
| 1017 } |
| 1018 ReportBaseClassMustDeclareVirtualTrace(info, left_most); |
| 1019 return; |
| 1020 } |
| 1021 |
| 1022 // Check condition (2): |
| 1023 if (DeclaresVirtualMethods(info->record())) |
| 1024 return; |
| 1025 if (left_most_base) { |
| 1026 ++it; // Get the base next to the "safe polymorphic base" |
| 1027 if (it != left_most->bases_end()) { |
| 1028 if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) { |
| 1029 if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) { |
| 1030 if (DeclaresVirtualMethods(next_left_most)) |
| 1031 return; |
| 1032 ReportLeftMostBaseMustBePolymorphic(info, next_left_most); |
| 1033 return; |
| 1034 } |
| 1035 } |
| 1036 } |
| 1037 } |
| 1038 ReportLeftMostBaseMustBePolymorphic(info, left_most); |
| 1039 } |
| 1040 } |
| 1041 |
| 1042 CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) { |
| 1043 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
| 1044 while (it != left_most->bases_end()) { |
| 1045 if (it->getType()->isDependentType()) |
| 1046 left_most = GetDependentTemplatedDecl(*it->getType()); |
| 1047 else |
| 1048 left_most = it->getType()->getAsCXXRecordDecl(); |
| 1049 if (!left_most || !left_most->hasDefinition()) |
| 1050 return 0; |
| 1051 it = left_most->bases_begin(); |
| 1052 } |
| 1053 return left_most; |
| 1054 } |
| 1055 |
| 1056 bool DeclaresVirtualMethods(CXXRecordDecl* decl) { |
| 1057 CXXRecordDecl::method_iterator it = decl->method_begin(); |
| 1058 for (; it != decl->method_end(); ++it) |
| 1059 if (it->isVirtual() && !it->isPure()) |
| 1060 return true; |
| 1061 return false; |
| 1062 } |
| 1063 |
935 void CheckLeftMostDerived(RecordInfo* info) { | 1064 void CheckLeftMostDerived(RecordInfo* info) { |
936 CXXRecordDecl* left_most = info->record(); | 1065 CXXRecordDecl* left_most = info->record(); |
937 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | 1066 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
938 while (it != left_most->bases_end()) { | 1067 while (it != left_most->bases_end()) { |
939 left_most = it->getType()->getAsCXXRecordDecl(); | 1068 left_most = it->getType()->getAsCXXRecordDecl(); |
940 it = left_most->bases_begin(); | 1069 it = left_most->bases_begin(); |
941 } | 1070 } |
942 if (!Config::IsGCBase(left_most->getName())) | 1071 if (!Config::IsGCBase(left_most->getName())) |
943 ReportClassMustLeftMostlyDeriveGC(info); | 1072 ReportClassMustLeftMostlyDeriveGC(info); |
944 } | 1073 } |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1067 CheckTraceMethod(parent, method, isTraceAfterDispatch); | 1196 CheckTraceMethod(parent, method, isTraceAfterDispatch); |
1068 } | 1197 } |
1069 // Dispatch methods are checked when we identify subclasses. | 1198 // Dispatch methods are checked when we identify subclasses. |
1070 } | 1199 } |
1071 } | 1200 } |
1072 | 1201 |
1073 // Check an actual trace method. | 1202 // Check an actual trace method. |
1074 void CheckTraceMethod(RecordInfo* parent, | 1203 void CheckTraceMethod(RecordInfo* parent, |
1075 CXXMethodDecl* trace, | 1204 CXXMethodDecl* trace, |
1076 bool isTraceAfterDispatch) { | 1205 bool isTraceAfterDispatch) { |
1077 // A non-virtual trace method must not override another trace. | 1206 // A trace method must not override any non-virtual trace methods. |
1078 if (!isTraceAfterDispatch && !trace->isVirtual()) { | 1207 if (!isTraceAfterDispatch) { |
1079 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1208 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1080 it != parent->GetBases().end(); | 1209 it != parent->GetBases().end(); |
1081 ++it) { | 1210 ++it) { |
1082 RecordInfo* base = it->second.info(); | 1211 RecordInfo* base = it->second.info(); |
1083 // We allow mixin bases to contain a non-virtual trace since it will | |
1084 // never be used for dispatching. | |
1085 if (base->IsGCMixin()) | |
1086 continue; | |
1087 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) | 1212 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
1088 ReportOverriddenNonVirtualTrace(parent, trace, other); | 1213 ReportOverriddenNonVirtualTrace(parent, trace, other); |
1089 } | 1214 } |
1090 } | 1215 } |
1091 | 1216 |
1092 CheckTraceVisitor visitor(trace, parent); | 1217 CheckTraceVisitor visitor(trace, parent); |
1093 visitor.TraverseCXXMethodDecl(trace); | 1218 visitor.TraverseCXXMethodDecl(trace); |
1094 | 1219 |
1095 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1220 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1096 it != parent->GetBases().end(); | 1221 it != parent->GetBases().end(); |
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1477 | 1602 |
1478 void ReportClassDeclaresPureVirtualTrace(RecordInfo* info, | 1603 void ReportClassDeclaresPureVirtualTrace(RecordInfo* info, |
1479 CXXMethodDecl* trace) { | 1604 CXXMethodDecl* trace) { |
1480 SourceLocation loc = trace->getLocStart(); | 1605 SourceLocation loc = trace->getLocStart(); |
1481 SourceManager& manager = instance_.getSourceManager(); | 1606 SourceManager& manager = instance_.getSourceManager(); |
1482 FullSourceLoc full_loc(loc, manager); | 1607 FullSourceLoc full_loc(loc, manager); |
1483 diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_) | 1608 diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_) |
1484 << info->record(); | 1609 << info->record(); |
1485 } | 1610 } |
1486 | 1611 |
| 1612 void ReportLeftMostBaseMustBePolymorphic(RecordInfo* derived, |
| 1613 CXXRecordDecl* base) { |
| 1614 SourceLocation loc = base->getLocStart(); |
| 1615 SourceManager& manager = instance_.getSourceManager(); |
| 1616 FullSourceLoc full_loc(loc, manager); |
| 1617 diagnostic_.Report(full_loc, diag_left_most_base_must_be_polymorphic_) |
| 1618 << base << derived->record(); |
| 1619 } |
| 1620 |
| 1621 void ReportBaseClassMustDeclareVirtualTrace(RecordInfo* derived, |
| 1622 CXXRecordDecl* base) { |
| 1623 SourceLocation loc = base->getLocStart(); |
| 1624 SourceManager& manager = instance_.getSourceManager(); |
| 1625 FullSourceLoc full_loc(loc, manager); |
| 1626 diagnostic_.Report(full_loc, diag_base_class_must_declare_virtual_trace_) |
| 1627 << base << derived->record(); |
| 1628 } |
| 1629 |
1487 void NoteManualDispatchMethod(CXXMethodDecl* dispatch) { | 1630 void NoteManualDispatchMethod(CXXMethodDecl* dispatch) { |
1488 SourceLocation loc = dispatch->getLocStart(); | 1631 SourceLocation loc = dispatch->getLocStart(); |
1489 SourceManager& manager = instance_.getSourceManager(); | 1632 SourceManager& manager = instance_.getSourceManager(); |
1490 FullSourceLoc full_loc(loc, manager); | 1633 FullSourceLoc full_loc(loc, manager); |
1491 diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch; | 1634 diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch; |
1492 } | 1635 } |
1493 | 1636 |
1494 void NoteBaseRequiresTracing(BasePoint* base) { | 1637 void NoteBaseRequiresTracing(BasePoint* base) { |
1495 SourceLocation loc = base->spec().getLocStart(); | 1638 SourceLocation loc = base->spec().getLocStart(); |
1496 SourceManager& manager = instance_.getSourceManager(); | 1639 SourceManager& manager = instance_.getSourceManager(); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1567 unsigned diag_finalizer_accesses_finalized_field_; | 1710 unsigned diag_finalizer_accesses_finalized_field_; |
1568 unsigned diag_overridden_non_virtual_trace_; | 1711 unsigned diag_overridden_non_virtual_trace_; |
1569 unsigned diag_missing_trace_dispatch_method_; | 1712 unsigned diag_missing_trace_dispatch_method_; |
1570 unsigned diag_missing_finalize_dispatch_method_; | 1713 unsigned diag_missing_finalize_dispatch_method_; |
1571 unsigned diag_virtual_and_manual_dispatch_; | 1714 unsigned diag_virtual_and_manual_dispatch_; |
1572 unsigned diag_missing_trace_dispatch_; | 1715 unsigned diag_missing_trace_dispatch_; |
1573 unsigned diag_missing_finalize_dispatch_; | 1716 unsigned diag_missing_finalize_dispatch_; |
1574 unsigned diag_derives_non_stack_allocated_; | 1717 unsigned diag_derives_non_stack_allocated_; |
1575 unsigned diag_class_overrides_new_; | 1718 unsigned diag_class_overrides_new_; |
1576 unsigned diag_class_declares_pure_virtual_trace_; | 1719 unsigned diag_class_declares_pure_virtual_trace_; |
| 1720 unsigned diag_left_most_base_must_be_polymorphic_; |
| 1721 unsigned diag_base_class_must_declare_virtual_trace_; |
1577 | 1722 |
1578 unsigned diag_base_requires_tracing_note_; | 1723 unsigned diag_base_requires_tracing_note_; |
1579 unsigned diag_field_requires_tracing_note_; | 1724 unsigned diag_field_requires_tracing_note_; |
1580 unsigned diag_raw_ptr_to_gc_managed_class_note_; | 1725 unsigned diag_raw_ptr_to_gc_managed_class_note_; |
1581 unsigned diag_ref_ptr_to_gc_managed_class_note_; | 1726 unsigned diag_ref_ptr_to_gc_managed_class_note_; |
1582 unsigned diag_own_ptr_to_gc_managed_class_note_; | 1727 unsigned diag_own_ptr_to_gc_managed_class_note_; |
1583 unsigned diag_stack_allocated_field_note_; | 1728 unsigned diag_stack_allocated_field_note_; |
1584 unsigned diag_member_in_unmanaged_class_note_; | 1729 unsigned diag_member_in_unmanaged_class_note_; |
1585 unsigned diag_part_object_to_gc_derived_class_note_; | 1730 unsigned diag_part_object_to_gc_derived_class_note_; |
1586 unsigned diag_part_object_contains_gc_root_note_; | 1731 unsigned diag_part_object_contains_gc_root_note_; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1631 | 1776 |
1632 private: | 1777 private: |
1633 BlinkGCPluginOptions options_; | 1778 BlinkGCPluginOptions options_; |
1634 }; | 1779 }; |
1635 | 1780 |
1636 } // namespace | 1781 } // namespace |
1637 | 1782 |
1638 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 1783 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
1639 "blink-gc-plugin", | 1784 "blink-gc-plugin", |
1640 "Check Blink GC invariants"); | 1785 "Check Blink GC invariants"); |
OLD | NEW |