Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  testGCHeapBarriers.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"

#include "gc/AllocKind.h"
#include "gc/Cell.h"
#include "gc/GCInternals.h"
#include "gc/GCRuntime.h"
#include "js/ArrayBuffer.h"  // JS::NewArrayBuffer
#include "js/experimental/TypedData.h"
#include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_GetProperty
#include "js/RootingAPI.h"
#include "jsapi-tests/tests.h"
#include "vm/Runtime.h"
#include "vm/TypedArrayObject.h"

#include "gc/Heap-inl.h"
#include "vm/JSContext-inl.h"

using namespace js;

static js::gc::CellColor GetColor(JSObject* obj) { return obj->color(); }
static js::gc::CellColor GetColor(const JS::ArrayBufferOrView& view) {
  return view.asObjectUnbarriered()->color();
}

static MOZ_MAYBE_UNUSED bool IsInsideNursery(gc::Cell* cell) {
  return !cell->isTenured();
}
static MOZ_MAYBE_UNUSED bool IsInsideNursery(
    const JS::ArrayBufferOrView& view) {
  return IsInsideNursery(view.asObjectUnbarriered());
}

// A heap-allocated structure containing one of our barriered pointer wrappers
// to test.
template <typename W, typename T>
struct TestStruct {
  W wrapper;

  void trace(JSTracer* trc) {
    TraceNullableEdge(trc, &wrapper, "TestStruct::wrapper");
  }

  TestStruct() {}
  explicit TestStruct(T init) : wrapper(init) {}
};

template <typename T>
static T CreateNurseryGCThing(JSContext* cx) = delete;

template <>
JSObject* CreateNurseryGCThing(JSContext* cx) {
  JS::RootedObject obj(cx, JS_NewPlainObject(cx));
  if (!obj) {
    return nullptr;
  }
  JS_DefineProperty(cx, obj, "x", 42, 0);
  MOZ_ASSERT(IsInsideNursery(obj));
  return obj;
}

template <>
JSFunction* CreateNurseryGCThing(JSContext* cx) {
  /*
   * We don't actually use the function as a function, so here we cheat and
   * cast a JSObject.
   */

  return static_cast<JSFunction*>(CreateNurseryGCThing<JSObject*>(cx));
}

template <>
JS::Uint8Array CreateNurseryGCThing(JSContext* cx) {
  JS::Rooted<JS::Uint8Array> arr(cx, JS::Uint8Array::create(cx, 100));
  JS::RootedObject obj(cx, arr.asObject());
  JS_DefineProperty(cx, obj, "x", 42, 0);
  MOZ_ASSERT(IsInsideNursery(obj));
  return arr;
}

template <typename T>
static T CreateTenuredGCThing(JSContext* cx) = delete;

template <>
JSObject* CreateTenuredGCThing(JSContext* cx) {
  // Use ArrayBuffers because they have finalizers, which allows using them in
  // TenuredHeap<> without awkward conversations about nursery allocatability.
  // Note that at some point ArrayBuffers might become nursery allocated at
  // which point this test will have to change.
  JSObject* obj = JS::NewArrayBuffer(cx, 20);
  MOZ_ASSERT(!IsInsideNursery(obj));
  MOZ_ASSERT(obj->getClass()->hasFinalize() &&
             !(obj->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
  return obj;
}

template <>
JS::ArrayBuffer CreateTenuredGCThing(JSContext* cx) {
  return JS::ArrayBuffer::fromObject(CreateTenuredGCThing<JSObject*>(cx));
}

template <>
JS::Uint8Array CreateTenuredGCThing(JSContext* cx) {
  // Use internal APIs that lets us specify the InitialHeap so we can ensure
  // that this is tenured.
  JSObject* obj = js::NewUint8ArrayWithLength(cx, 100, gc::Heap::Tenured);
  MOZ_ASSERT(!IsInsideNursery(obj));
  return JS::Uint8Array::fromObject(obj);
}

template <typename T>
void* CreateHiddenTenuredGCThing(JSContext* cx) {
  return CreateTenuredGCThing<T>(cx);
}

template <>
void* CreateHiddenTenuredGCThing<JS::ArrayBuffer>(JSContext* cx) {
  return CreateTenuredGCThing<JS::ArrayBuffer>(cx).asObjectUnbarriered();
}

template <>
void* CreateHiddenTenuredGCThing<JS::Uint8Array>(JSContext* cx) {
  return CreateTenuredGCThing<JS::Uint8Array>(cx).asObjectUnbarriered();
}

static uintptr_t UnbarrieredCastToInt(gc::Cell* cell) {
  return reinterpret_cast<uintptr_t>(cell);
}
static uintptr_t UnbarrieredCastToInt(const JS::ArrayBufferOrView& view) {
  return UnbarrieredCastToInt(view.asObjectUnbarriered());
}

template <typename T>
T RecoverHiddenGCThing(void* ptr) {
  return reinterpret_cast<T>(ptr);
}

template <>
JS::ArrayBuffer RecoverHiddenGCThing(void* ptr) {
  return JS::ArrayBuffer::fromObject(RecoverHiddenGCThing<JSObject*>(ptr));
}

template <>
JS::Uint8Array RecoverHiddenGCThing(void* ptr) {
  return JS::Uint8Array::fromObject(RecoverHiddenGCThing<JSObject*>(ptr));
}

static void MakeGray(JSObject* obj) {
  gc::TenuredCell* cell = &obj->asTenured();
  cell->unmark();
  cell->markIfUnmarked(gc::MarkColor::Gray);
  MOZ_ASSERT(obj->isMarkedGray());
}

static void MakeGray(const JS::ArrayBufferOrView& view) {
  MakeGray(view.asObjectUnbarriered());
}

// Test post-barrier implementation on wrapper types. The following wrapper
// types have post barriers:
//  - JS::Heap
//  - GCPtr
//  - HeapPtr
//  - WeakHeapPtr
BEGIN_TEST(testGCHeapPostBarriers) {
  AutoLeaveZeal nozeal(cx);
  AutoGCParameter disableSemispace(cx, JSGC_SEMISPACE_NURSERY_ENABLED, 0);

  /* Sanity check - objects start in the nursery and then become tenured. */
  JS_GC(cx);
  JS::RootedObject obj(cx, CreateNurseryGCThing<JSObject*>(cx));
  CHECK(IsInsideNursery(obj.get()));
  JS_GC(cx);
  CHECK(!IsInsideNursery(obj.get()));
  JS::RootedObject tenuredObject(cx, obj);

  /* JSObject and JSFunction objects are nursery allocated. */
  CHECK(TestHeapPostBarriersForType<JSObject*>());
  CHECK(TestHeapPostBarriersForType<JSFunction*>());
  CHECK(TestHeapPostBarriersForType<JS::Uint8Array>());
  // Bug 1599378: Add string tests.

  return true;
}

[[nodiscard]] bool TestCanAccessObject(JSObject* obj) {
  JS::RootedObject rootedObj(cx, obj);
  JS::RootedValue value(cx);
  CHECK(JS_GetProperty(cx, rootedObj, "x", &value));
  CHECK(value.isInt32());
  CHECK(value.toInt32() == 42);
  return true;
}
[[nodiscard]] bool TestCanAccessObject(const JS::ArrayBufferOrView& view) {
  return TestCanAccessObject(view.asObject());
}

template <typename T>
[[nodiscard]] bool TestHeapPostBarriersForType() {
  CHECK((TestHeapPostBarriersForWrapper<js::GCPtr, T>()));
  CHECK((TestHeapPostBarriersForMovableWrapper<JS::Heap, T>()));
  CHECK((TestHeapPostBarriersForMovableWrapper<js::HeapPtr, T>()));
  CHECK((TestHeapPostBarriersForMovableWrapper<js::WeakHeapPtr, T>()));
  return true;
}

template <template <typenameclass W, typename T>
[[nodiscard]] bool TestHeapPostBarriersForMovableWrapper() {
  CHECK((TestHeapPostBarriersForWrapper<W, T>()));
  CHECK((TestHeapPostBarrierMoveConstruction<W<T>, T>()));
  CHECK((TestHeapPostBarrierMoveAssignment<W<T>, T>()));
  return true;
}

template <template <typenameclass W, typename T>
[[nodiscard]] bool TestHeapPostBarriersForWrapper() {
  CHECK((TestHeapPostBarrierConstruction<W<T>, T>()));
  CHECK((TestHeapPostBarrierConstruction<const W<T>, T>()));
  CHECK((TestHeapPostBarrierUpdate<W<T>, T>()));
  if constexpr (!std::is_same_v<W<T>, GCPtr<T>>) {
    // It is not allowed to delete heap memory containing GCPtrs on
    // initialization failure like this and doing so will cause an assertion to
    // fail in the GCPtr destructor (although we disable this in some places in
    // this test).
    CHECK((TestHeapPostBarrierInitFailure<W<T>, T>()));
    CHECK((TestHeapPostBarrierInitFailure<const W<T>, T>()));
  }
  return true;
}

template <typename W, typename T>
[[nodiscard]] bool TestHeapPostBarrierConstruction() {
  T initialObj = CreateNurseryGCThing<T>(cx);
  CHECK(initialObj);
  CHECK(IsInsideNursery(initialObj));
  uintptr_t initialObjAsInt = UnbarrieredCastToInt(initialObj);

  {
    // We don't root our structure because that would end up tracing it on minor
    // GC and we're testing that heap post barrier works for things that aren't
    // roots.
    JS::AutoSuppressGCAnalysis noAnalysis(cx);

    auto* testStruct = js_new<TestStruct<W, T>>(initialObj);
    CHECK(testStruct);

    auto& wrapper = testStruct->wrapper;
    CHECK(wrapper == initialObj);

    cx->minorGC(JS::GCReason::API);

    CHECK(UnbarrieredCastToInt(wrapper.get()) != initialObjAsInt);
    CHECK(!IsInsideNursery(wrapper.get()));
    CHECK(TestCanAccessObject(wrapper.get()));

    // Disable the check that GCPtrs are only destroyed by the GC. What happens
    // on destruction isn't relevant to the test.
    gc::AutoSetThreadIsFinalizing threadIsFinalizing;

    js_delete(testStruct);
  }

  cx->minorGC(JS::GCReason::API);

  return true;
}

template <typename W, typename T>
[[nodiscard]] bool TestHeapPostBarrierUpdate() {
  // Normal case - allocate a heap object, write a nursery pointer into it and
  // check that it gets updated on minor GC.

  T initialObj = CreateNurseryGCThing<T>(cx);
  CHECK(initialObj);
  CHECK(IsInsideNursery(initialObj));
  uintptr_t initialObjAsInt = UnbarrieredCastToInt(initialObj);

  {
    // We don't root our structure because that would end up tracing it on minor
    // GC and we're testing that heap post barrier works for things that aren't
    // roots.
    JS::AutoSuppressGCAnalysis noAnalysis(cx);

    auto* testStruct = js_new<TestStruct<W, T>>();
    CHECK(testStruct);

    auto& wrapper = testStruct->wrapper;
    CHECK(!wrapper.get());

    wrapper = initialObj;
    CHECK(wrapper == initialObj);

    cx->minorGC(JS::GCReason::API);

    CHECK(UnbarrieredCastToInt(wrapper.get()) != initialObjAsInt);
    CHECK(!IsInsideNursery(wrapper.get()));
    CHECK(TestCanAccessObject(wrapper.get()));

    // Disable the check that GCPtrs are only destroyed by the GC. What happens
    // on destruction isn't relevant to the test.
    gc::AutoSetThreadIsFinalizing threadIsFinalizing;

    js_delete(testStruct);
  }

  cx->minorGC(JS::GCReason::API);

  return true;
}

template <typename W, typename T>
[[nodiscard]] bool TestHeapPostBarrierInitFailure() {
  // Failure case - allocate a heap object, write a nursery pointer into it
  // and fail to complete initialization.

  T initialObj = CreateNurseryGCThing<T>(cx);
  CHECK(initialObj);
  CHECK(IsInsideNursery(initialObj));

  {
    // We don't root our structure because that would end up tracing it on minor
    // GC and we're testing that heap post barrier works for things that aren't
    // roots.
    JS::AutoSuppressGCAnalysis noAnalysis(cx);

    auto testStruct = cx->make_unique<TestStruct<W, T>>(initialObj);
    CHECK(testStruct);

    auto& wrapper = testStruct->wrapper;
    CHECK(wrapper == initialObj);

    // testStruct deleted here, as if we left this block due to an error.
  }

  cx->minorGC(JS::GCReason::API);

  return true;
}

template <typename W, typename T>
[[nodiscard]] bool TestHeapPostBarrierMoveConstruction() {
  T initialObj = CreateNurseryGCThing<T>(cx);
  CHECK(initialObj);
  CHECK(IsInsideNursery(initialObj));
  uintptr_t initialObjAsInt = UnbarrieredCastToInt(initialObj);

  {
    // We don't root our structure because that would end up tracing it on minor
    // GC and we're testing that heap post barrier works for things that aren't
    // roots.
    JS::AutoSuppressGCAnalysis noAnalysis(cx);

    W wrapper1(initialObj);
    CHECK(wrapper1 == initialObj);

    W wrapper2(std::move(wrapper1));
    CHECK(wrapper2 == initialObj);

    cx->minorGC(JS::GCReason::API);

    CHECK(UnbarrieredCastToInt(wrapper1.get()) != initialObjAsInt);
    CHECK(UnbarrieredCastToInt(wrapper2.get()) != initialObjAsInt);
    CHECK(!IsInsideNursery(wrapper2.get()));
    CHECK(TestCanAccessObject(wrapper2.get()));
  }

  cx->minorGC(JS::GCReason::API);

  return true;
}

template <typename W, typename T>
[[nodiscard]] bool TestHeapPostBarrierMoveAssignment() {
  T initialObj = CreateNurseryGCThing<T>(cx);
  CHECK(initialObj);
  CHECK(IsInsideNursery(initialObj));
  uintptr_t initialObjAsInt = UnbarrieredCastToInt(initialObj);

  {
    // We don't root our structure because that would end up tracing it on minor
    // GC and we're testing that heap post barrier works for things that aren't
    // roots.
    JS::AutoSuppressGCAnalysis noAnalysis(cx);

    W wrapper1(initialObj);
    CHECK(wrapper1 == initialObj);

    W wrapper2;
    wrapper2 = std::move(wrapper1);
    CHECK(wrapper2 == initialObj);

    cx->minorGC(JS::GCReason::API);

    CHECK(UnbarrieredCastToInt(wrapper1.get()) != initialObjAsInt);
    CHECK(UnbarrieredCastToInt(wrapper2.get()) != initialObjAsInt);
    CHECK(!IsInsideNursery(wrapper2.get()));
    CHECK(TestCanAccessObject(wrapper2.get()));
  }

  cx->minorGC(JS::GCReason::API);

  return true;
}

END_TEST(testGCHeapPostBarriers)

// Test read barrier implementation on wrapper types. The following wrapper
// types have read barriers:
//  - JS::Heap
//  - JS::TenuredHeap
//  - WeakHeapPtr
//
// Also check that equality comparisons on wrappers do not trigger the read
// barrier.
BEGIN_TEST(testGCHeapReadBarriers) {
  AutoLeaveZeal nozeal(cx);

  CHECK((TestWrapperType<JS::Heap<JSObject*>, JSObject*>()));
  CHECK((TestWrapperType<JS::TenuredHeap<JSObject*>, JSObject*>()));
  CHECK((TestWrapperType<WeakHeapPtr<JSObject*>, JSObject*>()));
  CHECK((TestWrapperType<JS::Heap<JS::ArrayBuffer>, JS::ArrayBuffer>()));
  CHECK((TestWrapperType<JS::Heap<JS::Uint8Array>, JS::Uint8Array>()));

  return true;
}

template <typename WrapperT, typename ObjectT>
[[nodiscard]] bool TestWrapperType() {
  // Check that the read barrier normally marks gray things black.
  CHECK((TestReadBarrierUnmarksGray<WrapperT, ObjectT>()));

  // Check that the read barrier marks gray and white things black during an
  // incremental GC.
  CHECK((TestReadBarrierMarksBlack<WrapperT, ObjectT>(true)));
  CHECK((TestReadBarrierMarksBlack<WrapperT, ObjectT>(false)));

  // Allocate test objects and make them gray. We will make sure they stay
  // gray. (For most reads, the barrier will unmark gray.)
  Rooted<ObjectT> obj1(cx, CreateTenuredGCThing<ObjectT>(cx));
  Rooted<ObjectT> obj2(cx, CreateTenuredGCThing<ObjectT>(cx));
  MakeGray(obj1);
  MakeGray(obj2);

  WrapperT wrapper1(obj1);
  WrapperT wrapper2(obj2);
  const ObjectT constobj1 = obj1;
  const ObjectT constobj2 = obj2;
  CHECK((TestUnbarrieredOperations<WrapperT, ObjectT>(obj1, obj2, wrapper1,
                                                      wrapper2)));
  CHECK((TestUnbarrieredOperations<WrapperT, ObjectT>(constobj1, constobj2,
                                                      wrapper1, wrapper2)));

  return true;
}

template <typename WrapperT, typename ObjectT>
void Access(const WrapperT& wrapper) {
  if constexpr (std::is_base_of_v<JS::ArrayBufferOrView, ObjectT>) {
    (void)wrapper.asObject();
  } else {
    (void)*wrapper;
  }
}

template <typename WrapperT, typename ObjectT>
[[nodiscard]] bool TestReadBarrierUnmarksGray() {
  Rooted<ObjectT> obj(cx, CreateTenuredGCThing<ObjectT>(cx));
  WrapperT wrapper(obj);

  CHECK(GetColor(obj) == gc::CellColor::White);

  Access<WrapperT, ObjectT>(wrapper);

  CHECK(GetColor(obj) == gc::CellColor::White);

  MakeGray(obj);
  Access<WrapperT, ObjectT>(wrapper);

  CHECK(GetColor(obj) == gc::CellColor::Black);

  return true;
}

// Execute thunk |f| between two slices of an incremental GC controlled by zeal
// mode |mode|.
template <typename F>
[[nodiscard]] bool CallDuringIncrementalGC(uint32_t mode, F&& f) {
#ifndef JS_GC_ZEAL
  fprintf(stderr, "This test requires building with --enable-gczeal\n");
#else
  AutoGCParameter incremental(cx, JSGC_INCREMENTAL_GC_ENABLED, true);

  const int64_t BudgetMS = 10000;  // 10S should be long enough for anyone.

  JS::SetGCZeal(cx, mode, 0);
  JS::PrepareZoneForGC(cx, js::GetContextZone(cx));
  JS::SliceBudget budget{JS::TimeBudget(BudgetMS)};
  JS::StartIncrementalGC(cx, JS::GCOptions(), JS::GCReason::DEBUG_GC, budget);
  CHECK(JS::IsIncrementalGCInProgress(cx));

  CHECK(f());

  JS::FinishIncrementalGC(cx, JS::GCReason::DEBUG_GC);
#endif

  return true;
}

template <typename WrapperT, typename ObjectT>
[[nodiscard]] bool TestReadBarrierMarksBlack(bool fromWhite) {
  AutoLeaveZeal noZeal(cx);

  // Create an object and hide it from the hazard analysis.
  void* ptr = CreateHiddenTenuredGCThing<ObjectT>(cx);
  CHECK(ptr);

  CHECK(CallDuringIncrementalGC(9 /* YieldBeforeSweeping */, [&]() -> bool {
    CHECK(JS::IsIncrementalBarrierNeeded(cx));

    auto obj = RecoverHiddenGCThing<ObjectT>(ptr);

    WrapperT wrapper(obj);

    CHECK(GetColor(obj) == gc::CellColor::White);
    if (!fromWhite) {
      MakeGray(obj);
    }

    Access<WrapperT, ObjectT>(wrapper);

    CHECK(GetColor(obj) == gc::CellColor::Black);

    return true;
  }));

  return true;
}

template <typename WrapperT, typename ObjectT>
[[nodiscard]] bool TestUnbarrieredOperations(ObjectT obj, ObjectT obj2,
                                             WrapperT& wrapper,
                                             WrapperT& wrapper2) {
  (void)bool(wrapper);
  (void)bool(wrapper2);
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);

  int x = 0;

  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);
  x += obj == obj2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);
  x += obj == wrapper2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);
  x += wrapper == obj2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);
  x += wrapper == wrapper2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);

  CHECK(x == 0);

  x += obj != obj2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);
  x += obj != wrapper2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);
  x += wrapper != obj2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);
  x += wrapper != wrapper2;
  CHECK(GetColor(obj) == gc::CellColor::Gray);
  CHECK(GetColor(obj2) == gc::CellColor::Gray);

  CHECK(x == 4);

  return true;
}

END_TEST(testGCHeapReadBarriers)

using ObjectVector = Vector<JSObject*, 0, SystemAllocPolicy>;

// Test pre-barrier implementation on wrapper types. The following wrapper types
// have a pre-barrier:
//  - GCPtr
//  - HeapPtr
//  - PreBarriered
BEGIN_TEST(testGCHeapPreBarriers) {
  AutoLeaveZeal nozeal(cx);

  AutoGCParameter param1(cx, JSGC_INCREMENTAL_GC_ENABLED, true);

  // Create a bunch of objects. These are unrooted and will be used to test
  // whether barriers have fired by checking whether they have been marked
  // black.
  size_t objectCount = 100;  // Increase this if necessary when adding tests.
  ObjectVector testObjects;
  for (size_t i = 0; i < objectCount; i++) {
    JSObject* obj = CreateTenuredGCThing<JSObject*>(cx);
    CHECK(obj);
    CHECK(testObjects.append(obj));
  }

  // Start an incremental GC so we can detect if we cause barriers to fire, as
  // these will mark objects black.
  JS::PrepareForFullGC(cx);
  JS::SliceBudget budget(JS::WorkBudget(1));
  gc::GCRuntime* gc = &cx->runtime()->gc;
  gc->startDebugGC(JS::GCOptions::Normal, budget);
  while (gc->state() != gc::State::Mark) {
    gc->debugGCSlice(budget);
  }
  MOZ_ASSERT(cx->zone()->needsIncrementalBarrier());

  CHECK(TestWrapper<HeapPtr<JSObject*>>(testObjects));
  CHECK(TestWrapper<PreBarriered<JSObject*>>(testObjects));

  // GCPtr is different because 1) it doesn't support move operations as it's
  // supposed to be part of a GC thing and 2) it doesn't perform a pre-barrier
  // in its destructor because these are only destroyed as part of a GC where
  // the barrier is unnecessary.
  CHECK(TestGCPtr(testObjects));

  gc::FinishGC(cx, JS::GCReason::API);

  return true;
}

template <typename Wrapper>
[[nodiscard]] bool TestWrapper(ObjectVector& testObjects) {
  CHECK(TestCopyConstruction<Wrapper>(testObjects.popCopy()));
  CHECK(TestMoveConstruction<Wrapper>(testObjects.popCopy()));
  CHECK(TestAssignment<Wrapper>(testObjects.popCopy(), testObjects.popCopy()));
  CHECK(TestMoveAssignment<Wrapper>(testObjects.popCopy(),
                                    testObjects.popCopy()));
  return true;
}

template <typename Wrapper>
[[nodiscard]] bool TestCopyConstruction(JSObject* obj) {
  CHECK(GetColor(obj) == gc::CellColor::White);

  {
    Wrapper wrapper1(obj);
    Wrapper wrapper2(wrapper1);
    CHECK(wrapper1 == obj);
    CHECK(wrapper2 == obj);
    CHECK(GetColor(obj) == gc::CellColor::White);
  }

  // Check destructor performs pre-barrier.
  CHECK(GetColor(obj) == gc::CellColor::Black);

  return true;
}

template <typename Wrapper>
[[nodiscard]] bool TestMoveConstruction(JSObject* obj) {
  CHECK(GetColor(obj) == gc::CellColor::White);

  {
    Wrapper wrapper1(obj);
    MakeGray(obj);  // Check that we allow move of gray GC thing.
    Wrapper wrapper2(std::move(wrapper1));
    CHECK(!wrapper1);
    CHECK(wrapper2 == obj);
    CHECK(GetColor(obj) == gc::CellColor::Gray);
  }

  // Check destructor performs pre-barrier.
  CHECK(GetColor(obj) == gc::CellColor::Black);

  return true;
}

template <typename Wrapper>
[[nodiscard]] bool TestAssignment(JSObject* obj1, JSObject* obj2) {
  CHECK(GetColor(obj1) == gc::CellColor::White);
  CHECK(GetColor(obj2) == gc::CellColor::White);

  {
    Wrapper wrapper1(obj1);
    Wrapper wrapper2(obj2);

    wrapper2 = wrapper1;

    CHECK(wrapper1 == obj1);
    CHECK(wrapper2 == obj1);
    CHECK(GetColor(obj1) == gc::CellColor::White);  // No barrier fired.
    CHECK(GetColor(obj2) == gc::CellColor::Black);  // Pre barrier fired.
  }

  // Check destructor performs pre-barrier.
  CHECK(GetColor(obj1) == gc::CellColor::Black);

  return true;
}

template <typename Wrapper>
[[nodiscard]] bool TestMoveAssignment(JSObject* obj1, JSObject* obj2) {
  CHECK(GetColor(obj1) == gc::CellColor::White);
  CHECK(GetColor(obj2) == gc::CellColor::White);

  {
    Wrapper wrapper1(obj1);
    Wrapper wrapper2(obj2);

    MakeGray(obj1);  // Check we allow move of gray thing.
    wrapper2 = std::move(wrapper1);

    CHECK(!wrapper1);
    CHECK(wrapper2 == obj1);
    CHECK(GetColor(obj1) == gc::CellColor::Gray);   // No barrier fired.
    CHECK(GetColor(obj2) == gc::CellColor::Black);  // Pre barrier fired.
  }

  // Check destructor performs pre-barrier.
  CHECK(GetColor(obj1) == gc::CellColor::Black);

  return true;
}

[[nodiscard]] bool TestGCPtr(ObjectVector& testObjects) {
  CHECK(TestGCPtrCopyConstruction(testObjects.popCopy()));
  CHECK(TestGCPtrAssignment(testObjects.popCopy(), testObjects.popCopy()));
  return true;
}

[[nodiscard]] bool TestGCPtrCopyConstruction(JSObject* obj) {
  CHECK(GetColor(obj) == gc::CellColor::White);

  {
    // Let us destroy GCPtrs ourselves for testing purposes.
    gc::AutoSetThreadIsFinalizing threadIsFinalizing;

    GCPtr<JSObject*> wrapper1(obj);
    GCPtr<JSObject*> wrapper2(wrapper1);
    CHECK(wrapper1 == obj);
    CHECK(wrapper2 == obj);
    CHECK(GetColor(obj) == gc::CellColor::White);
  }

  // GCPtr doesn't perform pre-barrier in destructor.
  CHECK(GetColor(obj) == gc::CellColor::White);

  return true;
}

[[nodiscard]] bool TestGCPtrAssignment(JSObject* obj1, JSObject* obj2) {
  CHECK(GetColor(obj1) == gc::CellColor::White);
  CHECK(GetColor(obj2) == gc::CellColor::White);

  {
    // Let us destroy GCPtrs ourselves for testing purposes.
    gc::AutoSetThreadIsFinalizing threadIsFinalizing;

    GCPtr<JSObject*> wrapper1(obj1);
    GCPtr<JSObject*> wrapper2(obj2);

    wrapper2 = wrapper1;

    CHECK(wrapper1 == obj1);
    CHECK(wrapper2 == obj1);
    CHECK(GetColor(obj1) == gc::CellColor::White);  // No barrier fired.
    CHECK(GetColor(obj2) == gc::CellColor::Black);  // Pre barrier fired.
  }

  // GCPtr doesn't perform pre-barrier in destructor.
  CHECK(GetColor(obj1) == gc::CellColor::White);

  return true;
}

END_TEST(testGCHeapPreBarriers)

99%


¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge