| Index: Source/platform/heap/Visitor.h
|
| diff --git a/Source/platform/heap/Visitor.h b/Source/platform/heap/Visitor.h
|
| index adbdbc564a49569d2e1b0cec208cfbcfef12c3a7..8916300d089ab98b11e7d6e2f9c5ce9255c82432 100644
|
| --- a/Source/platform/heap/Visitor.h
|
| +++ b/Source/platform/heap/Visitor.h
|
| @@ -897,19 +897,149 @@ public:
|
| return visitor->isAlive(this); \
|
| } \
|
| private:
|
| +
|
| +// A C++ object's vptr will be initialized to its leftmost base's vtable after
|
| +// the constructors of all its subclasses have run, so if a subclass constructor
|
| +// tries to access any of the vtbl entries of its leftmost base prematurely,
|
| +// it'll find an as-yet incorrect vptr and fail. Which is exactly what a
|
| +// garbage collector will try to do if it tries to access the leftmost base
|
| +// while one of the subclass constructors of a GC mixin object triggers a GC.
|
| +// It is consequently not safe to allow any GCs while these objects are under
|
| +// (sub constructor) construction.
|
| +//
|
| +// To prevent GCs in that restricted window of a mixin object's construction:
|
| +//
|
| +// - The initial allocation of the mixin object will enter a no GC scope.
|
| +// This is done by overriding 'operator new' for mixin instances.
|
| +// - When the constructor for the mixin is invoked, after all the
|
| +// derived constructors have run, it will invoke the constructor
|
| +// for a field whose only purpose is to leave the GC scope.
|
| +// GarbageCollectedMixinConstructorMarker's constructor takes care of
|
| +// this and the field is declared by way of USING_GARBAGE_COLLECTED_MIXIN().
|
| +//
|
| +// If a GC mixin class, declared so using USING_GARBAGE_COLLECTED_MIXIN(), derives
|
| +// leftmost from another such USING_GARBAGE_COLLECTED_MIXIN-mixin class, extra
|
| +// care and handling is needed for the above two steps; see comment below.
|
| +
|
| +
|
| +// Trait template to resolve the effective base GC class to use when allocating
|
| +// objects at some type T. Requires specialization for Node and CSSValue
|
| +// derived types to have these be allocated on appropriate heaps.
|
| +//
|
| +// FIXME: this trait is needed to support Node's overriding 'operator new'
|
| +// implementation in combination with GC mixins (step 1 above.) Should
|
| +// Node' operator new no longer be needed, this trait can be removed.
|
| +template<typename T, typename Enabled = void>
|
| +class EffectiveGCBaseTrait {
|
| +public:
|
| + using Type = T;
|
| +};
|
| +
|
| +#define ALLOCATE_ALL_INSTANCES_ON_SAME_GC_HEAP(TYPE) \
|
| +template<typename T> \
|
| +class EffectiveGCBaseTrait<T, typename WTF::EnableIf<WTF::IsSubclass<T, blink::TYPE>::value>::Type> { \
|
| +public: \
|
| + using Type = TYPE; \
|
| +}
|
| +
|
| +#if ENABLE(OILPAN)
|
| +#define WILL_HAVE_ALL_INSTANCES_ON_SAME_GC_HEAP(TYPE) ALLOCATE_ALL_INSTANCES_ON_SAME_GC_HEAP(TYPE)
|
| +#else
|
| +#define WILL_HAVE_ALL_INSTANCES_ON_SAME_GC_HEAP(TYPE)
|
| +#endif
|
| +
|
| +#define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \
|
| +public: \
|
| + GC_PLUGIN_IGNORE("crbug.com/456823") \
|
| + void* operator new(size_t size) \
|
| + { \
|
| + void* object = Heap::allocate<typename EffectiveGCBaseTrait<TYPE>::Type>(size); \
|
| + ThreadState* state = ThreadStateFor<ThreadingTrait<TYPE>::Affinity>::state(); \
|
| + state->enterGCForbiddenScope(TYPE::mixinLevels); \
|
| + return object; \
|
| + } \
|
| +private: \
|
| + GarbageCollectedMixinConstructorMarker<TYPE> m_mixinConstructorMarker;
|
| +
|
| +#if ENABLE(ASSERT)
|
| +#define DEFINE_GARBAGE_COLLECTED_MIXIN_NONNESTED_DEBUG() \
|
| +protected: \
|
| + virtual void mixinsCannotBeImplicitlyNested() final { /* Please use USING_GARBAGE_COLLECTED_MIXIN_NESTED(Mixin, DerivedFromOtherMixin); */ }
|
| +#define DEFINE_GARBAGE_COLLECTED_MIXIN_NESTED_DEBUG(TYPE, SUBTYPE) \
|
| +protected: \
|
| + static void declaredNestedMixinsMustBeAccurate() \
|
| + { \
|
| + static_assert(WTF::IsSubclass<blink::TYPE, blink::SUBTYPE>::value, "Mixin class does not derive from its stated, nested mixin class."); \
|
| + }
|
| +#else
|
| +#define DEFINE_GARBAGE_COLLECTED_MIXIN_NONNESTED_DEBUG()
|
| +#define DEFINE_GARBAGE_COLLECTED_MIXIN_NESTED_DEBUG(TYPE, SUBTYPE)
|
| +#endif
|
| +
|
| +// Mixins that wrap/nest others requires extra handling:
|
| +//
|
| +// class A : public GarbageCollected<A>, public GarbageCollectedMixin {
|
| +// USING_GARBAGE_COLLECTED_MIXIN(A);
|
| +// ...
|
| +// }'
|
| +// public B final : public A, public SomeOtherMixinInterface {
|
| +// USING_GARBAGE_COLLECTED_MIXIN(B);
|
| +// ...
|
| +// };
|
| +//
|
| +// The "operator new" for B will enter the forbidden GC scope, but
|
| +// upon construction, two GarbageCollectedMixinConstructorMarker constructors
|
| +// will run -- one for A (first) and another for B (secondly.) Only
|
| +// the second one should leave the forbidden GC scope.
|
| +//
|
| +// Arrange for the balanced use of the forbidden GC scope counter by
|
| +// adding on the number of mixin constructor markers for a type.
|
| +// This is equal to the how many GC mixins that the type nests.
|
| +//
|
| +// FIXME: We currently require that a nested GC mixin (e.g., B)
|
| +// must declare what it nests: USING_GARBAGE_COLLECTED_MIXIN_NESTED(B, A);
|
| +// must be used for it. It's a static error if it doesn't.
|
| +//
|
| +// It would however be preferable to statically derive the
|
| +// "mixin nesting level" for these mixin types and use that without
|
| +// having to explicitly state the particular type that a mixin nests.
|
| +// It seems like it could be expressible as a compile-time type
|
| +// computation..
|
| +
|
| +#define DEFINE_GARBAGE_COLLECTED_MIXIN_NONNESTED() \
|
| +protected: \
|
| + static const unsigned mixinLevels = 1;
|
| +#define DEFINE_GARBAGE_COLLECTED_MIXIN_NESTED(TYPE, SUBTYPE) \
|
| +protected: \
|
| + static const unsigned mixinLevels = SUBTYPE::mixinLevels + 1;
|
| +
|
| #if ENABLE(INLINED_TRACE)
|
| -#define USING_GARBAGE_COLLECTED_MIXIN(TYPE) \
|
| +#define USING_GARBAGE_COLLECTED_MIXIN_BASE(TYPE) \
|
| DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::Visitor*, TYPE) \
|
| - DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::InlinedGlobalMarkingVisitor, TYPE)
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::InlinedGlobalMarkingVisitor, TYPE) \
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)
|
| #else
|
| -#define USING_GARBAGE_COLLECTED_MIXIN(TYPE) \
|
| - DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::Visitor*, TYPE)
|
| +#define USING_GARBAGE_COLLECTED_MIXIN_BASE(TYPE) \
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(blink::Visitor*, TYPE) \
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)
|
| #endif
|
|
|
| +#define USING_GARBAGE_COLLECTED_MIXIN(TYPE) \
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_NONNESTED_DEBUG() \
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_NONNESTED() \
|
| + USING_GARBAGE_COLLECTED_MIXIN_BASE(TYPE)
|
| +
|
| +#define USING_GARBAGE_COLLECTED_MIXIN_NESTED(TYPE, SUBTYPE) \
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_NESTED_DEBUG(TYPE, SUBTYPE) \
|
| + DEFINE_GARBAGE_COLLECTED_MIXIN_NESTED(TYPE, SUBTYPE) \
|
| + USING_GARBAGE_COLLECTED_MIXIN_BASE(TYPE)
|
| +
|
| #if ENABLE(OILPAN)
|
| #define WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(TYPE) USING_GARBAGE_COLLECTED_MIXIN(TYPE)
|
| +#define WILL_BE_USING_GARBAGE_COLLECTED_MIXIN_NESTED(TYPE, NESTEDMIXIN) USING_GARBAGE_COLLECTED_MIXIN_NESTED(TYPE, NESTEDMIXIN)
|
| #else
|
| #define WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(TYPE)
|
| +#define WILL_BE_USING_GARBAGE_COLLECTED_MIXIN_NESTED(TYPE, NESTEDMIXIN)
|
| #endif
|
|
|
| // Template to determine if a class is a GarbageCollectedMixin by checking if it
|
| @@ -929,6 +1059,28 @@ public:
|
| static const bool value = sizeof(checkMarker<T>(nullptr)) == sizeof(YesType);
|
| };
|
|
|
| +// An empty class with a constructor that's arranged invoked when all derived constructors
|
| +// of a mixin instance have completed and it is safe to allow GCs again. See
|
| +// AllocateObjectTrait<> comment for more.
|
| +//
|
| +// USING_GARBAGE_COLLECTED_MIXIN() declares a GarbageCollectedMixinConstructorMarker<> private
|
| +// field. By following Blink convention of using the macro at the top of a class declaration,
|
| +// its constructor will run first.
|
| +template<typename T>
|
| +class GarbageCollectedMixinConstructorMarker {
|
| +public:
|
| + GarbageCollectedMixinConstructorMarker()
|
| + {
|
| + // FIXME: if prompt conservative GCs are needed, forced GCs that
|
| + // were denied while within this scope, could now be performed.
|
| + // For now, assume the next out-of-line allocation request will
|
| + // happen soon enough and take care of it. Mixin objects aren't
|
| + // overly common.
|
| + ThreadState* state = ThreadStateFor<ThreadingTrait<T>::Affinity>::state();
|
| + state->leaveGCForbiddenScope();
|
| + }
|
| +};
|
| +
|
| #if ENABLE(GC_PROFILING)
|
| template<typename T>
|
| struct TypenameStringTrait {
|
|
|