Mozilla/mozilla/js2/src/collector.cpp
pschwartau%netscape.com dc3e631e9f Fixed Mac build warnings
git-svn-id: svn://10.0.0.236/trunk@117707 18797224-902f-48f8-a5cc-f745e15eee43
2002-03-28 23:24:47 +00:00

302 lines
8.2 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the JavaScript 2 Prototype.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Patrick Beard <beard@netscape.com>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
#include "collector.h"
namespace JavaScript
{
Collector::Collector()
: mObjectSpace(kObjectSpaceSize)
{
}
Collector::~Collector()
{
}
void
Collector::addRoot(pointer* root, size_type n)
{
mRoots.push_back(RootSegment(root, n));
}
void
Collector::removeRoot(pointer* root)
{
for (RootSegments::iterator i = mRoots.begin(), e = mRoots.end(); i != e; ++i) {
if (i->first == root) {
mRoots.erase(i);
return;
}
}
}
inline Collector::size_type align(Collector::size_type n)
{
return (n + (kObjectAlignment - 1)) & kObjectAddressMask;
}
/**
* Simple object header union. Contains a
* float64 element to ensure proper object
* alignment. On some architectures, the
* proper alignment may be wider.
*/
union ObjectHeader {
Collector::ObjectOwner* mOwner;
Collector::pointer mForwardingPointer;
float64 mAlignment;
};
Collector::pointer
Collector::allocateObject(size_type n, ObjectOwner* owner)
{
size_type size = align(n + sizeof(ObjectHeader));
pointer ptr = mObjectSpace.mAllocPtr;
if ((ptr + size) <= mObjectSpace.mLimitPtr) {
mObjectSpace.mAllocPtr += size;
ObjectHeader* header = (ObjectHeader*) ptr;
header->mOwner = owner;
return (pointer) STD::memset(header + 1, 0, n);
}
// need to run a garbage collection to recover more space, or double space size?
return 0;
}
void
Collector::collect()
{
// 0. swap from/to space. we now start allocating in the new toSpace.
Space<char>::pointer_type scanPtr = mObjectSpace.Swap();
// 1. scan all registered root segments.
for (RootSegments::iterator i = mRoots.begin(), e = mRoots.end(); i != e; ++i) {
RootSegment& r = *i;
pointer* refs = r.first;
pointer* limit = r.first + r.second;
while (refs < limit) {
pointer& ref = *refs++;
if (ref) {
ref = (pointer) copy(ref);
}
}
}
// 2. Scan through toSpace until scanPtr meets mAllocPtr.
while (scanPtr < mObjectSpace.mAllocPtr) {
ObjectHeader* header = (ObjectHeader*) scanPtr;
size_type size = header->mOwner->scan(this, (header + 1));
scanPtr += align(size + sizeof(ObjectHeader));
}
}
inline bool isForwardingPointer(void* ptr)
{
return (bool)(uint32(ptr) & kIsForwardingPointer);
}
void* Collector::copy(void* object, Collector::size_type size)
{
if (object == NULL)
return NULL;
// forwarding pointer?
ObjectHeader* oldHeader = ((ObjectHeader*)object) - 1;
if (isForwardingPointer(oldHeader->mForwardingPointer))
return (oldHeader->mForwardingPointer - kIsForwardingPointer);
// copy the old object into toSpace. copy will always succeed,
// because we only call it from within collect. the problem
// is when we don't recover any space... will have to be able
// to expand the heaps.
ObjectOwner* owner = oldHeader->mOwner;
ObjectHeader* newHeader = (ObjectHeader*) mObjectSpace.mAllocPtr;
newHeader->mOwner = owner;
void* newObject = (newHeader + 1);
if (size == 0) size = owner->size(object);
STD::memcpy(newObject, object, size);
mObjectSpace.mAllocPtr += align(size + sizeof(ObjectHeader));
oldHeader->mForwardingPointer = pointer(newObject) + kIsForwardingPointer;
return newObject;
}
void* Collector::arrayCopy(void* array)
{
size_t* header = (size_t*) (reinterpret_cast<char*>(array) - ARRAY_HEADER_SIZE);
void* newArray = reinterpret_cast<char*>(copy(header, ARRAY_HEADER_SIZE + (header[0] * header[1]))) + ARRAY_HEADER_SIZE;
return newArray;
}
#if DEBUG
struct ConsCell {
float32 car;
ConsCell* cdr;
ConsCell() : car(0.0), cdr(NULL) {}
~ConsCell() {}
private:
typedef Collector::InstanceOwner<ConsCell> ConsCellOwner;
friend class Collector::InstanceOwner<ConsCell>;
typedef Collector::ArrayOwner<ConsCell> ConsCellArrayOwner;
friend class Collector::ArrayOwner<ConsCell>;
/**
* Scans through the object, and copies all references.
*/
Collector::size_type scan(Collector* collector)
{
this->cdr = (ConsCell*) collector->copy(this->cdr, sizeof(ConsCell));
return sizeof(ConsCell);
}
public:
void* operator new(size_t n, Collector& gc)
{
static ConsCellOwner owner;
return gc.allocateObject(n, &owner);
}
void operator delete(void* /* ptr */, Collector& /* gc */) {}
void* operator new[] (size_t n, Collector& gc)
{
static ConsCellArrayOwner owner;
return gc.allocateObject(n, &owner);
}
};
#if 0
struct ConsCellArray {
ConsCell* cells;
ConsCellArray(size_t count, Collector& gc) : cells(NULL) {
cells = new (gc) ConsCell[count];
}
private:
typedef Collector::InstanceOwner<ConsCellArray> ConsCellArrayOwner;
friend class Collector::InstanceOwner<ConsCellArray>;
/**
* Scans through the object, and copies all references.
*/
Collector::size_type scan(Collector* collector)
{
this->cells = (ConsCell*) collector->arrayCopy(this->cells);
return sizeof(ConsCellArray);
}
public:
void* operator new(size_t n, Collector& gc)
{
static ConsCellArrayOwner owner;
return gc.allocateObject(n, &owner);
}
void operator delete(void* ptr, Collector& gc) {}
};
#endif
void testCollector()
{
Collector gc;
ConsCell* head = 0;
gc.addRoot((Collector::pointer*)&head);
const size_t kCellCount = 10;
ConsCell* cell;
ConsCell** link = &head;
size_t i;
for (i = 0; i < kCellCount; ++i) {
*link = cell = new (gc) ConsCell;
ASSERT(cell);
cell->car = i;
link = &cell->cdr;
}
// circularly link the list.
*link = head;
// run a garbage collection.
gc.collect();
// walk the list again to verify that it is intact.
link = &head;
for (i = 0; i < kCellCount; i++) {
cell = *link;
ASSERT(cell);
ASSERT(cell->car == (float32)i);
ASSERT(cell->cdr);
link = &cell->cdr;
}
// make sure list is still circularly linked.
ASSERT(*link == head);
gc.removeRoot((Collector::pointer*)&head);
#if 0
// create an array of ConsCells.
ConsCellArray* array = 0;
gc.addRoot((Collector::pointer*)&array);
array = new (gc) ConsCellArray(kCellCount, gc);
for (i = 0; i < kCellCount; i++) {
cell = &array->cells[i];
cell->car = i;
}
// run a garbage collection.
gc.collect();
for (i = 0; i < kCellCount; i++) {
cell = &array->cells[i];
ASSERT(cell);
ASSERT(cell->car == (float32)i);
ASSERT(cell->cdr == NULL);
}
#endif
}
#endif // DEBUG
}