| 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 |