/* -*- 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 * * 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::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(array) - ARRAY_HEADER_SIZE); void* newArray = reinterpret_cast(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 ConsCellOwner; friend class Collector::InstanceOwner; typedef Collector::ArrayOwner ConsCellArrayOwner; friend class Collector::ArrayOwner; /** * 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 ConsCellArrayOwner; friend class Collector::InstanceOwner; /** * 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 }