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 |
921 | |
Mads Ager (chromium)
2014/08/13 12:23:58
Remove extra empty line?
zerny-chromium
2014/08/13 12:50:35
Done.
| |
907 { | 922 { |
908 CheckFieldsVisitor visitor(options_); | 923 CheckFieldsVisitor visitor(options_); |
909 if (visitor.ContainsInvalidFields(info)) | 924 if (visitor.ContainsInvalidFields(info)) |
910 ReportClassContainsInvalidFields(info, &visitor.invalid_fields()); | 925 ReportClassContainsInvalidFields(info, &visitor.invalid_fields()); |
911 } | 926 } |
912 | 927 |
913 if (info->IsGCDerived()) { | 928 if (info->IsGCDerived()) { |
914 | 929 |
915 if (!info->IsGCMixin()) { | 930 if (!info->IsGCMixin()) { |
916 CheckLeftMostDerived(info); | 931 CheckLeftMostDerived(info); |
917 CheckDispatch(info); | 932 CheckDispatch(info); |
918 if (CXXMethodDecl* newop = info->DeclaresNewOperator()) | 933 if (CXXMethodDecl* newop = info->DeclaresNewOperator()) |
919 ReportClassOverridesNew(info, newop); | 934 ReportClassOverridesNew(info, newop); |
920 } | 935 } |
921 | 936 |
922 { | 937 { |
923 CheckGCRootsVisitor visitor; | 938 CheckGCRootsVisitor visitor; |
924 if (visitor.ContainsGCRoots(info)) | 939 if (visitor.ContainsGCRoots(info)) |
925 ReportClassContainsGCRoots(info, &visitor.gc_roots()); | 940 ReportClassContainsGCRoots(info, &visitor.gc_roots()); |
926 } | 941 } |
927 | 942 |
928 if (info->NeedsFinalization()) | 943 if (info->NeedsFinalization()) |
929 CheckFinalization(info); | 944 CheckFinalization(info); |
930 } | 945 } |
931 | 946 |
932 DumpClass(info); | 947 DumpClass(info); |
933 } | 948 } |
934 | 949 |
950 CXXRecordDecl* GetDependentTemplatedDecl(const Type& type) { | |
951 const TemplateSpecializationType* tmpl_type = | |
952 type.getAs<TemplateSpecializationType>(); | |
953 if (!tmpl_type) | |
954 return 0; | |
955 | |
956 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); | |
957 if (!tmpl_decl) | |
958 return 0; | |
959 | |
960 return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); | |
961 } | |
962 | |
963 // The GC infrastructure assumes that if the vtable of a polymorphic | |
964 // base-class is not initialized for a given object (ie, it is partially | |
965 // initialized) then the object does not need to be traced. Thus, we must | |
966 // ensure that any polymorphic class with a trace method does not have any | |
967 // tractable fields that are initialized before we are sure that the vtable | |
968 // and the trace method are both defined. There are two cases that need to | |
969 // hold to satisfy that assumption: | |
970 // | |
971 // 1. If trace is virtual, then it must be defined in the left-most base. | |
972 // This ensures that if the vtable is initialized and it contains a pointer to | |
973 // the trace method. | |
974 // | |
975 // 2. If trace is non-virtual, then the trace method is defined and we must | |
976 // ensure that the left-most base defines a vtable. This ensures that the | |
977 // first thing to be initialized when constructing the object is the vtable | |
978 // itself. | |
979 void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) { | |
980 CXXRecordDecl* left_most = info->record(); | |
981 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | |
982 CXXRecordDecl* left_most_base = 0; | |
983 while (it != left_most->bases_end()) { | |
984 left_most_base = it->getType()->getAsCXXRecordDecl(); | |
985 if (!left_most_base && it->getType()->isDependentType()) | |
986 left_most_base = GetDependentTemplatedDecl(*it->getType()); | |
987 | |
988 // TODO: Find a way to correctly check actual instantiations | |
989 // for dependent types. The escape below will be hit, eg, when | |
990 // we have a primary template with no definition and | |
991 // specializations for each case (such as SupplementBase) in | |
992 // which case we don't succeed in checking the required | |
993 // properties. | |
994 if (!left_most_base || !left_most_base->hasDefinition()) | |
995 return; | |
996 | |
997 StringRef name = left_most_base->getName(); | |
998 // We know GCMixin base defines virtual trace. | |
999 if (Config::IsGCMixinBase(name)) | |
1000 return; | |
1001 | |
1002 // Stop with the left-most prior to a safe polymorphic base, ie, the a | |
Mads Ager (chromium)
2014/08/13 12:23:58
the a -> the
zerny-chromium
2014/08/13 12:50:35
Also removed the ambiguity of "the base".
| |
1003 // base that is non-polymorphic and contains no fields that need tracing. | |
1004 if (Config::IsSafePolymorphicBase(name)) | |
1005 break; | |
1006 | |
1007 left_most = left_most_base; | |
1008 it = left_most->bases_begin(); | |
1009 } | |
1010 | |
1011 if (RecordInfo* left_most_info = cache_.Lookup(left_most)) { | |
1012 | |
1013 // Check condition (1): | |
1014 if (trace->isVirtual()) { | |
1015 if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) { | |
1016 if (trace->isVirtual()) | |
1017 return; | |
1018 } | |
1019 ReportBaseClassMustDeclareVirtualTrace(info, left_most); | |
1020 return; | |
1021 } | |
1022 | |
1023 // Check condition (2): | |
1024 if (DeclaresVirtualMethods(info->record())) | |
1025 return; | |
1026 if (left_most_base) { | |
1027 ++it; // Get the base next to the "safe polymorphic base" | |
1028 if (it != left_most->bases_end()) { | |
1029 if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) { | |
1030 if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) { | |
1031 if (DeclaresVirtualMethods(next_left_most)) | |
1032 return; | |
1033 ReportLeftMostBaseMustBePolymorphic(info, next_left_most); | |
1034 return; | |
1035 } | |
1036 } | |
1037 } | |
1038 } | |
1039 ReportLeftMostBaseMustBePolymorphic(info, left_most); | |
1040 } | |
1041 } | |
1042 | |
1043 CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) { | |
1044 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | |
1045 while (it != left_most->bases_end()) { | |
1046 if (it->getType()->isDependentType()) | |
1047 left_most = GetDependentTemplatedDecl(*it->getType()); | |
1048 else | |
1049 left_most = it->getType()->getAsCXXRecordDecl(); | |
1050 if (!left_most || !left_most->hasDefinition()) | |
1051 return 0; | |
1052 it = left_most->bases_begin(); | |
1053 } | |
1054 return left_most; | |
1055 } | |
1056 | |
1057 bool DeclaresVirtualMethods(CXXRecordDecl* decl) { | |
1058 CXXRecordDecl::method_iterator it = decl->method_begin(); | |
1059 for (; it != decl->method_end(); ++it) | |
1060 if (it->isVirtual() && !it->isPure()) | |
1061 return true; | |
1062 return false; | |
1063 } | |
1064 | |
935 void CheckLeftMostDerived(RecordInfo* info) { | 1065 void CheckLeftMostDerived(RecordInfo* info) { |
936 CXXRecordDecl* left_most = info->record(); | 1066 CXXRecordDecl* left_most = info->record(); |
937 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | 1067 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
938 while (it != left_most->bases_end()) { | 1068 while (it != left_most->bases_end()) { |
939 left_most = it->getType()->getAsCXXRecordDecl(); | 1069 left_most = it->getType()->getAsCXXRecordDecl(); |
940 it = left_most->bases_begin(); | 1070 it = left_most->bases_begin(); |
941 } | 1071 } |
942 if (!Config::IsGCBase(left_most->getName())) | 1072 if (!Config::IsGCBase(left_most->getName())) |
943 ReportClassMustLeftMostlyDeriveGC(info); | 1073 ReportClassMustLeftMostlyDeriveGC(info); |
944 } | 1074 } |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1067 CheckTraceMethod(parent, method, isTraceAfterDispatch); | 1197 CheckTraceMethod(parent, method, isTraceAfterDispatch); |
1068 } | 1198 } |
1069 // Dispatch methods are checked when we identify subclasses. | 1199 // Dispatch methods are checked when we identify subclasses. |
1070 } | 1200 } |
1071 } | 1201 } |
1072 | 1202 |
1073 // Check an actual trace method. | 1203 // Check an actual trace method. |
1074 void CheckTraceMethod(RecordInfo* parent, | 1204 void CheckTraceMethod(RecordInfo* parent, |
1075 CXXMethodDecl* trace, | 1205 CXXMethodDecl* trace, |
1076 bool isTraceAfterDispatch) { | 1206 bool isTraceAfterDispatch) { |
1077 // A non-virtual trace method must not override another trace. | 1207 // A trace method must not override any non-virtual trace methods. |
1078 if (!isTraceAfterDispatch && !trace->isVirtual()) { | 1208 if (!isTraceAfterDispatch) { |
1079 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1209 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1080 it != parent->GetBases().end(); | 1210 it != parent->GetBases().end(); |
1081 ++it) { | 1211 ++it) { |
1082 RecordInfo* base = it->second.info(); | 1212 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()) | 1213 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
1088 ReportOverriddenNonVirtualTrace(parent, trace, other); | 1214 ReportOverriddenNonVirtualTrace(parent, trace, other); |
1089 } | 1215 } |
1090 } | 1216 } |
1091 | 1217 |
1092 CheckTraceVisitor visitor(trace, parent); | 1218 CheckTraceVisitor visitor(trace, parent); |
1093 visitor.TraverseCXXMethodDecl(trace); | 1219 visitor.TraverseCXXMethodDecl(trace); |
1094 | 1220 |
1095 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1221 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1096 it != parent->GetBases().end(); | 1222 it != parent->GetBases().end(); |
(...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1477 | 1603 |
1478 void ReportClassDeclaresPureVirtualTrace(RecordInfo* info, | 1604 void ReportClassDeclaresPureVirtualTrace(RecordInfo* info, |
1479 CXXMethodDecl* trace) { | 1605 CXXMethodDecl* trace) { |
1480 SourceLocation loc = trace->getLocStart(); | 1606 SourceLocation loc = trace->getLocStart(); |
1481 SourceManager& manager = instance_.getSourceManager(); | 1607 SourceManager& manager = instance_.getSourceManager(); |
1482 FullSourceLoc full_loc(loc, manager); | 1608 FullSourceLoc full_loc(loc, manager); |
1483 diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_) | 1609 diagnostic_.Report(full_loc, diag_class_declares_pure_virtual_trace_) |
1484 << info->record(); | 1610 << info->record(); |
1485 } | 1611 } |
1486 | 1612 |
1613 void ReportLeftMostBaseMustBePolymorphic(RecordInfo* derived, | |
1614 CXXRecordDecl* base) { | |
1615 SourceLocation loc = base->getLocStart(); | |
1616 SourceManager& manager = instance_.getSourceManager(); | |
1617 FullSourceLoc full_loc(loc, manager); | |
1618 diagnostic_.Report(full_loc, diag_left_most_base_must_be_polymorphic_) | |
1619 << base << derived->record(); | |
1620 } | |
1621 | |
1622 void ReportBaseClassMustDeclareVirtualTrace(RecordInfo* derived, | |
1623 CXXRecordDecl* base) { | |
1624 SourceLocation loc = base->getLocStart(); | |
1625 SourceManager& manager = instance_.getSourceManager(); | |
1626 FullSourceLoc full_loc(loc, manager); | |
1627 diagnostic_.Report(full_loc, diag_base_class_must_declare_virtual_trace_) | |
1628 << base << derived->record(); | |
1629 } | |
1630 | |
1487 void NoteManualDispatchMethod(CXXMethodDecl* dispatch) { | 1631 void NoteManualDispatchMethod(CXXMethodDecl* dispatch) { |
1488 SourceLocation loc = dispatch->getLocStart(); | 1632 SourceLocation loc = dispatch->getLocStart(); |
1489 SourceManager& manager = instance_.getSourceManager(); | 1633 SourceManager& manager = instance_.getSourceManager(); |
1490 FullSourceLoc full_loc(loc, manager); | 1634 FullSourceLoc full_loc(loc, manager); |
1491 diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch; | 1635 diagnostic_.Report(full_loc, diag_manual_dispatch_method_note_) << dispatch; |
1492 } | 1636 } |
1493 | 1637 |
1494 void NoteBaseRequiresTracing(BasePoint* base) { | 1638 void NoteBaseRequiresTracing(BasePoint* base) { |
1495 SourceLocation loc = base->spec().getLocStart(); | 1639 SourceLocation loc = base->spec().getLocStart(); |
1496 SourceManager& manager = instance_.getSourceManager(); | 1640 SourceManager& manager = instance_.getSourceManager(); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1567 unsigned diag_finalizer_accesses_finalized_field_; | 1711 unsigned diag_finalizer_accesses_finalized_field_; |
1568 unsigned diag_overridden_non_virtual_trace_; | 1712 unsigned diag_overridden_non_virtual_trace_; |
1569 unsigned diag_missing_trace_dispatch_method_; | 1713 unsigned diag_missing_trace_dispatch_method_; |
1570 unsigned diag_missing_finalize_dispatch_method_; | 1714 unsigned diag_missing_finalize_dispatch_method_; |
1571 unsigned diag_virtual_and_manual_dispatch_; | 1715 unsigned diag_virtual_and_manual_dispatch_; |
1572 unsigned diag_missing_trace_dispatch_; | 1716 unsigned diag_missing_trace_dispatch_; |
1573 unsigned diag_missing_finalize_dispatch_; | 1717 unsigned diag_missing_finalize_dispatch_; |
1574 unsigned diag_derives_non_stack_allocated_; | 1718 unsigned diag_derives_non_stack_allocated_; |
1575 unsigned diag_class_overrides_new_; | 1719 unsigned diag_class_overrides_new_; |
1576 unsigned diag_class_declares_pure_virtual_trace_; | 1720 unsigned diag_class_declares_pure_virtual_trace_; |
1721 unsigned diag_left_most_base_must_be_polymorphic_; | |
1722 unsigned diag_base_class_must_declare_virtual_trace_; | |
1577 | 1723 |
1578 unsigned diag_base_requires_tracing_note_; | 1724 unsigned diag_base_requires_tracing_note_; |
1579 unsigned diag_field_requires_tracing_note_; | 1725 unsigned diag_field_requires_tracing_note_; |
1580 unsigned diag_raw_ptr_to_gc_managed_class_note_; | 1726 unsigned diag_raw_ptr_to_gc_managed_class_note_; |
1581 unsigned diag_ref_ptr_to_gc_managed_class_note_; | 1727 unsigned diag_ref_ptr_to_gc_managed_class_note_; |
1582 unsigned diag_own_ptr_to_gc_managed_class_note_; | 1728 unsigned diag_own_ptr_to_gc_managed_class_note_; |
1583 unsigned diag_stack_allocated_field_note_; | 1729 unsigned diag_stack_allocated_field_note_; |
1584 unsigned diag_member_in_unmanaged_class_note_; | 1730 unsigned diag_member_in_unmanaged_class_note_; |
1585 unsigned diag_part_object_to_gc_derived_class_note_; | 1731 unsigned diag_part_object_to_gc_derived_class_note_; |
1586 unsigned diag_part_object_contains_gc_root_note_; | 1732 unsigned diag_part_object_contains_gc_root_note_; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1631 | 1777 |
1632 private: | 1778 private: |
1633 BlinkGCPluginOptions options_; | 1779 BlinkGCPluginOptions options_; |
1634 }; | 1780 }; |
1635 | 1781 |
1636 } // namespace | 1782 } // namespace |
1637 | 1783 |
1638 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 1784 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
1639 "blink-gc-plugin", | 1785 "blink-gc-plugin", |
1640 "Check Blink GC invariants"); | 1786 "Check Blink GC invariants"); |
OLD | NEW |