2008-11-05 Cameron Zwarich <zwarich@apple.com>
Rubber-stamped by Sam Weinig.
Move more files to the runtime subdirectory of JavaScriptCore.
JavaScriptCore:
* API/APICast.h:
* API/JSBase.cpp:
* API/JSCallbackObject.cpp:
* API/JSClassRef.cpp:
* API/JSClassRef.h:
* API/JSStringRefCF.cpp:
* API/JSValueRef.cpp:
* API/OpaqueJSString.cpp:
* API/OpaqueJSString.h:
* AllInOneFile.cpp:
* GNUmakefile.am:
* JavaScriptCore.pri:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* JavaScriptCoreSources.bkl:
* VM/CodeBlock.h:
* VM/CodeGenerator.cpp:
* VM/Machine.cpp:
* VM/RegisterFile.h:
* debugger/Debugger.h:
* kjs/SourceProvider.h:
* kjs/TypeInfo.h: Removed.
* kjs/collector.cpp: Removed.
* kjs/collector.h: Removed.
* kjs/completion.h: Removed.
* kjs/create_hash_table:
* kjs/identifier.cpp: Removed.
* kjs/identifier.h: Removed.
* kjs/interpreter.cpp: Removed.
* kjs/interpreter.h: Removed.
* kjs/lexer.cpp:
* kjs/lexer.h:
* kjs/lookup.cpp: Removed.
* kjs/lookup.h: Removed.
* kjs/nodes.cpp:
* kjs/nodes.h:
* kjs/operations.cpp: Removed.
* kjs/operations.h: Removed.
* kjs/protect.h: Removed.
* kjs/regexp.cpp: Removed.
* kjs/regexp.h: Removed.
* kjs/ustring.cpp: Removed.
* kjs/ustring.h: Removed.
* pcre/pcre_exec.cpp:
* profiler/CallIdentifier.h:
* profiler/Profile.h:
* runtime/ArrayConstructor.cpp:
* runtime/ArrayPrototype.cpp:
* runtime/ArrayPrototype.h:
* runtime/Collector.cpp: Copied from kjs/collector.cpp.
* runtime/Collector.h: Copied from kjs/collector.h.
* runtime/CollectorHeapIterator.h:
* runtime/Completion.h: Copied from kjs/completion.h.
* runtime/ErrorPrototype.cpp:
* runtime/Identifier.cpp: Copied from kjs/identifier.cpp.
* runtime/Identifier.h: Copied from kjs/identifier.h.
* runtime/InitializeThreading.cpp:
* runtime/Interpreter.cpp: Copied from kjs/interpreter.cpp.
* runtime/Interpreter.h: Copied from kjs/interpreter.h.
* runtime/JSCell.h:
* runtime/JSGlobalData.cpp:
* runtime/JSGlobalData.h:
* runtime/JSLock.cpp:
* runtime/JSNumberCell.cpp:
* runtime/JSNumberCell.h:
* runtime/JSObject.cpp:
* runtime/JSValue.h:
* runtime/Lookup.cpp: Copied from kjs/lookup.cpp.
* runtime/Lookup.h: Copied from kjs/lookup.h.
* runtime/MathObject.cpp:
* runtime/NativeErrorPrototype.cpp:
* runtime/NumberPrototype.cpp:
* runtime/Operations.cpp: Copied from kjs/operations.cpp.
* runtime/Operations.h: Copied from kjs/operations.h.
* runtime/PropertyMapHashTable.h:
* runtime/Protect.h: Copied from kjs/protect.h.
* runtime/RegExp.cpp: Copied from kjs/regexp.cpp.
* runtime/RegExp.h: Copied from kjs/regexp.h.
* runtime/RegExpConstructor.cpp:
* runtime/RegExpObject.h:
* runtime/RegExpPrototype.cpp:
* runtime/SmallStrings.h:
* runtime/StringObjectThatMasqueradesAsUndefined.h:
* runtime/StructureID.cpp:
* runtime/StructureID.h:
* runtime/StructureIDTransitionTable.h:
* runtime/SymbolTable.h:
* runtime/TypeInfo.h: Copied from kjs/TypeInfo.h.
* runtime/UString.cpp: Copied from kjs/ustring.cpp.
* runtime/UString.h: Copied from kjs/ustring.h.
* wrec/CharacterClassConstructor.h:
* wrec/WREC.h:
WebCore:
* ForwardingHeaders/kjs/collector.h: Removed.
* ForwardingHeaders/kjs/completion.h: Removed.
* ForwardingHeaders/kjs/identifier.h: Removed.
* ForwardingHeaders/kjs/interpreter.h: Removed.
* ForwardingHeaders/kjs/lookup.h: Removed.
* ForwardingHeaders/kjs/operations.h: Removed.
* ForwardingHeaders/kjs/protect.h: Removed.
* ForwardingHeaders/kjs/ustring.h: Removed.
* ForwardingHeaders/runtime/Collector.h: Copied from ForwardingHeaders/kjs/collector.h.
* ForwardingHeaders/runtime/Completion.h: Copied from ForwardingHeaders/kjs/completion.h.
* ForwardingHeaders/runtime/Identifier.h: Copied from ForwardingHeaders/kjs/identifier.h.
* ForwardingHeaders/runtime/Interpreter.h: Copied from ForwardingHeaders/kjs/interpreter.h.
* ForwardingHeaders/runtime/Lookup.h: Copied from ForwardingHeaders/kjs/lookup.h.
* ForwardingHeaders/runtime/Operations.h: Copied from ForwardingHeaders/kjs/operations.h.
* ForwardingHeaders/runtime/Protect.h: Copied from ForwardingHeaders/kjs/protect.h.
* ForwardingHeaders/runtime/UString.h: Copied from ForwardingHeaders/kjs/ustring.h.
* bindings/js/GCController.cpp:
* bindings/js/JSCustomPositionCallback.h:
* bindings/js/JSCustomPositionErrorCallback.h:
* bindings/js/JSCustomSQLStatementCallback.h:
* bindings/js/JSCustomSQLStatementErrorCallback.h:
* bindings/js/JSCustomSQLTransactionErrorCallback.h:
* bindings/js/JSCustomVoidCallback.h:
* bindings/js/JSDOMBinding.h:
* bindings/js/JSDOMWindowBase.h:
* bindings/js/JSEventListener.h:
* bindings/js/ScheduledAction.h:
* bindings/js/ScriptController.cpp:
* bindings/js/ScriptController.h:
* bindings/objc/WebScriptObject.mm:
* bindings/scripts/CodeGeneratorJS.pm:
* bridge/NP_jsobject.cpp:
* bridge/c/c_class.cpp:
* bridge/jni/jni_class.cpp:
* bridge/jni/jni_jsobject.mm:
* bridge/npruntime.cpp:
* bridge/runtime_root.h:
* history/CachedPage.h:
* html/CanvasRenderingContext2D.cpp:
* html/HTMLCanvasElement.cpp:
* inspector/InspectorController.cpp:
* inspector/JavaScriptCallFrame.cpp:
* page/Console.cpp:
* page/Page.cpp:
* platform/text/AtomicString.cpp:
* platform/text/PlatformString.h:
WebKit/mac:
* ForwardingHeaders/kjs/collector.h: Removed.
* ForwardingHeaders/kjs/identifier.h: Removed.
* ForwardingHeaders/kjs/interpreter.h: Removed.
* ForwardingHeaders/kjs/lookup.h: Removed.
* ForwardingHeaders/kjs/operations.h: Removed.
* ForwardingHeaders/kjs/protect.h: Removed.
* ForwardingHeaders/runtime/Interpreter.h: Copied from ForwardingHeaders/kjs/interpreter.h.
* WebView/WebScriptDebugDelegate.mm:
WebKit/wx:
* WebFrame.cpp:
* WebView.cpp:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@38137 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/runtime/ArrayConstructor.cpp b/JavaScriptCore/runtime/ArrayConstructor.cpp
index 5784af0..a9949ba 100644
--- a/JavaScriptCore/runtime/ArrayConstructor.cpp
+++ b/JavaScriptCore/runtime/ArrayConstructor.cpp
@@ -26,7 +26,7 @@
#include "ArrayPrototype.h"
#include "JSArray.h"
-#include "lookup.h"
+#include "Lookup.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/ArrayPrototype.cpp b/JavaScriptCore/runtime/ArrayPrototype.cpp
index 5280784..ecf9c19 100644
--- a/JavaScriptCore/runtime/ArrayPrototype.cpp
+++ b/JavaScriptCore/runtime/ArrayPrototype.cpp
@@ -26,8 +26,8 @@
#include "Machine.h"
#include "ObjectPrototype.h"
-#include "lookup.h"
-#include "operations.h"
+#include "Lookup.h"
+#include "Operations.h"
#include <algorithm>
#include <wtf/Assertions.h>
#include <wtf/HashSet.h>
diff --git a/JavaScriptCore/runtime/ArrayPrototype.h b/JavaScriptCore/runtime/ArrayPrototype.h
index 33ce30b..95f69c7 100644
--- a/JavaScriptCore/runtime/ArrayPrototype.h
+++ b/JavaScriptCore/runtime/ArrayPrototype.h
@@ -22,7 +22,7 @@
#define ArrayPrototype_h
#include "JSArray.h"
-#include "lookup.h"
+#include "Lookup.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/Collector.cpp b/JavaScriptCore/runtime/Collector.cpp
new file mode 100644
index 0000000..67273cf
--- /dev/null
+++ b/JavaScriptCore/runtime/Collector.cpp
@@ -0,0 +1,1090 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+#include "Collector.h"
+
+#include "ArgList.h"
+#include "CollectorHeapIterator.h"
+#include "ExecState.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "JSString.h"
+#include "JSValue.h"
+#include "Machine.h"
+#include "Tracing.h"
+#include <algorithm>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/HashCountedSet.h>
+#include <wtf/UnusedParam.h>
+
+#if PLATFORM(DARWIN)
+
+#include <mach/mach_port.h>
+#include <mach/mach_init.h>
+#include <mach/task.h>
+#include <mach/thread_act.h>
+#include <mach/vm_map.h>
+
+#elif PLATFORM(WIN_OS)
+
+#include <windows.h>
+
+#elif PLATFORM(UNIX)
+
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#if PLATFORM(SOLARIS)
+#include <thread.h>
+#endif
+
+#if PLATFORM(OPENBSD)
+#include <pthread.h>
+#endif
+
+#if HAVE(PTHREAD_NP_H)
+#include <pthread_np.h>
+#endif
+
+#endif
+
+#define DEBUG_COLLECTOR 0
+#define COLLECT_ON_EVERY_ALLOCATION 0
+
+using std::max;
+
+namespace JSC {
+
+// tunable parameters
+
+const size_t SPARE_EMPTY_BLOCKS = 2;
+const size_t GROWTH_FACTOR = 2;
+const size_t LOW_WATER_FACTOR = 4;
+const size_t ALLOCATIONS_PER_COLLECTION = 4000;
+// This value has to be a macro to be used in max() without introducing
+// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
+#define MIN_ARRAY_SIZE (static_cast<size_t>(14))
+
+static void freeHeap(CollectorHeap*);
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+
+#if PLATFORM(DARWIN)
+typedef mach_port_t PlatformThread;
+#elif PLATFORM(WIN_OS)
+struct PlatformThread {
+ PlatformThread(DWORD _id, HANDLE _handle) : id(_id), handle(_handle) {}
+ DWORD id;
+ HANDLE handle;
+};
+#endif
+
+class Heap::Thread {
+public:
+ Thread(pthread_t pthread, const PlatformThread& platThread, void* base)
+ : posixThread(pthread)
+ , platformThread(platThread)
+ , stackBase(base)
+ {
+ }
+
+ Thread* next;
+ pthread_t posixThread;
+ PlatformThread platformThread;
+ void* stackBase;
+};
+
+#endif
+
+Heap::Heap(JSGlobalData* globalData)
+ : m_markListSet(0)
+#if ENABLE(JSC_MULTIPLE_THREADS)
+ , m_registeredThreads(0)
+#endif
+ , m_globalData(globalData)
+{
+ ASSERT(globalData);
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+ int error = pthread_key_create(&m_currentThreadRegistrar, unregisterThread);
+ if (error)
+ CRASH();
+#endif
+
+ memset(&primaryHeap, 0, sizeof(CollectorHeap));
+ memset(&numberHeap, 0, sizeof(CollectorHeap));
+}
+
+Heap::~Heap()
+{
+ // The destroy function must already have been called, so assert this.
+ ASSERT(!m_globalData);
+}
+
+void Heap::destroy()
+{
+ JSLock lock(false);
+
+ if (!m_globalData)
+ return;
+
+ // The global object is not GC protected at this point, so sweeping may delete it
+ // (and thus the global data) before other objects that may use the global data.
+ RefPtr<JSGlobalData> protect(m_globalData);
+
+ delete m_markListSet;
+ m_markListSet = 0;
+
+ sweep<PrimaryHeap>();
+ // No need to sweep number heap, because the JSNumber destructor doesn't do anything.
+
+ ASSERT(!primaryHeap.numLiveObjects);
+
+ freeHeap(&primaryHeap);
+ freeHeap(&numberHeap);
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+#ifndef NDEBUG
+ int error =
+#endif
+ pthread_key_delete(m_currentThreadRegistrar);
+ ASSERT(!error);
+
+ MutexLocker registeredThreadsLock(m_registeredThreadsMutex);
+ for (Heap::Thread* t = m_registeredThreads; t;) {
+ Heap::Thread* next = t->next;
+ delete t;
+ t = next;
+ }
+#endif
+
+ m_globalData = 0;
+}
+
+template <HeapType heapType>
+static NEVER_INLINE CollectorBlock* allocateBlock()
+{
+#if PLATFORM(DARWIN)
+ vm_address_t address = 0;
+ // FIXME: tag the region as a JavaScriptCore heap when we get a registered VM tag: <rdar://problem/6054788>.
+ vm_map(current_task(), &address, BLOCK_SIZE, BLOCK_OFFSET_MASK, VM_FLAGS_ANYWHERE, MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT);
+#elif PLATFORM(WIN_OS)
+ // windows virtual address granularity is naturally 64k
+ LPVOID address = VirtualAlloc(NULL, BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+#elif HAVE(POSIX_MEMALIGN)
+ void* address;
+ posix_memalign(&address, BLOCK_SIZE, BLOCK_SIZE);
+ memset(address, 0, BLOCK_SIZE);
+#else
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+#error Need to initialize pagesize safely.
+#endif
+ static size_t pagesize = getpagesize();
+
+ size_t extra = 0;
+ if (BLOCK_SIZE > pagesize)
+ extra = BLOCK_SIZE - pagesize;
+
+ void* mmapResult = mmap(NULL, BLOCK_SIZE + extra, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ uintptr_t address = reinterpret_cast<uintptr_t>(mmapResult);
+
+ size_t adjust = 0;
+ if ((address & BLOCK_OFFSET_MASK) != 0)
+ adjust = BLOCK_SIZE - (address & BLOCK_OFFSET_MASK);
+
+ if (adjust > 0)
+ munmap(reinterpret_cast<char*>(address), adjust);
+
+ if (adjust < extra)
+ munmap(reinterpret_cast<char*>(address + adjust + BLOCK_SIZE), extra - adjust);
+
+ address += adjust;
+ memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE);
+#endif
+ reinterpret_cast<CollectorBlock*>(address)->type = heapType;
+ return reinterpret_cast<CollectorBlock*>(address);
+}
+
+static void freeBlock(CollectorBlock* block)
+{
+#if PLATFORM(DARWIN)
+ vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(block), BLOCK_SIZE);
+#elif PLATFORM(WIN_OS)
+ VirtualFree(block, 0, MEM_RELEASE);
+#elif HAVE(POSIX_MEMALIGN)
+ free(block);
+#else
+ munmap(reinterpret_cast<char*>(block), BLOCK_SIZE);
+#endif
+}
+
+static void freeHeap(CollectorHeap* heap)
+{
+ for (size_t i = 0; i < heap->usedBlocks; ++i)
+ if (heap->blocks[i])
+ freeBlock(heap->blocks[i]);
+ fastFree(heap->blocks);
+ memset(heap, 0, sizeof(CollectorHeap));
+}
+
+void Heap::recordExtraCost(size_t cost)
+{
+ // Our frequency of garbage collection tries to balance memory use against speed
+ // by collecting based on the number of newly created values. However, for values
+ // that hold on to a great deal of memory that's not in the form of other JS values,
+ // that is not good enough - in some cases a lot of those objects can pile up and
+ // use crazy amounts of memory without a GC happening. So we track these extra
+ // memory costs. Only unusually large objects are noted, and we only keep track
+ // of this extra cost until the next GC. In garbage collected languages, most values
+ // are either very short lived temporaries, or have extremely long lifetimes. So
+ // if a large value survives one garbage collection, there is not much point to
+ // collecting more frequently as long as it stays alive.
+ // NOTE: we target the primaryHeap unconditionally as JSNumber doesn't modify cost
+
+ primaryHeap.extraCost += cost;
+}
+
+template <HeapType heapType> ALWAYS_INLINE void* Heap::heapAllocate(size_t s)
+{
+ typedef typename HeapConstants<heapType>::Block Block;
+ typedef typename HeapConstants<heapType>::Cell Cell;
+
+ CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap;
+ ASSERT(JSLock::lockCount() > 0);
+ ASSERT(JSLock::currentThreadIsHoldingLock());
+ ASSERT(s <= HeapConstants<heapType>::cellSize);
+ UNUSED_PARAM(s); // s is now only used for the above assert
+
+ ASSERT(heap.operationInProgress == NoOperation);
+ ASSERT(heapType == PrimaryHeap || heap.extraCost == 0);
+ // FIXME: If another global variable access here doesn't hurt performance
+ // too much, we could abort() in NDEBUG builds, which could help ensure we
+ // don't spend any time debugging cases where we allocate inside an object's
+ // deallocation code.
+
+ size_t numLiveObjects = heap.numLiveObjects;
+ size_t usedBlocks = heap.usedBlocks;
+ size_t i = heap.firstBlockWithPossibleSpace;
+
+#if COLLECT_ON_EVERY_ALLOCATION
+ collect();
+#endif
+
+ // if we have a huge amount of extra cost, we'll try to collect even if we still have
+ // free cells left.
+ if (heapType == PrimaryHeap && heap.extraCost > ALLOCATIONS_PER_COLLECTION) {
+ size_t numLiveObjectsAtLastCollect = heap.numLiveObjectsAtLastCollect;
+ size_t numNewObjects = numLiveObjects - numLiveObjectsAtLastCollect;
+ const size_t newCost = numNewObjects + heap.extraCost;
+ if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect)
+ goto collect;
+ }
+
+ ASSERT(heap.operationInProgress == NoOperation);
+#ifndef NDEBUG
+ // FIXME: Consider doing this in NDEBUG builds too (see comment above).
+ heap.operationInProgress = Allocation;
+#endif
+
+scan:
+ Block* targetBlock;
+ size_t targetBlockUsedCells;
+ if (i != usedBlocks) {
+ targetBlock = reinterpret_cast<Block*>(heap.blocks[i]);
+ targetBlockUsedCells = targetBlock->usedCells;
+ ASSERT(targetBlockUsedCells <= HeapConstants<heapType>::cellsPerBlock);
+ while (targetBlockUsedCells == HeapConstants<heapType>::cellsPerBlock) {
+ if (++i == usedBlocks)
+ goto collect;
+ targetBlock = reinterpret_cast<Block*>(heap.blocks[i]);
+ targetBlockUsedCells = targetBlock->usedCells;
+ ASSERT(targetBlockUsedCells <= HeapConstants<heapType>::cellsPerBlock);
+ }
+ heap.firstBlockWithPossibleSpace = i;
+ } else {
+
+collect:
+ size_t numLiveObjectsAtLastCollect = heap.numLiveObjectsAtLastCollect;
+ size_t numNewObjects = numLiveObjects - numLiveObjectsAtLastCollect;
+ const size_t newCost = numNewObjects + heap.extraCost;
+
+ if (newCost >= ALLOCATIONS_PER_COLLECTION && newCost >= numLiveObjectsAtLastCollect) {
+#ifndef NDEBUG
+ heap.operationInProgress = NoOperation;
+#endif
+ bool collected = collect();
+#ifndef NDEBUG
+ heap.operationInProgress = Allocation;
+#endif
+ if (collected) {
+ numLiveObjects = heap.numLiveObjects;
+ usedBlocks = heap.usedBlocks;
+ i = heap.firstBlockWithPossibleSpace;
+ goto scan;
+ }
+ }
+
+ // didn't find a block, and GC didn't reclaim anything, need to allocate a new block
+ size_t numBlocks = heap.numBlocks;
+ if (usedBlocks == numBlocks) {
+ numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR);
+ heap.numBlocks = numBlocks;
+ heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, numBlocks * sizeof(CollectorBlock*)));
+ }
+
+ targetBlock = reinterpret_cast<Block*>(allocateBlock<heapType>());
+ targetBlock->freeList = targetBlock->cells;
+ targetBlock->heap = this;
+ targetBlockUsedCells = 0;
+ heap.blocks[usedBlocks] = reinterpret_cast<CollectorBlock*>(targetBlock);
+ heap.usedBlocks = usedBlocks + 1;
+ heap.firstBlockWithPossibleSpace = usedBlocks;
+ }
+
+ // find a free spot in the block and detach it from the free list
+ Cell* newCell = targetBlock->freeList;
+
+ // "next" field is a cell offset -- 0 means next cell, so a zeroed block is already initialized
+ targetBlock->freeList = (newCell + 1) + newCell->u.freeCell.next;
+
+ targetBlock->usedCells = static_cast<uint32_t>(targetBlockUsedCells + 1);
+ heap.numLiveObjects = numLiveObjects + 1;
+
+#ifndef NDEBUG
+ // FIXME: Consider doing this in NDEBUG builds too (see comment above).
+ heap.operationInProgress = NoOperation;
+#endif
+
+ return newCell;
+}
+
+void* Heap::allocate(size_t s)
+{
+ return heapAllocate<PrimaryHeap>(s);
+}
+
+void* Heap::allocateNumber(size_t s)
+{
+ return heapAllocate<NumberHeap>(s);
+}
+
+static inline void* currentThreadStackBase()
+{
+#if PLATFORM(DARWIN)
+ pthread_t thread = pthread_self();
+ return pthread_get_stackaddr_np(thread);
+#elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(MSVC)
+ // offset 0x18 from the FS segment register gives a pointer to
+ // the thread information block for the current thread
+ NT_TIB* pTib;
+ __asm {
+ MOV EAX, FS:[18h]
+ MOV pTib, EAX
+ }
+ return static_cast<void*>(pTib->StackBase);
+#elif PLATFORM(WIN_OS) && PLATFORM(X86_64) && COMPILER(MSVC)
+ PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
+ return reinterpret_cast<void*>(pTib->StackBase);
+#elif PLATFORM(WIN_OS) && PLATFORM(X86) && COMPILER(GCC)
+ // offset 0x18 from the FS segment register gives a pointer to
+ // the thread information block for the current thread
+ NT_TIB* pTib;
+ asm ( "movl %%fs:0x18, %0\n"
+ : "=r" (pTib)
+ );
+ return static_cast<void*>(pTib->StackBase);
+#elif PLATFORM(SOLARIS)
+ stack_t s;
+ thr_stksegment(&s);
+ return s.ss_sp;
+#elif PLATFORM(OPENBSD)
+ pthread_t thread = pthread_self();
+ stack_t stack;
+ pthread_stackseg_np(thread, &stack);
+ return stack.ss_sp;
+#elif PLATFORM(UNIX)
+ static void* stackBase = 0;
+ static size_t stackSize = 0;
+ static pthread_t stackThread;
+ pthread_t thread = pthread_self();
+ if (stackBase == 0 || thread != stackThread) {
+ pthread_attr_t sattr;
+ pthread_attr_init(&sattr);
+#if HAVE(PTHREAD_NP_H)
+ // e.g. on FreeBSD 5.4, neundorf@kde.org
+ pthread_attr_get_np(thread, &sattr);
+#else
+ // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
+ pthread_getattr_np(thread, &sattr);
+#endif
+ int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
+ (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
+ ASSERT(stackBase);
+ pthread_attr_destroy(&sattr);
+ stackThread = thread;
+ }
+ return static_cast<char*>(stackBase) + stackSize;
+#else
+#error Need a way to get the stack base on this platform
+#endif
+}
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+
+static inline PlatformThread getCurrentPlatformThread()
+{
+#if PLATFORM(DARWIN)
+ return pthread_mach_thread_np(pthread_self());
+#elif PLATFORM(WIN_OS)
+ HANDLE threadHandle = pthread_getw32threadhandle_np(pthread_self());
+ return PlatformThread(GetCurrentThreadId(), threadHandle);
+#endif
+}
+
+void Heap::registerThread()
+{
+ if (pthread_getspecific(m_currentThreadRegistrar))
+ return;
+
+ pthread_setspecific(m_currentThreadRegistrar, this);
+ Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), currentThreadStackBase());
+
+ MutexLocker lock(m_registeredThreadsMutex);
+
+ thread->next = m_registeredThreads;
+ m_registeredThreads = thread;
+}
+
+void Heap::unregisterThread(void* p)
+{
+ if (p)
+ static_cast<Heap*>(p)->unregisterThread();
+}
+
+void Heap::unregisterThread()
+{
+ pthread_t currentPosixThread = pthread_self();
+
+ MutexLocker lock(m_registeredThreadsMutex);
+
+ if (pthread_equal(currentPosixThread, m_registeredThreads->posixThread)) {
+ Thread* t = m_registeredThreads;
+ m_registeredThreads = m_registeredThreads->next;
+ delete t;
+ } else {
+ Heap::Thread* last = m_registeredThreads;
+ Heap::Thread* t;
+ for (t = m_registeredThreads->next; t; t = t->next) {
+ if (pthread_equal(t->posixThread, currentPosixThread)) {
+ last->next = t->next;
+ break;
+ }
+ last = t;
+ }
+ ASSERT(t); // If t is NULL, we never found ourselves in the list.
+ delete t;
+ }
+}
+
+#else // ENABLE(JSC_MULTIPLE_THREADS)
+
+void Heap::registerThread()
+{
+}
+
+#endif
+
+#define IS_POINTER_ALIGNED(p) (((intptr_t)(p) & (sizeof(char*) - 1)) == 0)
+
+// cell size needs to be a power of two for this to be valid
+#define IS_HALF_CELL_ALIGNED(p) (((intptr_t)(p) & (CELL_MASK >> 1)) == 0)
+
+void Heap::markConservatively(void* start, void* end)
+{
+ if (start > end) {
+ void* tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000);
+ ASSERT(IS_POINTER_ALIGNED(start));
+ ASSERT(IS_POINTER_ALIGNED(end));
+
+ char** p = static_cast<char**>(start);
+ char** e = static_cast<char**>(end);
+
+ size_t usedPrimaryBlocks = primaryHeap.usedBlocks;
+ size_t usedNumberBlocks = numberHeap.usedBlocks;
+ CollectorBlock** primaryBlocks = primaryHeap.blocks;
+ CollectorBlock** numberBlocks = numberHeap.blocks;
+
+ const size_t lastCellOffset = sizeof(CollectorCell) * (CELLS_PER_BLOCK - 1);
+
+ while (p != e) {
+ char* x = *p++;
+ if (IS_HALF_CELL_ALIGNED(x) && x) {
+ uintptr_t xAsBits = reinterpret_cast<uintptr_t>(x);
+ xAsBits &= CELL_ALIGN_MASK;
+ uintptr_t offset = xAsBits & BLOCK_OFFSET_MASK;
+ CollectorBlock* blockAddr = reinterpret_cast<CollectorBlock*>(xAsBits - offset);
+ // Mark the the number heap, we can mark these Cells directly to avoid the virtual call cost
+ for (size_t block = 0; block < usedNumberBlocks; block++) {
+ if ((numberBlocks[block] == blockAddr) & (offset <= lastCellOffset)) {
+ Heap::markCell(reinterpret_cast<JSCell*>(xAsBits));
+ goto endMarkLoop;
+ }
+ }
+
+ // Mark the primary heap
+ for (size_t block = 0; block < usedPrimaryBlocks; block++) {
+ if ((primaryBlocks[block] == blockAddr) & (offset <= lastCellOffset)) {
+ if (reinterpret_cast<CollectorCell*>(xAsBits)->u.freeCell.zeroIfFree != 0) {
+ JSCell* imp = reinterpret_cast<JSCell*>(xAsBits);
+ if (!imp->marked())
+ imp->mark();
+ }
+ break;
+ }
+ }
+ endMarkLoop:
+ ;
+ }
+ }
+}
+
+void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal()
+{
+ void* dummy;
+ void* stackPointer = &dummy;
+ void* stackBase = currentThreadStackBase();
+ markConservatively(stackPointer, stackBase);
+}
+
+void Heap::markCurrentThreadConservatively()
+{
+ // setjmp forces volatile registers onto the stack
+ jmp_buf registers;
+#if COMPILER(MSVC)
+#pragma warning(push)
+#pragma warning(disable: 4611)
+#endif
+ setjmp(registers);
+#if COMPILER(MSVC)
+#pragma warning(pop)
+#endif
+
+ markCurrentThreadConservativelyInternal();
+}
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+
+static inline void suspendThread(const PlatformThread& platformThread)
+{
+#if PLATFORM(DARWIN)
+ thread_suspend(platformThread);
+#elif PLATFORM(WIN_OS)
+ SuspendThread(platformThread.handle);
+#else
+#error Need a way to suspend threads on this platform
+#endif
+}
+
+static inline void resumeThread(const PlatformThread& platformThread)
+{
+#if PLATFORM(DARWIN)
+ thread_resume(platformThread);
+#elif PLATFORM(WIN_OS)
+ ResumeThread(platformThread.handle);
+#else
+#error Need a way to resume threads on this platform
+#endif
+}
+
+typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
+
+#if PLATFORM(DARWIN)
+
+#if PLATFORM(X86)
+typedef i386_thread_state_t PlatformThreadRegisters;
+#elif PLATFORM(X86_64)
+typedef x86_thread_state64_t PlatformThreadRegisters;
+#elif PLATFORM(PPC)
+typedef ppc_thread_state_t PlatformThreadRegisters;
+#elif PLATFORM(PPC64)
+typedef ppc_thread_state64_t PlatformThreadRegisters;
+#elif PLATFORM(ARM)
+typedef arm_thread_state_t PlatformThreadRegisters;
+#else
+#error Unknown Architecture
+#endif
+
+#elif PLATFORM(WIN_OS)&& PLATFORM(X86)
+typedef CONTEXT PlatformThreadRegisters;
+#else
+#error Need a thread register struct for this platform
+#endif
+
+size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs)
+{
+#if PLATFORM(DARWIN)
+
+#if PLATFORM(X86)
+ unsigned user_count = sizeof(regs)/sizeof(int);
+ thread_state_flavor_t flavor = i386_THREAD_STATE;
+#elif PLATFORM(X86_64)
+ unsigned user_count = x86_THREAD_STATE64_COUNT;
+ thread_state_flavor_t flavor = x86_THREAD_STATE64;
+#elif PLATFORM(PPC)
+ unsigned user_count = PPC_THREAD_STATE_COUNT;
+ thread_state_flavor_t flavor = PPC_THREAD_STATE;
+#elif PLATFORM(PPC64)
+ unsigned user_count = PPC_THREAD_STATE64_COUNT;
+ thread_state_flavor_t flavor = PPC_THREAD_STATE64;
+#elif PLATFORM(ARM)
+ unsigned user_count = ARM_THREAD_STATE_COUNT;
+ thread_state_flavor_t flavor = ARM_THREAD_STATE;
+#else
+#error Unknown Architecture
+#endif
+
+ kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)®s, &user_count);
+ if (result != KERN_SUCCESS) {
+ WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
+ "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result);
+ CRASH();
+ }
+ return user_count * sizeof(usword_t);
+// end PLATFORM(DARWIN)
+
+#elif PLATFORM(WIN_OS) && PLATFORM(X86)
+ regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS;
+ GetThreadContext(platformThread.handle, ®s);
+ return sizeof(CONTEXT);
+#else
+#error Need a way to get thread registers on this platform
+#endif
+}
+
+static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
+{
+#if PLATFORM(DARWIN)
+
+#if __DARWIN_UNIX03
+
+#if PLATFORM(X86)
+ return reinterpret_cast<void*>(regs.__esp);
+#elif PLATFORM(X86_64)
+ return reinterpret_cast<void*>(regs.__rsp);
+#elif PLATFORM(PPC) || PLATFORM(PPC64)
+ return reinterpret_cast<void*>(regs.__r1);
+#elif PLATFORM(ARM)
+ return reinterpret_cast<void*>(regs.__sp);
+#else
+#error Unknown Architecture
+#endif
+
+#else // !__DARWIN_UNIX03
+
+#if PLATFORM(X86)
+ return reinterpret_cast<void*>(regs.esp);
+#elif PLATFORM(X86_64)
+ return reinterpret_cast<void*>(regs.rsp);
+#elif (PLATFORM(PPC) || PLATFORM(PPC64))
+ return reinterpret_cast<void*>(regs.r1);
+#else
+#error Unknown Architecture
+#endif
+
+#endif // __DARWIN_UNIX03
+
+// end PLATFORM(DARWIN)
+#elif PLATFORM(X86) && PLATFORM(WIN_OS)
+ return reinterpret_cast<void*>((uintptr_t) regs.Esp);
+#else
+#error Need a way to get the stack pointer for another thread on this platform
+#endif
+}
+
+void Heap::markOtherThreadConservatively(Thread* thread)
+{
+ suspendThread(thread->platformThread);
+
+ PlatformThreadRegisters regs;
+ size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
+
+ // mark the thread's registers
+ markConservatively(static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize));
+
+ void* stackPointer = otherThreadStackPointer(regs);
+ markConservatively(stackPointer, thread->stackBase);
+
+ resumeThread(thread->platformThread);
+}
+
+#endif
+
+void Heap::markStackObjectsConservatively()
+{
+ markCurrentThreadConservatively();
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+
+ if (m_currentThreadRegistrar) {
+
+ MutexLocker lock(m_registeredThreadsMutex);
+
+#ifndef NDEBUG
+ // Forbid malloc during the mark phase. Marking a thread suspends it, so
+ // a malloc inside mark() would risk a deadlock with a thread that had been
+ // suspended while holding the malloc lock.
+ fastMallocForbid();
+#endif
+ // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
+ // and since this is a shared heap, they are real locks.
+ for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
+ if (!pthread_equal(thread->posixThread, pthread_self()))
+ markOtherThreadConservatively(thread);
+ }
+#ifndef NDEBUG
+ fastMallocAllow();
+#endif
+ }
+#endif
+}
+
+void Heap::setGCProtectNeedsLocking()
+{
+ // Most clients do not need to call this, with the notable exception of WebCore.
+ // Clients that use shared heap have JSLock protection, while others are supposed
+ // to do explicit locking. WebCore violates this contract in Database code,
+ // which calls gcUnprotect from a secondary thread.
+ if (!m_protectedValuesMutex)
+ m_protectedValuesMutex.set(new Mutex);
+}
+
+void Heap::protect(JSValue* k)
+{
+ ASSERT(k);
+ ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance);
+
+ if (JSImmediate::isImmediate(k))
+ return;
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ m_protectedValues.add(k->asCell());
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+}
+
+void Heap::unprotect(JSValue* k)
+{
+ ASSERT(k);
+ ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance);
+
+ if (JSImmediate::isImmediate(k))
+ return;
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ m_protectedValues.remove(k->asCell());
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+}
+
+Heap* Heap::heap(JSValue* v)
+{
+ if (JSImmediate::isImmediate(v))
+ return 0;
+ return Heap::cellBlock(v->asCell())->heap;
+}
+
+void Heap::markProtectedObjects()
+{
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ ProtectCountSet::iterator end = m_protectedValues.end();
+ for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) {
+ JSCell* val = it->first;
+ if (!val->marked())
+ val->mark();
+ }
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+}
+
+template <HeapType heapType> size_t Heap::sweep()
+{
+ typedef typename HeapConstants<heapType>::Block Block;
+ typedef typename HeapConstants<heapType>::Cell Cell;
+
+ // SWEEP: delete everything with a zero refcount (garbage) and unmark everything else
+ CollectorHeap& heap = heapType == PrimaryHeap ? primaryHeap : numberHeap;
+
+ size_t emptyBlocks = 0;
+ size_t numLiveObjects = heap.numLiveObjects;
+
+ for (size_t block = 0; block < heap.usedBlocks; block++) {
+ Block* curBlock = reinterpret_cast<Block*>(heap.blocks[block]);
+
+ size_t usedCells = curBlock->usedCells;
+ Cell* freeList = curBlock->freeList;
+
+ if (usedCells == HeapConstants<heapType>::cellsPerBlock) {
+ // special case with a block where all cells are used -- testing indicates this happens often
+ for (size_t i = 0; i < HeapConstants<heapType>::cellsPerBlock; i++) {
+ if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) {
+ Cell* cell = curBlock->cells + i;
+
+ if (heapType != NumberHeap) {
+ JSCell* imp = reinterpret_cast<JSCell*>(cell);
+ // special case for allocated but uninitialized object
+ // (We don't need this check earlier because nothing prior this point
+ // assumes the object has a valid vptr.)
+ if (cell->u.freeCell.zeroIfFree == 0)
+ continue;
+
+ imp->~JSCell();
+ }
+
+ --usedCells;
+ --numLiveObjects;
+
+ // put cell on the free list
+ cell->u.freeCell.zeroIfFree = 0;
+ cell->u.freeCell.next = freeList - (cell + 1);
+ freeList = cell;
+ }
+ }
+ } else {
+ size_t minimumCellsToProcess = usedCells;
+ for (size_t i = 0; (i < minimumCellsToProcess) & (i < HeapConstants<heapType>::cellsPerBlock); i++) {
+ Cell* cell = curBlock->cells + i;
+ if (cell->u.freeCell.zeroIfFree == 0) {
+ ++minimumCellsToProcess;
+ } else {
+ if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) {
+ if (heapType != NumberHeap) {
+ JSCell* imp = reinterpret_cast<JSCell*>(cell);
+ imp->~JSCell();
+ }
+ --usedCells;
+ --numLiveObjects;
+
+ // put cell on the free list
+ cell->u.freeCell.zeroIfFree = 0;
+ cell->u.freeCell.next = freeList - (cell + 1);
+ freeList = cell;
+ }
+ }
+ }
+ }
+
+ curBlock->usedCells = static_cast<uint32_t>(usedCells);
+ curBlock->freeList = freeList;
+ curBlock->marked.clearAll();
+
+ if (usedCells == 0) {
+ emptyBlocks++;
+ if (emptyBlocks > SPARE_EMPTY_BLOCKS) {
+#if !DEBUG_COLLECTOR
+ freeBlock(reinterpret_cast<CollectorBlock*>(curBlock));
+#endif
+ // swap with the last block so we compact as we go
+ heap.blocks[block] = heap.blocks[heap.usedBlocks - 1];
+ heap.usedBlocks--;
+ block--; // Don't move forward a step in this case
+
+ if (heap.numBlocks > MIN_ARRAY_SIZE && heap.usedBlocks < heap.numBlocks / LOW_WATER_FACTOR) {
+ heap.numBlocks = heap.numBlocks / GROWTH_FACTOR;
+ heap.blocks = static_cast<CollectorBlock**>(fastRealloc(heap.blocks, heap.numBlocks * sizeof(CollectorBlock*)));
+ }
+ }
+ }
+ }
+
+ if (heap.numLiveObjects != numLiveObjects)
+ heap.firstBlockWithPossibleSpace = 0;
+
+ heap.numLiveObjects = numLiveObjects;
+ heap.numLiveObjectsAtLastCollect = numLiveObjects;
+ heap.extraCost = 0;
+ return numLiveObjects;
+}
+
+bool Heap::collect()
+{
+#ifndef NDEBUG
+ if (m_globalData->isSharedInstance) {
+ ASSERT(JSLock::lockCount() > 0);
+ ASSERT(JSLock::currentThreadIsHoldingLock());
+ }
+#endif
+
+ ASSERT((primaryHeap.operationInProgress == NoOperation) | (numberHeap.operationInProgress == NoOperation));
+ if ((primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation))
+ abort();
+
+ JAVASCRIPTCORE_GC_BEGIN();
+ primaryHeap.operationInProgress = Collection;
+ numberHeap.operationInProgress = Collection;
+
+ // MARK: first mark all referenced objects recursively starting out from the set of root objects
+
+ markStackObjectsConservatively();
+ markProtectedObjects();
+ if (m_markListSet && m_markListSet->size())
+ ArgList::markLists(*m_markListSet);
+ if (m_globalData->exception && !m_globalData->exception->marked())
+ m_globalData->exception->mark();
+ m_globalData->machine->registerFile().markCallFrames(this);
+ m_globalData->smallStrings.mark();
+
+ JSGlobalObject* globalObject = m_globalData->head;
+ if (globalObject) {
+ do {
+ globalObject->markCrossHeapDependentObjects();
+ globalObject = globalObject->next();
+ } while (globalObject != m_globalData->head);
+ }
+
+ JAVASCRIPTCORE_GC_MARKED();
+
+ size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects;
+ size_t numLiveObjects = sweep<PrimaryHeap>();
+ numLiveObjects += sweep<NumberHeap>();
+
+ primaryHeap.operationInProgress = NoOperation;
+ numberHeap.operationInProgress = NoOperation;
+ JAVASCRIPTCORE_GC_END(originalLiveObjects, numLiveObjects);
+
+ return numLiveObjects < originalLiveObjects;
+}
+
+size_t Heap::size()
+{
+ return primaryHeap.numLiveObjects + numberHeap.numLiveObjects;
+}
+
+size_t Heap::globalObjectCount()
+{
+ size_t count = 0;
+ if (JSGlobalObject* head = m_globalData->head) {
+ JSGlobalObject* o = head;
+ do {
+ ++count;
+ o = o->next();
+ } while (o != head);
+ }
+ return count;
+}
+
+size_t Heap::protectedGlobalObjectCount()
+{
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ size_t count = 0;
+ if (JSGlobalObject* head = m_globalData->head) {
+ JSGlobalObject* o = head;
+ do {
+ if (m_protectedValues.contains(o))
+ ++count;
+ o = o->next();
+ } while (o != head);
+ }
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+
+ return count;
+}
+
+size_t Heap::protectedObjectCount()
+{
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ size_t result = m_protectedValues.size();
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+
+ return result;
+}
+
+static const char* typeName(JSCell* val)
+{
+ if (val->isString())
+ return "string";
+ if (val->isNumber())
+ return "number";
+ if (val->isGetterSetter())
+ return "gettersetter";
+ ASSERT(val->isObject());
+ const ClassInfo* info = static_cast<JSObject*>(val)->classInfo();
+ return info ? info->className : "Object";
+}
+
+HashCountedSet<const char*>* Heap::protectedObjectTypeCounts()
+{
+ HashCountedSet<const char*>* counts = new HashCountedSet<const char*>;
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ ProtectCountSet::iterator end = m_protectedValues.end();
+ for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
+ counts->add(typeName(it->first));
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+
+ return counts;
+}
+
+bool Heap::isBusy()
+{
+ return (primaryHeap.operationInProgress != NoOperation) | (numberHeap.operationInProgress != NoOperation);
+}
+
+Heap::iterator Heap::primaryHeapBegin()
+{
+ return iterator(primaryHeap.blocks, primaryHeap.blocks + primaryHeap.usedBlocks);
+}
+
+Heap::iterator Heap::primaryHeapEnd()
+{
+ return iterator(primaryHeap.blocks + primaryHeap.usedBlocks, primaryHeap.blocks + primaryHeap.usedBlocks);
+}
+
+} // namespace JSC
diff --git a/JavaScriptCore/runtime/Collector.h b/JavaScriptCore/runtime/Collector.h
new file mode 100644
index 0000000..4233b06
--- /dev/null
+++ b/JavaScriptCore/runtime/Collector.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef KJSCOLLECTOR_H_
+#define KJSCOLLECTOR_H_
+
+#include "JSImmediate.h"
+#include <string.h>
+#include <wtf/HashCountedSet.h>
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Threading.h>
+
+// This is supremely lame that we require pthreads to build on windows.
+#if ENABLE(JSC_MULTIPLE_THREADS)
+#include <pthread.h>
+#endif
+
+#define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell)
+
+namespace JSC {
+
+ class ArgList;
+ class CollectorBlock;
+ class JSCell;
+ class JSGlobalData;
+
+ enum OperationInProgress { NoOperation, Allocation, Collection };
+ enum HeapType { PrimaryHeap, NumberHeap };
+
+ template <HeapType> class CollectorHeapIterator;
+
+ struct CollectorHeap {
+ CollectorBlock** blocks;
+ size_t numBlocks;
+ size_t usedBlocks;
+ size_t firstBlockWithPossibleSpace;
+
+ size_t numLiveObjects;
+ size_t numLiveObjectsAtLastCollect;
+ size_t extraCost;
+
+ OperationInProgress operationInProgress;
+ };
+
+ class Heap : Noncopyable {
+ public:
+ class Thread;
+ typedef CollectorHeapIterator<PrimaryHeap> iterator;
+
+ void destroy();
+
+#ifdef JAVASCRIPTCORE_BUILDING_ALL_IN_ONE_FILE
+ // We can inline these functions because everything is compiled as
+ // one file, so the heapAllocate template definitions are available.
+ // However, allocateNumber is used via jsNumberCell outside JavaScriptCore.
+ // Thus allocateNumber needs to provide a non-inline version too.
+ void* inlineAllocateNumber(size_t s) { return heapAllocate<NumberHeap>(s); }
+ void* inlineAllocate(size_t s) { return heapAllocate<PrimaryHeap>(s); }
+#endif
+ void* allocateNumber(size_t);
+ void* allocate(size_t);
+
+ bool collect();
+ bool isBusy(); // true if an allocation or collection is in progress
+
+ static const size_t minExtraCostSize = 256;
+
+ void reportExtraMemoryCost(size_t cost);
+
+ size_t size();
+
+ void setGCProtectNeedsLocking();
+ void protect(JSValue*);
+ void unprotect(JSValue*);
+
+ static Heap* heap(JSValue*); // 0 for immediate values
+
+ size_t globalObjectCount();
+ size_t protectedObjectCount();
+ size_t protectedGlobalObjectCount();
+ HashCountedSet<const char*>* protectedObjectTypeCounts();
+
+ void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads.
+
+ static bool isCellMarked(const JSCell*);
+ static void markCell(JSCell*);
+
+ void markConservatively(void* start, void* end);
+
+ HashSet<ArgList*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<ArgList*>; return *m_markListSet; }
+
+ JSGlobalData* globalData() const { return m_globalData; }
+ static bool isNumber(JSCell*);
+
+ // Iterators for the object heap.
+ iterator primaryHeapBegin();
+ iterator primaryHeapEnd();
+
+ private:
+ template <HeapType heapType> void* heapAllocate(size_t);
+ template <HeapType heapType> size_t sweep();
+ static CollectorBlock* cellBlock(const JSCell*);
+ static size_t cellOffset(const JSCell*);
+
+ friend class JSGlobalData;
+ Heap(JSGlobalData*);
+ ~Heap();
+
+ void recordExtraCost(size_t);
+ void markProtectedObjects();
+ void markCurrentThreadConservatively();
+ void markCurrentThreadConservativelyInternal();
+ void markOtherThreadConservatively(Thread*);
+ void markStackObjectsConservatively();
+
+ typedef HashCountedSet<JSCell*> ProtectCountSet;
+
+ CollectorHeap primaryHeap;
+ CollectorHeap numberHeap;
+
+ OwnPtr<Mutex> m_protectedValuesMutex; // Only non-null if the client explicitly requested it via setGCPrtotectNeedsLocking().
+ ProtectCountSet m_protectedValues;
+
+ HashSet<ArgList*>* m_markListSet;
+
+#if ENABLE(JSC_MULTIPLE_THREADS)
+ static void unregisterThread(void*);
+ void unregisterThread();
+
+ Mutex m_registeredThreadsMutex;
+ Thread* m_registeredThreads;
+ pthread_key_t m_currentThreadRegistrar;
+#endif
+
+ JSGlobalData* m_globalData;
+ };
+
+ // tunable parameters
+ template<size_t bytesPerWord> struct CellSize;
+
+ // cell size needs to be a power of two for certain optimizations in collector.cpp
+ template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 32; }; // 32-bit
+ template<> struct CellSize<sizeof(uint64_t)> { static const size_t m_value = 64; }; // 64-bit
+ const size_t BLOCK_SIZE = 16 * 4096; // 64k
+
+ // derived constants
+ const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1;
+ const size_t BLOCK_MASK = ~BLOCK_OFFSET_MASK;
+ const size_t MINIMUM_CELL_SIZE = CellSize<sizeof(void*)>::m_value;
+ const size_t CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0);
+ const size_t CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double);
+ const size_t SMALL_CELL_SIZE = CELL_SIZE / 2;
+ const size_t CELL_MASK = CELL_SIZE - 1;
+ const size_t CELL_ALIGN_MASK = ~CELL_MASK;
+ const size_t CELLS_PER_BLOCK = (BLOCK_SIZE * 8 - sizeof(uint32_t) * 8 - sizeof(void *) * 8 - 2 * (7 + 3 * 8)) / (CELL_SIZE * 8 + 2);
+ const size_t SMALL_CELLS_PER_BLOCK = 2 * CELLS_PER_BLOCK;
+ const size_t BITMAP_SIZE = (CELLS_PER_BLOCK + 7) / 8;
+ const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t);
+
+ struct CollectorBitmap {
+ uint32_t bits[BITMAP_WORDS];
+ bool get(size_t n) const { return !!(bits[n >> 5] & (1 << (n & 0x1F))); }
+ void set(size_t n) { bits[n >> 5] |= (1 << (n & 0x1F)); }
+ void clear(size_t n) { bits[n >> 5] &= ~(1 << (n & 0x1F)); }
+ void clearAll() { memset(bits, 0, sizeof(bits)); }
+ };
+
+ struct CollectorCell {
+ union {
+ double memory[CELL_ARRAY_LENGTH];
+ struct {
+ void* zeroIfFree;
+ ptrdiff_t next;
+ } freeCell;
+ } u;
+ };
+
+ struct SmallCollectorCell {
+ union {
+ double memory[CELL_ARRAY_LENGTH / 2];
+ struct {
+ void* zeroIfFree;
+ ptrdiff_t next;
+ } freeCell;
+ } u;
+ };
+
+ class CollectorBlock {
+ public:
+ CollectorCell cells[CELLS_PER_BLOCK];
+ uint32_t usedCells;
+ CollectorCell* freeList;
+ CollectorBitmap marked;
+ Heap* heap;
+ HeapType type;
+ };
+
+ class SmallCellCollectorBlock {
+ public:
+ SmallCollectorCell cells[SMALL_CELLS_PER_BLOCK];
+ uint32_t usedCells;
+ SmallCollectorCell* freeList;
+ CollectorBitmap marked;
+ Heap* heap;
+ HeapType type;
+ };
+
+ template <HeapType heapType> struct HeapConstants;
+
+ template <> struct HeapConstants<PrimaryHeap> {
+ static const size_t cellSize = CELL_SIZE;
+ static const size_t cellsPerBlock = CELLS_PER_BLOCK;
+ static const size_t bitmapShift = 0;
+ typedef CollectorCell Cell;
+ typedef CollectorBlock Block;
+ };
+
+ template <> struct HeapConstants<NumberHeap> {
+ static const size_t cellSize = SMALL_CELL_SIZE;
+ static const size_t cellsPerBlock = SMALL_CELLS_PER_BLOCK;
+ static const size_t bitmapShift = 1;
+ typedef SmallCollectorCell Cell;
+ typedef SmallCellCollectorBlock Block;
+ };
+
+ inline CollectorBlock* Heap::cellBlock(const JSCell* cell)
+ {
+ return reinterpret_cast<CollectorBlock*>(reinterpret_cast<uintptr_t>(cell) & BLOCK_MASK);
+ }
+
+ inline bool Heap::isNumber(JSCell* cell)
+ {
+ return Heap::cellBlock(cell)->type == NumberHeap;
+ }
+
+ inline size_t Heap::cellOffset(const JSCell* cell)
+ {
+ return (reinterpret_cast<uintptr_t>(cell) & BLOCK_OFFSET_MASK) / CELL_SIZE;
+ }
+
+ inline bool Heap::isCellMarked(const JSCell* cell)
+ {
+ return cellBlock(cell)->marked.get(cellOffset(cell));
+ }
+
+ inline void Heap::markCell(JSCell* cell)
+ {
+ cellBlock(cell)->marked.set(cellOffset(cell));
+ }
+
+ inline void Heap::reportExtraMemoryCost(size_t cost)
+ {
+ if (cost > minExtraCostSize)
+ recordExtraCost(cost / (CELL_SIZE * 2));
+ }
+
+} // namespace JSC
+
+#endif /* KJSCOLLECTOR_H_ */
diff --git a/JavaScriptCore/runtime/CollectorHeapIterator.h b/JavaScriptCore/runtime/CollectorHeapIterator.h
index c5e1d78..c4b9dfc 100644
--- a/JavaScriptCore/runtime/CollectorHeapIterator.h
+++ b/JavaScriptCore/runtime/CollectorHeapIterator.h
@@ -24,7 +24,7 @@
*/
#include "config.h"
-#include "collector.h"
+#include "Collector.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/Completion.h b/JavaScriptCore/runtime/Completion.h
new file mode 100644
index 0000000..56d13ed
--- /dev/null
+++ b/JavaScriptCore/runtime/Completion.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003, 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KJS_COMPLETION_H
+#define KJS_COMPLETION_H
+
+#include "JSValue.h"
+
+namespace JSC {
+
+ enum ComplType { Normal, Break, Continue, ReturnValue, Throw, Interrupted };
+
+ /*
+ * Completion objects are used to convey the return status and value
+ * from functions.
+ */
+ class Completion {
+ public:
+ Completion(ComplType type = Normal, JSValue* value = noValue())
+ : m_type(type)
+ , m_value(value)
+ {
+ }
+
+ ComplType complType() const { return m_type; }
+ JSValue* value() const { return m_value; }
+ void setValue(JSValue* v) { m_value = v; }
+ bool isValueCompletion() const { return !!m_value; }
+
+ private:
+ ComplType m_type;
+ JSValue* m_value;
+ };
+
+} // namespace JSC
+
+#endif // KJS_COMPLETION_H
diff --git a/JavaScriptCore/runtime/ErrorPrototype.cpp b/JavaScriptCore/runtime/ErrorPrototype.cpp
index 69255c1..993f179 100644
--- a/JavaScriptCore/runtime/ErrorPrototype.cpp
+++ b/JavaScriptCore/runtime/ErrorPrototype.cpp
@@ -24,7 +24,7 @@
#include "JSString.h"
#include "ObjectPrototype.h"
#include "PrototypeFunction.h"
-#include "ustring.h"
+#include "UString.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/Identifier.cpp b/JavaScriptCore/runtime/Identifier.cpp
new file mode 100644
index 0000000..50a6cc3
--- /dev/null
+++ b/JavaScriptCore/runtime/Identifier.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "identifier.h"
+
+#include "ExecState.h"
+#include <new> // for placement new
+#include <string.h> // for strlen
+#include <wtf/Assertions.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/HashSet.h>
+
+namespace JSC {
+
+typedef HashMap<const char*, RefPtr<UString::Rep>, PtrHash<const char*> > LiteralIdentifierTable;
+
+class IdentifierTable {
+public:
+ ~IdentifierTable()
+ {
+ HashSet<UString::Rep*>::iterator end = m_table.end();
+ for (HashSet<UString::Rep*>::iterator iter = m_table.begin(); iter != end; ++iter)
+ (*iter)->setIdentifierTable(0);
+ }
+
+ std::pair<HashSet<UString::Rep*>::iterator, bool> add(UString::Rep* value)
+ {
+ std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add(value);
+ (*result.first)->setIdentifierTable(this);
+ return result;
+ }
+
+ template<typename U, typename V>
+ std::pair<HashSet<UString::Rep*>::iterator, bool> add(U value)
+ {
+ std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add<U, V>(value);
+ (*result.first)->setIdentifierTable(this);
+ return result;
+ }
+
+ void remove(UString::Rep* r) { m_table.remove(r); }
+
+ LiteralIdentifierTable& literalTable() { return m_literalTable; }
+
+private:
+ HashSet<UString::Rep*> m_table;
+ LiteralIdentifierTable m_literalTable;
+};
+
+IdentifierTable* createIdentifierTable()
+{
+ return new IdentifierTable;
+}
+
+void deleteIdentifierTable(IdentifierTable* table)
+{
+ delete table;
+}
+
+bool Identifier::equal(const UString::Rep* r, const char* s)
+{
+ int length = r->len;
+ const UChar* d = r->data();
+ for (int i = 0; i != length; ++i)
+ if (d[i] != (unsigned char)s[i])
+ return false;
+ return s[length] == 0;
+}
+
+bool Identifier::equal(const UString::Rep* r, const UChar* s, int length)
+{
+ if (r->len != length)
+ return false;
+ const UChar* d = r->data();
+ for (int i = 0; i != length; ++i)
+ if (d[i] != s[i])
+ return false;
+ return true;
+}
+
+struct CStringTranslator
+{
+ static unsigned hash(const char* c)
+ {
+ return UString::Rep::computeHash(c);
+ }
+
+ static bool equal(UString::Rep* r, const char* s)
+ {
+ return Identifier::equal(r, s);
+ }
+
+ static void translate(UString::Rep*& location, const char* c, unsigned hash)
+ {
+ size_t length = strlen(c);
+ UChar* d = static_cast<UChar*>(fastMalloc(sizeof(UChar) * length));
+ for (size_t i = 0; i != length; i++)
+ d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
+
+ UString::Rep* r = UString::Rep::create(d, static_cast<int>(length)).releaseRef();
+ r->rc = 0;
+ r->_hash = hash;
+
+ location = r;
+ }
+};
+
+PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const char* c)
+{
+ if (!c) {
+ UString::Rep::null.hash();
+ return &UString::Rep::null;
+ }
+ if (!c[0]) {
+ UString::Rep::empty.hash();
+ return &UString::Rep::empty;
+ }
+ if (!c[1])
+ return add(globalData, globalData->smallStrings.singleCharacterStringRep(static_cast<unsigned char>(c[0])));
+
+ IdentifierTable& identifierTable = *globalData->identifierTable;
+ LiteralIdentifierTable& literalIdentifierTable = identifierTable.literalTable();
+
+ const LiteralIdentifierTable::iterator& iter = literalIdentifierTable.find(c);
+ if (iter != literalIdentifierTable.end())
+ return iter->second;
+
+ UString::Rep* addedString = *identifierTable.add<const char*, CStringTranslator>(c).first;
+ literalIdentifierTable.add(c, addedString);
+
+ return addedString;
+}
+
+PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const char* c)
+{
+ return add(&exec->globalData(), c);
+}
+
+struct UCharBuffer {
+ const UChar* s;
+ unsigned int length;
+};
+
+struct UCharBufferTranslator
+{
+ static unsigned hash(const UCharBuffer& buf)
+ {
+ return UString::Rep::computeHash(buf.s, buf.length);
+ }
+
+ static bool equal(UString::Rep* str, const UCharBuffer& buf)
+ {
+ return Identifier::equal(str, buf.s, buf.length);
+ }
+
+ static void translate(UString::Rep*& location, const UCharBuffer& buf, unsigned hash)
+ {
+ UChar* d = static_cast<UChar*>(fastMalloc(sizeof(UChar) * buf.length));
+ for (unsigned i = 0; i != buf.length; i++)
+ d[i] = buf.s[i];
+
+ UString::Rep* r = UString::Rep::create(d, buf.length).releaseRef();
+ r->rc = 0;
+ r->_hash = hash;
+
+ location = r;
+ }
+};
+
+PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const UChar* s, int length)
+{
+ if (length == 1) {
+ UChar c = s[0];
+ if (c <= 0xFF)
+ return add(globalData, globalData->smallStrings.singleCharacterStringRep(c));
+ }
+ if (!length) {
+ UString::Rep::empty.hash();
+ return &UString::Rep::empty;
+ }
+ UCharBuffer buf = {s, length};
+ return *globalData->identifierTable->add<UCharBuffer, UCharBufferTranslator>(buf).first;
+}
+
+PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const UChar* s, int length)
+{
+ return add(&exec->globalData(), s, length);
+}
+
+PassRefPtr<UString::Rep> Identifier::addSlowCase(JSGlobalData* globalData, UString::Rep* r)
+{
+ ASSERT(!r->identifierTable());
+ if (r->len == 1) {
+ UChar c = r->data()[0];
+ if (c <= 0xFF)
+ r = globalData->smallStrings.singleCharacterStringRep(c);
+ if (r->identifierTable()) {
+#ifndef NDEBUG
+ checkSameIdentifierTable(globalData, r);
+#endif
+ return r;
+ }
+ }
+ if (!r->len) {
+ UString::Rep::empty.hash();
+ return &UString::Rep::empty;
+ }
+ return *globalData->identifierTable->add(r).first;
+}
+
+PassRefPtr<UString::Rep> Identifier::addSlowCase(ExecState* exec, UString::Rep* r)
+{
+ return addSlowCase(&exec->globalData(), r);
+}
+
+void Identifier::remove(UString::Rep* r)
+{
+ r->identifierTable()->remove(r);
+}
+
+#ifndef NDEBUG
+
+void Identifier::checkSameIdentifierTable(ExecState* exec, UString::Rep* rep)
+{
+ ASSERT(rep->identifierTable() == exec->globalData().identifierTable);
+}
+
+void Identifier::checkSameIdentifierTable(JSGlobalData* globalData, UString::Rep* rep)
+{
+ ASSERT(rep->identifierTable() == globalData->identifierTable);
+}
+
+#else
+
+void Identifier::checkSameIdentifierTable(ExecState*, UString::Rep*)
+{
+}
+
+void Identifier::checkSameIdentifierTable(JSGlobalData*, UString::Rep*)
+{
+}
+
+#endif
+
+} // namespace JSC
diff --git a/JavaScriptCore/runtime/Identifier.h b/JavaScriptCore/runtime/Identifier.h
new file mode 100644
index 0000000..9088d53
--- /dev/null
+++ b/JavaScriptCore/runtime/Identifier.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KJS_IDENTIFIER_H
+#define KJS_IDENTIFIER_H
+
+#include "JSGlobalData.h"
+#include "UString.h"
+
+namespace JSC {
+
+ class ExecState;
+
+ class Identifier {
+ friend class StructureID;
+ public:
+ Identifier() { }
+
+ Identifier(ExecState* exec, const char* s) : _ustring(add(exec, s)) { } // Only to be used with string literals.
+ Identifier(ExecState* exec, const UChar* s, int length) : _ustring(add(exec, s, length)) { }
+ Identifier(ExecState* exec, UString::Rep* rep) : _ustring(add(exec, rep)) { }
+ Identifier(ExecState* exec, const UString& s) : _ustring(add(exec, s.rep())) { }
+
+ Identifier(JSGlobalData* globalData, const char* s) : _ustring(add(globalData, s)) { } // Only to be used with string literals.
+ Identifier(JSGlobalData* globalData, const UChar* s, int length) : _ustring(add(globalData, s, length)) { }
+ Identifier(JSGlobalData* globalData, UString::Rep* rep) : _ustring(add(globalData, rep)) { }
+ Identifier(JSGlobalData* globalData, const UString& s) : _ustring(add(globalData, s.rep())) { }
+
+ // Special constructor for cases where we overwrite an object in place.
+ Identifier(PlacementNewAdoptType) : _ustring(PlacementNewAdopt) { }
+
+ const UString& ustring() const { return _ustring; }
+
+ const UChar* data() const { return _ustring.data(); }
+ int size() const { return _ustring.size(); }
+
+ const char* ascii() const { return _ustring.ascii(); }
+
+ static Identifier from(ExecState* exec, unsigned y) { return Identifier(exec, UString::from(y)); }
+
+ bool isNull() const { return _ustring.isNull(); }
+ bool isEmpty() const { return _ustring.isEmpty(); }
+
+ uint32_t toUInt32(bool* ok) const { return _ustring.toUInt32(ok); }
+ uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const { return _ustring.toUInt32(ok, tolerateEmptyString); };
+ uint32_t toStrictUInt32(bool* ok) const { return _ustring.toStrictUInt32(ok); }
+ unsigned toArrayIndex(bool* ok) const { return _ustring.toArrayIndex(ok); }
+ double toDouble() const { return _ustring.toDouble(); }
+
+ friend bool operator==(const Identifier&, const Identifier&);
+ friend bool operator!=(const Identifier&, const Identifier&);
+
+ friend bool operator==(const Identifier&, const char*);
+
+ static void remove(UString::Rep*);
+
+ static bool equal(const UString::Rep*, const char*);
+ static bool equal(const UString::Rep*, const UChar*, int length);
+ static bool equal(const UString::Rep* a, const UString::Rep* b) { return JSC::equal(a, b); }
+
+ static PassRefPtr<UString::Rep> add(ExecState*, const char*); // Only to be used with string literals.
+ static PassRefPtr<UString::Rep> add(JSGlobalData*, const char*); // Only to be used with string literals.
+
+ static void initializeIdentifierThreading();
+
+ private:
+ UString _ustring;
+
+ static bool equal(const Identifier& a, const Identifier& b) { return a._ustring.rep() == b._ustring.rep(); }
+ static bool equal(const Identifier& a, const char* b) { return equal(a._ustring.rep(), b); }
+
+ static PassRefPtr<UString::Rep> add(ExecState*, const UChar*, int length);
+ static PassRefPtr<UString::Rep> add(JSGlobalData*, const UChar*, int length);
+
+ static PassRefPtr<UString::Rep> add(ExecState* exec, UString::Rep* r)
+ {
+ if (r->identifierTable()) {
+#ifndef NDEBUG
+ checkSameIdentifierTable(exec, r);
+#endif
+ return r;
+ }
+ return addSlowCase(exec, r);
+ }
+ static PassRefPtr<UString::Rep> add(JSGlobalData* globalData, UString::Rep* r)
+ {
+ if (r->identifierTable()) {
+#ifndef NDEBUG
+ checkSameIdentifierTable(globalData, r);
+#endif
+ return r;
+ }
+ return addSlowCase(globalData, r);
+ }
+
+ static PassRefPtr<UString::Rep> addSlowCase(ExecState*, UString::Rep* r);
+ static PassRefPtr<UString::Rep> addSlowCase(JSGlobalData*, UString::Rep* r);
+
+ static void checkSameIdentifierTable(ExecState*, UString::Rep*);
+ static void checkSameIdentifierTable(JSGlobalData*, UString::Rep*);
+ };
+
+ inline bool operator==(const Identifier& a, const Identifier& b)
+ {
+ return Identifier::equal(a, b);
+ }
+
+ inline bool operator!=(const Identifier& a, const Identifier& b)
+ {
+ return !Identifier::equal(a, b);
+ }
+
+ inline bool operator==(const Identifier& a, const char* b)
+ {
+ return Identifier::equal(a, b);
+ }
+
+ IdentifierTable* createIdentifierTable();
+ void deleteIdentifierTable(IdentifierTable*);
+
+} // namespace JSC
+
+#endif // KJS_IDENTIFIER_H
diff --git a/JavaScriptCore/runtime/InitializeThreading.cpp b/JavaScriptCore/runtime/InitializeThreading.cpp
index 9e14768..26bfb75 100644
--- a/JavaScriptCore/runtime/InitializeThreading.cpp
+++ b/JavaScriptCore/runtime/InitializeThreading.cpp
@@ -29,12 +29,12 @@
#include "config.h"
#include "InitializeThreading.h"
-#include "collector.h"
+#include "Collector.h"
#include "DateMath.h"
#include "dtoa.h"
#include "identifier.h"
#include "JSGlobalObject.h"
-#include "ustring.h"
+#include "UString.h"
#include <wtf/Threading.h>
namespace JSC {
diff --git a/JavaScriptCore/runtime/Interpreter.cpp b/JavaScriptCore/runtime/Interpreter.cpp
new file mode 100644
index 0000000..1188349
--- /dev/null
+++ b/JavaScriptCore/runtime/Interpreter.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003, 2007 Apple Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "interpreter.h"
+
+#include "ExecState.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "Machine.h"
+#include "Parser.h"
+#include "completion.h"
+#include "Debugger.h"
+#include <stdio.h>
+
+#if !PLATFORM(WIN_OS)
+#include <unistd.h>
+#endif
+
+namespace JSC {
+
+Completion Interpreter::checkSyntax(ExecState* exec, const SourceCode& source)
+{
+ JSLock lock(exec);
+
+ int errLine;
+ UString errMsg;
+
+ RefPtr<ProgramNode> progNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg);
+ if (!progNode)
+ return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url()));
+ return Completion(Normal);
+}
+
+Completion Interpreter::evaluate(ExecState* exec, ScopeChain& scopeChain, const SourceCode& source, JSValue* thisValue)
+{
+ JSLock lock(exec);
+
+ int errLine;
+ UString errMsg;
+ RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg);
+
+ if (!programNode)
+ return Completion(Throw, Error::create(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url()));
+
+ JSObject* thisObj = (!thisValue || thisValue->isUndefinedOrNull()) ? exec->dynamicGlobalObject() : thisValue->toObject(exec);
+
+ JSValue* exception = noValue();
+ JSValue* result = exec->machine()->execute(programNode.get(), exec, scopeChain.node(), thisObj, &exception);
+
+ if (exception) {
+ if (exception->isObject() && asObject(exception)->isWatchdogException())
+ return Completion(Interrupted, result);
+ return Completion(Throw, exception);
+ }
+ return Completion(Normal, result);
+}
+
+} // namespace JSC
diff --git a/JavaScriptCore/runtime/Interpreter.h b/JavaScriptCore/runtime/Interpreter.h
new file mode 100644
index 0000000..0366063
--- /dev/null
+++ b/JavaScriptCore/runtime/Interpreter.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003, 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KJS_Interpreter_h
+#define KJS_Interpreter_h
+
+#include "JSValue.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/unicode/Unicode.h>
+
+namespace JSC {
+
+ class Completion;
+ class ExecState;
+ class ScopeChain;
+ class SourceCode;
+
+ class Interpreter {
+ public:
+ /**
+ * Parses the supplied ECMAScript code and checks for syntax errors.
+ *
+ * @param code The code to check
+ * @return A normal completion if there were no syntax errors in the code,
+ * otherwise a throw completion with the syntax error as its value.
+ */
+ static Completion checkSyntax(ExecState*, const SourceCode&);
+
+ /**
+ * Evaluates the supplied ECMAScript code.
+ *
+ * Since this method returns a Completion, you should check the type of
+ * completion to detect an error or before attempting to access the returned
+ * value. For example, if an error occurs during script execution and is not
+ * caught by the script, the completion type will be Throw.
+ *
+ * If the supplied code is invalid, a SyntaxError will be thrown.
+ *
+ * @param code The code to evaluate
+ * @param thisValue The value to pass in as the "this" value for the script
+ * execution. This should either be jsNull() or an Object.
+ * @return A completion object representing the result of the execution.
+ */
+ static Completion evaluate(ExecState*, ScopeChain&, const SourceCode&, JSValue* thisValue = noValue());
+ };
+
+} // namespace JSC
+
+#endif // KJS_Interpreter_h
diff --git a/JavaScriptCore/runtime/JSCell.h b/JavaScriptCore/runtime/JSCell.h
index 108dab6..8d8e30c 100644
--- a/JavaScriptCore/runtime/JSCell.h
+++ b/JavaScriptCore/runtime/JSCell.h
@@ -25,7 +25,7 @@
#include "StructureID.h"
#include "JSValue.h"
-#include "collector.h"
+#include "Collector.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/JSGlobalData.cpp b/JavaScriptCore/runtime/JSGlobalData.cpp
index 8910679..bd725f5 100644
--- a/JavaScriptCore/runtime/JSGlobalData.cpp
+++ b/JavaScriptCore/runtime/JSGlobalData.cpp
@@ -38,9 +38,9 @@
#include "JSStaticScopeObject.h"
#include "Machine.h"
#include "Parser.h"
-#include "collector.h"
+#include "Collector.h"
#include "lexer.h"
-#include "lookup.h"
+#include "Lookup.h"
#include "nodes.h"
#if ENABLE(JSC_MULTIPLE_THREADS)
diff --git a/JavaScriptCore/runtime/JSGlobalData.h b/JavaScriptCore/runtime/JSGlobalData.h
index 3210149..814ff9d 100644
--- a/JavaScriptCore/runtime/JSGlobalData.h
+++ b/JavaScriptCore/runtime/JSGlobalData.h
@@ -32,7 +32,7 @@
#include <wtf/Forward.h>
#include <wtf/HashMap.h>
#include <wtf/RefCounted.h>
-#include "collector.h"
+#include "Collector.h"
#include "SmallStrings.h"
struct OpaqueJSClass;
diff --git a/JavaScriptCore/runtime/JSLock.cpp b/JavaScriptCore/runtime/JSLock.cpp
index ee7fb3b..708553a 100644
--- a/JavaScriptCore/runtime/JSLock.cpp
+++ b/JavaScriptCore/runtime/JSLock.cpp
@@ -21,7 +21,7 @@
#include "config.h"
#include "JSLock.h"
-#include "collector.h"
+#include "Collector.h"
#include "ExecState.h"
#if ENABLE(JSC_MULTIPLE_THREADS)
diff --git a/JavaScriptCore/runtime/JSNumberCell.cpp b/JavaScriptCore/runtime/JSNumberCell.cpp
index 5b3f3bd..0e33da6 100644
--- a/JavaScriptCore/runtime/JSNumberCell.cpp
+++ b/JavaScriptCore/runtime/JSNumberCell.cpp
@@ -24,7 +24,7 @@
#include "JSNumberCell.h"
#include "NumberObject.h"
-#include "ustring.h"
+#include "UString.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/JSNumberCell.h b/JavaScriptCore/runtime/JSNumberCell.h
index e2f6990..89fdf1b0 100644
--- a/JavaScriptCore/runtime/JSNumberCell.h
+++ b/JavaScriptCore/runtime/JSNumberCell.h
@@ -26,8 +26,8 @@
#include "ExecState.h"
#include "JSCell.h"
#include "JSImmediate.h"
-#include "collector.h"
-#include "ustring.h"
+#include "Collector.h"
+#include "UString.h"
#include <stddef.h> // for size_t
namespace JSC {
diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp
index 82c1c63..897cf38 100644
--- a/JavaScriptCore/runtime/JSObject.cpp
+++ b/JavaScriptCore/runtime/JSObject.cpp
@@ -31,9 +31,9 @@
#include "NativeErrorConstructor.h"
#include "ObjectPrototype.h"
#include "PropertyNameArray.h"
-#include "lookup.h"
+#include "Lookup.h"
#include "nodes.h"
-#include "operations.h"
+#include "Operations.h"
#include <math.h>
#include <wtf/Assertions.h>
diff --git a/JavaScriptCore/runtime/JSValue.h b/JavaScriptCore/runtime/JSValue.h
index 059a5e6..815a600 100644
--- a/JavaScriptCore/runtime/JSValue.h
+++ b/JavaScriptCore/runtime/JSValue.h
@@ -26,7 +26,7 @@
#include "CallData.h"
#include "ConstructData.h"
#include "JSImmediate.h"
-#include "ustring.h"
+#include "UString.h"
#include <stddef.h> // for size_t
// The magic number 0x4000 is not important here, it is being subtracted back out (avoiding using zero since this
diff --git a/JavaScriptCore/runtime/Lookup.cpp b/JavaScriptCore/runtime/Lookup.cpp
new file mode 100644
index 0000000..b44b1e4
--- /dev/null
+++ b/JavaScriptCore/runtime/Lookup.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+#include "Lookup.h"
+
+#include "PrototypeFunction.h"
+
+namespace JSC {
+
+void HashTable::createTable(JSGlobalData* globalData) const
+{
+ ASSERT(!table);
+ HashEntry* entries = new HashEntry[hashSizeMask + 1];
+ for (int i = 0; i <= hashSizeMask; ++i)
+ entries[i].setKey(0);
+ for (int i = 0; values[i].key; ++i) {
+ UString::Rep* identifier = Identifier::add(globalData, values[i].key).releaseRef();
+ int hashIndex = identifier->computedHash() & hashSizeMask;
+ ASSERT(!entries[hashIndex].key());
+ entries[hashIndex].initialize(identifier, values[i].attributes, values[i].value1, values[i].value2);
+ }
+ table = entries;
+}
+
+void HashTable::deleteTable() const
+{
+ if (table) {
+ for (int i = 0; i != hashSizeMask + 1; ++i) {
+ if (UString::Rep* key = table[i].key())
+ key->deref();
+ }
+ delete [] table;
+ table = 0;
+ }
+}
+
+void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot)
+{
+ ASSERT(entry->attributes() & Function);
+ JSValue** location = thisObj->getDirectLocation(propertyName);
+
+ if (!location) {
+ PrototypeFunction* function = new (exec) PrototypeFunction(exec, entry->functionLength(), propertyName, entry->function());
+ thisObj->putDirect(propertyName, function, entry->attributes());
+ location = thisObj->getDirectLocation(propertyName);
+ }
+
+ slot.setValueSlot(thisObj, location, thisObj->offsetForLocation(location));
+}
+
+} // namespace JSC
diff --git a/JavaScriptCore/runtime/Lookup.h b/JavaScriptCore/runtime/Lookup.h
new file mode 100644
index 0000000..a547613
--- /dev/null
+++ b/JavaScriptCore/runtime/Lookup.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef KJS_lookup_h
+#define KJS_lookup_h
+
+#include "ExecState.h"
+#include "JSFunction.h"
+#include "JSGlobalObject.h"
+#include "JSObject.h"
+#include "PropertySlot.h"
+#include "identifier.h"
+#include <stdio.h>
+#include <wtf/Assertions.h>
+
+namespace JSC {
+
+ // Hash table generated by the create_hash_table script.
+ struct HashTableValue {
+ const char* key; // property name
+ unsigned char attributes; // JSObject attributes
+ intptr_t value1;
+ intptr_t value2;
+ };
+
+ // FIXME: There is no reason this get function can't be simpler.
+ // ie. typedef JSValue* (*GetFunction)(ExecState*, JSObject* baseObject)
+ typedef PropertySlot::GetValueFunc GetFunction;
+ typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue* value);
+
+ class HashEntry {
+ public:
+ void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2)
+ {
+ m_key = key;
+ m_attributes = attributes;
+ m_u.store.value1 = v1;
+ m_u.store.value2 = v2;
+ }
+
+ void setKey(UString::Rep* key) { m_key = key; }
+ UString::Rep* key() const { return m_key; }
+
+ unsigned char attributes() const { return m_attributes; }
+
+ NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; }
+ unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); }
+
+ GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; }
+ PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; }
+
+ intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; }
+
+ private:
+ UString::Rep* m_key;
+ unsigned char m_attributes; // JSObject attributes
+
+ union {
+ struct {
+ intptr_t value1;
+ intptr_t value2;
+ } store;
+ struct {
+ NativeFunction functionValue;
+ intptr_t length; // number of arguments for function
+ } function;
+ struct {
+ GetFunction get;
+ PutFunction put;
+ } property;
+ struct {
+ intptr_t value;
+ intptr_t unused;
+ } lexer;
+ } m_u;
+ };
+
+ struct HashTable {
+ int hashSizeMask; // Precomputed size for the hash table (minus 1).
+ const HashTableValue* values; // Fixed values generated by script.
+ mutable const HashEntry* table; // Table allocated at runtime.
+
+ ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const
+ {
+ if (!table)
+ createTable(globalData);
+ }
+
+ ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const
+ {
+ if (!table)
+ createTable(&exec->globalData());
+ }
+
+ void deleteTable() const;
+
+ // Find an entry in the table, and return the entry.
+ ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const
+ {
+ initializeIfNeeded(globalData);
+ return entry(identifier);
+ }
+
+ ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const
+ {
+ initializeIfNeeded(exec);
+ return entry(identifier);
+ }
+
+ private:
+ ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const
+ {
+ ASSERT(table);
+ const HashEntry* entry = &table[identifier.ustring().rep()->computedHash() & hashSizeMask];
+ if (entry->key() != identifier.ustring().rep())
+ return 0;
+ return entry;
+ }
+
+ // Convert the hash table keys to identifiers.
+ void createTable(JSGlobalData*) const;
+ };
+
+ void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&);
+
+ /**
+ * This method does it all (looking in the hashtable, checking for function
+ * overrides, creating the function or retrieving from cache, calling
+ * getValueProperty in case of a non-function property, forwarding to parent if
+ * unknown property).
+ */
+ template <class ThisImp, class ParentImp>
+ inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
+ {
+ const HashEntry* entry = table->entry(exec, propertyName);
+
+ if (!entry) // not found, forward to parent
+ return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
+
+ if (entry->attributes() & Function)
+ setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
+ else
+ slot.setCustom(thisObj, entry->propertyGetter());
+
+ return true;
+ }
+
+ /**
+ * Simplified version of getStaticPropertySlot in case there are only functions.
+ * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
+ * a dummy getValueProperty.
+ */
+ template <class ParentImp>
+ inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot)
+ {
+ if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot))
+ return true;
+
+ const HashEntry* entry = table->entry(exec, propertyName);
+ if (!entry)
+ return false;
+
+ setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
+ return true;
+ }
+
+ /**
+ * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
+ * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
+ */
+ template <class ThisImp, class ParentImp>
+ inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
+ {
+ const HashEntry* entry = table->entry(exec, propertyName);
+
+ if (!entry) // not found, forward to parent
+ return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
+
+ ASSERT(!(entry->attributes() & Function));
+
+ slot.setCustom(thisObj, entry->propertyGetter());
+ return true;
+ }
+
+ /**
+ * This one is for "put".
+ * It looks up a hash entry for the property to be set. If an entry
+ * is found it sets the value and returns true, else it returns false.
+ */
+ template <class ThisImp>
+ inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValue* value, const HashTable* table, ThisImp* thisObj)
+ {
+ const HashEntry* entry = table->entry(exec, propertyName);
+
+ if (!entry)
+ return false;
+
+ if (entry->attributes() & Function) // function: put as override property
+ thisObj->putDirect(propertyName, value);
+ else if (!(entry->attributes() & ReadOnly))
+ entry->propertyPutter()(exec, thisObj, value);
+
+ return true;
+ }
+
+ /**
+ * This one is for "put".
+ * It calls lookupPut<ThisImp>() to set the value. If that call
+ * returns false (meaning no entry in the hash table was found),
+ * then it calls put() on the ParentImp class.
+ */
+ template <class ThisImp, class ParentImp>
+ inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValue* value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot)
+ {
+ if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj))
+ thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent
+ }
+
+} // namespace JSC
+
+#endif // KJS_lookup_h
diff --git a/JavaScriptCore/runtime/MathObject.cpp b/JavaScriptCore/runtime/MathObject.cpp
index 8b972d3..ee7b700 100644
--- a/JavaScriptCore/runtime/MathObject.cpp
+++ b/JavaScriptCore/runtime/MathObject.cpp
@@ -22,7 +22,7 @@
#include "MathObject.h"
#include "ObjectPrototype.h"
-#include "operations.h"
+#include "Operations.h"
#include <time.h>
#include <wtf/Assertions.h>
#include <wtf/MathExtras.h>
diff --git a/JavaScriptCore/runtime/NativeErrorPrototype.cpp b/JavaScriptCore/runtime/NativeErrorPrototype.cpp
index 9403aa9..df311d5 100644
--- a/JavaScriptCore/runtime/NativeErrorPrototype.cpp
+++ b/JavaScriptCore/runtime/NativeErrorPrototype.cpp
@@ -23,7 +23,7 @@
#include "ErrorPrototype.h"
#include "JSString.h"
-#include "ustring.h"
+#include "UString.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/NumberPrototype.cpp b/JavaScriptCore/runtime/NumberPrototype.cpp
index d203e3f..4f5b4f9 100644
--- a/JavaScriptCore/runtime/NumberPrototype.cpp
+++ b/JavaScriptCore/runtime/NumberPrototype.cpp
@@ -26,7 +26,7 @@
#include "JSString.h"
#include "PrototypeFunction.h"
#include "dtoa.h"
-#include "operations.h"
+#include "Operations.h"
#include <wtf/Assertions.h>
#include <wtf/MathExtras.h>
#include <wtf/Vector.h>
diff --git a/JavaScriptCore/runtime/Operations.cpp b/JavaScriptCore/runtime/Operations.cpp
new file mode 100644
index 0000000..a857bf2
--- /dev/null
+++ b/JavaScriptCore/runtime/Operations.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "Operations.h"
+
+#include "Error.h"
+#include "JSObject.h"
+#include "JSString.h"
+#include <math.h>
+#include <stdio.h>
+#include <wtf/MathExtras.h>
+
+#if HAVE(FLOAT_H)
+#include <float.h>
+#endif
+
+namespace JSC {
+
+// ECMA 11.9.3
+bool equal(ExecState* exec, JSValue* v1, JSValue* v2)
+{
+ if (JSImmediate::areBothImmediateNumbers(v1, v2))
+ return v1 == v2;
+
+ return equalSlowCaseInline(exec, v1, v2);
+}
+
+bool equalSlowCase(ExecState* exec, JSValue* v1, JSValue* v2)
+{
+ return equalSlowCaseInline(exec, v1, v2);
+}
+
+bool strictEqual(JSValue* v1, JSValue* v2)
+{
+ if (JSImmediate::areBothImmediate(v1, v2))
+ return v1 == v2;
+
+ if (JSImmediate::isEitherImmediate(v1, v2) & (v1 != JSImmediate::from(0)) & (v2 != JSImmediate::from(0)))
+ return false;
+
+ return strictEqualSlowCaseInline(v1, v2);
+}
+
+bool strictEqualSlowCase(JSValue* v1, JSValue* v2)
+{
+ return strictEqualSlowCaseInline(v1, v2);
+}
+
+NEVER_INLINE JSValue* throwOutOfMemoryError(ExecState* exec)
+{
+ JSObject* error = Error::create(exec, GeneralError, "Out of memory");
+ exec->setException(error);
+ return error;
+}
+
+} // namespace JSC
diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h
new file mode 100644
index 0000000..fad9720
--- /dev/null
+++ b/JavaScriptCore/runtime/Operations.h
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_OPERATIONS_H_
+#define _KJS_OPERATIONS_H_
+
+#include "JSImmediate.h"
+#include "JSNumberCell.h"
+#include "JSString.h"
+
+namespace JSC {
+
+ // ECMA 11.9.3
+ bool equal(ExecState*, JSValue*, JSValue*);
+ bool equalSlowCase(ExecState*, JSValue*, JSValue*);
+
+ ALWAYS_INLINE bool equalSlowCaseInline(ExecState* exec, JSValue* v1, JSValue* v2)
+ {
+ ASSERT(!JSImmediate::areBothImmediateNumbers(v1, v2));
+
+ do {
+ if (v1->isNumber() && v2->isNumber())
+ return v1->uncheckedGetNumber() == v2->uncheckedGetNumber();
+
+ bool s1 = v1->isString();
+ bool s2 = v2->isString();
+ if (s1 && s2)
+ return asString(v1)->value() == asString(v2)->value();
+
+ if (v1->isUndefinedOrNull()) {
+ if (v2->isUndefinedOrNull())
+ return true;
+ if (JSImmediate::isImmediate(v2))
+ return false;
+ return v2->asCell()->structureID()->typeInfo().masqueradesAsUndefined();
+ }
+
+ if (v2->isUndefinedOrNull()) {
+ if (JSImmediate::isImmediate(v1))
+ return false;
+ return v1->asCell()->structureID()->typeInfo().masqueradesAsUndefined();
+ }
+
+ if (v1->isObject()) {
+ if (v2->isObject())
+ return v1 == v2;
+ JSValue* p1 = v1->toPrimitive(exec);
+ if (exec->hadException())
+ return false;
+ v1 = p1;
+ if (JSImmediate::areBothImmediateNumbers(v1, v2))
+ return v1 == v2;
+ continue;
+ }
+
+ if (v2->isObject()) {
+ JSValue* p2 = v2->toPrimitive(exec);
+ if (exec->hadException())
+ return false;
+ v2 = p2;
+ if (JSImmediate::areBothImmediateNumbers(v1, v2))
+ return v1 == v2;
+ continue;
+ }
+
+ if (s1 || s2) {
+ double d1 = v1->toNumber(exec);
+ double d2 = v2->toNumber(exec);
+ return d1 == d2;
+ }
+
+ if (v1->isBoolean()) {
+ if (v2->isNumber())
+ return static_cast<double>(v1->getBoolean()) == v2->uncheckedGetNumber();
+ } else if (v2->isBoolean()) {
+ if (v1->isNumber())
+ return v1->uncheckedGetNumber() == static_cast<double>(v2->getBoolean());
+ }
+
+ return v1 == v2;
+ } while (true);
+ }
+
+
+ bool strictEqual(JSValue*, JSValue*);
+ bool strictEqualSlowCase(JSValue*, JSValue*);
+
+ inline bool strictEqualSlowCaseInline(JSValue* v1, JSValue* v2)
+ {
+ ASSERT(!JSImmediate::areBothImmediate(v1, v2));
+
+ if (JSImmediate::isEitherImmediate(v1, v2)) {
+ ASSERT(v1 == JSImmediate::zeroImmediate() || v2 == JSImmediate::zeroImmediate());
+ ASSERT(v1 != v2);
+
+ // The reason we can't just return false here is that 0 === -0,
+ // and while the former is an immediate number, the latter is not.
+ if (v1 == JSImmediate::zeroImmediate())
+ return asCell(v2)->isNumber() && asNumberCell(v2)->value() == 0;
+ return asCell(v1)->isNumber() && asNumberCell(v1)->value() == 0;
+ }
+
+ if (asCell(v1)->isNumber()) {
+ return asCell(v2)->isNumber()
+ && asNumberCell(v1)->value() == asNumberCell(v2)->value();
+ }
+
+ if (asCell(v1)->isString()) {
+ return asCell(v2)->isString()
+ && asString(v1)->value() == asString(v2)->value();
+ }
+
+ return v1 == v2;
+ }
+
+ JSValue* throwOutOfMemoryError(ExecState*);
+}
+
+#endif
diff --git a/JavaScriptCore/runtime/PropertyMapHashTable.h b/JavaScriptCore/runtime/PropertyMapHashTable.h
index 98b0727..0d6c521 100644
--- a/JavaScriptCore/runtime/PropertyMapHashTable.h
+++ b/JavaScriptCore/runtime/PropertyMapHashTable.h
@@ -21,7 +21,7 @@
#ifndef PropertyMapHashTable_h
#define PropertyMapHashTable_h
-#include "ustring.h"
+#include "UString.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/Protect.h b/JavaScriptCore/runtime/Protect.h
new file mode 100644
index 0000000..9a406bf
--- /dev/null
+++ b/JavaScriptCore/runtime/Protect.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2004, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef protect_h
+#define protect_h
+
+#include "JSCell.h"
+#include "Collector.h"
+
+namespace JSC {
+
+ inline void gcProtect(JSCell* val)
+ {
+ Heap::heap(val)->protect(val);
+ }
+
+ inline void gcUnprotect(JSCell* val)
+ {
+ Heap::heap(val)->unprotect(val);
+ }
+
+ inline void gcProtectNullTolerant(JSCell* val)
+ {
+ if (val)
+ gcProtect(val);
+ }
+
+ inline void gcUnprotectNullTolerant(JSCell* val)
+ {
+ if (val)
+ gcUnprotect(val);
+ }
+
+ inline void gcProtect(JSValue* value)
+ {
+ if (JSImmediate::isImmediate(value))
+ return;
+ gcProtect(asCell(value));
+ }
+
+ inline void gcUnprotect(JSValue* value)
+ {
+ if (JSImmediate::isImmediate(value))
+ return;
+ gcUnprotect(asCell(value));
+ }
+
+ inline void gcProtectNullTolerant(JSValue* value)
+ {
+ if (!value || JSImmediate::isImmediate(value))
+ return;
+ gcProtect(asCell(value));
+ }
+
+ inline void gcUnprotectNullTolerant(JSValue* value)
+ {
+ if (!value || JSImmediate::isImmediate(value))
+ return;
+ gcUnprotect(asCell(value));
+ }
+
+ // FIXME: Share more code with RefPtr template? The only differences are the ref/deref operation
+ // and the implicit conversion to raw pointer
+ template <class T> class ProtectedPtr {
+ public:
+ ProtectedPtr() : m_ptr(0) { }
+ ProtectedPtr(T* ptr);
+ ProtectedPtr(const ProtectedPtr&);
+ ~ProtectedPtr();
+
+ template <class U> ProtectedPtr(const ProtectedPtr<U>&);
+
+ T* get() const { return m_ptr; }
+ operator T*() const { return m_ptr; }
+ T* operator->() const { return m_ptr; }
+
+ bool operator!() const { return !m_ptr; }
+
+ ProtectedPtr& operator=(const ProtectedPtr&);
+ ProtectedPtr& operator=(T*);
+
+ private:
+ T* m_ptr;
+ };
+
+ template <class T> ProtectedPtr<T>::ProtectedPtr(T* ptr)
+ : m_ptr(ptr)
+ {
+ gcProtectNullTolerant(m_ptr);
+ }
+
+ template <class T> ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr& o)
+ : m_ptr(o.get())
+ {
+ gcProtectNullTolerant(m_ptr);
+ }
+
+ template <class T> ProtectedPtr<T>::~ProtectedPtr()
+ {
+ gcUnprotectNullTolerant(m_ptr);
+ }
+
+ template <class T> template <class U> ProtectedPtr<T>::ProtectedPtr(const ProtectedPtr<U>& o)
+ : m_ptr(o.get())
+ {
+ gcProtectNullTolerant(m_ptr);
+ }
+
+ template <class T> ProtectedPtr<T>& ProtectedPtr<T>::operator=(const ProtectedPtr<T>& o)
+ {
+ T* optr = o.m_ptr;
+ gcProtectNullTolerant(optr);
+ gcUnprotectNullTolerant(m_ptr);
+ m_ptr = optr;
+ return *this;
+ }
+
+ template <class T> inline ProtectedPtr<T>& ProtectedPtr<T>::operator=(T* optr)
+ {
+ gcProtectNullTolerant(optr);
+ gcUnprotectNullTolerant(m_ptr);
+ m_ptr = optr;
+ return *this;
+ }
+
+ template <class T> inline bool operator==(const ProtectedPtr<T>& a, const ProtectedPtr<T>& b) { return a.get() == b.get(); }
+ template <class T> inline bool operator==(const ProtectedPtr<T>& a, const T* b) { return a.get() == b; }
+ template <class T> inline bool operator==(const T* a, const ProtectedPtr<T>& b) { return a == b.get(); }
+
+ template <class T> inline bool operator!=(const ProtectedPtr<T>& a, const ProtectedPtr<T>& b) { return a.get() != b.get(); }
+ template <class T> inline bool operator!=(const ProtectedPtr<T>& a, const T* b) { return a.get() != b; }
+ template <class T> inline bool operator!=(const T* a, const ProtectedPtr<T>& b) { return a != b.get(); }
+
+} // namespace JSC
+
+#endif // protect_h
diff --git a/JavaScriptCore/runtime/RegExp.cpp b/JavaScriptCore/runtime/RegExp.cpp
new file mode 100644
index 0000000..661a1c7
--- /dev/null
+++ b/JavaScriptCore/runtime/RegExp.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org)
+ * Copyright (c) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+#include "RegExp.h"
+
+#include "CTI.h"
+#include "lexer.h"
+#include <pcre/pcre.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wrec/WREC.h>
+#include <wtf/Assertions.h>
+#include <wtf/OwnArrayPtr.h>
+
+namespace JSC {
+
+inline RegExp::RegExp(JSGlobalData* globalData, const UString& pattern)
+ : m_pattern(pattern)
+ , m_flagBits(0)
+ , m_regExp(0)
+ , m_constructionError(0)
+ , m_numSubpatterns(0)
+{
+#if ENABLE(WREC)
+ m_wrecFunction = CTI::compileRegExp(globalData->machine, pattern, &m_numSubpatterns, &m_constructionError);
+ if (m_wrecFunction)
+ return;
+ // Fall through to non-WREC case.
+#else
+ UNUSED_PARAM(globalData);
+#endif
+ m_regExp = jsRegExpCompile(reinterpret_cast<const UChar*>(pattern.data()), pattern.size(),
+ JSRegExpDoNotIgnoreCase, JSRegExpSingleLine, &m_numSubpatterns, &m_constructionError);
+}
+
+PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& pattern)
+{
+ return adoptRef(new RegExp(globalData, pattern));
+}
+
+inline RegExp::RegExp(JSGlobalData* globalData, const UString& pattern, const UString& flags)
+ : m_pattern(pattern)
+ , m_flags(flags)
+ , m_flagBits(0)
+ , m_regExp(0)
+ , m_constructionError(0)
+ , m_numSubpatterns(0)
+{
+ // NOTE: The global flag is handled on a case-by-case basis by functions like
+ // String::match and RegExpObject::match.
+ if (flags.find('g') != -1)
+ m_flagBits |= Global;
+
+ // FIXME: Eliminate duplication by adding a way ask a JSRegExp what its flags are?
+ JSRegExpIgnoreCaseOption ignoreCaseOption = JSRegExpDoNotIgnoreCase;
+ if (flags.find('i') != -1) {
+ m_flagBits |= IgnoreCase;
+ ignoreCaseOption = JSRegExpIgnoreCase;
+ }
+
+ JSRegExpMultilineOption multilineOption = JSRegExpSingleLine;
+ if (flags.find('m') != -1) {
+ m_flagBits |= Multiline;
+ multilineOption = JSRegExpMultiline;
+ }
+
+#if ENABLE(WREC)
+ m_wrecFunction = CTI::compileRegExp(globalData->machine, pattern, &m_numSubpatterns, &m_constructionError, (m_flagBits & IgnoreCase), (m_flagBits & Multiline));
+ if (m_wrecFunction)
+ return;
+ // Fall through to non-WREC case.
+#else
+ UNUSED_PARAM(globalData);
+#endif
+ m_regExp = jsRegExpCompile(reinterpret_cast<const UChar*>(pattern.data()), pattern.size(),
+ ignoreCaseOption, multilineOption, &m_numSubpatterns, &m_constructionError);
+}
+
+PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& pattern, const UString& flags)
+{
+ return adoptRef(new RegExp(globalData, pattern, flags));
+}
+
+RegExp::~RegExp()
+{
+ jsRegExpFree(m_regExp);
+#if ENABLE(WREC)
+ if (m_wrecFunction)
+ WTF::fastFreeExecutable(m_wrecFunction);
+#endif
+}
+
+int RegExp::match(const UString& s, int i, OwnArrayPtr<int>* ovector)
+{
+ if (i < 0)
+ i = 0;
+ if (ovector)
+ ovector->clear();
+
+ if (i > s.size() || s.isNull())
+ return -1;
+
+#if ENABLE(WREC)
+ if (m_wrecFunction) {
+ int offsetVectorSize = (m_numSubpatterns + 1) * 2;
+ int* offsetVector = new int [offsetVectorSize];
+ for (int j = 0; j < offsetVectorSize; ++j)
+ offsetVector[j] = -1;
+
+ OwnArrayPtr<int> nonReturnedOvector;
+ if (!ovector)
+ nonReturnedOvector.set(offsetVector);
+ else
+ ovector->set(offsetVector);
+
+ int result = reinterpret_cast<WRECFunction>(m_wrecFunction)(s.data(), i, s.size(), offsetVector);
+
+ if (result < 0) {
+#ifndef NDEBUG
+ // TODO: define up a symbol, rather than magic -1
+ if (result != -1)
+ fprintf(stderr, "jsRegExpExecute failed with result %d\n", result);
+#endif
+ if (ovector)
+ ovector->clear();
+ }
+ return result;
+ } else
+#endif
+ if (m_regExp) {
+ // Set up the offset vector for the result.
+ // First 2/3 used for result, the last third used by PCRE.
+ int* offsetVector;
+ int offsetVectorSize;
+ int fixedSizeOffsetVector[3];
+ if (!ovector) {
+ offsetVectorSize = 3;
+ offsetVector = fixedSizeOffsetVector;
+ } else {
+ offsetVectorSize = (m_numSubpatterns + 1) * 3;
+ offsetVector = new int [offsetVectorSize];
+ ovector->set(offsetVector);
+ }
+
+ int numMatches = jsRegExpExecute(m_regExp, reinterpret_cast<const UChar*>(s.data()), s.size(), i, offsetVector, offsetVectorSize);
+
+ if (numMatches < 0) {
+#ifndef NDEBUG
+ if (numMatches != JSRegExpErrorNoMatch)
+ fprintf(stderr, "jsRegExpExecute failed with result %d\n", numMatches);
+#endif
+ if (ovector)
+ ovector->clear();
+ return -1;
+ }
+
+ return offsetVector[0];
+ }
+
+ return -1;
+}
+
+} // namespace JSC
diff --git a/JavaScriptCore/runtime/RegExp.h b/JavaScriptCore/runtime/RegExp.h
new file mode 100644
index 0000000..2abb805
--- /dev/null
+++ b/JavaScriptCore/runtime/RegExp.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef KJS_REGEXP_H
+#define KJS_REGEXP_H
+
+#include "UString.h"
+#include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+
+struct JSRegExp;
+
+namespace JSC {
+
+ class JSGlobalData;
+
+ class RegExp : public RefCounted<RegExp> {
+ public:
+ static PassRefPtr<RegExp> create(JSGlobalData*, const UString& pattern);
+ static PassRefPtr<RegExp> create(JSGlobalData*, const UString& pattern, const UString& flags);
+ ~RegExp();
+
+ bool global() const { return m_flagBits & Global; }
+ bool ignoreCase() const { return m_flagBits & IgnoreCase; }
+ bool multiline() const { return m_flagBits & Multiline; }
+
+ const UString& pattern() const { return m_pattern; }
+ const UString& flags() const { return m_flags; }
+
+ bool isValid() const { return !m_constructionError; }
+ const char* errorMessage() const { return m_constructionError; }
+
+ int match(const UString&, int offset, OwnArrayPtr<int>* ovector = 0);
+ unsigned numSubpatterns() const { return m_numSubpatterns; }
+
+ private:
+ RegExp(JSGlobalData*, const UString& pattern);
+ RegExp(JSGlobalData*, const UString& pattern, const UString& flags);
+
+ void compile();
+
+ enum FlagBits { Global = 1, IgnoreCase = 2, Multiline = 4 };
+
+ UString m_pattern; // FIXME: Just decompile m_regExp instead of storing this.
+ UString m_flags; // FIXME: Just decompile m_regExp instead of storing this.
+ int m_flagBits;
+ JSRegExp* m_regExp;
+ const char* m_constructionError;
+ unsigned m_numSubpatterns;
+
+#if ENABLE(WREC)
+ // Called as a WRECFunction
+ void* m_wrecFunction;
+#endif
+ };
+
+} // namespace JSC
+
+#endif // KJS_REGEXP_H
diff --git a/JavaScriptCore/runtime/RegExpConstructor.cpp b/JavaScriptCore/runtime/RegExpConstructor.cpp
index 4c4db39..6e3b90cd 100644
--- a/JavaScriptCore/runtime/RegExpConstructor.cpp
+++ b/JavaScriptCore/runtime/RegExpConstructor.cpp
@@ -29,7 +29,7 @@
#include "RegExpMatchesArray.h"
#include "RegExpObject.h"
#include "RegExpPrototype.h"
-#include "regexp.h"
+#include "RegExp.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/RegExpObject.h b/JavaScriptCore/runtime/RegExpObject.h
index d80b47c..d5bf225 100644
--- a/JavaScriptCore/runtime/RegExpObject.h
+++ b/JavaScriptCore/runtime/RegExpObject.h
@@ -22,7 +22,7 @@
#define RegExpObject_h
#include "JSObject.h"
-#include "regexp.h"
+#include "RegExp.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/RegExpPrototype.cpp b/JavaScriptCore/runtime/RegExpPrototype.cpp
index ceee32a..6514ad6 100644
--- a/JavaScriptCore/runtime/RegExpPrototype.cpp
+++ b/JavaScriptCore/runtime/RegExpPrototype.cpp
@@ -29,7 +29,7 @@
#include "ObjectPrototype.h"
#include "PrototypeFunction.h"
#include "RegExpObject.h"
-#include "regexp.h"
+#include "RegExp.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/SmallStrings.h b/JavaScriptCore/runtime/SmallStrings.h
index 7c71208..7e5f5c8 100644
--- a/JavaScriptCore/runtime/SmallStrings.h
+++ b/JavaScriptCore/runtime/SmallStrings.h
@@ -26,7 +26,7 @@
#ifndef SmallStrings_h
#define SmallStrings_h
-#include "ustring.h"
+#include "UString.h"
#include <wtf/OwnPtr.h>
namespace JSC {
diff --git a/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h b/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h
index d703228..fad3852 100644
--- a/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h
+++ b/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h
@@ -23,7 +23,7 @@
#include "JSGlobalObject.h"
#include "StringObject.h"
-#include "ustring.h"
+#include "UString.h"
namespace JSC {
diff --git a/JavaScriptCore/runtime/StructureID.cpp b/JavaScriptCore/runtime/StructureID.cpp
index 8333595..95da830 100644
--- a/JavaScriptCore/runtime/StructureID.cpp
+++ b/JavaScriptCore/runtime/StructureID.cpp
@@ -30,7 +30,7 @@
#include "PropertyNameArray.h"
#include "StructureIDChain.h"
#include "identifier.h"
-#include "lookup.h"
+#include "Lookup.h"
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/RefPtr.h>
diff --git a/JavaScriptCore/runtime/StructureID.h b/JavaScriptCore/runtime/StructureID.h
index 4f45dac..fcd2eee 100644
--- a/JavaScriptCore/runtime/StructureID.h
+++ b/JavaScriptCore/runtime/StructureID.h
@@ -33,7 +33,7 @@
#include "StructureIDTransitionTable.h"
#include "TypeInfo.h"
#include "identifier.h"
-#include "ustring.h"
+#include "UString.h"
#include <wtf/HashFunctions.h>
#include <wtf/HashTraits.h>
#include <wtf/OwnArrayPtr.h>
diff --git a/JavaScriptCore/runtime/StructureIDTransitionTable.h b/JavaScriptCore/runtime/StructureIDTransitionTable.h
index dd65971..1e757d8 100644
--- a/JavaScriptCore/runtime/StructureIDTransitionTable.h
+++ b/JavaScriptCore/runtime/StructureIDTransitionTable.h
@@ -26,7 +26,7 @@
#ifndef StructureIDTransitionTable_h
#define StructureIDTransitionTable_h
-#include "ustring.h"
+#include "UString.h"
#include <wtf/HashFunctions.h>
#include <wtf/HashMap.h>
#include <wtf/HashTraits.h>
diff --git a/JavaScriptCore/runtime/SymbolTable.h b/JavaScriptCore/runtime/SymbolTable.h
index d730d58..c00f95a 100644
--- a/JavaScriptCore/runtime/SymbolTable.h
+++ b/JavaScriptCore/runtime/SymbolTable.h
@@ -30,7 +30,7 @@
#define SymbolTable_h
#include "JSObject.h"
-#include "ustring.h"
+#include "UString.h"
#include <wtf/AlwaysInline.h>
namespace JSC {
diff --git a/JavaScriptCore/runtime/TypeInfo.h b/JavaScriptCore/runtime/TypeInfo.h
new file mode 100644
index 0000000..4f0b16c
--- /dev/null
+++ b/JavaScriptCore/runtime/TypeInfo.h
@@ -0,0 +1,63 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TypeInfo_h
+#define TypeInfo_h
+
+#include "JSType.h"
+
+namespace JSC {
+
+ // WebCore uses MasqueradesAsUndefined to make document.all and style.filter undetectable.
+ static const unsigned MasqueradesAsUndefined = 1;
+ static const unsigned ImplementsHasInstance = 1 << 1;
+ static const unsigned OverridesHasInstance = 1 << 2;
+ static const unsigned NeedsThisConversion = 1 << 3;
+ static const unsigned HasStandardGetOwnPropertySlot = 1 << 4;
+
+ class TypeInfo {
+ friend class CTI;
+ public:
+ TypeInfo(JSType type, unsigned flags = 0) : m_type(type), m_flags(flags) { }
+
+ JSType type() const { return m_type; }
+
+ bool masqueradesAsUndefined() const { return m_flags & MasqueradesAsUndefined; }
+ bool implementsHasInstance() const { return m_flags & ImplementsHasInstance; }
+ bool overridesHasInstance() const { return m_flags & OverridesHasInstance; }
+ bool needsThisConversion() const { return m_flags & NeedsThisConversion; }
+ bool hasStandardGetOwnPropertySlot() const { return m_flags & HasStandardGetOwnPropertySlot; }
+
+ unsigned flags() const { return m_flags; }
+
+ private:
+ JSType m_type;
+ unsigned m_flags;
+ };
+
+}
+
+#endif // TypeInfo_h
diff --git a/JavaScriptCore/runtime/UString.cpp b/JavaScriptCore/runtime/UString.cpp
new file mode 100644
index 0000000..395933f
--- /dev/null
+++ b/JavaScriptCore/runtime/UString.cpp
@@ -0,0 +1,1638 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "UString.h"
+
+#include "JSGlobalObjectFunctions.h"
+#include "Collector.h"
+#include "dtoa.h"
+#include "identifier.h"
+#include "Operations.h"
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wtf/ASCIICType.h>
+#include <wtf/Assertions.h>
+#include <wtf/MathExtras.h>
+#include <wtf/Vector.h>
+#include <wtf/unicode/UTF8.h>
+
+#if HAVE(STRING_H)
+#include <string.h>
+#endif
+#if HAVE(STRINGS_H)
+#include <strings.h>
+#endif
+
+using namespace WTF;
+using namespace WTF::Unicode;
+using namespace std;
+
+// This can be tuned differently per platform by putting platform #ifs right here.
+// If you don't define this macro at all, then copyChars will just call directly
+// to memcpy.
+#define USTRING_COPY_CHARS_INLINE_CUTOFF 20
+
+namespace JSC {
+
+extern const double NaN;
+extern const double Inf;
+
+static inline size_t overflowIndicator() { return std::numeric_limits<size_t>::max(); }
+static inline size_t maxUChars() { return std::numeric_limits<size_t>::max() / sizeof(UChar); }
+
+static inline UChar* allocChars(size_t length)
+{
+ ASSERT(length);
+ if (length > maxUChars())
+ return 0;
+ return static_cast<UChar*>(tryFastMalloc(sizeof(UChar) * length));
+}
+
+static inline UChar* reallocChars(UChar* buffer, size_t length)
+{
+ ASSERT(length);
+ if (length > maxUChars())
+ return 0;
+ return static_cast<UChar*>(tryFastRealloc(buffer, sizeof(UChar) * length));
+}
+
+static inline void copyChars(UChar* destination, const UChar* source, unsigned numCharacters)
+{
+#ifdef USTRING_COPY_CHARS_INLINE_CUTOFF
+ if (numCharacters <= USTRING_COPY_CHARS_INLINE_CUTOFF) {
+ for (unsigned i = 0; i < numCharacters; ++i)
+ destination[i] = source[i];
+ return;
+ }
+#endif
+ memcpy(destination, source, numCharacters * sizeof(UChar));
+}
+
+COMPILE_ASSERT(sizeof(UChar) == 2, uchar_is_2_bytes)
+
+CString::CString(const char* c)
+ : m_length(strlen(c))
+ , m_data(new char[m_length + 1])
+{
+ memcpy(m_data, c, m_length + 1);
+}
+
+CString::CString(const char* c, size_t length)
+ : m_length(length)
+ , m_data(new char[length + 1])
+{
+ memcpy(m_data, c, m_length);
+ m_data[m_length] = 0;
+}
+
+CString::CString(const CString& b)
+{
+ m_length = b.m_length;
+ if (b.m_data) {
+ m_data = new char[m_length + 1];
+ memcpy(m_data, b.m_data, m_length + 1);
+ } else
+ m_data = 0;
+}
+
+CString::~CString()
+{
+ delete [] m_data;
+}
+
+CString CString::adopt(char* c, size_t length)
+{
+ CString s;
+ s.m_data = c;
+ s.m_length = length;
+ return s;
+}
+
+CString& CString::append(const CString& t)
+{
+ char* n;
+ n = new char[m_length + t.m_length + 1];
+ if (m_length)
+ memcpy(n, m_data, m_length);
+ if (t.m_length)
+ memcpy(n + m_length, t.m_data, t.m_length);
+ m_length += t.m_length;
+ n[m_length] = 0;
+
+ delete [] m_data;
+ m_data = n;
+
+ return *this;
+}
+
+CString& CString::operator=(const char* c)
+{
+ if (m_data)
+ delete [] m_data;
+ m_length = strlen(c);
+ m_data = new char[m_length + 1];
+ memcpy(m_data, c, m_length + 1);
+
+ return *this;
+}
+
+CString& CString::operator=(const CString& str)
+{
+ if (this == &str)
+ return *this;
+
+ if (m_data)
+ delete [] m_data;
+ m_length = str.m_length;
+ if (str.m_data) {
+ m_data = new char[m_length + 1];
+ memcpy(m_data, str.m_data, m_length + 1);
+ } else
+ m_data = 0;
+
+ return *this;
+}
+
+bool operator==(const CString& c1, const CString& c2)
+{
+ size_t len = c1.size();
+ return len == c2.size() && (len == 0 || memcmp(c1.c_str(), c2.c_str(), len) == 0);
+}
+
+// These static strings are immutable, except for rc, whose initial value is chosen to
+// reduce the possibility of it becoming zero due to ref/deref not being thread-safe.
+static UChar sharedEmptyChar;
+UString::Rep UString::Rep::null = { 0, 0, INT_MAX / 2, 0, 1, &UString::Rep::null, 0, 0, 0, 0, 0, 0 };
+UString::Rep UString::Rep::empty = { 0, 0, INT_MAX / 2, 0, 1, &UString::Rep::empty, 0, &sharedEmptyChar, 0, 0, 0, 0 };
+
+static char* statBuffer = 0; // Only used for debugging via UString::ascii().
+
+PassRefPtr<UString::Rep> UString::Rep::createCopying(const UChar* d, int l)
+{
+ UChar* copyD = static_cast<UChar*>(fastMalloc(l * sizeof(UChar)));
+ copyChars(copyD, d, l);
+ return create(copyD, l);
+}
+
+PassRefPtr<UString::Rep> UString::Rep::create(UChar* d, int l)
+{
+ Rep* r = new Rep;
+ r->offset = 0;
+ r->len = l;
+ r->rc = 1;
+ r->_hash = 0;
+ r->m_identifierTable = 0;
+ r->baseString = r;
+ r->reportedCost = 0;
+ r->buf = d;
+ r->usedCapacity = l;
+ r->capacity = l;
+ r->usedPreCapacity = 0;
+ r->preCapacity = 0;
+
+ r->checkConsistency();
+
+ // steal the single reference this Rep was created with
+ return adoptRef(r);
+}
+
+PassRefPtr<UString::Rep> UString::Rep::create(PassRefPtr<Rep> base, int offset, int length)
+{
+ ASSERT(base);
+ base->checkConsistency();
+
+ int baseOffset = base->offset;
+
+ base = base->baseString;
+
+ ASSERT(-(offset + baseOffset) <= base->usedPreCapacity);
+ ASSERT(offset + baseOffset + length <= base->usedCapacity);
+
+ Rep* r = new Rep;
+ r->offset = baseOffset + offset;
+ r->len = length;
+ r->rc = 1;
+ r->_hash = 0;
+ r->m_identifierTable = 0;
+ r->baseString = base.releaseRef();
+ r->reportedCost = 0;
+ r->buf = 0;
+ r->usedCapacity = 0;
+ r->capacity = 0;
+ r->usedPreCapacity = 0;
+ r->preCapacity = 0;
+
+ r->checkConsistency();
+
+ // steal the single reference this Rep was created with
+ return adoptRef(r);
+}
+
+PassRefPtr<UString::Rep> UString::Rep::createFromUTF8(const char* string)
+{
+ if (!string)
+ return &UString::Rep::null;
+
+ size_t length = strlen(string);
+ Vector<UChar, 1024> buffer(length);
+ UChar* p = buffer.data();
+ if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length))
+ return &UString::Rep::null;
+
+ return UString::Rep::createCopying(buffer.data(), p - buffer.data());
+}
+
+void UString::Rep::destroy()
+{
+ checkConsistency();
+
+ // Static null and empty strings can never be destroyed, but we cannot rely on
+ // reference counting, because ref/deref are not thread-safe.
+ if (!isStatic()) {
+ if (identifierTable())
+ Identifier::remove(this);
+ if (baseString == this)
+ fastFree(buf);
+ else
+ baseString->deref();
+
+ delete this;
+ }
+}
+
+// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
+// or anything like that.
+const unsigned PHI = 0x9e3779b9U;
+
+// Paul Hsieh's SuperFastHash
+// http://www.azillionmonkeys.com/qed/hash.html
+unsigned UString::Rep::computeHash(const UChar* s, int len)
+{
+ unsigned l = len;
+ uint32_t hash = PHI;
+ uint32_t tmp;
+
+ int rem = l & 1;
+ l >>= 1;
+
+ // Main loop
+ for (; l > 0; l--) {
+ hash += s[0];
+ tmp = (s[1] << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ s += 2;
+ hash += hash >> 11;
+ }
+
+ // Handle end case
+ if (rem) {
+ hash += s[0];
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ }
+
+ // Force "avalanching" of final 127 bits
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 2;
+ hash += hash >> 15;
+ hash ^= hash << 10;
+
+ // this avoids ever returning a hash code of 0, since that is used to
+ // signal "hash not computed yet", using a value that is likely to be
+ // effectively the same as 0 when the low bits are masked
+ if (hash == 0)
+ hash = 0x80000000;
+
+ return hash;
+}
+
+// Paul Hsieh's SuperFastHash
+// http://www.azillionmonkeys.com/qed/hash.html
+unsigned UString::Rep::computeHash(const char* s, int l)
+{
+ // This hash is designed to work on 16-bit chunks at a time. But since the normal case
+ // (above) is to hash UTF-16 characters, we just treat the 8-bit chars as if they
+ // were 16-bit chunks, which should give matching results
+
+ uint32_t hash = PHI;
+ uint32_t tmp;
+
+ size_t rem = l & 1;
+ l >>= 1;
+
+ // Main loop
+ for (; l > 0; l--) {
+ hash += static_cast<unsigned char>(s[0]);
+ tmp = (static_cast<unsigned char>(s[1]) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ s += 2;
+ hash += hash >> 11;
+ }
+
+ // Handle end case
+ if (rem) {
+ hash += static_cast<unsigned char>(s[0]);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ }
+
+ // Force "avalanching" of final 127 bits
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 2;
+ hash += hash >> 15;
+ hash ^= hash << 10;
+
+ // this avoids ever returning a hash code of 0, since that is used to
+ // signal "hash not computed yet", using a value that is likely to be
+ // effectively the same as 0 when the low bits are masked
+ if (hash == 0)
+ hash = 0x80000000;
+
+ return hash;
+}
+
+#ifndef NDEBUG
+void UString::Rep::checkConsistency() const
+{
+ // Only base strings have non-zero shared data.
+ if (this != baseString) {
+ ASSERT(!buf);
+ ASSERT(!usedCapacity);
+ ASSERT(!capacity);
+ ASSERT(!usedPreCapacity);
+ ASSERT(!preCapacity);
+ }
+
+ // There is no recursion for base strings.
+ ASSERT(baseString == baseString->baseString);
+
+ if (isStatic()) {
+ // There are only two static strings: null and empty.
+ ASSERT(!len);
+
+ // Static strings cannot get in identifier tables, because they are globally shared.
+ ASSERT(!identifierTable());
+ }
+
+ // The string fits in buffer.
+ ASSERT(baseString->usedPreCapacity <= baseString->preCapacity);
+ ASSERT(baseString->usedCapacity <= baseString->capacity);
+ ASSERT(-offset <= baseString->usedPreCapacity);
+ ASSERT(offset + len <= baseString->usedCapacity);
+}
+#endif
+
+// put these early so they can be inlined
+static inline size_t expandedSize(size_t size, size_t otherSize)
+{
+ // Do the size calculation in two parts, returning overflowIndicator if
+ // we overflow the maximum value that we can handle.
+
+ if (size > maxUChars())
+ return overflowIndicator();
+
+ size_t expandedSize = ((size + 10) / 10 * 11) + 1;
+ if (maxUChars() - expandedSize < otherSize)
+ return overflowIndicator();
+
+ return expandedSize + otherSize;
+}
+
+inline int UString::usedCapacity() const
+{
+ return m_rep->baseString->usedCapacity;
+}
+
+inline int UString::usedPreCapacity() const
+{
+ return m_rep->baseString->usedPreCapacity;
+}
+
+
+static inline bool expandCapacity(UString::Rep* rep, int requiredLength)
+{
+ rep->checkConsistency();
+
+ UString::Rep* r = rep->baseString;
+
+ if (requiredLength > r->capacity) {
+ size_t newCapacity = expandedSize(requiredLength, r->preCapacity);
+ UChar* oldBuf = r->buf;
+ r->buf = reallocChars(r->buf, newCapacity);
+ if (!r->buf) {
+ r->buf = oldBuf;
+ return false;
+ }
+ r->capacity = newCapacity - r->preCapacity;
+ }
+ if (requiredLength > r->usedCapacity)
+ r->usedCapacity = requiredLength;
+
+ rep->checkConsistency();
+ return true;
+}
+
+void UString::expandCapacity(int requiredLength)
+{
+ if (!JSC::expandCapacity(m_rep.get(), requiredLength))
+ makeNull();
+}
+
+void UString::expandPreCapacity(int requiredPreCap)
+{
+ m_rep->checkConsistency();
+
+ Rep* r = m_rep->baseString;
+
+ if (requiredPreCap > r->preCapacity) {
+ size_t newCapacity = expandedSize(requiredPreCap, r->capacity);
+ int delta = newCapacity - r->capacity - r->preCapacity;
+
+ UChar* newBuf = allocChars(newCapacity);
+ if (!newBuf) {
+ makeNull();
+ return;
+ }
+ copyChars(newBuf + delta, r->buf, r->capacity + r->preCapacity);
+ fastFree(r->buf);
+ r->buf = newBuf;
+
+ r->preCapacity = newCapacity - r->capacity;
+ }
+ if (requiredPreCap > r->usedPreCapacity)
+ r->usedPreCapacity = requiredPreCap;
+
+ m_rep->checkConsistency();
+}
+
+PassRefPtr<UString::Rep> createRep(const char* c)
+{
+ if (!c)
+ return &UString::Rep::null;
+
+ if (!c[0])
+ return &UString::Rep::empty;
+
+ size_t length = strlen(c);
+ UChar* d = allocChars(length);
+ if (!d)
+ return &UString::Rep::null;
+ else {
+ for (size_t i = 0; i < length; i++)
+ d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
+ return UString::Rep::create(d, static_cast<int>(length));
+ }
+
+}
+
+UString::UString(const char* c)
+ : m_rep(createRep(c))
+{
+}
+
+UString::UString(const UChar* c, int length)
+{
+ if (length == 0)
+ m_rep = &Rep::empty;
+ else
+ m_rep = Rep::createCopying(c, length);
+}
+
+UString::UString(UChar* c, int length, bool copy)
+{
+ if (length == 0)
+ m_rep = &Rep::empty;
+ else if (copy)
+ m_rep = Rep::createCopying(c, length);
+ else
+ m_rep = Rep::create(c, length);
+}
+
+UString::UString(const Vector<UChar>& buffer)
+{
+ if (!buffer.size())
+ m_rep = &Rep::empty;
+ else
+ m_rep = Rep::createCopying(buffer.data(), buffer.size());
+}
+
+static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const UChar* tData, int tSize)
+{
+ RefPtr<UString::Rep> rep = r;
+
+ rep->checkConsistency();
+
+ int thisSize = rep->size();
+ int thisOffset = rep->offset;
+ int length = thisSize + tSize;
+
+ // possible cases:
+ if (tSize == 0) {
+ // t is empty
+ } else if (thisSize == 0) {
+ // this is empty
+ rep = UString::Rep::createCopying(tData, tSize);
+ } else if (rep->baseIsSelf() && rep->rc == 1) {
+ // this is direct and has refcount of 1 (so we can just alter it directly)
+ if (!expandCapacity(rep.get(), thisOffset + length))
+ rep = &UString::Rep::null;
+ if (rep->data()) {
+ copyChars(rep->data() + thisSize, tData, tSize);
+ rep->len = length;
+ rep->_hash = 0;
+ }
+ } else if (thisOffset + thisSize == rep->baseString->usedCapacity && thisSize >= minShareSize) {
+ // this reaches the end of the buffer - extend it if it's long enough to append to
+ if (!expandCapacity(rep.get(), thisOffset + length))
+ rep = &UString::Rep::null;
+ if (rep->data()) {
+ copyChars(rep->data() + thisSize, tData, tSize);
+ rep = UString::Rep::create(rep, 0, length);
+ }
+ } else {
+ // this is shared with someone using more capacity, gotta make a whole new string
+ size_t newCapacity = expandedSize(length, 0);
+ UChar* d = allocChars(newCapacity);
+ if (!d)
+ rep = &UString::Rep::null;
+ else {
+ copyChars(d, rep->data(), thisSize);
+ copyChars(d + thisSize, tData, tSize);
+ rep = UString::Rep::create(d, length);
+ rep->capacity = newCapacity;
+ }
+ }
+
+ rep->checkConsistency();
+
+ return rep.release();
+}
+
+static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const char* t)
+{
+ RefPtr<UString::Rep> rep = r;
+
+ rep->checkConsistency();
+
+ int thisSize = rep->size();
+ int thisOffset = rep->offset;
+ int tSize = static_cast<int>(strlen(t));
+ int length = thisSize + tSize;
+
+ // possible cases:
+ if (thisSize == 0) {
+ // this is empty
+ rep = createRep(t);
+ } else if (tSize == 0) {
+ // t is empty, we'll just return *this below.
+ } else if (rep->baseIsSelf() && rep->rc == 1) {
+ // this is direct and has refcount of 1 (so we can just alter it directly)
+ expandCapacity(rep.get(), thisOffset + length);
+ UChar* d = rep->data();
+ if (d) {
+ for (int i = 0; i < tSize; ++i)
+ d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
+ rep->len = length;
+ rep->_hash = 0;
+ }
+ } else if (thisOffset + thisSize == rep->baseString->usedCapacity && thisSize >= minShareSize) {
+ // this string reaches the end of the buffer - extend it
+ expandCapacity(rep.get(), thisOffset + length);
+ UChar* d = rep->data();
+ if (d) {
+ for (int i = 0; i < tSize; ++i)
+ d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
+ rep = UString::Rep::create(rep, 0, length);
+ }
+ } else {
+ // this is shared with someone using more capacity, gotta make a whole new string
+ size_t newCapacity = expandedSize(length, 0);
+ UChar* d = allocChars(newCapacity);
+ if (!d)
+ rep = &UString::Rep::null;
+ else {
+ copyChars(d, rep->data(), thisSize);
+ for (int i = 0; i < tSize; ++i)
+ d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
+ rep = UString::Rep::create(d, length);
+ rep->capacity = newCapacity;
+ }
+ }
+
+ rep->checkConsistency();
+
+ return rep.release();
+}
+
+PassRefPtr<UString::Rep> concatenate(UString::Rep* a, UString::Rep* b)
+{
+ a->checkConsistency();
+ b->checkConsistency();
+
+ int aSize = a->size();
+ int aOffset = a->offset;
+ int bSize = b->size();
+ int bOffset = b->offset;
+ int length = aSize + bSize;
+
+ // possible cases:
+
+ // a is empty
+ if (aSize == 0)
+ return b;
+ // b is empty
+ if (bSize == 0)
+ return a;
+
+ if (bSize == 1 && aOffset + aSize == a->baseString->usedCapacity && aOffset + length <= a->baseString->capacity) {
+ // b is a single character (common fast case)
+ a->baseString->usedCapacity = aOffset + length;
+ a->data()[aSize] = b->data()[0];
+ return UString::Rep::create(a, 0, length);
+ }
+
+ if (aOffset + aSize == a->baseString->usedCapacity && aSize >= minShareSize && 4 * aSize >= bSize &&
+ (-bOffset != b->baseString->usedPreCapacity || aSize >= bSize)) {
+ // - a reaches the end of its buffer so it qualifies for shared append
+ // - also, it's at least a quarter the length of b - appending to a much shorter
+ // string does more harm than good
+ // - however, if b qualifies for prepend and is longer than a, we'd rather prepend
+ UString x(a);
+ x.expandCapacity(aOffset + length);
+ if (!a->data() || !x.data())
+ return 0;
+ copyChars(a->data() + aSize, b->data(), bSize);
+ PassRefPtr<UString::Rep> result = UString::Rep::create(a, 0, length);
+
+ a->checkConsistency();
+ b->checkConsistency();
+ result->checkConsistency();
+
+ return result;
+ }
+
+ if (-bOffset == b->baseString->usedPreCapacity && bSize >= minShareSize && 4 * bSize >= aSize) {
+ // - b reaches the beginning of its buffer so it qualifies for shared prepend
+ // - also, it's at least a quarter the length of a - prepending to a much shorter
+ // string does more harm than good
+ UString y(b);
+ y.expandPreCapacity(-bOffset + aSize);
+ if (!b->data() || !y.data())
+ return 0;
+ copyChars(b->data() - aSize, a->data(), aSize);
+ PassRefPtr<UString::Rep> result = UString::Rep::create(b, -aSize, length);
+
+ a->checkConsistency();
+ b->checkConsistency();
+ result->checkConsistency();
+
+ return result;
+ }
+
+ // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string
+ size_t newCapacity = expandedSize(length, 0);
+ UChar* d = allocChars(newCapacity);
+ if (!d)
+ return 0;
+ copyChars(d, a->data(), aSize);
+ copyChars(d + aSize, b->data(), bSize);
+ PassRefPtr<UString::Rep> result = UString::Rep::create(d, length);
+ result->capacity = newCapacity;
+
+ a->checkConsistency();
+ b->checkConsistency();
+ result->checkConsistency();
+
+ return result;
+}
+
+PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, int i)
+{
+ UChar buf[1 + sizeof(i) * 3];
+ UChar* end = buf + sizeof(buf) / sizeof(UChar);
+ UChar* p = end;
+
+ if (i == 0)
+ *--p = '0';
+ else if (i == INT_MIN) {
+ char minBuf[1 + sizeof(i) * 3];
+ sprintf(minBuf, "%d", INT_MIN);
+ return concatenate(rep, minBuf);
+ } else {
+ bool negative = false;
+ if (i < 0) {
+ negative = true;
+ i = -i;
+ }
+ while (i) {
+ *--p = static_cast<unsigned short>((i % 10) + '0');
+ i /= 10;
+ }
+ if (negative)
+ *--p = '-';
+ }
+
+ return concatenate(rep, p, static_cast<int>(end - p));
+
+}
+
+PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, double d)
+{
+ // avoid ever printing -NaN, in JS conceptually there is only one NaN value
+ if (isnan(d))
+ return concatenate(rep, "NaN");
+
+ if (d == 0.0) // stringify -0 as 0
+ d = 0.0;
+
+ char buf[80];
+ int decimalPoint;
+ int sign;
+
+ char* result = dtoa(d, 0, &decimalPoint, &sign, NULL);
+ int length = static_cast<int>(strlen(result));
+
+ int i = 0;
+ if (sign)
+ buf[i++] = '-';
+
+ if (decimalPoint <= 0 && decimalPoint > -6) {
+ buf[i++] = '0';
+ buf[i++] = '.';
+ for (int j = decimalPoint; j < 0; j++)
+ buf[i++] = '0';
+ strcpy(buf + i, result);
+ } else if (decimalPoint <= 21 && decimalPoint > 0) {
+ if (length <= decimalPoint) {
+ strcpy(buf + i, result);
+ i += length;
+ for (int j = 0; j < decimalPoint - length; j++)
+ buf[i++] = '0';
+ buf[i] = '\0';
+ } else {
+ strncpy(buf + i, result, decimalPoint);
+ i += decimalPoint;
+ buf[i++] = '.';
+ strcpy(buf + i, result + decimalPoint);
+ }
+ } else if (result[0] < '0' || result[0] > '9')
+ strcpy(buf + i, result);
+ else {
+ buf[i++] = result[0];
+ if (length > 1) {
+ buf[i++] = '.';
+ strcpy(buf + i, result + 1);
+ i += length - 1;
+ }
+
+ buf[i++] = 'e';
+ buf[i++] = (decimalPoint >= 0) ? '+' : '-';
+ // decimalPoint can't be more than 3 digits decimal given the
+ // nature of float representation
+ int exponential = decimalPoint - 1;
+ if (exponential < 0)
+ exponential = -exponential;
+ if (exponential >= 100)
+ buf[i++] = static_cast<char>('0' + exponential / 100);
+ if (exponential >= 10)
+ buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
+ buf[i++] = static_cast<char>('0' + exponential % 10);
+ buf[i++] = '\0';
+ }
+
+ freedtoa(result);
+
+ return concatenate(rep, buf);
+}
+
+const UString& UString::null()
+{
+ static UString* n = new UString; // Should be called from main thread at least once to be safely initialized.
+ return *n;
+}
+
+UString UString::from(int i)
+{
+ UChar buf[1 + sizeof(i) * 3];
+ UChar* end = buf + sizeof(buf) / sizeof(UChar);
+ UChar* p = end;
+
+ if (i == 0)
+ *--p = '0';
+ else if (i == INT_MIN) {
+ char minBuf[1 + sizeof(i) * 3];
+ sprintf(minBuf, "%d", INT_MIN);
+ return UString(minBuf);
+ } else {
+ bool negative = false;
+ if (i < 0) {
+ negative = true;
+ i = -i;
+ }
+ while (i) {
+ *--p = static_cast<unsigned short>((i % 10) + '0');
+ i /= 10;
+ }
+ if (negative)
+ *--p = '-';
+ }
+
+ return UString(p, static_cast<int>(end - p));
+}
+
+UString UString::from(unsigned int u)
+{
+ UChar buf[sizeof(u) * 3];
+ UChar* end = buf + sizeof(buf) / sizeof(UChar);
+ UChar* p = end;
+
+ if (u == 0)
+ *--p = '0';
+ else {
+ while (u) {
+ *--p = static_cast<unsigned short>((u % 10) + '0');
+ u /= 10;
+ }
+ }
+
+ return UString(p, static_cast<int>(end - p));
+}
+
+UString UString::from(long l)
+{
+ UChar buf[1 + sizeof(l) * 3];
+ UChar* end = buf + sizeof(buf) / sizeof(UChar);
+ UChar* p = end;
+
+ if (l == 0)
+ *--p = '0';
+ else if (l == LONG_MIN) {
+ char minBuf[1 + sizeof(l) * 3];
+ sprintf(minBuf, "%ld", LONG_MIN);
+ return UString(minBuf);
+ } else {
+ bool negative = false;
+ if (l < 0) {
+ negative = true;
+ l = -l;
+ }
+ while (l) {
+ *--p = static_cast<unsigned short>((l % 10) + '0');
+ l /= 10;
+ }
+ if (negative)
+ *--p = '-';
+ }
+
+ return UString(p, static_cast<int>(end - p));
+}
+
+UString UString::from(double d)
+{
+ // avoid ever printing -NaN, in JS conceptually there is only one NaN value
+ if (isnan(d))
+ return "NaN";
+
+ char buf[80];
+ int decimalPoint;
+ int sign;
+
+ char* result = dtoa(d, 0, &decimalPoint, &sign, NULL);
+ int length = static_cast<int>(strlen(result));
+
+ int i = 0;
+ if (sign)
+ buf[i++] = '-';
+
+ if (decimalPoint <= 0 && decimalPoint > -6) {
+ buf[i++] = '0';
+ buf[i++] = '.';
+ for (int j = decimalPoint; j < 0; j++)
+ buf[i++] = '0';
+ strcpy(buf + i, result);
+ } else if (decimalPoint <= 21 && decimalPoint > 0) {
+ if (length <= decimalPoint) {
+ strcpy(buf + i, result);
+ i += length;
+ for (int j = 0; j < decimalPoint - length; j++)
+ buf[i++] = '0';
+ buf[i] = '\0';
+ } else {
+ strncpy(buf + i, result, decimalPoint);
+ i += decimalPoint;
+ buf[i++] = '.';
+ strcpy(buf + i, result + decimalPoint);
+ }
+ } else if (result[0] < '0' || result[0] > '9')
+ strcpy(buf + i, result);
+ else {
+ buf[i++] = result[0];
+ if (length > 1) {
+ buf[i++] = '.';
+ strcpy(buf + i, result + 1);
+ i += length - 1;
+ }
+
+ buf[i++] = 'e';
+ buf[i++] = (decimalPoint >= 0) ? '+' : '-';
+ // decimalPoint can't be more than 3 digits decimal given the
+ // nature of float representation
+ int exponential = decimalPoint - 1;
+ if (exponential < 0)
+ exponential = -exponential;
+ if (exponential >= 100)
+ buf[i++] = static_cast<char>('0' + exponential / 100);
+ if (exponential >= 10)
+ buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
+ buf[i++] = static_cast<char>('0' + exponential % 10);
+ buf[i++] = '\0';
+ }
+
+ freedtoa(result);
+
+ return UString(buf);
+}
+
+UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const
+{
+ m_rep->checkConsistency();
+
+ if (rangeCount == 1 && separatorCount == 0) {
+ int thisSize = size();
+ int position = substringRanges[0].position;
+ int length = substringRanges[0].length;
+ if (position <= 0 && length >= thisSize)
+ return *this;
+ return UString::Rep::create(m_rep, max(0, position), min(thisSize, length));
+ }
+
+ int totalLength = 0;
+ for (int i = 0; i < rangeCount; i++)
+ totalLength += substringRanges[i].length;
+ for (int i = 0; i < separatorCount; i++)
+ totalLength += separators[i].size();
+
+ if (totalLength == 0)
+ return "";
+
+ UChar* buffer = allocChars(totalLength);
+ if (!buffer)
+ return null();
+
+ int maxCount = max(rangeCount, separatorCount);
+ int bufferPos = 0;
+ for (int i = 0; i < maxCount; i++) {
+ if (i < rangeCount) {
+ copyChars(buffer + bufferPos, data() + substringRanges[i].position, substringRanges[i].length);
+ bufferPos += substringRanges[i].length;
+ }
+ if (i < separatorCount) {
+ copyChars(buffer + bufferPos, separators[i].data(), separators[i].size());
+ bufferPos += separators[i].size();
+ }
+ }
+
+ return UString::Rep::create(buffer, totalLength);
+}
+
+UString& UString::append(const UString &t)
+{
+ m_rep->checkConsistency();
+ t.rep()->checkConsistency();
+
+ int thisSize = size();
+ int thisOffset = m_rep->offset;
+ int tSize = t.size();
+ int length = thisSize + tSize;
+
+ // possible cases:
+ if (thisSize == 0) {
+ // this is empty
+ *this = t;
+ } else if (tSize == 0) {
+ // t is empty
+ } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
+ // this is direct and has refcount of 1 (so we can just alter it directly)
+ expandCapacity(thisOffset + length);
+ if (data()) {
+ copyChars(m_rep->data() + thisSize, t.data(), tSize);
+ m_rep->len = length;
+ m_rep->_hash = 0;
+ }
+ } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) {
+ // this reaches the end of the buffer - extend it if it's long enough to append to
+ expandCapacity(thisOffset + length);
+ if (data()) {
+ copyChars(m_rep->data() + thisSize, t.data(), tSize);
+ m_rep = Rep::create(m_rep, 0, length);
+ }
+ } else {
+ // this is shared with someone using more capacity, gotta make a whole new string
+ size_t newCapacity = expandedSize(length, 0);
+ UChar* d = allocChars(newCapacity);
+ if (!d)
+ makeNull();
+ else {
+ copyChars(d, data(), thisSize);
+ copyChars(d + thisSize, t.data(), tSize);
+ m_rep = Rep::create(d, length);
+ m_rep->capacity = newCapacity;
+ }
+ }
+
+ m_rep->checkConsistency();
+ t.rep()->checkConsistency();
+
+ return *this;
+}
+
+UString& UString::append(const UChar* tData, int tSize)
+{
+ m_rep = concatenate(m_rep.release(), tData, tSize);
+ return *this;
+}
+
+UString& UString::append(const char* t)
+{
+ m_rep = concatenate(m_rep.release(), t);
+ return *this;
+}
+
+UString& UString::append(UChar c)
+{
+ m_rep->checkConsistency();
+
+ int thisOffset = m_rep->offset;
+ int length = size();
+
+ // possible cases:
+ if (length == 0) {
+ // this is empty - must make a new m_rep because we don't want to pollute the shared empty one
+ size_t newCapacity = expandedSize(1, 0);
+ UChar* d = allocChars(newCapacity);
+ if (!d)
+ makeNull();
+ else {
+ d[0] = c;
+ m_rep = Rep::create(d, 1);
+ m_rep->capacity = newCapacity;
+ }
+ } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
+ // this is direct and has refcount of 1 (so we can just alter it directly)
+ expandCapacity(thisOffset + length + 1);
+ UChar* d = m_rep->data();
+ if (d) {
+ d[length] = c;
+ m_rep->len = length + 1;
+ m_rep->_hash = 0;
+ }
+ } else if (thisOffset + length == usedCapacity() && length >= minShareSize) {
+ // this reaches the end of the string - extend it and share
+ expandCapacity(thisOffset + length + 1);
+ UChar* d = m_rep->data();
+ if (d) {
+ d[length] = c;
+ m_rep = Rep::create(m_rep, 0, length + 1);
+ }
+ } else {
+ // this is shared with someone using more capacity, gotta make a whole new string
+ size_t newCapacity = expandedSize(length + 1, 0);
+ UChar* d = allocChars(newCapacity);
+ if (!d)
+ makeNull();
+ else {
+ copyChars(d, data(), length);
+ d[length] = c;
+ m_rep = Rep::create(d, length + 1);
+ m_rep->capacity = newCapacity;
+ }
+ }
+
+ m_rep->checkConsistency();
+
+ return *this;
+}
+
+bool UString::getCString(CStringBuffer& buffer) const
+{
+ int length = size();
+ int neededSize = length + 1;
+ buffer.resize(neededSize);
+ char* buf = buffer.data();
+
+ UChar ored = 0;
+ const UChar* p = data();
+ char* q = buf;
+ const UChar* limit = p + length;
+ while (p != limit) {
+ UChar c = p[0];
+ ored |= c;
+ *q = static_cast<char>(c);
+ ++p;
+ ++q;
+ }
+ *q = '\0';
+
+ return !(ored & 0xFF00);
+}
+
+char* UString::ascii() const
+{
+ int length = size();
+ int neededSize = length + 1;
+ delete[] statBuffer;
+ statBuffer = new char[neededSize];
+
+ const UChar* p = data();
+ char* q = statBuffer;
+ const UChar* limit = p + length;
+ while (p != limit) {
+ *q = static_cast<char>(p[0]);
+ ++p;
+ ++q;
+ }
+ *q = '\0';
+
+ return statBuffer;
+}
+
+UString& UString::operator=(const char* c)
+{
+ if (!c) {
+ m_rep = &Rep::null;
+ return *this;
+ }
+
+ if (!c[0]) {
+ m_rep = &Rep::empty;
+ return *this;
+ }
+
+ int l = static_cast<int>(strlen(c));
+ UChar* d;
+ if (m_rep->rc == 1 && l <= m_rep->capacity && m_rep->baseIsSelf() && m_rep->offset == 0 && m_rep->preCapacity == 0) {
+ d = m_rep->buf;
+ m_rep->_hash = 0;
+ m_rep->len = l;
+ } else {
+ d = allocChars(l);
+ if (!d) {
+ makeNull();
+ return *this;
+ }
+ m_rep = Rep::create(d, l);
+ }
+ for (int i = 0; i < l; i++)
+ d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
+
+ return *this;
+}
+
+bool UString::is8Bit() const
+{
+ const UChar* u = data();
+ const UChar* limit = u + size();
+ while (u < limit) {
+ if (u[0] > 0xFF)
+ return false;
+ ++u;
+ }
+
+ return true;
+}
+
+UChar UString::operator[](int pos) const
+{
+ if (pos >= size())
+ return '\0';
+ return data()[pos];
+}
+
+double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const
+{
+ if (size() == 1) {
+ UChar c = data()[0];
+ if (isASCIIDigit(c))
+ return c - '0';
+ if (isASCIISpace(c) && tolerateEmptyString)
+ return 0;
+ return NaN;
+ }
+
+ // FIXME: If tolerateTrailingJunk is true, then we want to tolerate non-8-bit junk
+ // after the number, so this is too strict a check.
+ CStringBuffer s;
+ if (!getCString(s))
+ return NaN;
+ const char* c = s.data();
+
+ // skip leading white space
+ while (isASCIISpace(*c))
+ c++;
+
+ // empty string ?
+ if (*c == '\0')
+ return tolerateEmptyString ? 0.0 : NaN;
+
+ double d;
+
+ // hex number ?
+ if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X')) {
+ const char* firstDigitPosition = c + 2;
+ c++;
+ d = 0.0;
+ while (*(++c)) {
+ if (*c >= '0' && *c <= '9')
+ d = d * 16.0 + *c - '0';
+ else if ((*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f'))
+ d = d * 16.0 + (*c & 0xdf) - 'A' + 10.0;
+ else
+ break;
+ }
+
+ if (d >= mantissaOverflowLowerBound)
+ d = parseIntOverflow(firstDigitPosition, c - firstDigitPosition, 16);
+ } else {
+ // regular number ?
+ char* end;
+ d = strtod(c, &end);
+ if ((d != 0.0 || end != c) && d != Inf && d != -Inf) {
+ c = end;
+ } else {
+ double sign = 1.0;
+
+ if (*c == '+')
+ c++;
+ else if (*c == '-') {
+ sign = -1.0;
+ c++;
+ }
+
+ // We used strtod() to do the conversion. However, strtod() handles
+ // infinite values slightly differently than JavaScript in that it
+ // converts the string "inf" with any capitalization to infinity,
+ // whereas the ECMA spec requires that it be converted to NaN.
+
+ if (c[0] == 'I' && c[1] == 'n' && c[2] == 'f' && c[3] == 'i' && c[4] == 'n' && c[5] == 'i' && c[6] == 't' && c[7] == 'y') {
+ d = sign * Inf;
+ c += 8;
+ } else if ((d == Inf || d == -Inf) && *c != 'I' && *c != 'i')
+ c = end;
+ else
+ return NaN;
+ }
+ }
+
+ // allow trailing white space
+ while (isASCIISpace(*c))
+ c++;
+ // don't allow anything after - unless tolerant=true
+ if (!tolerateTrailingJunk && *c != '\0')
+ d = NaN;
+
+ return d;
+}
+
+double UString::toDouble(bool tolerateTrailingJunk) const
+{
+ return toDouble(tolerateTrailingJunk, true);
+}
+
+double UString::toDouble() const
+{
+ return toDouble(false, true);
+}
+
+uint32_t UString::toUInt32(bool* ok) const
+{
+ double d = toDouble();
+ bool b = true;
+
+ if (d != static_cast<uint32_t>(d)) {
+ b = false;
+ d = 0;
+ }
+
+ if (ok)
+ *ok = b;
+
+ return static_cast<uint32_t>(d);
+}
+
+uint32_t UString::toUInt32(bool* ok, bool tolerateEmptyString) const
+{
+ double d = toDouble(false, tolerateEmptyString);
+ bool b = true;
+
+ if (d != static_cast<uint32_t>(d)) {
+ b = false;
+ d = 0;
+ }
+
+ if (ok)
+ *ok = b;
+
+ return static_cast<uint32_t>(d);
+}
+
+uint32_t UString::toStrictUInt32(bool* ok) const
+{
+ if (ok)
+ *ok = false;
+
+ // Empty string is not OK.
+ int len = m_rep->len;
+ if (len == 0)
+ return 0;
+ const UChar* p = m_rep->data();
+ unsigned short c = p[0];
+
+ // If the first digit is 0, only 0 itself is OK.
+ if (c == '0') {
+ if (len == 1 && ok)
+ *ok = true;
+ return 0;
+ }
+
+ // Convert to UInt32, checking for overflow.
+ uint32_t i = 0;
+ while (1) {
+ // Process character, turning it into a digit.
+ if (c < '0' || c > '9')
+ return 0;
+ const unsigned d = c - '0';
+
+ // Multiply by 10, checking for overflow out of 32 bits.
+ if (i > 0xFFFFFFFFU / 10)
+ return 0;
+ i *= 10;
+
+ // Add in the digit, checking for overflow out of 32 bits.
+ const unsigned max = 0xFFFFFFFFU - d;
+ if (i > max)
+ return 0;
+ i += d;
+
+ // Handle end of string.
+ if (--len == 0) {
+ if (ok)
+ *ok = true;
+ return i;
+ }
+
+ // Get next character.
+ c = *(++p);
+ }
+}
+
+int UString::find(const UString& f, int pos) const
+{
+ int sz = size();
+ int fsz = f.size();
+ if (sz < fsz)
+ return -1;
+ if (pos < 0)
+ pos = 0;
+ if (fsz == 0)
+ return pos;
+ const UChar* end = data() + sz - fsz;
+ int fsizeminusone = (fsz - 1) * sizeof(UChar);
+ const UChar* fdata = f.data();
+ unsigned short fchar = fdata[0];
+ ++fdata;
+ for (const UChar* c = data() + pos; c <= end; c++) {
+ if (c[0] == fchar && !memcmp(c + 1, fdata, fsizeminusone))
+ return static_cast<int>(c - data());
+ }
+
+ return -1;
+}
+
+int UString::find(UChar ch, int pos) const
+{
+ if (pos < 0)
+ pos = 0;
+ const UChar* end = data() + size();
+ for (const UChar* c = data() + pos; c < end; c++) {
+ if (*c == ch)
+ return static_cast<int>(c - data());
+ }
+
+ return -1;
+}
+
+int UString::rfind(const UString& f, int pos) const
+{
+ int sz = size();
+ int fsz = f.size();
+ if (sz < fsz)
+ return -1;
+ if (pos < 0)
+ pos = 0;
+ if (pos > sz - fsz)
+ pos = sz - fsz;
+ if (fsz == 0)
+ return pos;
+ int fsizeminusone = (fsz - 1) * sizeof(UChar);
+ const UChar* fdata = f.data();
+ for (const UChar* c = data() + pos; c >= data(); c--) {
+ if (*c == *fdata && !memcmp(c + 1, fdata + 1, fsizeminusone))
+ return static_cast<int>(c - data());
+ }
+
+ return -1;
+}
+
+int UString::rfind(UChar ch, int pos) const
+{
+ if (isEmpty())
+ return -1;
+ if (pos + 1 >= size())
+ pos = size() - 1;
+ for (const UChar* c = data() + pos; c >= data(); c--) {
+ if (*c == ch)
+ return static_cast<int>(c - data());
+ }
+
+ return -1;
+}
+
+UString UString::substr(int pos, int len) const
+{
+ int s = size();
+
+ if (pos < 0)
+ pos = 0;
+ else if (pos >= s)
+ pos = s;
+ if (len < 0)
+ len = s;
+ if (pos + len >= s)
+ len = s - pos;
+
+ if (pos == 0 && len == s)
+ return *this;
+
+ return UString(Rep::create(m_rep, pos, len));
+}
+
+bool operator==(const UString& s1, const UString& s2)
+{
+ int size = s1.size();
+ switch (size) {
+ case 0:
+ return !s2.size();
+ case 1:
+ return s2.size() == 1 && s1.data()[0] == s2.data()[0];
+ default:
+ return s2.size() == size && memcmp(s1.data(), s2.data(), size * sizeof(UChar)) == 0;
+ }
+}
+
+bool operator==(const UString& s1, const char *s2)
+{
+ if (s2 == 0)
+ return s1.isEmpty();
+
+ const UChar* u = s1.data();
+ const UChar* uend = u + s1.size();
+ while (u != uend && *s2) {
+ if (u[0] != (unsigned char)*s2)
+ return false;
+ s2++;
+ u++;
+ }
+
+ return u == uend && *s2 == 0;
+}
+
+bool operator<(const UString& s1, const UString& s2)
+{
+ const int l1 = s1.size();
+ const int l2 = s2.size();
+ const int lmin = l1 < l2 ? l1 : l2;
+ const UChar* c1 = s1.data();
+ const UChar* c2 = s2.data();
+ int l = 0;
+ while (l < lmin && *c1 == *c2) {
+ c1++;
+ c2++;
+ l++;
+ }
+ if (l < lmin)
+ return (c1[0] < c2[0]);
+
+ return (l1 < l2);
+}
+
+bool operator>(const UString& s1, const UString& s2)
+{
+ const int l1 = s1.size();
+ const int l2 = s2.size();
+ const int lmin = l1 < l2 ? l1 : l2;
+ const UChar* c1 = s1.data();
+ const UChar* c2 = s2.data();
+ int l = 0;
+ while (l < lmin && *c1 == *c2) {
+ c1++;
+ c2++;
+ l++;
+ }
+ if (l < lmin)
+ return (c1[0] > c2[0]);
+
+ return (l1 > l2);
+}
+
+int compare(const UString& s1, const UString& s2)
+{
+ const int l1 = s1.size();
+ const int l2 = s2.size();
+ const int lmin = l1 < l2 ? l1 : l2;
+ const UChar* c1 = s1.data();
+ const UChar* c2 = s2.data();
+ int l = 0;
+ while (l < lmin && *c1 == *c2) {
+ c1++;
+ c2++;
+ l++;
+ }
+
+ if (l < lmin)
+ return (c1[0] > c2[0]) ? 1 : -1;
+
+ if (l1 == l2)
+ return 0;
+
+ return (l1 > l2) ? 1 : -1;
+}
+
+bool equal(const UString::Rep* r, const UString::Rep* b)
+{
+ int length = r->len;
+ if (length != b->len)
+ return false;
+ const UChar* d = r->data();
+ const UChar* s = b->data();
+ for (int i = 0; i != length; ++i) {
+ if (d[i] != s[i])
+ return false;
+ }
+ return true;
+}
+
+CString UString::UTF8String(bool strict) const
+{
+ // Allocate a buffer big enough to hold all the characters.
+ const int length = size();
+ Vector<char, 1024> buffer(length * 3);
+
+ // Convert to runs of 8-bit characters.
+ char* p = buffer.data();
+ const UChar* d = reinterpret_cast<const UChar*>(&data()[0]);
+ ConversionResult result = convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), strict);
+ if (result != conversionOK)
+ return CString();
+
+ return CString(buffer.data(), p - buffer.data());
+}
+
+// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X.
+NEVER_INLINE void UString::makeNull()
+{
+ m_rep = &Rep::null;
+}
+
+// For use in error handling code paths -- having this not be inlined helps avoid PIC branches to fetch the global on Mac OS X.
+NEVER_INLINE UString::Rep* UString::nullRep()
+{
+ return &Rep::null;
+}
+
+} // namespace JSC
diff --git a/JavaScriptCore/runtime/UString.h b/JavaScriptCore/runtime/UString.h
new file mode 100644
index 0000000..b964849
--- /dev/null
+++ b/JavaScriptCore/runtime/UString.h
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _KJS_USTRING_H_
+#define _KJS_USTRING_H_
+
+#include "Collector.h"
+#include <stdint.h>
+#include <string.h>
+#include <wtf/Assertions.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/unicode/Unicode.h>
+
+namespace JSC {
+
+ using WTF::PlacementNewAdoptType;
+ using WTF::PlacementNewAdopt;
+
+ class IdentifierTable;
+
+ class CString {
+ public:
+ CString()
+ : m_length(0)
+ , m_data(0)
+ {
+ }
+
+ CString(const char*);
+ CString(const char*, size_t);
+ CString(const CString&);
+
+ ~CString();
+
+ static CString adopt(char*, size_t); // buffer should be allocated with new[].
+
+ CString& append(const CString&);
+ CString& operator=(const char* c);
+ CString& operator=(const CString&);
+ CString& operator+=(const CString& c) { return append(c); }
+
+ size_t size() const { return m_length; }
+ const char* c_str() const { return m_data; }
+
+ private:
+ size_t m_length;
+ char* m_data;
+ };
+
+ typedef Vector<char, 32> CStringBuffer;
+
+ class UString {
+ friend class CTI;
+
+ public:
+ struct Rep {
+ friend class CTI;
+
+ static PassRefPtr<Rep> create(UChar*, int);
+ static PassRefPtr<Rep> createCopying(const UChar*, int);
+ static PassRefPtr<Rep> create(PassRefPtr<Rep> base, int offset, int length);
+
+ // Constructs a string from a UTF-8 string, using strict conversion (see comments in UTF8.h).
+ // Returns UString::Rep::null for null input or conversion failure.
+ static PassRefPtr<Rep> createFromUTF8(const char*);
+
+ void destroy();
+
+ bool baseIsSelf() const { return baseString == this; }
+ UChar* data() const { return baseString->buf + baseString->preCapacity + offset; }
+ int size() const { return len; }
+
+ unsigned hash() const { if (_hash == 0) _hash = computeHash(data(), len); return _hash; }
+ unsigned computedHash() const { ASSERT(_hash); return _hash; } // fast path for Identifiers
+
+ static unsigned computeHash(const UChar*, int length);
+ static unsigned computeHash(const char*, int length);
+ static unsigned computeHash(const char* s) { return computeHash(s, strlen(s)); }
+
+ IdentifierTable* identifierTable() const { return reinterpret_cast<IdentifierTable*>(m_identifierTable & ~static_cast<uintptr_t>(1)); }
+ void setIdentifierTable(IdentifierTable* table) { ASSERT(!isStatic()); m_identifierTable = reinterpret_cast<intptr_t>(table); }
+
+ bool isStatic() const { return m_identifierTable & 1; }
+ void setStatic(bool v) { ASSERT(!identifierTable()); m_identifierTable = v; }
+
+ Rep* ref() { ++rc; return this; }
+ ALWAYS_INLINE void deref() { if (--rc == 0) destroy(); }
+
+ void checkConsistency() const;
+
+ // unshared data
+ int offset;
+ int len;
+ int rc; // For null and empty static strings, this field does not reflect a correct count, because ref/deref are not thread-safe. A special case in destroy() guarantees that these do not get deleted.
+ mutable unsigned _hash;
+ intptr_t m_identifierTable; // A pointer to identifier table. The lowest bit is used to indicate whether the string is static (null or empty).
+ UString::Rep* baseString;
+ size_t reportedCost;
+
+ // potentially shared data. 0 if backed up by a base string.
+ UChar* buf;
+ int usedCapacity;
+ int capacity;
+ int usedPreCapacity;
+ int preCapacity;
+
+ static Rep null;
+ static Rep empty;
+ };
+
+ public:
+ UString();
+ UString(const char*);
+ UString(const UChar*, int length);
+ UString(UChar*, int length, bool copy);
+
+ UString(const UString& s)
+ : m_rep(s.m_rep)
+ {
+ }
+
+ UString(const Vector<UChar>& buffer);
+
+ ~UString()
+ {
+ }
+
+ // Special constructor for cases where we overwrite an object in place.
+ UString(PlacementNewAdoptType)
+ : m_rep(PlacementNewAdopt)
+ {
+ }
+
+ static UString from(int);
+ static UString from(unsigned int);
+ static UString from(long);
+ static UString from(double);
+
+ struct Range {
+ public:
+ Range(int pos, int len)
+ : position(pos)
+ , length(len)
+ {
+ }
+
+ Range()
+ {
+ }
+
+ int position;
+ int length;
+ };
+
+ UString spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const;
+
+ UString& append(const UString&);
+ UString& append(const char*);
+ UString& append(UChar);
+ UString& append(char c) { return append(static_cast<UChar>(static_cast<unsigned char>(c))); }
+ UString& append(const UChar*, int size);
+
+ bool getCString(CStringBuffer&) const;
+
+ // NOTE: This method should only be used for *debugging* purposes as it
+ // is neither Unicode safe nor free from side effects nor thread-safe.
+ char* ascii() const;
+
+ /**
+ * Convert the string to UTF-8, assuming it is UTF-16 encoded.
+ * In non-strict mode, this function is tolerant of badly formed UTF-16, it
+ * can create UTF-8 strings that are invalid because they have characters in
+ * the range U+D800-U+DDFF, U+FFFE, or U+FFFF, but the UTF-8 string is
+ * guaranteed to be otherwise valid.
+ * In strict mode, error is returned as null CString.
+ */
+ CString UTF8String(bool strict = false) const;
+
+ UString& operator=(const char*c);
+
+ UString& operator+=(const UString& s) { return append(s); }
+ UString& operator+=(const char* s) { return append(s); }
+
+ const UChar* data() const { return m_rep->data(); }
+
+ bool isNull() const { return (m_rep == &Rep::null); }
+ bool isEmpty() const { return (!m_rep->len); }
+
+ bool is8Bit() const;
+
+ int size() const { return m_rep->size(); }
+
+ UChar operator[](int pos) const;
+
+ double toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const;
+ double toDouble(bool tolerateTrailingJunk) const;
+ double toDouble() const;
+
+ uint32_t toUInt32(bool* ok = 0) const;
+ uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const;
+ uint32_t toStrictUInt32(bool* ok = 0) const;
+
+ unsigned toArrayIndex(bool* ok = 0) const;
+
+ int find(const UString& f, int pos = 0) const;
+ int find(UChar, int pos = 0) const;
+ int rfind(const UString& f, int pos) const;
+ int rfind(UChar, int pos) const;
+
+ UString substr(int pos = 0, int len = -1) const;
+
+ static const UString& null();
+
+ Rep* rep() const { return m_rep.get(); }
+ static Rep* nullRep();
+
+ UString(PassRefPtr<Rep> r)
+ : m_rep(r)
+ {
+ ASSERT(m_rep);
+ }
+
+ size_t cost() const;
+
+ private:
+ int usedCapacity() const;
+ int usedPreCapacity() const;
+ void expandCapacity(int requiredLength);
+ void expandPreCapacity(int requiredPreCap);
+ void makeNull();
+
+ RefPtr<Rep> m_rep;
+
+ friend bool operator==(const UString&, const UString&);
+ friend PassRefPtr<Rep> concatenate(Rep*, Rep*); // returns 0 if out of memory
+ };
+ PassRefPtr<UString::Rep> concatenate(UString::Rep*, UString::Rep*);
+ PassRefPtr<UString::Rep> concatenate(UString::Rep*, int);
+ PassRefPtr<UString::Rep> concatenate(UString::Rep*, double);
+
+ bool operator==(const UString&, const UString&);
+
+ inline bool operator!=(const UString& s1, const UString& s2)
+ {
+ return !JSC::operator==(s1, s2);
+ }
+
+ bool operator<(const UString& s1, const UString& s2);
+ bool operator>(const UString& s1, const UString& s2);
+
+ bool operator==(const UString& s1, const char* s2);
+
+ inline bool operator!=(const UString& s1, const char* s2)
+ {
+ return !JSC::operator==(s1, s2);
+ }
+
+ inline bool operator==(const char *s1, const UString& s2)
+ {
+ return operator==(s2, s1);
+ }
+
+ inline bool operator!=(const char *s1, const UString& s2)
+ {
+ return !JSC::operator==(s1, s2);
+ }
+
+ bool operator==(const CString&, const CString&);
+
+ inline UString operator+(const UString& s1, const UString& s2)
+ {
+ RefPtr<UString::Rep> result = concatenate(s1.rep(), s2.rep());
+ return UString(result ? result.release() : UString::nullRep());
+ }
+
+ int compare(const UString&, const UString&);
+
+ bool equal(const UString::Rep*, const UString::Rep*);
+
+#ifdef NDEBUG
+ inline void UString::Rep::checkConsistency() const
+ {
+ }
+#endif
+
+ inline UString::UString()
+ : m_rep(&Rep::null)
+ {
+ }
+
+ // Rule from ECMA 15.2 about what an array index is.
+ // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1.
+ inline unsigned UString::toArrayIndex(bool* ok) const
+ {
+ unsigned i = toStrictUInt32(ok);
+ if (ok && i >= 0xFFFFFFFFU)
+ *ok = false;
+ return i;
+ }
+
+ // We'd rather not do shared substring append for small strings, since
+ // this runs too much risk of a tiny initial string holding down a
+ // huge buffer.
+ // FIXME: this should be size_t but that would cause warnings until we
+ // fix UString sizes to be size_t instead of int
+ static const int minShareSize = Heap::minExtraCostSize / sizeof(UChar);
+
+ inline size_t UString::cost() const
+ {
+ size_t capacity = (m_rep->baseString->capacity + m_rep->baseString->preCapacity) * sizeof(UChar);
+ size_t reportedCost = m_rep->baseString->reportedCost;
+ ASSERT(capacity >= reportedCost);
+
+ size_t capacityDelta = capacity - reportedCost;
+
+ if (capacityDelta < static_cast<size_t>(minShareSize))
+ return 0;
+
+ m_rep->baseString->reportedCost = capacity;
+
+ return capacityDelta;
+ }
+
+ struct IdentifierRepHash : PtrHash<RefPtr<JSC::UString::Rep> > {
+ static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->computedHash(); }
+ static unsigned hash(JSC::UString::Rep* key) { return key->computedHash(); }
+ };
+
+} // namespace JSC
+
+namespace WTF {
+
+ template<typename T> struct DefaultHash;
+ template<typename T> struct StrHash;
+
+ template<> struct StrHash<JSC::UString::Rep*> {
+ static unsigned hash(const JSC::UString::Rep* key) { return key->hash(); }
+ static bool equal(const JSC::UString::Rep* a, const JSC::UString::Rep* b) { return JSC::equal(a, b); }
+ static const bool safeToCompareToEmptyOrDeleted = false;
+ };
+
+ template<> struct StrHash<RefPtr<JSC::UString::Rep> > : public StrHash<JSC::UString::Rep*> {
+ using StrHash<JSC::UString::Rep*>::hash;
+ static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->hash(); }
+ using StrHash<JSC::UString::Rep*>::equal;
+ static bool equal(const RefPtr<JSC::UString::Rep>& a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a.get(), b.get()); }
+ static bool equal(const JSC::UString::Rep* a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a, b.get()); }
+ static bool equal(const RefPtr<JSC::UString::Rep>& a, const JSC::UString::Rep* b) { return JSC::equal(a.get(), b); }
+
+ static const bool safeToCompareToEmptyOrDeleted = false;
+ };
+
+ template<> struct DefaultHash<JSC::UString::Rep*> {
+ typedef StrHash<JSC::UString::Rep*> Hash;
+ };
+
+ template<> struct DefaultHash<RefPtr<JSC::UString::Rep> > {
+ typedef StrHash<RefPtr<JSC::UString::Rep> > Hash;
+
+ };
+
+} // namespace WTF
+
+#endif