From cc161a3dadfd8f1d0ab1e2d39ad88af93e5303e0 Mon Sep 17 00:00:00 2001 From: "waldemar%netscape.com" Date: Wed, 1 Mar 2000 07:00:05 +0000 Subject: [PATCH] Added linked list and arena destructor utilities --- js/js2/utilities.cpp | 54 ++++++++++++++++++- js/js2/utilities.h | 120 ++++++++++++++++++++++++++++++++++++------ js2/src/utilities.cpp | 54 ++++++++++++++++++- js2/src/utilities.h | 120 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 310 insertions(+), 38 deletions(-) diff --git a/js/js2/utilities.cpp b/js/js2/utilities.cpp index 6381f70ce190..514c45ed57a1 100644 --- a/js/js2/utilities.cpp +++ b/js/js2/utilities.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include "utilities.h" @@ -1543,6 +1542,14 @@ const char16 *JS::skipWhiteSpace(const char16 *str, const char16 *strEnd) // #define DEBUG_ARENA to allocate each object in its own malloc block. // This allows tools such as Purify to do bounds checking on all blocks. +struct JS::Arena::DestructorEntry: JS::ArenaObject { + DestructorEntry *next; // Next destructor registration in linked list + void (*destructor)(void *); // Destructor function + void *object; // Object on which to call the destructor + + DestructorEntry(void (*destructor)(void *), void *object): destructor(destructor), object(object) {} +}; + // Construct an Arena that allocates memory in chunks of the given size. JS::Arena::Arena(size_t blockSize): blockSize(blockSize), freeBegin(0), freeEnd(0) @@ -1562,9 +1569,17 @@ void JS::Arena::Directory::clear() } -// Deallocate the Arena's blocks and directories. +// Call the Arena's registered destructors and then deallocate the Arena's blocks and +// directories. void JS::Arena::clear() { + DestructorEntry *e = destructorEntries; + while (e) { + e->destructor(e->object); + e = e->next; + } + destructorEntries = 0; + Directory *d = rootDirectory.next; while (d) { Directory *next = d->next; @@ -1629,6 +1644,41 @@ void *JS::Arena::allocate(size_t size) } +// Ensure that object's destructor is called at the time the arena is deallocated or cleared. +// The destructors will be called in reverse order of being registered. +// registerDestructor might itself runs out of memory, in which case it immediately +// calls object's destructor before throwing bad_alloc. +void JS::Arena::newDestructorEntry(void (*destructor)(void *), void *object) +{ + try { + DestructorEntry *e = new(*this) DestructorEntry(destructor, object); + e->next = destructorEntries; + destructorEntries = e; + } catch (...) { + destructor(object); + throw; + } +} + + +// Allocate a String in the Arena and register that String so that it is deallocated at +// the same time as the Arena. +// DO NOT CALL DELETE ON THE RESULT! +JS::String *JS::newArenaString(Arena &arena) +{ + String *s = new(arena) String(); + arena.registerDestructor(s); + return s; +} + +JS::String *JS::newArenaString(Arena &arena, const String &str) +{ + String *s = new(arena) String(str); + arena.registerDestructor(s); + return s; +} + + // // C++ I/O // diff --git a/js/js2/utilities.h b/js/js2/utilities.h index d8316a8582e8..8fc6dc24ac61 100644 --- a/js/js2/utilities.h +++ b/js/js2/utilities.h @@ -22,7 +22,9 @@ #include "systemtypes.h" #include +#include #include +#include #include #ifndef _WIN32 // Microsoft VC6 bug: standard identifiers should be in std namespace @@ -236,14 +238,54 @@ namespace JavaScript { const char16 *skipWhiteSpace(const char16 *str, const char16 *strEnd); +// +// Algorithms +// + + // Assign zero to every element between first inclusive and last exclusive. + // This is equivalent ot fill(first, last, 0) but may be more efficient. + template + inline void zero(For first, For last) + { + while (first != last) + *first++ = 0; + } + + + // Assign zero to n elements starting at first. + // This is equivalent ot fill_n(first, n, 0) but may be more efficient. + template + inline void zero_n(For first, Size n) + { + while (n--) + *first++ = 0; + } + + // // Arenas // + // Pretend that obj points to a value of class T and call obj's destructor. + template + void classDestructor(void *obj) + { + static_cast(obj)->~T(); + } + + // An arena is a region of memory from which objects either derived from ArenaObject or allocated // using a ArenaAllocator can be allocated. Deleting these objects individually runs the destructors, // if any, but does not deallocate the memory. On the other hand, the entire arena can be deallocated // as a whole. + // + // One may also allocate other objects in an arena by using the Arena specialization of the global + // operator new. However, be careful not to delete any such objects explicitly! + // + // Destructors can be registered for objects (or parts of objects) allocated in the arena. These + // destructors are called, in reverse order of being registered, at the time the arena is deallocated + // or cleared. When registering destructors for an object O be careful not to delete O manually because that + // would run its destructor twice. class Arena { struct Directory { enum {maxNBlocks = 31}; @@ -256,11 +298,14 @@ namespace JavaScript { void clear(); }; + struct DestructorEntry; + char *freeBegin; // Pointer to free bytes left in current block char *freeEnd; // Pointer to end of free bytes left in current block size_t blockSize; // Size of individual arena blocks Directory *currentDirectory; // Directory in which the last block was allocated Directory rootDirectory; // Initial directory; root of linked list of Directories + DestructorEntry *destructorEntries; // Linked list of destructor registrations, ordered from most to least recently registered public: explicit Arena(size_t blockSize = 1024); @@ -273,8 +318,14 @@ namespace JavaScript { private: void *newBlock(size_t size); + void newDestructorEntry(void (*destructor)(void *), void *object); public: void *allocate(size_t size); + // Ensure that object's destructor is called at the time the arena is deallocated or cleared. + // The destructors will be called in reverse order of being registered. + // registerDestructor might itself runs out of memory, in which case it immediately + // calls object's destructor before throwing bad_alloc. + template void registerDestructor(T *object) {newDestructorEntry(&classDestructor, object);} }; @@ -282,6 +333,7 @@ namespace JavaScript { struct ArenaObject { void *operator new(size_t size, Arena &arena) {return arena.allocate(size);} void *operator new[](size_t size, Arena &arena) {return arena.allocate(size);} + private: void operator delete(void *, size_t) {} void operator delete[](void *) {} }; @@ -320,6 +372,10 @@ namespace JavaScript { }; + String *newArenaString(Arena &arena); + String *newArenaString(Arena &arena, const String &str); + + // // Array auto_ptr's // @@ -409,27 +465,47 @@ namespace JavaScript { // -// Algorithms +// Linked Lists // - // Assign zero to every element between first inclusive and last exclusive. - // This is equivalent ot fill(first, last, 0) but may be more efficient. - template - inline void zero(For first, For last) - { - while (first != last) - *first++ = 0; - } + // In some cases it is desirable to manipulate ordinary C-style linked lists as though + // they were STL-like sequences. These classes define STL forward iterators that walk + // through singly-linked lists of objects threaded through fields named 'next'. The type + // parameter E must be a class that has a member named 'next' whose type is E* or const E*. + + template + class ListIterator: public std::iterator { + E *element; + public: + ListIterator() {} + explicit ListIterator(E *e): element(e) {} - // Assign zero to n elements starting at first. - // This is equivalent ot fill_n(first, n, 0) but may be more efficient. - template - inline void zero_n(For first, Size n) - { - while (n--) - *first++ = 0; - } + E &operator*() const {return *element;} + E *operator->() const {return element;} + ListIterator &operator++() {element = element->next; return *this;} + ListIterator operator++(int) {ListIterator i(*this); element = element->next; return i;} + friend bool operator==(const ListIterator &i, const ListIterator &j) {return i.element == j.element;} + friend bool operator!=(const ListIterator &i, const ListIterator &j) {return i.element != j.element;} + }; + + + template + class ConstListIterator: public std::iterator { + const E *element; + + public: + ConstListIterator() {} + ConstListIterator(const ListIterator &i): element(&*i) {} + explicit ConstListIterator(const E *e): element(e) {} + + const E &operator*() const {return *element;} + const E *operator->() const {return element;} + ConstListIterator &operator++() {element = element->next; return *this;} + ConstListIterator operator++(int) {ConstListIterator i(*this); element = element->next; return i;} + friend bool operator==(const ConstListIterator &i, const ConstListIterator &j) {return i.element == j.element;} + friend bool operator!=(const ConstListIterator &i, const ConstListIterator &j) {return i.element != j.element;} + }; // @@ -507,4 +583,14 @@ namespace JavaScript { String fullMessage() const; }; } + + +inline void *operator new(size_t size, JavaScript::Arena &arena) {return arena.allocate(size);} +inline void *operator new[](size_t size, JavaScript::Arena &arena) {return arena.allocate(size);} +#if 0 // Most compilers don't support this yet +// Global delete operators. These are only called in the rare cases that a constructor throws an exception +// and has to undo an operator new. An explicit delete statement will never invoke these. +inline void operator delete(void *, JavaScript::Arena &) {} +inline void operator delete[](void *, JavaScript::Arena &) {} +#endif #endif diff --git a/js2/src/utilities.cpp b/js2/src/utilities.cpp index 6381f70ce190..514c45ed57a1 100644 --- a/js2/src/utilities.cpp +++ b/js2/src/utilities.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include "utilities.h" @@ -1543,6 +1542,14 @@ const char16 *JS::skipWhiteSpace(const char16 *str, const char16 *strEnd) // #define DEBUG_ARENA to allocate each object in its own malloc block. // This allows tools such as Purify to do bounds checking on all blocks. +struct JS::Arena::DestructorEntry: JS::ArenaObject { + DestructorEntry *next; // Next destructor registration in linked list + void (*destructor)(void *); // Destructor function + void *object; // Object on which to call the destructor + + DestructorEntry(void (*destructor)(void *), void *object): destructor(destructor), object(object) {} +}; + // Construct an Arena that allocates memory in chunks of the given size. JS::Arena::Arena(size_t blockSize): blockSize(blockSize), freeBegin(0), freeEnd(0) @@ -1562,9 +1569,17 @@ void JS::Arena::Directory::clear() } -// Deallocate the Arena's blocks and directories. +// Call the Arena's registered destructors and then deallocate the Arena's blocks and +// directories. void JS::Arena::clear() { + DestructorEntry *e = destructorEntries; + while (e) { + e->destructor(e->object); + e = e->next; + } + destructorEntries = 0; + Directory *d = rootDirectory.next; while (d) { Directory *next = d->next; @@ -1629,6 +1644,41 @@ void *JS::Arena::allocate(size_t size) } +// Ensure that object's destructor is called at the time the arena is deallocated or cleared. +// The destructors will be called in reverse order of being registered. +// registerDestructor might itself runs out of memory, in which case it immediately +// calls object's destructor before throwing bad_alloc. +void JS::Arena::newDestructorEntry(void (*destructor)(void *), void *object) +{ + try { + DestructorEntry *e = new(*this) DestructorEntry(destructor, object); + e->next = destructorEntries; + destructorEntries = e; + } catch (...) { + destructor(object); + throw; + } +} + + +// Allocate a String in the Arena and register that String so that it is deallocated at +// the same time as the Arena. +// DO NOT CALL DELETE ON THE RESULT! +JS::String *JS::newArenaString(Arena &arena) +{ + String *s = new(arena) String(); + arena.registerDestructor(s); + return s; +} + +JS::String *JS::newArenaString(Arena &arena, const String &str) +{ + String *s = new(arena) String(str); + arena.registerDestructor(s); + return s; +} + + // // C++ I/O // diff --git a/js2/src/utilities.h b/js2/src/utilities.h index d8316a8582e8..8fc6dc24ac61 100644 --- a/js2/src/utilities.h +++ b/js2/src/utilities.h @@ -22,7 +22,9 @@ #include "systemtypes.h" #include +#include #include +#include #include #ifndef _WIN32 // Microsoft VC6 bug: standard identifiers should be in std namespace @@ -236,14 +238,54 @@ namespace JavaScript { const char16 *skipWhiteSpace(const char16 *str, const char16 *strEnd); +// +// Algorithms +// + + // Assign zero to every element between first inclusive and last exclusive. + // This is equivalent ot fill(first, last, 0) but may be more efficient. + template + inline void zero(For first, For last) + { + while (first != last) + *first++ = 0; + } + + + // Assign zero to n elements starting at first. + // This is equivalent ot fill_n(first, n, 0) but may be more efficient. + template + inline void zero_n(For first, Size n) + { + while (n--) + *first++ = 0; + } + + // // Arenas // + // Pretend that obj points to a value of class T and call obj's destructor. + template + void classDestructor(void *obj) + { + static_cast(obj)->~T(); + } + + // An arena is a region of memory from which objects either derived from ArenaObject or allocated // using a ArenaAllocator can be allocated. Deleting these objects individually runs the destructors, // if any, but does not deallocate the memory. On the other hand, the entire arena can be deallocated // as a whole. + // + // One may also allocate other objects in an arena by using the Arena specialization of the global + // operator new. However, be careful not to delete any such objects explicitly! + // + // Destructors can be registered for objects (or parts of objects) allocated in the arena. These + // destructors are called, in reverse order of being registered, at the time the arena is deallocated + // or cleared. When registering destructors for an object O be careful not to delete O manually because that + // would run its destructor twice. class Arena { struct Directory { enum {maxNBlocks = 31}; @@ -256,11 +298,14 @@ namespace JavaScript { void clear(); }; + struct DestructorEntry; + char *freeBegin; // Pointer to free bytes left in current block char *freeEnd; // Pointer to end of free bytes left in current block size_t blockSize; // Size of individual arena blocks Directory *currentDirectory; // Directory in which the last block was allocated Directory rootDirectory; // Initial directory; root of linked list of Directories + DestructorEntry *destructorEntries; // Linked list of destructor registrations, ordered from most to least recently registered public: explicit Arena(size_t blockSize = 1024); @@ -273,8 +318,14 @@ namespace JavaScript { private: void *newBlock(size_t size); + void newDestructorEntry(void (*destructor)(void *), void *object); public: void *allocate(size_t size); + // Ensure that object's destructor is called at the time the arena is deallocated or cleared. + // The destructors will be called in reverse order of being registered. + // registerDestructor might itself runs out of memory, in which case it immediately + // calls object's destructor before throwing bad_alloc. + template void registerDestructor(T *object) {newDestructorEntry(&classDestructor, object);} }; @@ -282,6 +333,7 @@ namespace JavaScript { struct ArenaObject { void *operator new(size_t size, Arena &arena) {return arena.allocate(size);} void *operator new[](size_t size, Arena &arena) {return arena.allocate(size);} + private: void operator delete(void *, size_t) {} void operator delete[](void *) {} }; @@ -320,6 +372,10 @@ namespace JavaScript { }; + String *newArenaString(Arena &arena); + String *newArenaString(Arena &arena, const String &str); + + // // Array auto_ptr's // @@ -409,27 +465,47 @@ namespace JavaScript { // -// Algorithms +// Linked Lists // - // Assign zero to every element between first inclusive and last exclusive. - // This is equivalent ot fill(first, last, 0) but may be more efficient. - template - inline void zero(For first, For last) - { - while (first != last) - *first++ = 0; - } + // In some cases it is desirable to manipulate ordinary C-style linked lists as though + // they were STL-like sequences. These classes define STL forward iterators that walk + // through singly-linked lists of objects threaded through fields named 'next'. The type + // parameter E must be a class that has a member named 'next' whose type is E* or const E*. + + template + class ListIterator: public std::iterator { + E *element; + public: + ListIterator() {} + explicit ListIterator(E *e): element(e) {} - // Assign zero to n elements starting at first. - // This is equivalent ot fill_n(first, n, 0) but may be more efficient. - template - inline void zero_n(For first, Size n) - { - while (n--) - *first++ = 0; - } + E &operator*() const {return *element;} + E *operator->() const {return element;} + ListIterator &operator++() {element = element->next; return *this;} + ListIterator operator++(int) {ListIterator i(*this); element = element->next; return i;} + friend bool operator==(const ListIterator &i, const ListIterator &j) {return i.element == j.element;} + friend bool operator!=(const ListIterator &i, const ListIterator &j) {return i.element != j.element;} + }; + + + template + class ConstListIterator: public std::iterator { + const E *element; + + public: + ConstListIterator() {} + ConstListIterator(const ListIterator &i): element(&*i) {} + explicit ConstListIterator(const E *e): element(e) {} + + const E &operator*() const {return *element;} + const E *operator->() const {return element;} + ConstListIterator &operator++() {element = element->next; return *this;} + ConstListIterator operator++(int) {ConstListIterator i(*this); element = element->next; return i;} + friend bool operator==(const ConstListIterator &i, const ConstListIterator &j) {return i.element == j.element;} + friend bool operator!=(const ConstListIterator &i, const ConstListIterator &j) {return i.element != j.element;} + }; // @@ -507,4 +583,14 @@ namespace JavaScript { String fullMessage() const; }; } + + +inline void *operator new(size_t size, JavaScript::Arena &arena) {return arena.allocate(size);} +inline void *operator new[](size_t size, JavaScript::Arena &arena) {return arena.allocate(size);} +#if 0 // Most compilers don't support this yet +// Global delete operators. These are only called in the rare cases that a constructor throws an exception +// and has to undo an operator new. An explicit delete statement will never invoke these. +inline void operator delete(void *, JavaScript::Arena &) {} +inline void operator delete[](void *, JavaScript::Arena &) {} +#endif #endif