xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
author Birunthan Mohanathas <birunthan@mohanathas.com>
Mon, 08 Jun 2015 13:39:49 -0700
changeset 270979 117d25cbd07c1680aa38a9563e7cfc2d12898870
parent 259111 8ed771ecfecd259a6236fbf0ad3214affb3b98aa
child 295874 6c93d1044b7e66f9c9c368ebfcd9c9da3d481081
permissions -rw-r--r--
Bug 968520 - Add mozilla::fallible to FallibleArray calls in tests. r=froydnj

/* -*- 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/. */

/*
 * Tests that generational garbage collection post-barriers are correctly
 * implemented for nsTArrays that contain JavaScript Values.
 */

#include "jsapi.h"
#include "nsTArray.h"

#include "gtest/gtest.h"

#include "js/TracingAPI.h"
#include "js/HeapAPI.h"

#include "mozilla/CycleCollectedJSRuntime.h"

using namespace JS;
using namespace mozilla;

template<class ArrayT>
static void
TraceArray(JSTracer* trc, void* data)
{
  ArrayT* array = static_cast<ArrayT *>(data);
  for (unsigned i = 0; i < array->Length(); ++i)
    JS_CallObjectTracer(trc, &array->ElementAt(i), "array-element");
}

/*
 * Use arrays with initial size much smaller than the final number of elements
 * to test that moving Heap<T> elements works correctly.
 */
const size_t ElementCount = 100;
const size_t InitialElements = ElementCount / 10;

template<class ArrayT>
static void
RunTest(JSRuntime* rt, JSContext* cx, ArrayT* array)
{
  JS_GC(rt);

  ASSERT_TRUE(array != nullptr);
  JS_AddExtraGCRootsTracer(rt, TraceArray<ArrayT>, array);

  /*
   * Create the array and fill it with new JS objects. With GGC these will be
   * allocated in the nursery.
   */
  RootedValue value(cx);
  const char* property = "foo";
  for (size_t i = 0; i < ElementCount; ++i) {
    RootedObject obj(cx, JS_NewPlainObject(cx));
    ASSERT_FALSE(JS::ObjectIsTenured(obj));
    value = Int32Value(i);
    ASSERT_TRUE(JS_SetProperty(cx, obj, property, value));
    ASSERT_TRUE(array->AppendElement(obj, fallible));
  }

  /*
   * If postbarriers are not working, we will crash here when we try to mark
   * objects that have been moved to the tenured heap.
   */
  JS_GC(rt);

  /*
   * Sanity check that our array contains what we expect.
   */
  for (size_t i = 0; i < ElementCount; ++i) {
    RootedObject obj(cx, array->ElementAt(i));
    ASSERT_TRUE(JS::ObjectIsTenured(obj));
    ASSERT_TRUE(JS_GetProperty(cx, obj, property, &value));
    ASSERT_TRUE(value.isInt32());
    ASSERT_EQ(static_cast<int32_t>(i), value.toInt32());
  }

  JS_RemoveExtraGCRootsTracer(rt, TraceArray<ArrayT>, array);
}

static void
CreateGlobalAndRunTest(JSRuntime* rt, JSContext* cx)
{
  static const JSClass GlobalClass = {
    "global", JSCLASS_GLOBAL_FLAGS,
    nullptr, nullptr, nullptr, nullptr,
    nullptr, nullptr, nullptr, nullptr,
    nullptr, nullptr, nullptr, nullptr,
    JS_GlobalObjectTraceHook
  };

  JS::CompartmentOptions options;
  options.setVersion(JSVERSION_LATEST);
  JS::PersistentRootedObject global(cx);
  global = JS_NewGlobalObject(cx, &GlobalClass, nullptr, JS::FireOnNewGlobalHook, options);
  ASSERT_TRUE(global != nullptr);

  JSCompartment *oldCompartment = JS_EnterCompartment(cx, global);

  typedef Heap<JSObject*> ElementT;

  {
    nsTArray<ElementT>* array = new nsTArray<ElementT>(InitialElements);
    RunTest(rt, cx, array);
    delete array;
  }

  {
    FallibleTArray<ElementT>* array = new FallibleTArray<ElementT>(InitialElements);
    RunTest(rt, cx, array);
    delete array;
  }

  {
    nsAutoTArray<ElementT, InitialElements> array;
    RunTest(rt, cx, &array);
  }

  {
    AutoFallibleTArray<ElementT, InitialElements> array;
    RunTest(rt, cx, &array);
  }

  JS_LeaveCompartment(cx, oldCompartment);
}

TEST(GCPostBarriers, nsTArray) {
  CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get();
  ASSERT_TRUE(ccrt != nullptr);
  JSRuntime* rt = ccrt->Runtime();
  ASSERT_TRUE(rt != nullptr);

  JSContext *cx = JS_NewContext(rt, 8192);
  ASSERT_TRUE(cx != nullptr);
  JS_BeginRequest(cx);

  CreateGlobalAndRunTest(rt, cx);

  JS_EndRequest(cx);
  JS_DestroyContext(cx);
}