diff options
Diffstat (limited to 'JavaScriptCore/runtime')
83 files changed, 3078 insertions, 1162 deletions
diff --git a/JavaScriptCore/runtime/ArgList.cpp b/JavaScriptCore/runtime/ArgList.cpp index 0b5d958..ab2b5d7 100644 --- a/JavaScriptCore/runtime/ArgList.cpp +++ b/JavaScriptCore/runtime/ArgList.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009 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 @@ -37,16 +37,12 @@ void ArgList::getSlice(int startIndex, ArgList& result) const result = ArgList(m_args + startIndex, m_argCount - startIndex); } -void MarkedArgumentBuffer::markLists(ListSet& markSet) +void MarkedArgumentBuffer::markLists(MarkStack& markStack, ListSet& markSet) { ListSet::iterator end = markSet.end(); for (ListSet::iterator it = markSet.begin(); it != end; ++it) { MarkedArgumentBuffer* list = *it; - - iterator end2 = list->end(); - for (iterator it2 = list->begin(); it2 != end2; ++it2) - if (!(*it2).marked()) - (*it2).mark(); + markStack.appendValues(reinterpret_cast<JSValue*>(list->m_buffer), list->m_size); } } diff --git a/JavaScriptCore/runtime/ArgList.h b/JavaScriptCore/runtime/ArgList.h index 8e85d7f..ab501b6 100644 --- a/JavaScriptCore/runtime/ArgList.h +++ b/JavaScriptCore/runtime/ArgList.h @@ -22,7 +22,6 @@ #ifndef ArgList_h #define ArgList_h -#include "JSImmediate.h" #include "Register.h" #include <wtf/HashSet.h> @@ -31,7 +30,7 @@ namespace JSC { - class MarkedArgumentBuffer : Noncopyable { + class MarkedArgumentBuffer : public Noncopyable { private: static const unsigned inlineCapacity = 8; typedef Vector<Register, inlineCapacity> VectorType; @@ -44,7 +43,8 @@ namespace JSC { // Constructor for a read-write list, to which you may append values. // FIXME: Remove all clients of this API, then remove this API. MarkedArgumentBuffer() - : m_markSet(0) + : m_isUsingInlineBuffer(true) + , m_markSet(0) #ifndef NDEBUG , m_isReadOnly(false) #endif @@ -57,6 +57,7 @@ namespace JSC { MarkedArgumentBuffer(Register* buffer, size_t size) : m_buffer(buffer) , m_size(size) + , m_isUsingInlineBuffer(true) , m_markSet(0) #ifndef NDEBUG , m_isReadOnly(true) @@ -103,7 +104,7 @@ namespace JSC { { ASSERT(!m_isReadOnly); - if (m_size < inlineCapacity) { + if (m_isUsingInlineBuffer && m_size < inlineCapacity) { m_vector.uncheckedAppend(v); ++m_size; } else { @@ -111,22 +112,37 @@ namespace JSC { // the performance of the fast "just append to inline buffer" case. slowAppend(v); ++m_size; + m_isUsingInlineBuffer = false; } } + void removeLast() + { + ASSERT(m_size); + m_size--; + m_vector.removeLast(); + } + + JSValue last() + { + ASSERT(m_size); + return m_buffer[m_size - 1].jsValue(); + } + iterator begin() { return m_buffer; } iterator end() { return m_buffer + m_size; } const_iterator begin() const { return m_buffer; } const_iterator end() const { return m_buffer + m_size; } - static void markLists(ListSet&); + static void markLists(MarkStack&, ListSet&); private: void slowAppend(JSValue); Register* m_buffer; size_t m_size; + bool m_isUsingInlineBuffer; VectorType m_vector; ListSet* m_markSet; diff --git a/JavaScriptCore/runtime/Arguments.cpp b/JavaScriptCore/runtime/Arguments.cpp index f867fe8..ec9c450 100644 --- a/JavaScriptCore/runtime/Arguments.cpp +++ b/JavaScriptCore/runtime/Arguments.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2002 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. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * @@ -43,30 +43,22 @@ Arguments::~Arguments() delete [] d->extraArguments; } -void Arguments::mark() +void Arguments::markChildren(MarkStack& markStack) { - JSObject::mark(); + JSObject::markChildren(markStack); - if (d->registerArray) { - for (unsigned i = 0; i < d->numParameters; ++i) { - if (!d->registerArray[i].marked()) - d->registerArray[i].mark(); - } - } + if (d->registerArray) + markStack.appendValues(reinterpret_cast<JSValue*>(d->registerArray.get()), d->numParameters); if (d->extraArguments) { unsigned numExtraArguments = d->numArguments - d->numParameters; - for (unsigned i = 0; i < numExtraArguments; ++i) { - if (!d->extraArguments[i].marked()) - d->extraArguments[i].mark(); - } + markStack.appendValues(reinterpret_cast<JSValue*>(d->extraArguments), numExtraArguments); } - if (!d->callee->marked()) - d->callee->mark(); + markStack.append(d->callee); - if (d->activation && !d->activation->marked()) - d->activation->mark(); + if (d->activation) + markStack.append(d->activation); } void Arguments::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize) diff --git a/JavaScriptCore/runtime/Arguments.h b/JavaScriptCore/runtime/Arguments.h index 72697eb..79fe720 100644 --- a/JavaScriptCore/runtime/Arguments.h +++ b/JavaScriptCore/runtime/Arguments.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * @@ -61,7 +61,7 @@ namespace JSC { static const ClassInfo info; - virtual void mark(); + virtual void markChildren(MarkStack&); void fillArgList(ExecState*, MarkedArgumentBuffer&); @@ -230,6 +230,14 @@ namespace JSC { static_cast<Arguments*>(arguments)->setActivation(this); } + ALWAYS_INLINE Arguments* Register::arguments() const + { + if (jsValue() == JSValue()) + return 0; + return asArguments(jsValue()); + } + + } // namespace JSC #endif // Arguments_h diff --git a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h index 13dd95c..b9f738f 100644 --- a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h +++ b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -32,7 +32,7 @@ namespace JSC { - class BatchedTransitionOptimizer : Noncopyable { + class BatchedTransitionOptimizer : public Noncopyable { public: BatchedTransitionOptimizer(JSObject* object) : m_object(object) diff --git a/JavaScriptCore/runtime/ClassInfo.h b/JavaScriptCore/runtime/ClassInfo.h index 097fb09..acec4e7 100644 --- a/JavaScriptCore/runtime/ClassInfo.h +++ b/JavaScriptCore/runtime/ClassInfo.h @@ -27,7 +27,7 @@ namespace JSC { - struct HashEntry; + class HashEntry; struct HashTable; struct ClassInfo { diff --git a/JavaScriptCore/runtime/Collector.cpp b/JavaScriptCore/runtime/Collector.cpp index c799424..c188016 100644 --- a/JavaScriptCore/runtime/Collector.cpp +++ b/JavaScriptCore/runtime/Collector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel <eric@webkit.org> * * This library is free software; you can redistribute it and/or @@ -27,11 +27,14 @@ #include "Interpreter.h" #include "JSGlobalObject.h" #include "JSLock.h" +#include "JSONObject.h" #include "JSString.h" #include "JSValue.h" +#include "MarkStack.h" #include "Nodes.h" #include "Tracing.h" #include <algorithm> +#include <limits.h> #include <setjmp.h> #include <stdlib.h> #include <wtf/FastMalloc.h> @@ -47,6 +50,11 @@ #include <mach/thread_act.h> #include <mach/vm_map.h> +#elif PLATFORM(SYMBIAN) +#include <e32std.h> +#include <e32cmn.h> +#include <unistd.h> + #elif PLATFORM(WIN_OS) #include <windows.h> @@ -86,6 +94,11 @@ const size_t ALLOCATIONS_PER_COLLECTION = 4000; // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. #define MIN_ARRAY_SIZE (static_cast<size_t>(14)) +#if PLATFORM(SYMBIAN) +const size_t MAX_NUM_BLOCKS = 256; // Max size of collector heap set to 16 MB +static RHeap* userChunk = 0; +#endif + static void freeHeap(CollectorHeap*); #if ENABLE(JSC_MULTIPLE_THREADS) @@ -127,6 +140,26 @@ Heap::Heap(JSGlobalData* globalData) { ASSERT(globalData); +#if PLATFORM(SYMBIAN) + // Symbian OpenC supports mmap but currently not the MAP_ANON flag. + // Using fastMalloc() does not properly align blocks on 64k boundaries + // and previous implementation was flawed/incomplete. + // UserHeap::ChunkHeap allows allocation of continuous memory and specification + // of alignment value for (symbian) cells within that heap. + // + // Clarification and mapping of terminology: + // RHeap (created by UserHeap::ChunkHeap below) is continuos memory chunk, + // which can dynamically grow up to 8 MB, + // that holds all CollectorBlocks of this session (static). + // Each symbian cell within RHeap maps to a 64kb aligned CollectorBlock. + // JSCell objects are maintained as usual within CollectorBlocks. + if (!userChunk) { + userChunk = UserHeap::ChunkHeap(0, 0, MAX_NUM_BLOCKS * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); + if (!userChunk) + CRASH(); + } +#endif // PLATFORM(SYMBIAN) + memset(&primaryHeap, 0, sizeof(CollectorHeap)); memset(&numberHeap, 0, sizeof(CollectorHeap)); } @@ -139,7 +172,7 @@ Heap::~Heap() void Heap::destroy() { - JSLock lock(false); + JSLock lock(SilenceAssertionsOnly); if (!m_globalData) return; @@ -184,8 +217,12 @@ static NEVER_INLINE CollectorBlock* allocateBlock() // 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 | VM_TAG_FOR_COLLECTOR_MEMORY, MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT); #elif PLATFORM(SYMBIAN) - // no memory map in symbian, need to hack with fastMalloc - void* address = fastMalloc(BLOCK_SIZE); + // Allocate a 64 kb aligned CollectorBlock + unsigned char* mask = reinterpret_cast<unsigned char*>(userChunk->Alloc(BLOCK_SIZE)); + if (!mask) + CRASH(); + uintptr_t address = reinterpret_cast<uintptr_t>(mask); + memset(reinterpret_cast<void*>(address), 0, BLOCK_SIZE); #elif PLATFORM(WIN_OS) // windows virtual address granularity is naturally 64k @@ -230,7 +267,7 @@ static void freeBlock(CollectorBlock* block) #if PLATFORM(DARWIN) vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(block), BLOCK_SIZE); #elif PLATFORM(SYMBIAN) - fastFree(block); + userChunk->Free(reinterpret_cast<TAny*>(block)); #elif PLATFORM(WIN_OS) VirtualFree(block, 0, MEM_RELEASE); #elif HAVE(POSIX_MEMALIGN) @@ -392,6 +429,63 @@ void* Heap::allocateNumber(size_t s) return heapAllocate<NumberHeap>(s); } +#if PLATFORM(WINCE) +void* g_stackBase = 0; + +inline bool isPageWritable(void* page) +{ + MEMORY_BASIC_INFORMATION memoryInformation; + DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation)); + + // return false on error, including ptr outside memory + if (result != sizeof(memoryInformation)) + return false; + + DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE); + return protect == PAGE_READWRITE + || protect == PAGE_WRITECOPY + || protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_EXECUTE_WRITECOPY; +} + +static void* getStackBase(void* previousFrame) +{ + // find the address of this stack frame by taking the address of a local variable + bool isGrowingDownward; + void* thisFrame = (void*)(&isGrowingDownward); + + isGrowingDownward = previousFrame < &thisFrame; + static DWORD pageSize = 0; + if (!pageSize) { + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + pageSize = systemInfo.dwPageSize; + } + + // scan all of memory starting from this frame, and return the last writeable page found + register char* currentPage = (char*)((DWORD)thisFrame & ~(pageSize - 1)); + if (isGrowingDownward) { + while (currentPage > 0) { + // check for underflow + if (currentPage >= (char*)pageSize) + currentPage -= pageSize; + else + currentPage = 0; + if (!isPageWritable(currentPage)) + return currentPage + pageSize; + } + return 0; + } else { + while (true) { + // guaranteed to complete because isPageWritable returns false at end of memory + currentPage += pageSize; + if (!isPageWritable(currentPage)) + return currentPage; + } + } +} +#endif + static inline void* currentThreadStackBase() { #if PLATFORM(DARWIN) @@ -457,6 +551,13 @@ static inline void* currentThreadStackBase() stackThread = thread; } return static_cast<char*>(stackBase) + stackSize; +#elif PLATFORM(WINCE) + if (g_stackBase) + return g_stackBase; + else { + int dummy; + return getStackBase(&dummy); + } #else #error Need a way to get the stack base on this platform #endif @@ -542,7 +643,7 @@ void Heap::registerThread() // 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) +void Heap::markConservatively(MarkStack& markStack, void* start, void* end) { if (start > end) { void* tmp = start; @@ -583,9 +684,8 @@ void Heap::markConservatively(void* start, void* end) 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(); + markStack.append(reinterpret_cast<JSCell*>(xAsBits)); + markStack.drain(); } break; } @@ -596,15 +696,15 @@ void Heap::markConservatively(void* start, void* end) } } -void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal() +void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal(MarkStack& markStack) { void* dummy; void* stackPointer = &dummy; void* stackBase = currentThreadStackBase(); - markConservatively(stackPointer, stackBase); + markConservatively(markStack, stackPointer, stackBase); } -void Heap::markCurrentThreadConservatively() +void Heap::markCurrentThreadConservatively(MarkStack& markStack) { // setjmp forces volatile registers onto the stack jmp_buf registers; @@ -617,7 +717,7 @@ void Heap::markCurrentThreadConservatively() #pragma warning(pop) #endif - markCurrentThreadConservativelyInternal(); + markCurrentThreadConservativelyInternal(markStack); } #if ENABLE(JSC_MULTIPLE_THREADS) @@ -749,7 +849,7 @@ static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs) #endif } -void Heap::markOtherThreadConservatively(Thread* thread) +void Heap::markOtherThreadConservatively(MarkStack& markStack, Thread* thread) { suspendThread(thread->platformThread); @@ -757,19 +857,19 @@ void Heap::markOtherThreadConservatively(Thread* thread) 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)); + markConservatively(markStack, static_cast<void*>(®s), static_cast<void*>(reinterpret_cast<char*>(®s) + regSize)); void* stackPointer = otherThreadStackPointer(regs); - markConservatively(stackPointer, thread->stackBase); + markConservatively(markStack, stackPointer, thread->stackBase); resumeThread(thread->platformThread); } #endif -void Heap::markStackObjectsConservatively() +void Heap::markStackObjectsConservatively(MarkStack& markStack) { - markCurrentThreadConservatively(); + markCurrentThreadConservatively(markStack); #if ENABLE(JSC_MULTIPLE_THREADS) @@ -779,7 +879,7 @@ void Heap::markStackObjectsConservatively() #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 + // a malloc inside markChildren() would risk a deadlock with a thread that had been // suspended while holding the malloc lock. fastMallocForbid(); #endif @@ -787,7 +887,7 @@ void Heap::markStackObjectsConservatively() // 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); + markOtherThreadConservatively(markStack, thread); } #ifndef NDEBUG fastMallocAllow(); @@ -847,7 +947,7 @@ Heap* Heap::heap(JSValue v) return Heap::cellBlock(v.asCell())->heap; } -void Heap::markProtectedObjects() +void Heap::markProtectedObjects(MarkStack& markStack) { if (m_protectedValuesMutex) m_protectedValuesMutex->lock(); @@ -855,8 +955,10 @@ void Heap::markProtectedObjects() 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 (!val->marked()) { + markStack.append(val); + markStack.drain(); + } } if (m_protectedValuesMutex) @@ -961,7 +1063,7 @@ template <HeapType heapType> size_t Heap::sweep() heap.extraCost = 0; return numLiveObjects; } - + bool Heap::collect() { #ifndef NDEBUG @@ -980,18 +1082,22 @@ bool Heap::collect() numberHeap.operationInProgress = Collection; // MARK: first mark all referenced objects recursively starting out from the set of root objects - - markStackObjectsConservatively(); - markProtectedObjects(); + MarkStack& markStack = m_globalData->markStack; + markStackObjectsConservatively(markStack); + markProtectedObjects(markStack); if (m_markListSet && m_markListSet->size()) - MarkedArgumentBuffer::markLists(*m_markListSet); + MarkedArgumentBuffer::markLists(markStack, *m_markListSet); if (m_globalData->exception && !m_globalData->exception.marked()) - m_globalData->exception.mark(); - m_globalData->interpreter->registerFile().markCallFrames(this); + markStack.append(m_globalData->exception); + m_globalData->interpreter->registerFile().markCallFrames(markStack, this); m_globalData->smallStrings.mark(); if (m_globalData->scopeNodeBeingReparsed) - m_globalData->scopeNodeBeingReparsed->mark(); + m_globalData->scopeNodeBeingReparsed->markAggregate(markStack); + if (m_globalData->firstStringifierToMark) + JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark); + markStack.drain(); + markStack.compact(); JAVASCRIPTCORE_GC_MARKED(); size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects; @@ -1081,8 +1187,10 @@ static const char* typeName(JSCell* cell) { if (cell->isString()) return "string"; +#if USE(JSVALUE32) if (cell->isNumber()) return "number"; +#endif if (cell->isGetterSetter()) return "gettersetter"; ASSERT(cell->isObject()); diff --git a/JavaScriptCore/runtime/Collector.h b/JavaScriptCore/runtime/Collector.h index 23f9f15..877f890 100644 --- a/JavaScriptCore/runtime/Collector.h +++ b/JavaScriptCore/runtime/Collector.h @@ -39,11 +39,12 @@ namespace JSC { - class MarkedArgumentBuffer; class CollectorBlock; class JSCell; class JSGlobalData; class JSValue; + class MarkedArgumentBuffer; + class MarkStack; enum OperationInProgress { NoOperation, Allocation, Collection }; enum HeapType { PrimaryHeap, NumberHeap }; @@ -63,7 +64,7 @@ namespace JSC { OperationInProgress operationInProgress; }; - class Heap : Noncopyable { + class Heap : public Noncopyable { public: class Thread; typedef CollectorHeapIterator<PrimaryHeap> iterator; @@ -111,7 +112,7 @@ namespace JSC { static bool isCellMarked(const JSCell*); static void markCell(JSCell*); - void markConservatively(void* start, void* end); + void markConservatively(MarkStack&, void* start, void* end); HashSet<MarkedArgumentBuffer*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<MarkedArgumentBuffer*>; return *m_markListSet; } @@ -133,11 +134,11 @@ namespace JSC { ~Heap(); void recordExtraCost(size_t); - void markProtectedObjects(); - void markCurrentThreadConservatively(); - void markCurrentThreadConservativelyInternal(); - void markOtherThreadConservatively(Thread*); - void markStackObjectsConservatively(); + void markProtectedObjects(MarkStack&); + void markCurrentThreadConservatively(MarkStack&); + void markCurrentThreadConservativelyInternal(MarkStack&); + void markOtherThreadConservatively(MarkStack&, Thread*); + void markStackObjectsConservatively(MarkStack&); typedef HashCountedSet<JSCell*> ProtectCountSet; @@ -167,8 +168,13 @@ namespace JSC { 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 +#if USE(JSVALUE32) + template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 32; }; +#else + template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 64; }; +#endif + template<> struct CellSize<sizeof(uint64_t)> { static const size_t m_value = 64; }; + const size_t BLOCK_SIZE = 16 * 4096; // 64k // derived constants diff --git a/JavaScriptCore/runtime/CommonIdentifiers.cpp b/JavaScriptCore/runtime/CommonIdentifiers.cpp index fe0a830..3837817 100644 --- a/JavaScriptCore/runtime/CommonIdentifiers.cpp +++ b/JavaScriptCore/runtime/CommonIdentifiers.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2009 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 @@ -23,12 +23,13 @@ namespace JSC { -const char* const nullCString = 0; +static const char* const nullCString = 0; #define INITIALIZE_PROPERTY_NAME(name) , name(globalData, #name) CommonIdentifiers::CommonIdentifiers(JSGlobalData* globalData) : nullIdentifier(globalData, nullCString) + , emptyIdentifier(globalData, "") , underscoreProto(globalData, "__proto__") , thisIdentifier(globalData, "this") JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME) diff --git a/JavaScriptCore/runtime/CommonIdentifiers.h b/JavaScriptCore/runtime/CommonIdentifiers.h index d4c5d52..148d3dd 100644 --- a/JavaScriptCore/runtime/CommonIdentifiers.h +++ b/JavaScriptCore/runtime/CommonIdentifiers.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003,2007 Apple Computer, Inc + * Copyright (C) 2003, 2007, 2009 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 @@ -42,6 +42,7 @@ macro(exec) \ macro(fromCharCode) \ macro(global) \ + macro(getPrototypeOf) \ macro(hasOwnProperty) \ macro(ignoreCase) \ macro(index) \ @@ -59,6 +60,8 @@ macro(test) \ macro(toExponential) \ macro(toFixed) \ + macro(toISOString) \ + macro(toJSON) \ macro(toLocaleString) \ macro(toPrecision) \ macro(toString) \ @@ -68,13 +71,14 @@ namespace JSC { - class CommonIdentifiers : Noncopyable { + class CommonIdentifiers : public Noncopyable { private: CommonIdentifiers(JSGlobalData*); friend class JSGlobalData; public: const Identifier nullIdentifier; + const Identifier emptyIdentifier; const Identifier underscoreProto; const Identifier thisIdentifier; diff --git a/JavaScriptCore/runtime/DateConstructor.cpp b/JavaScriptCore/runtime/DateConstructor.cpp index f1cf933..2f52cff 100644 --- a/JavaScriptCore/runtime/DateConstructor.cpp +++ b/JavaScriptCore/runtime/DateConstructor.cpp @@ -35,6 +35,10 @@ #include <wtf/DateMath.h> #include <wtf/MathExtras.h> +#if PLATFORM(WINCE) && !PLATFORM(QT) +extern "C" time_t time(time_t* timer); // Provided by libce. +#endif + #if HAVE(SYS_TIME_H) #include <sys/time.h> #endif @@ -47,8 +51,6 @@ using namespace WTF; namespace JSC { -// TODO: MakeTime (15.9.11.1) etc. ? - ASSERT_CLASS_FITS_IN_CELL(DateConstructor); static JSValue JSC_HOST_CALL dateParse(ExecState*, JSObject*, JSValue, const ArgList&); @@ -96,17 +98,17 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) || (numArgs >= 7 && isnan(args.at(6).toNumber(exec)))) value = NaN; else { - GregorianDateTime t; - int year = args.at(0).toInt32(exec); - t.year = (year >= 0 && year <= 99) ? year : year - 1900; - t.month = args.at(1).toInt32(exec); - t.monthDay = (numArgs >= 3) ? args.at(2).toInt32(exec) : 1; - t.hour = args.at(3).toInt32(exec); - t.minute = args.at(4).toInt32(exec); - t.second = args.at(5).toInt32(exec); - t.isDST = -1; - double ms = (numArgs >= 7) ? args.at(6).toNumber(exec) : 0; - value = gregorianDateTimeToMS(t, ms, false); + GregorianDateTime t; + int year = args.at(0).toInt32(exec); + t.year = (year >= 0 && year <= 99) ? year : year - 1900; + t.month = args.at(1).toInt32(exec); + t.monthDay = (numArgs >= 3) ? args.at(2).toInt32(exec) : 1; + t.hour = args.at(3).toInt32(exec); + t.minute = args.at(4).toInt32(exec); + t.second = args.at(5).toInt32(exec); + t.isDST = -1; + double ms = (numArgs >= 7) ? args.at(6).toNumber(exec) : 0; + value = gregorianDateTimeToMS(t, ms, false); } } diff --git a/JavaScriptCore/runtime/DatePrototype.cpp b/JavaScriptCore/runtime/DatePrototype.cpp index 1406197..e2482f4 100644 --- a/JavaScriptCore/runtime/DatePrototype.cpp +++ b/JavaScriptCore/runtime/DatePrototype.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile, 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 @@ -59,6 +60,10 @@ #include <CoreFoundation/CoreFoundation.h> #endif +#if PLATFORM(WINCE) && !PLATFORM(QT) +extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); //provided by libce +#endif + using namespace WTF; namespace JSC { @@ -108,6 +113,9 @@ static JSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState*, JSObjec static JSValue JSC_HOST_CALL dateProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); static JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState*, JSObject*, JSValue, const ArgList&); static JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState*, JSObject*, JSValue, const ArgList&); +static JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState*, JSObject*, JSValue, const ArgList&); + +static JSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState*, JSObject*, JSValue, const ArgList&); } @@ -190,6 +198,9 @@ static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, L { #if HAVE(LANGINFO_H) static const nl_item formats[] = { D_T_FMT, D_FMT, T_FMT }; +#elif PLATFORM(WINCE) && !PLATFORM(QT) + // strftime() we are using does not support # + static const char* const formatStrings[] = { "%c", "%x", "%X" }; #else static const char* const formatStrings[] = { "%#c", "%#x", "%X" }; #endif @@ -334,6 +345,7 @@ const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, 0, ExecState /* Source for DatePrototype.lut.h @begin dateTable toString dateProtoFuncToString DontEnum|Function 0 + toISOString dateProtoFuncToISOString DontEnum|Function 0 toUTCString dateProtoFuncToUTCString DontEnum|Function 0 toDateString dateProtoFuncToDateString DontEnum|Function 0 toTimeString dateProtoFuncToTimeString DontEnum|Function 0 @@ -377,6 +389,7 @@ const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, 0, ExecState setUTCFullYear dateProtoFuncSetUTCFullYear DontEnum|Function 3 setYear dateProtoFuncSetYear DontEnum|Function 1 getYear dateProtoFuncGetYear DontEnum|Function 0 + toJSON dateProtoFuncToJSON DontEnum|Function 0 @end */ @@ -430,6 +443,28 @@ JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSVal return jsNontrivialString(exec, formatDateUTCVariant(t) + " " + formatTime(t, utc)); } +JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +{ + if (!thisValue.isObject(&DateInstance::info)) + return throwError(exec, TypeError); + + const bool utc = true; + + DateInstance* thisDateObj = asDateInstance(thisValue); + double milli = thisDateObj->internalNumber(); + if (!isfinite(milli)) + return jsNontrivialString(exec, "Invalid Date"); + + GregorianDateTime t; + thisDateObj->msToGregorianDateTime(milli, utc, t); + // Maximum amount of space we need in buffer: 6 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + // 6 for formatting and one for null termination = 23. We add one extra character to allow us to force null termination. + char buffer[24]; + snprintf(buffer, sizeof(buffer) - 1, "%04d-%02d-%02dT%02d:%02d:%02dZ", 1900 + t.year, t.month + 1, t.monthDay, t.hour, t.minute, t.second); + buffer[sizeof(buffer) - 1] = 0; + return jsNontrivialString(exec, buffer); +} + JSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { if (!thisValue.isObject(&DateInstance::info)) @@ -1044,4 +1079,27 @@ JSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec, JSObject*, JSValue t return jsNumber(exec, t.year); } +JSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +{ + JSObject* object = thisValue.toThisObject(exec); + if (exec->hadException()) + return jsNull(); + + JSValue toISOValue = object->get(exec, exec->globalData().propertyNames->toISOString); + if (exec->hadException()) + return jsNull(); + + CallData callData; + CallType callType = toISOValue.getCallData(callData); + if (callType == CallTypeNone) + return throwError(exec, TypeError, "toISOString is not a function"); + + JSValue result = call(exec, asObject(toISOValue), callType, callData, object, exec->emptyList()); + if (exec->hadException()) + return jsNull(); + if (result.isObject()) + return throwError(exec, TypeError, "toISOString did not return a primitive value"); + return result; +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/ExceptionHelpers.h b/JavaScriptCore/runtime/ExceptionHelpers.h index 09d99dc..4c5bec1 100644 --- a/JavaScriptCore/runtime/ExceptionHelpers.h +++ b/JavaScriptCore/runtime/ExceptionHelpers.h @@ -29,20 +29,19 @@ #ifndef ExceptionHelpers_h #define ExceptionHelpers_h -#include "JSImmediate.h" namespace JSC { class CodeBlock; class ExecState; class Identifier; - class Instruction; class JSGlobalData; class JSNotAnObjectErrorStub; class JSObject; class JSValue; class Node; - + struct Instruction; + JSValue createInterruptedExecutionException(JSGlobalData*); JSValue createStackOverflowError(ExecState*); JSValue createUndefinedVariableError(ExecState*, const Identifier&, unsigned bytecodeOffset, CodeBlock*); diff --git a/JavaScriptCore/runtime/GetterSetter.cpp b/JavaScriptCore/runtime/GetterSetter.cpp index cd1b40a..cc85354 100644 --- a/JavaScriptCore/runtime/GetterSetter.cpp +++ b/JavaScriptCore/runtime/GetterSetter.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2007, 2008, 2009 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 @@ -28,14 +28,14 @@ namespace JSC { -void GetterSetter::mark() +void GetterSetter::markChildren(MarkStack& markStack) { - JSCell::mark(); + JSCell::markChildren(markStack); if (m_getter && !m_getter->marked()) - m_getter->mark(); + markStack.append(m_getter); if (m_setter && !m_setter->marked()) - m_setter->mark(); + markStack.append(m_setter); } JSValue GetterSetter::toPrimitive(ExecState*, PreferredPrimitiveType) const diff --git a/JavaScriptCore/runtime/GetterSetter.h b/JavaScriptCore/runtime/GetterSetter.h index e6b74a1..b7a8794 100644 --- a/JavaScriptCore/runtime/GetterSetter.h +++ b/JavaScriptCore/runtime/GetterSetter.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 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. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 @@ -25,6 +25,8 @@ #include "JSCell.h" +#include "CallFrame.h" + namespace JSC { class JSObject; @@ -33,20 +35,23 @@ namespace JSC { // for a property. class GetterSetter : public JSCell { public: - GetterSetter() - : JSCell(0) + GetterSetter(ExecState* exec) + : JSCell(exec->globalData().getterSetterStructure.get()) , m_getter(0) , m_setter(0) { } - virtual void mark(); + virtual void markChildren(MarkStack&); JSObject* getter() const { return m_getter; } void setGetter(JSObject* getter) { m_getter = getter; } JSObject* setter() const { return m_setter; } void setSetter(JSObject* setter) { m_setter = setter; } - + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(GetterSetterType)); + } private: virtual bool isGetterSetter() const; diff --git a/JavaScriptCore/runtime/GlobalEvalFunction.cpp b/JavaScriptCore/runtime/GlobalEvalFunction.cpp index b0d4c25..3074f95 100644 --- a/JavaScriptCore/runtime/GlobalEvalFunction.cpp +++ b/JavaScriptCore/runtime/GlobalEvalFunction.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2002 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. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * @@ -39,11 +39,10 @@ GlobalEvalFunction::GlobalEvalFunction(ExecState* exec, PassRefPtr<Structure> st ASSERT_ARG(cachedGlobalObject, cachedGlobalObject); } -void GlobalEvalFunction::mark() +void GlobalEvalFunction::markChildren(MarkStack& markStack) { - PrototypeFunction::mark(); - if (!m_cachedGlobalObject->marked()) - m_cachedGlobalObject->mark(); + PrototypeFunction::markChildren(markStack); + markStack.append(m_cachedGlobalObject); } } // namespace JSC diff --git a/JavaScriptCore/runtime/GlobalEvalFunction.h b/JavaScriptCore/runtime/GlobalEvalFunction.h index 49b1847..cdba4a0 100644 --- a/JavaScriptCore/runtime/GlobalEvalFunction.h +++ b/JavaScriptCore/runtime/GlobalEvalFunction.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * @@ -36,7 +36,7 @@ namespace JSC { JSGlobalObject* cachedGlobalObject() const { return m_cachedGlobalObject; } private: - virtual void mark(); + virtual void markChildren(MarkStack&); JSGlobalObject* m_cachedGlobalObject; }; diff --git a/JavaScriptCore/runtime/Identifier.cpp b/JavaScriptCore/runtime/Identifier.cpp index 040c123..7db723b 100644 --- a/JavaScriptCore/runtime/Identifier.cpp +++ b/JavaScriptCore/runtime/Identifier.cpp @@ -32,7 +32,7 @@ namespace JSC { typedef HashMap<const char*, RefPtr<UString::Rep>, PtrHash<const char*> > LiteralIdentifierTable; -class IdentifierTable { +class IdentifierTable : public FastAllocBase { public: ~IdentifierTable() { diff --git a/JavaScriptCore/runtime/InitializeThreading.cpp b/JavaScriptCore/runtime/InitializeThreading.cpp index a0620e7..fea89f8 100644 --- a/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/JavaScriptCore/runtime/InitializeThreading.cpp @@ -29,7 +29,6 @@ #include "config.h" #include "InitializeThreading.h" -#include "JSImmediate.h" #include "Collector.h" #include "dtoa.h" #include "Identifier.h" diff --git a/JavaScriptCore/runtime/JSAPIValueWrapper.cpp b/JavaScriptCore/runtime/JSAPIValueWrapper.cpp new file mode 100644 index 0000000..475fad5 --- /dev/null +++ b/JavaScriptCore/runtime/JSAPIValueWrapper.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 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 "JSAPIValueWrapper.h" + +#include "NumberObject.h" +#include "UString.h" + +namespace JSC { + +JSValue JSAPIValueWrapper::toPrimitive(ExecState*, PreferredPrimitiveType) const +{ + ASSERT_NOT_REACHED(); + return JSValue(); +} + +bool JSAPIValueWrapper::getPrimitiveNumber(ExecState*, double&, JSValue&) +{ + ASSERT_NOT_REACHED(); + return false; +} + +bool JSAPIValueWrapper::toBoolean(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return false; +} + +double JSAPIValueWrapper::toNumber(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +UString JSAPIValueWrapper::toString(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return UString(); +} + +JSObject* JSAPIValueWrapper::toObject(ExecState*) const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +} // namespace JSC diff --git a/JavaScriptCore/runtime/JSAPIValueWrapper.h b/JavaScriptCore/runtime/JSAPIValueWrapper.h new file mode 100644 index 0000000..21a9710 --- /dev/null +++ b/JavaScriptCore/runtime/JSAPIValueWrapper.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2004, 2005, 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 JSAPIValueWrapper_h +#define JSAPIValueWrapper_h + +#include <wtf/Platform.h> + +#include "JSCell.h" +#include "CallFrame.h" + +namespace JSC { + + class JSAPIValueWrapper : public JSCell { + friend JSValue jsAPIValueWrapper(ExecState*, JSValue); + public: + JSValue value() const { return m_value; } + + virtual bool isAPIValueWrapper() const { return true; } + + virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; + virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue&); + virtual bool toBoolean(ExecState*) const; + virtual double toNumber(ExecState*) const; + virtual UString toString(ExecState*) const; + virtual JSObject* toObject(ExecState*) const; + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(CompoundType)); + } + + + private: + JSAPIValueWrapper(ExecState* exec, JSValue value) + : JSCell(exec->globalData().apiWrapperStructure.get()) + , m_value(value) + { + } + + JSValue m_value; + }; + + inline JSValue jsAPIValueWrapper(ExecState* exec, JSValue value) + { + return new (exec) JSAPIValueWrapper(exec, value); + } + +} // namespace JSC + +#endif // JSAPIValueWrapper_h diff --git a/JavaScriptCore/runtime/JSActivation.cpp b/JavaScriptCore/runtime/JSActivation.cpp index 8996629..87adbcd 100644 --- a/JavaScriptCore/runtime/JSActivation.cpp +++ b/JavaScriptCore/runtime/JSActivation.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,7 +40,7 @@ ASSERT_CLASS_FITS_IN_CELL(JSActivation); const ClassInfo JSActivation::info = { "JSActivation", 0, 0, 0 }; JSActivation::JSActivation(CallFrame* callFrame, PassRefPtr<FunctionBodyNode> functionBody) - : Base(callFrame->globalData().activationStructure, new JSActivationData(functionBody, callFrame)) + : Base(callFrame->globalData().activationStructure, new JSActivationData(functionBody, callFrame->registers())) { } @@ -49,9 +49,9 @@ JSActivation::~JSActivation() delete d(); } -void JSActivation::mark() +void JSActivation::markChildren(MarkStack& markStack) { - Base::mark(); + Base::markChildren(markStack); Register* registerArray = d()->registerArray.get(); if (!registerArray) @@ -59,25 +59,13 @@ void JSActivation::mark() size_t numParametersMinusThis = d()->functionBody->generatedBytecode().m_numParameters - 1; - size_t i = 0; - size_t count = numParametersMinusThis; - for ( ; i < count; ++i) { - Register& r = registerArray[i]; - if (!r.marked()) - r.mark(); - } + size_t count = numParametersMinusThis; + markStack.appendValues(registerArray, count); size_t numVars = d()->functionBody->generatedBytecode().m_numVars; // Skip the call frame, which sits between the parameters and vars. - i += RegisterFile::CallFrameHeaderSize; - count += RegisterFile::CallFrameHeaderSize + numVars; - - for ( ; i < count; ++i) { - Register& r = registerArray[i]; - if (r.jsValue() && !r.marked()) - r.mark(); - } + markStack.appendValues(registerArray + count + RegisterFile::CallFrameHeaderSize, numVars, MayContainNullValues); } bool JSActivation::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) diff --git a/JavaScriptCore/runtime/JSActivation.h b/JavaScriptCore/runtime/JSActivation.h index c183dac..6a08439 100644 --- a/JavaScriptCore/runtime/JSActivation.h +++ b/JavaScriptCore/runtime/JSActivation.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -46,7 +46,7 @@ namespace JSC { JSActivation(CallFrame*, PassRefPtr<FunctionBodyNode>); virtual ~JSActivation(); - virtual void mark(); + virtual void markChildren(MarkStack&); virtual bool isDynamicScope() const; diff --git a/JavaScriptCore/runtime/JSArray.cpp b/JavaScriptCore/runtime/JSArray.cpp index 296ac9d..7d7d4c4 100644 --- a/JavaScriptCore/runtime/JSArray.cpp +++ b/JavaScriptCore/runtime/JSArray.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2003 Peter Kelly (pmk@post.com) * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) * @@ -134,9 +134,9 @@ JSArray::JSArray(PassRefPtr<Structure> structure) unsigned initialCapacity = 0; m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); - m_fastAccessCutoff = 0; m_storage->m_vectorLength = initialCapacity; - m_storage->m_length = 0; + + m_fastAccessCutoff = 0; checkConsistency(); } @@ -146,40 +146,45 @@ JSArray::JSArray(PassRefPtr<Structure> structure, unsigned initialLength) { unsigned initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX); - m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); - m_fastAccessCutoff = 0; - m_storage->m_vectorLength = initialCapacity; + m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); m_storage->m_length = initialLength; + m_storage->m_vectorLength = initialCapacity; + m_storage->m_numValuesInVector = 0; + m_storage->m_sparseValueMap = 0; + m_storage->lazyCreationData = 0; - Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue)); + JSValue* vector = m_storage->m_vector; + for (size_t i = 0; i < initialCapacity; ++i) + vector[i] = JSValue(); + + m_fastAccessCutoff = 0; checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue)); } JSArray::JSArray(PassRefPtr<Structure> structure, const ArgList& list) : JSObject(structure) { - unsigned length = list.size(); + unsigned initialCapacity = list.size(); - m_fastAccessCutoff = length; - - ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length))); - - storage->m_vectorLength = length; - storage->m_numValuesInVector = length; - storage->m_sparseValueMap = 0; - storage->m_length = length; + m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); + m_storage->m_length = initialCapacity; + m_storage->m_vectorLength = initialCapacity; + m_storage->m_numValuesInVector = initialCapacity; + m_storage->m_sparseValueMap = 0; size_t i = 0; ArgList::const_iterator end = list.end(); for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i) - storage->m_vector[i] = *it; + m_storage->m_vector[i] = *it; - m_storage = storage; - - Heap::heap(this)->reportExtraMemoryCost(storageSize(length)); + m_fastAccessCutoff = initialCapacity; checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity)); } JSArray::~JSArray() @@ -596,26 +601,19 @@ void JSArray::push(ExecState* exec, JSValue value) putSlowCase(exec, m_storage->m_length++, value); } -void JSArray::mark() +void JSArray::markChildren(MarkStack& markStack) { - JSObject::mark(); + JSObject::markChildren(markStack); ArrayStorage* storage = m_storage; unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength); - for (unsigned i = 0; i < usedVectorLength; ++i) { - JSValue value = storage->m_vector[i]; - if (value && !value.marked()) - value.mark(); - } + markStack.appendValues(storage->m_vector, usedVectorLength, MayContainNullValues); if (SparseArrayValueMap* map = storage->m_sparseValueMap) { SparseArrayValueMap::iterator end = map->end(); - for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) { - JSValue value = it->second; - if (!value.marked()) - value.mark(); - } + for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) + markStack.append(it->second); } } diff --git a/JavaScriptCore/runtime/JSArray.h b/JavaScriptCore/runtime/JSArray.h index ea490d8..49df6c4 100644 --- a/JavaScriptCore/runtime/JSArray.h +++ b/JavaScriptCore/runtime/JSArray.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2008, 2009 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 @@ -88,7 +88,7 @@ namespace JSC { virtual bool deleteProperty(ExecState*, const Identifier& propertyName); virtual bool deleteProperty(ExecState*, unsigned propertyName); virtual void getPropertyNames(ExecState*, PropertyNameArray&); - virtual void mark(); + virtual void markChildren(MarkStack&); void* lazyCreationData(); void setLazyCreationData(void*); diff --git a/JavaScriptCore/runtime/JSByteArray.h b/JavaScriptCore/runtime/JSByteArray.h index 57374e0..a56aca6 100644 --- a/JavaScriptCore/runtime/JSByteArray.h +++ b/JavaScriptCore/runtime/JSByteArray.h @@ -33,7 +33,7 @@ namespace JSC { class JSByteArray : public JSObject { - friend class VPtrSet; + friend struct VPtrSet; public: bool canAccessIndex(unsigned i) { return i < m_storage->length(); } JSValue getIndex(ExecState* exec, unsigned i) diff --git a/JavaScriptCore/runtime/JSCell.cpp b/JavaScriptCore/runtime/JSCell.cpp index 8cf7943..c733ed9 100644 --- a/JavaScriptCore/runtime/JSCell.cpp +++ b/JavaScriptCore/runtime/JSCell.cpp @@ -90,16 +90,6 @@ bool JSCell::getUInt32(uint32_t&) const return false; } -bool JSCell::getTruncatedInt32(int32_t&) const -{ - return false; -} - -bool JSCell::getTruncatedUInt32(uint32_t&) const -{ - return false; -} - bool JSCell::getString(UString&stringValue) const { if (!isString()) diff --git a/JavaScriptCore/runtime/JSCell.h b/JavaScriptCore/runtime/JSCell.h index e0a9b4d..75ccf7f 100644 --- a/JavaScriptCore/runtime/JSCell.h +++ b/JavaScriptCore/runtime/JSCell.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2004, 2005, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 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 @@ -31,7 +31,7 @@ namespace JSC { - class JSCell : Noncopyable { + class JSCell : public NoncopyableCustomAllocated { friend class GetterSetter; friend class Heap; friend class JIT; @@ -40,7 +40,8 @@ namespace JSC { friend class JSPropertyNameIterator; friend class JSString; friend class JSValue; - friend class VPtrSet; + friend class JSAPIValueWrapper; + friend struct VPtrSet; private: explicit JSCell(Structure*); @@ -48,11 +49,14 @@ namespace JSC { public: // Querying the type. +#if USE(JSVALUE32) bool isNumber() const; +#endif bool isString() const; bool isObject() const; virtual bool isGetterSetter() const; virtual bool isObject(const ClassInfo*) const; + virtual bool isAPIValueWrapper() const { return false; } Structure* structure() const; @@ -68,8 +72,6 @@ namespace JSC { // Extracting integer values. // FIXME: remove these methods, can check isNumberCell in JSValue && then call asNumberCell::*. virtual bool getUInt32(uint32_t&) const; - virtual bool getTruncatedInt32(int32_t&) const; - virtual bool getTruncatedUInt32(uint32_t&) const; // Basic conversions. virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const = 0; @@ -83,7 +85,9 @@ namespace JSC { void* operator new(size_t, ExecState*); void* operator new(size_t, JSGlobalData*); void* operator new(size_t, void* placementNewDestination) { return placementNewDestination; } - virtual void mark(); + + void markCellDirect(); + virtual void markChildren(MarkStack&); bool marked() const; // Object operations, with the toObject operation included. @@ -124,10 +128,12 @@ namespace JSC { { } +#if USE(JSVALUE32) inline bool JSCell::isNumber() const { return Heap::isNumber(const_cast<JSCell*>(this)); } +#endif inline bool JSCell::isObject() const { @@ -149,15 +155,14 @@ namespace JSC { return Heap::isCellMarked(this); } - inline void JSCell::mark() + inline void JSCell::markCellDirect() { - return Heap::markCell(this); + Heap::markCell(this); } - ALWAYS_INLINE JSCell* JSValue::asCell() const + inline void JSCell::markChildren(MarkStack&) { - ASSERT(isCell()); - return m_ptr; + ASSERT(marked()); } inline void* JSCell::operator new(size_t size, JSGlobalData* globalData) @@ -173,128 +178,231 @@ namespace JSC { inline bool JSValue::isString() const { - return !JSImmediate::isImmediate(asValue()) && asCell()->isString(); + return isCell() && asCell()->isString(); } inline bool JSValue::isGetterSetter() const { - return !JSImmediate::isImmediate(asValue()) && asCell()->isGetterSetter(); + return isCell() && asCell()->isGetterSetter(); } inline bool JSValue::isObject() const { - return !JSImmediate::isImmediate(asValue()) && asCell()->isObject(); + return isCell() && asCell()->isObject(); } inline bool JSValue::getString(UString& s) const { - return !JSImmediate::isImmediate(asValue()) && asCell()->getString(s); + return isCell() && asCell()->getString(s); } inline UString JSValue::getString() const { - return JSImmediate::isImmediate(asValue()) ? UString() : asCell()->getString(); + return isCell() ? asCell()->getString() : UString(); } inline JSObject* JSValue::getObject() const { - return JSImmediate::isImmediate(asValue()) ? 0 : asCell()->getObject(); + return isCell() ? asCell()->getObject() : 0; } inline CallType JSValue::getCallData(CallData& callData) { - return JSImmediate::isImmediate(asValue()) ? CallTypeNone : asCell()->getCallData(callData); + return isCell() ? asCell()->getCallData(callData) : CallTypeNone; } inline ConstructType JSValue::getConstructData(ConstructData& constructData) { - return JSImmediate::isImmediate(asValue()) ? ConstructTypeNone : asCell()->getConstructData(constructData); + return isCell() ? asCell()->getConstructData(constructData) : ConstructTypeNone; } ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::getUInt32(asValue(), v) : asCell()->getUInt32(v); + if (isInt32()) { + int32_t i = asInt32(); + v = static_cast<uint32_t>(i); + return i >= 0; + } + if (isDouble()) { + double d = asDouble(); + v = static_cast<uint32_t>(d); + return v == d; + } + return false; } - ALWAYS_INLINE bool JSValue::getTruncatedInt32(int32_t& v) const + inline void JSValue::markDirect() { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::getTruncatedInt32(asValue(), v) : asCell()->getTruncatedInt32(v); + ASSERT(!marked()); + asCell()->markCellDirect(); } - inline bool JSValue::getTruncatedUInt32(uint32_t& v) const + inline void JSValue::markChildren(MarkStack& markStack) { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::getTruncatedUInt32(asValue(), v) : asCell()->getTruncatedUInt32(v); + ASSERT(marked()); + asCell()->markChildren(markStack); } - inline void JSValue::mark() + inline bool JSValue::marked() const { - asCell()->mark(); // callers should check !marked() before calling mark(), so this should only be called with cells + return !isCell() || asCell()->marked(); } - inline bool JSValue::marked() const +#if !USE(JSVALUE32_64) + ALWAYS_INLINE JSCell* JSValue::asCell() const { - return JSImmediate::isImmediate(asValue()) || asCell()->marked(); + ASSERT(isCell()); + return m_ptr; } +#endif // !USE(JSVALUE32_64) inline JSValue JSValue::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const { - return JSImmediate::isImmediate(asValue()) ? asValue() : asCell()->toPrimitive(exec, preferredType); + return isCell() ? asCell()->toPrimitive(exec, preferredType) : asValue(); } inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value) { - if (JSImmediate::isImmediate(asValue())) { - number = JSImmediate::toDouble(asValue()); - value = asValue(); + if (isInt32()) { + number = asInt32(); + value = *this; + return true; + } + if (isDouble()) { + number = asDouble(); + value = *this; + return true; + } + if (isCell()) + return asCell()->getPrimitiveNumber(exec, number, value); + if (isTrue()) { + number = 1.0; + value = *this; + return true; + } + if (isFalse() || isNull()) { + number = 0.0; + value = *this; return true; } - return asCell()->getPrimitiveNumber(exec, number, value); + ASSERT(isUndefined()); + number = nonInlineNaN(); + value = *this; + return true; } inline bool JSValue::toBoolean(ExecState* exec) const { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::toBoolean(asValue()) : asCell()->toBoolean(exec); + if (isInt32()) + return asInt32() != 0; + if (isDouble()) + return asDouble() > 0.0 || asDouble() < 0.0; // false for NaN + if (isCell()) + return asCell()->toBoolean(exec); + return isTrue(); // false, null, and undefined all convert to false. } ALWAYS_INLINE double JSValue::toNumber(ExecState* exec) const { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::toDouble(asValue()) : asCell()->toNumber(exec); + if (isInt32()) + return asInt32(); + if (isDouble()) + return asDouble(); + if (isCell()) + return asCell()->toNumber(exec); + if (isTrue()) + return 1.0; + return isUndefined() ? nonInlineNaN() : 0; // null and false both convert to 0. } inline UString JSValue::toString(ExecState* exec) const { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::toString(asValue()) : asCell()->toString(exec); - } - - inline JSObject* JSValue::toObject(ExecState* exec) const - { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::toObject(asValue(), exec) : asCell()->toObject(exec); - } - - inline JSObject* JSValue::toThisObject(ExecState* exec) const - { - if (UNLIKELY(JSImmediate::isImmediate(asValue()))) - return JSImmediate::toThisObject(asValue(), exec); - return asCell()->toThisObject(exec); + if (isCell()) + return asCell()->toString(exec); + if (isInt32()) + return UString::from(asInt32()); + if (isDouble()) + return asDouble() == 0.0 ? "0" : UString::from(asDouble()); + if (isTrue()) + return "true"; + if (isFalse()) + return "false"; + if (isNull()) + return "null"; + ASSERT(isUndefined()); + return "undefined"; } inline bool JSValue::needsThisConversion() const { - if (UNLIKELY(JSImmediate::isImmediate(asValue()))) + if (UNLIKELY(!isCell())) return true; return asCell()->structure()->typeInfo().needsThisConversion(); } inline UString JSValue::toThisString(ExecState* exec) const { - return JSImmediate::isImmediate(asValue()) ? JSImmediate::toString(asValue()) : asCell()->toThisString(exec); + return isCell() ? asCell()->toThisString(exec) : toString(exec); } inline JSValue JSValue::getJSNumber() { - return JSImmediate::isNumber(asValue()) ? asValue() : JSImmediate::isImmediate(asValue()) ? JSValue() : asCell()->getJSNumber(); + if (isInt32() || isDouble()) + return *this; + if (isCell()) + return asCell()->getJSNumber(); + return JSValue(); + } + + inline bool JSValue::hasChildren() const + { + return asCell()->structure()->typeInfo().type() >= CompoundType; + } + + + inline JSObject* JSValue::toObject(ExecState* exec) const + { + return isCell() ? asCell()->toObject(exec) : toObjectSlowCase(exec); } + inline JSObject* JSValue::toThisObject(ExecState* exec) const + { + return isCell() ? asCell()->toThisObject(exec) : toThisObjectSlowCase(exec); + } + + ALWAYS_INLINE void MarkStack::append(JSCell* cell) + { + ASSERT(cell); + if (cell->marked()) + return; + cell->markCellDirect(); + if (cell->structure()->typeInfo().type() >= CompoundType) + m_values.append(cell); + } + + inline void MarkStack::drain() { + while (!m_markSets.isEmpty() || !m_values.isEmpty()) { + while ((!m_markSets.isEmpty()) && m_values.size() < 50) { + const MarkSet& current = m_markSets.removeLast(); + JSValue* ptr = current.m_values; + JSValue* end = current.m_end; + if (current.m_properties == NoNullValues) { + while (ptr != end) + append(*ptr++); + } else { + while (ptr != end) { + if (JSValue value = *ptr++) + append(value); + } + } + } + while (!m_values.isEmpty()) { + JSCell* current = m_values.removeLast(); + ASSERT(current->marked()); + current->markChildren(*this); + } + } + } } // namespace JSC #endif // JSCell_h diff --git a/JavaScriptCore/runtime/JSFunction.cpp b/JavaScriptCore/runtime/JSFunction.cpp index f456451..84c6263 100644 --- a/JavaScriptCore/runtime/JSFunction.cpp +++ b/JavaScriptCore/runtime/JSFunction.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2002 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. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * @@ -48,7 +48,7 @@ const ClassInfo JSFunction::info = { "Function", &InternalFunction::info, 0, 0 } JSFunction::JSFunction(ExecState* exec, PassRefPtr<Structure> structure, int length, const Identifier& name, NativeFunction func) : Base(&exec->globalData(), structure, name) #if ENABLE(JIT) - , m_body(exec->globalData().nativeFunctionThunk()) + , m_body(FunctionBodyNode::createNativeThunk(&exec->globalData())) #else , m_body(0) #endif @@ -72,26 +72,23 @@ JSFunction::JSFunction(ExecState* exec, const Identifier& name, FunctionBodyNode JSFunction::~JSFunction() { -#if ENABLE(JIT) // JIT code for other functions may have had calls linked directly to the code for this function; these links // are based on a check for the this pointer value for this JSFunction - which will no longer be valid once // this memory is freed and may be reused (potentially for another, different JSFunction). - if (!isHostFunction()) { - if (m_body && m_body->isGenerated()) - m_body->generatedBytecode().unlinkCallers(); - scopeChain().~ScopeChain(); - } - +#if ENABLE(JIT_OPTIMIZE_CALL) + if (m_body && m_body->isGenerated()) + m_body->generatedBytecode().unlinkCallers(); #endif + if (!isHostFunction()) + scopeChain().~ScopeChain(); // FIXME: Don't we need to do this in the interpreter too? } -void JSFunction::mark() +void JSFunction::markChildren(MarkStack& markStack) { - Base::mark(); - if (!isHostFunction()) { - m_body->mark(); - scopeChain().mark(); - } + Base::markChildren(markStack); + m_body->markAggregate(markStack); + if (!isHostFunction()) + scopeChain().markAggregate(markStack); } CallType JSFunction::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/JSFunction.h b/JavaScriptCore/runtime/JSFunction.h index b27e515..cab1e5b 100644 --- a/JavaScriptCore/runtime/JSFunction.h +++ b/JavaScriptCore/runtime/JSFunction.h @@ -39,7 +39,7 @@ namespace JSC { class JSFunction : public InternalFunction { friend class JIT; - friend class VPtrSet; + friend struct VPtrSet; typedef InternalFunction Base; @@ -68,7 +68,7 @@ namespace JSC { void setBody(PassRefPtr<FunctionBodyNode> body) { m_body = body; } FunctionBodyNode* body() const { return m_body.get(); } - virtual void mark(); + virtual void markChildren(MarkStack&); static JS_EXPORTDATA const ClassInfo info; diff --git a/JavaScriptCore/runtime/JSGlobalData.cpp b/JavaScriptCore/runtime/JSGlobalData.cpp index 1594848..03df41d 100644 --- a/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/JavaScriptCore/runtime/JSGlobalData.cpp @@ -33,14 +33,17 @@ #include "Collector.h" #include "CommonIdentifiers.h" #include "FunctionConstructor.h" +#include "GetterSetter.h" #include "Interpreter.h" #include "JSActivation.h" +#include "JSAPIValueWrapper.h" #include "JSArray.h" #include "JSByteArray.h" #include "JSClassRef.h" #include "JSFunction.h" #include "JSLock.h" #include "JSNotAnObject.h" +#include "JSPropertyNameIterator.h" #include "JSStaticScopeObject.h" #include "Parser.h" #include "Lexer.h" @@ -59,13 +62,14 @@ using namespace WTF; namespace JSC { -extern const HashTable arrayTable; -extern const HashTable dateTable; -extern const HashTable mathTable; -extern const HashTable numberTable; -extern const HashTable regExpTable; -extern const HashTable regExpConstructorTable; -extern const HashTable stringTable; +extern JSC_CONST_HASHTABLE HashTable arrayTable; +extern JSC_CONST_HASHTABLE HashTable jsonTable; +extern JSC_CONST_HASHTABLE HashTable dateTable; +extern JSC_CONST_HASHTABLE HashTable mathTable; +extern JSC_CONST_HASHTABLE HashTable numberTable; +extern JSC_CONST_HASHTABLE HashTable regExpTable; +extern JSC_CONST_HASHTABLE HashTable regExpConstructorTable; +extern JSC_CONST_HASHTABLE HashTable stringTable; struct VPtrSet { VPtrSet(); @@ -105,6 +109,7 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) , clientData(0) , arrayTable(fastNew<HashTable>(JSC::arrayTable)) , dateTable(fastNew<HashTable>(JSC::dateTable)) + , jsonTable(fastNew<HashTable>(JSC::jsonTable)) , mathTable(fastNew<HashTable>(JSC::mathTable)) , numberTable(fastNew<HashTable>(JSC::numberTable)) , regExpTable(fastNew<HashTable>(JSC::regExpTable)) @@ -116,7 +121,10 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) , stringStructure(JSString::createStructure(jsNull())) , notAnObjectErrorStubStructure(JSNotAnObjectErrorStub::createStructure(jsNull())) , notAnObjectStructure(JSNotAnObject::createStructure(jsNull())) -#if !USE(ALTERNATE_JSIMMEDIATE) + , propertyNameIteratorStructure(JSPropertyNameIterator::createStructure(jsNull())) + , getterSetterStructure(GetterSetter::createStructure(jsNull())) + , apiWrapperStructure(JSAPIValueWrapper::createStructure(jsNull())) +#if USE(JSVALUE32) , numberStructure(JSNumberCell::createStructure(jsNull())) #endif , jsArrayVPtr(vptrSet.jsArrayVPtr) @@ -137,6 +145,7 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) , head(0) , dynamicGlobalObject(0) , scopeNodeBeingReparsed(0) + , firstStringifierToMark(0) { #if PLATFORM(MAC) startProfilerServerIfNeeded(); @@ -155,17 +164,16 @@ JSGlobalData::~JSGlobalData() arrayTable->deleteTable(); dateTable->deleteTable(); + jsonTable->deleteTable(); mathTable->deleteTable(); numberTable->deleteTable(); regExpTable->deleteTable(); regExpConstructorTable->deleteTable(); stringTable->deleteTable(); -#if ENABLE(JIT) - lazyNativeFunctionThunk.clear(); -#endif fastDelete(const_cast<HashTable*>(arrayTable)); fastDelete(const_cast<HashTable*>(dateTable)); + fastDelete(const_cast<HashTable*>(jsonTable)); fastDelete(const_cast<HashTable*>(mathTable)); fastDelete(const_cast<HashTable*>(numberTable)); fastDelete(const_cast<HashTable*>(regExpTable)); @@ -222,15 +230,6 @@ JSGlobalData*& JSGlobalData::sharedInstanceInternal() return sharedInstance; } -#if ENABLE(JIT) - -void JSGlobalData::createNativeThunk() -{ - lazyNativeFunctionThunk = FunctionBodyNode::createNativeThunk(this); -} - -#endif - // FIXME: We can also detect forms like v1 < v2 ? -1 : 0, reverse comparison, etc. const Vector<Instruction>& JSGlobalData::numericCompareFunction(ExecState* exec) { diff --git a/JavaScriptCore/runtime/JSGlobalData.h b/JavaScriptCore/runtime/JSGlobalData.h index e53746b..88cb516 100644 --- a/JavaScriptCore/runtime/JSGlobalData.h +++ b/JavaScriptCore/runtime/JSGlobalData.h @@ -33,6 +33,7 @@ #include "ExecutableAllocator.h" #include "JITStubs.h" #include "JSValue.h" +#include "MarkStack.h" #include "SmallStrings.h" #include "TimeoutChecker.h" #include <wtf/Forward.h> @@ -47,16 +48,18 @@ namespace JSC { class CommonIdentifiers; class FunctionBodyNode; class IdentifierTable; - class Instruction; class Interpreter; class JSGlobalObject; class JSObject; class Lexer; class Parser; class ScopeNode; + class Stringifier; class Structure; class UString; + struct HashTable; + struct Instruction; struct VPtrSet; class JSGlobalData : public RefCounted<JSGlobalData> { @@ -82,6 +85,7 @@ namespace JSC { const HashTable* arrayTable; const HashTable* dateTable; + const HashTable* jsonTable; const HashTable* mathTable; const HashTable* numberTable; const HashTable* regExpTable; @@ -94,7 +98,11 @@ namespace JSC { RefPtr<Structure> stringStructure; RefPtr<Structure> notAnObjectErrorStubStructure; RefPtr<Structure> notAnObjectStructure; -#if !USE(ALTERNATE_JSIMMEDIATE) + RefPtr<Structure> propertyNameIteratorStructure; + RefPtr<Structure> getterSetterStructure; + RefPtr<Structure> apiWrapperStructure; + +#if USE(JSVALUE32) RefPtr<Structure> numberStructure; #endif @@ -117,20 +125,13 @@ namespace JSC { Interpreter* interpreter; #if ENABLE(JIT) JITThunks jitStubs; - FunctionBodyNode* nativeFunctionThunk() - { - if (!lazyNativeFunctionThunk) - createNativeThunk(); - return lazyNativeFunctionThunk.get(); - } - RefPtr<FunctionBodyNode> lazyNativeFunctionThunk; #endif TimeoutChecker timeoutChecker; Heap heap; JSValue exception; #if ENABLE(JIT) - void* exceptionLocation; + ReturnAddressPtr exceptionLocation; #endif const Vector<Instruction>& numericCompareFunction(ExecState*); @@ -145,7 +146,9 @@ namespace JSC { HashSet<JSObject*> arrayVisitedElements; ScopeNode* scopeNodeBeingReparsed; + Stringifier* firstStringifierToMark; + MarkStack markStack; private: JSGlobalData(bool isShared, const VPtrSet&); static JSGlobalData*& sharedInstanceInternal(); diff --git a/JavaScriptCore/runtime/JSGlobalObject.cpp b/JavaScriptCore/runtime/JSGlobalObject.cpp index 1e9f670..a90f18f 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -50,6 +50,7 @@ #include "JSFunction.h" #include "JSGlobalObjectFunctions.h" #include "JSLock.h" +#include "JSONObject.h" #include "Interpreter.h" #include "MathObject.h" #include "NativeErrorConstructor.h" @@ -79,16 +80,16 @@ static const int initialTickCountThreshold = 255; // Preferred number of milliseconds between each timeout check static const int preferredScriptCheckTimeInterval = 1000; -static inline void markIfNeeded(JSValue v) +static inline void markIfNeeded(MarkStack& markStack, JSValue v) { - if (v && !v.marked()) - v.mark(); + if (v) + markStack.append(v); } -static inline void markIfNeeded(const RefPtr<Structure>& s) +static inline void markIfNeeded(MarkStack& markStack, const RefPtr<Structure>& s) { if (s) - s->mark(); + s->markAggregate(markStack); } JSGlobalObject::~JSGlobalObject() @@ -255,7 +256,7 @@ void JSGlobalObject::reset(JSValue prototype) // Constructors - JSCell* objectConstructor = new (exec) ObjectConstructor(exec, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype); + JSCell* objectConstructor = new (exec) ObjectConstructor(exec, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype, d()->prototypeFunctionStructure.get()); JSCell* functionConstructor = new (exec) FunctionConstructor(exec, FunctionConstructor::createStructure(d()->functionPrototype), d()->functionPrototype); JSCell* arrayConstructor = new (exec) ArrayConstructor(exec, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype); JSCell* stringConstructor = new (exec) StringConstructor(exec, StringConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->stringPrototype); @@ -318,7 +319,8 @@ void JSGlobalObject::reset(JSValue prototype) GlobalPropertyInfo(Identifier(exec, "Math"), new (exec) MathObject(exec, MathObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete), GlobalPropertyInfo(Identifier(exec, "NaN"), jsNaN(exec), DontEnum | DontDelete), GlobalPropertyInfo(Identifier(exec, "Infinity"), jsNumber(exec, Inf), DontEnum | DontDelete), - GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete) + GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete), + GlobalPropertyInfo(Identifier(exec, "JSON"), new (exec) JSONObject(JSONObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete) }; addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo)); @@ -355,43 +357,43 @@ void JSGlobalObject::resetPrototype(JSValue prototype) oldLastInPrototypeChain->setPrototype(objectPrototype); } -void JSGlobalObject::mark() +void JSGlobalObject::markChildren(MarkStack& markStack) { - JSVariableObject::mark(); + JSVariableObject::markChildren(markStack); HashSet<ProgramCodeBlock*>::const_iterator end = codeBlocks().end(); for (HashSet<ProgramCodeBlock*>::const_iterator it = codeBlocks().begin(); it != end; ++it) - (*it)->mark(); + (*it)->markAggregate(markStack); RegisterFile& registerFile = globalData()->interpreter->registerFile(); if (registerFile.globalObject() == this) - registerFile.markGlobals(&globalData()->heap); - - markIfNeeded(d()->regExpConstructor); - markIfNeeded(d()->errorConstructor); - markIfNeeded(d()->evalErrorConstructor); - markIfNeeded(d()->rangeErrorConstructor); - markIfNeeded(d()->referenceErrorConstructor); - markIfNeeded(d()->syntaxErrorConstructor); - markIfNeeded(d()->typeErrorConstructor); - markIfNeeded(d()->URIErrorConstructor); - - markIfNeeded(d()->evalFunction); - markIfNeeded(d()->callFunction); - markIfNeeded(d()->applyFunction); - - markIfNeeded(d()->objectPrototype); - markIfNeeded(d()->functionPrototype); - markIfNeeded(d()->arrayPrototype); - markIfNeeded(d()->booleanPrototype); - markIfNeeded(d()->stringPrototype); - markIfNeeded(d()->numberPrototype); - markIfNeeded(d()->datePrototype); - markIfNeeded(d()->regExpPrototype); - - markIfNeeded(d()->methodCallDummy); - - markIfNeeded(d()->errorStructure); + registerFile.markGlobals(markStack, &globalData()->heap); + + markIfNeeded(markStack, d()->regExpConstructor); + markIfNeeded(markStack, d()->errorConstructor); + markIfNeeded(markStack, d()->evalErrorConstructor); + markIfNeeded(markStack, d()->rangeErrorConstructor); + markIfNeeded(markStack, d()->referenceErrorConstructor); + markIfNeeded(markStack, d()->syntaxErrorConstructor); + markIfNeeded(markStack, d()->typeErrorConstructor); + markIfNeeded(markStack, d()->URIErrorConstructor); + + markIfNeeded(markStack, d()->evalFunction); + markIfNeeded(markStack, d()->callFunction); + markIfNeeded(markStack, d()->applyFunction); + + markIfNeeded(markStack, d()->objectPrototype); + markIfNeeded(markStack, d()->functionPrototype); + markIfNeeded(markStack, d()->arrayPrototype); + markIfNeeded(markStack, d()->booleanPrototype); + markIfNeeded(markStack, d()->stringPrototype); + markIfNeeded(markStack, d()->numberPrototype); + markIfNeeded(markStack, d()->datePrototype); + markIfNeeded(markStack, d()->regExpPrototype); + + markIfNeeded(markStack, d()->methodCallDummy); + + markIfNeeded(markStack, d()->errorStructure); // No need to mark the other structures, because their prototypes are all // guaranteed to be referenced elsewhere. @@ -401,11 +403,7 @@ void JSGlobalObject::mark() return; size_t size = d()->registerArraySize; - for (size_t i = 0; i < size; ++i) { - Register& r = registerArray[i]; - if (!r.marked()) - r.mark(); - } + markStack.appendValues(reinterpret_cast<JSValue*>(registerArray), size); } ExecState* JSGlobalObject::globalExec() diff --git a/JavaScriptCore/runtime/JSGlobalObject.h b/JavaScriptCore/runtime/JSGlobalObject.h index da9a819..cda49bd 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.h +++ b/JavaScriptCore/runtime/JSGlobalObject.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Eric Seidel <eric@webkit.org> - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 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 @@ -166,10 +166,10 @@ namespace JSC { public: virtual ~JSGlobalObject(); - virtual void mark(); + virtual void markChildren(MarkStack&); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); - virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable); + virtual bool hasOwnPropertyForWrite(ExecState*, const Identifier&); virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); @@ -325,10 +325,12 @@ namespace JSC { return symbolTableGet(propertyName, slot); } - inline bool JSGlobalObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot, bool& slotIsWriteable) + inline bool JSGlobalObject::hasOwnPropertyForWrite(ExecState* exec, const Identifier& propertyName) { - if (JSVariableObject::getOwnPropertySlotForWrite(exec, propertyName, slot, slotIsWriteable)) + PropertySlot slot; + if (JSVariableObject::getOwnPropertySlot(exec, propertyName, slot)) return true; + bool slotIsWriteable; return symbolTableGet(propertyName, slot, slotIsWriteable); } @@ -345,11 +347,16 @@ namespace JSC { if (typeInfo().type() == ObjectType) return m_prototype; +#if USE(JSVALUE32) if (typeInfo().type() == StringType) return exec->lexicalGlobalObject()->stringPrototype(); ASSERT(typeInfo().type() == NumberType); return exec->lexicalGlobalObject()->numberPrototype(); +#else + ASSERT(typeInfo().type() == StringType); + return exec->lexicalGlobalObject()->stringPrototype(); +#endif } inline StructureChain* Structure::prototypeChain(ExecState* exec) const @@ -389,7 +396,7 @@ namespace JSC { return globalData().dynamicGlobalObject; } - class DynamicGlobalObjectScope : Noncopyable { + class DynamicGlobalObjectScope : public Noncopyable { public: DynamicGlobalObjectScope(CallFrame* callFrame, JSGlobalObject* dynamicGlobalObject) : m_dynamicGlobalObjectSlot(callFrame->globalData().dynamicGlobalObject) diff --git a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index b013957..affb99c 100644 --- a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -282,7 +282,7 @@ JSValue JSC_HOST_CALL globalFuncEval(ExecState* exec, JSObject* function, JSValu UString s = x.toString(exec); - LiteralParser preparser(exec, s); + LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) return parsedObject; @@ -303,14 +303,18 @@ JSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec, JSObject*, JSValue, co JSValue value = args.at(0); int32_t radix = args.at(1).toInt32(exec); - if (value.isNumber() && (radix == 0 || radix == 10)) { - if (value.isInt32Fast()) - return value; - double d = value.uncheckedGetNumber(); + if (radix != 0 && radix != 10) + return jsNumber(exec, parseInt(value.toString(exec), radix)); + + if (value.isInt32()) + return value; + + if (value.isDouble()) { + double d = value.asDouble(); if (isfinite(d)) return jsNumber(exec, (d > 0) ? floor(d) : ceil(d)); if (isnan(d) || isinf(d)) - return jsNaN(&exec->globalData()); + return jsNaN(exec); return jsNumber(exec, 0); } diff --git a/JavaScriptCore/runtime/JSImmediate.cpp b/JavaScriptCore/runtime/JSImmediate.cpp index 201e56c..846238d 100644 --- a/JavaScriptCore/runtime/JSImmediate.cpp +++ b/JavaScriptCore/runtime/JSImmediate.cpp @@ -21,83 +21,6 @@ #include "config.h" #include "JSImmediate.h" -#include "BooleanConstructor.h" -#include "BooleanPrototype.h" -#include "Error.h" -#include "ExceptionHelpers.h" -#include "JSGlobalObject.h" -#include "JSNotAnObject.h" -#include "NumberConstructor.h" -#include "NumberPrototype.h" - namespace JSC { -JSObject* JSImmediate::toThisObject(JSValue v, ExecState* exec) -{ - ASSERT(isImmediate(v)); - if (isNumber(v)) - return constructNumber(exec, v); - if (isBoolean(v)) - return constructBooleanFromImmediateBoolean(exec, v); - ASSERT(v.isUndefinedOrNull()); - return exec->globalThisValue(); -} - -JSObject* JSImmediate::toObject(JSValue v, ExecState* exec) -{ - ASSERT(isImmediate(v)); - if (isNumber(v)) - return constructNumber(exec, v); - if (isBoolean(v)) - return constructBooleanFromImmediateBoolean(exec, v); - - JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, v.isNull()); - exec->setException(exception); - return new (exec) JSNotAnObject(exec, exception); -} - -JSObject* JSImmediate::prototype(JSValue v, ExecState* exec) -{ - ASSERT(isImmediate(v)); - if (isNumber(v)) - return exec->lexicalGlobalObject()->numberPrototype(); - if (isBoolean(v)) - return exec->lexicalGlobalObject()->booleanPrototype(); - - JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, v.isNull()); - exec->setException(exception); - return new (exec) JSNotAnObject(exec, exception); -} - -UString JSImmediate::toString(JSValue v) -{ - ASSERT(isImmediate(v)); - if (isIntegerNumber(v)) - return UString::from(getTruncatedInt32(v)); -#if USE(ALTERNATE_JSIMMEDIATE) - if (isNumber(v)) { - ASSERT(isDoubleNumber(v)); - double value = doubleValue(v); - if (value == 0.0) // +0.0 or -0.0 - return "0"; - return UString::from(value); - } -#else - ASSERT(!isNumber(v)); -#endif - if (jsBoolean(false) == v) - return "false"; - if (jsBoolean(true) == v) - return "true"; - if (v.isNull()) - return "null"; - ASSERT(v.isUndefined()); - return "undefined"; -} - -NEVER_INLINE double JSImmediate::nonInlineNaN() -{ - return std::numeric_limits<double>::quiet_NaN(); -} - } // namespace JSC diff --git a/JavaScriptCore/runtime/JSImmediate.h b/JavaScriptCore/runtime/JSImmediate.h index 706396e..4ed35fc 100644 --- a/JavaScriptCore/runtime/JSImmediate.h +++ b/JavaScriptCore/runtime/JSImmediate.h @@ -22,6 +22,10 @@ #ifndef JSImmediate_h #define JSImmediate_h +#include <wtf/Platform.h> + +#if !USE(JSVALUE32_64) + #include <wtf/Assertions.h> #include <wtf/AlwaysInline.h> #include <wtf/MathExtras.h> @@ -42,7 +46,7 @@ namespace JSC { class JSObject; class UString; -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) inline intptr_t reinterpretDoubleToIntptr(double value) { return WTF::bitwise_cast<intptr_t>(value); @@ -98,7 +102,7 @@ namespace JSC { /* * On 64-bit platforms, we support an alternative encoding form for immediates, if - * USE(ALTERNATE_JSIMMEDIATE) is defined. When this format is used, double precision + * USE(JSVALUE64) is defined. When this format is used, double precision * floating point values may also be encoded as JSImmediates. * * The encoding makes use of unused NaN space in the IEEE754 representation. Any value @@ -155,7 +159,7 @@ namespace JSC { friend JSValue jsNumber(JSGlobalData* globalData, long long i); friend JSValue jsNumber(JSGlobalData* globalData, unsigned long long i); -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) // If all bits in the mask are set, this indicates an integer number, // if any but not all are set this value is a double precision number. static const intptr_t TagTypeNumber = 0xffff000000000000ll; @@ -177,7 +181,7 @@ namespace JSC { static const intptr_t FullTagTypeUndefined = TagBitTypeOther | ExtendedTagBitUndefined; static const intptr_t FullTagTypeNull = TagBitTypeOther; -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) static const int32_t IntegerPayloadShift = 0; #else static const int32_t IntegerPayloadShift = 1; @@ -200,15 +204,15 @@ namespace JSC { static ALWAYS_INLINE bool isIntegerNumber(JSValue v) { -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) return (rawValue(v) & TagTypeNumber) == TagTypeNumber; #else return isNumber(v); #endif } -#if USE(ALTERNATE_JSIMMEDIATE) - static ALWAYS_INLINE bool isDoubleNumber(JSValue v) +#if USE(JSVALUE64) + static ALWAYS_INLINE bool isDouble(JSValue v) { return isNumber(v) && !isIntegerNumber(v); } @@ -256,7 +260,7 @@ namespace JSC { static ALWAYS_INLINE bool areBothImmediateIntegerNumbers(JSValue v1, JSValue v2) { -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) return (rawValue(v1) & rawValue(v2) & TagTypeNumber) == TagTypeNumber; #else return rawValue(v1) & rawValue(v2) & TagTypeNumber; @@ -265,9 +269,6 @@ namespace JSC { static double toDouble(JSValue); static bool toBoolean(JSValue); - static JSObject* toObject(JSValue, ExecState*); - static JSObject* toThisObject(JSValue, ExecState*); - static UString toString(JSValue); static bool getUInt32(JSValue, uint32_t&); static bool getTruncatedInt32(JSValue, int32_t&); @@ -283,10 +284,8 @@ namespace JSC { static JSValue zeroImmediate(); static JSValue oneImmediate(); - static JSObject* prototype(JSValue, ExecState*); - private: -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) static const int minImmediateInt = ((-INT_MAX) - 1); static const int maxImmediateInt = INT_MAX; #else @@ -300,10 +299,10 @@ namespace JSC { return JSValue::makeImmediate(integer); } - // With USE(ALTERNATE_JSIMMEDIATE) we want the argument to be zero extended, so the + // With USE(JSVALUE64) we want the argument to be zero extended, so the // integer doesn't interfere with the tag bits in the upper word. In the default encoding, // if intptr_t id larger then int32_t we sign extend the value through the upper word. -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) static ALWAYS_INLINE JSValue makeInt(uint32_t value) #else static ALWAYS_INLINE JSValue makeInt(int32_t value) @@ -312,7 +311,7 @@ namespace JSC { return makeValue((static_cast<intptr_t>(value) << IntegerPayloadShift) | TagTypeNumber); } -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) static ALWAYS_INLINE JSValue makeDouble(double value) { return makeValue(reinterpretDoubleToIntptr(value) + DoubleEncodeOffset); @@ -337,7 +336,7 @@ namespace JSC { template<typename T> static JSValue fromNumberOutsideIntegerRange(T); -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) static ALWAYS_INLINE double doubleValue(JSValue v) { return reinterpretIntptrToDouble(rawValue(v) - DoubleEncodeOffset); @@ -363,8 +362,6 @@ namespace JSC { { return v.immediateValue(); } - - static double nonInlineNaN(); }; ALWAYS_INLINE JSValue JSImmediate::trueImmediate() { return makeBool(true); } @@ -374,7 +371,7 @@ namespace JSC { ALWAYS_INLINE JSValue JSImmediate::zeroImmediate() { return makeInt(0); } ALWAYS_INLINE JSValue JSImmediate::oneImmediate() { return makeInt(1); } -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) inline bool doubleToBoolean(double value) { return value < 0.0 || value > 0.0; @@ -401,7 +398,7 @@ namespace JSC { return intValue(v); } -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) template<typename T> inline JSValue JSImmediate::fromNumberOutsideIntegerRange(T value) { @@ -442,7 +439,7 @@ namespace JSC { ALWAYS_INLINE JSValue JSImmediate::from(int i) { -#if !USE(ALTERNATE_JSIMMEDIATE) +#if !USE(JSVALUE64) if ((i < minImmediateInt) | (i > maxImmediateInt)) return fromNumberOutsideIntegerRange(i); #endif @@ -508,9 +505,9 @@ namespace JSC { if (isIntegerNumber(v)) return intValue(v); -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) if (isNumber(v)) { - ASSERT(isDoubleNumber(v)); + ASSERT(isDouble(v)); return doubleValue(v); } #else @@ -541,12 +538,6 @@ namespace JSC { return getUInt32(v, i); } - // These are identical logic to the JSValue functions above, and faster than jsNumber(number).toInt32(). - int32_t toInt32(double); - uint32_t toUInt32(double); - int32_t toInt32SlowCase(double, bool& ok); - uint32_t toUInt32SlowCase(double, bool& ok); - inline JSValue::JSValue(JSNullTag) { *this = JSImmediate::nullImmediate(); @@ -577,6 +568,16 @@ namespace JSC { return JSImmediate::isBoolean(asValue()); } + inline bool JSValue::isTrue() const + { + return asValue() == JSImmediate::trueImmediate(); + } + + inline bool JSValue::isFalse() const + { + return asValue() == JSImmediate::falseImmediate(); + } + inline bool JSValue::getBoolean(bool& v) const { if (JSImmediate::isBoolean(asValue())) { @@ -592,99 +593,33 @@ namespace JSC { return asValue() == jsBoolean(true); } - ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const - { - int32_t i; - if (getTruncatedInt32(i)) - return i; - bool ignored; - return toInt32SlowCase(toNumber(exec), ignored); - } - - inline uint32_t JSValue::toUInt32(ExecState* exec) const - { - uint32_t i; - if (getTruncatedUInt32(i)) - return i; - bool ignored; - return toUInt32SlowCase(toNumber(exec), ignored); - } - - inline int32_t toInt32(double val) - { - if (!(val >= -2147483648.0 && val < 2147483648.0)) { - bool ignored; - return toInt32SlowCase(val, ignored); - } - return static_cast<int32_t>(val); - } - - inline uint32_t toUInt32(double val) - { - if (!(val >= 0.0 && val < 4294967296.0)) { - bool ignored; - return toUInt32SlowCase(val, ignored); - } - return static_cast<uint32_t>(val); - } - - inline int32_t JSValue::toInt32(ExecState* exec, bool& ok) const - { - int32_t i; - if (getTruncatedInt32(i)) { - ok = true; - return i; - } - return toInt32SlowCase(toNumber(exec), ok); - } - - inline uint32_t JSValue::toUInt32(ExecState* exec, bool& ok) const - { - uint32_t i; - if (getTruncatedUInt32(i)) { - ok = true; - return i; - } - return toUInt32SlowCase(toNumber(exec), ok); - } - inline bool JSValue::isCell() const { return !JSImmediate::isImmediate(asValue()); } - inline bool JSValue::isInt32Fast() const + inline bool JSValue::isInt32() const { return JSImmediate::isIntegerNumber(asValue()); } - inline int32_t JSValue::getInt32Fast() const + inline int32_t JSValue::asInt32() const { - ASSERT(isInt32Fast()); + ASSERT(isInt32()); return JSImmediate::getTruncatedInt32(asValue()); } - inline bool JSValue::isUInt32Fast() const + inline bool JSValue::isUInt32() const { return JSImmediate::isPositiveIntegerNumber(asValue()); } - inline uint32_t JSValue::getUInt32Fast() const + inline uint32_t JSValue::asUInt32() const { - ASSERT(isUInt32Fast()); + ASSERT(isUInt32()); return JSImmediate::getTruncatedUInt32(asValue()); } - inline JSValue JSValue::makeInt32Fast(int32_t i) - { - return JSImmediate::from(i); - } - - inline bool JSValue::areBothInt32Fast(JSValue v1, JSValue v2) - { - return JSImmediate::areBothImmediateIntegerNumbers(v1, v2); - } - class JSFastMath { public: static ALWAYS_INLINE bool canDoFastBitwiseOperations(JSValue v1, JSValue v2) @@ -735,7 +670,7 @@ namespace JSC { static ALWAYS_INLINE JSValue rightShiftImmediateNumbers(JSValue val, JSValue shift) { ASSERT(canDoFastRshift(val, shift) || canDoFastUrshift(val, shift)); -#if USE(ALTERNATE_JSIMMEDIATE) +#if USE(JSVALUE64) return JSImmediate::makeValue(static_cast<intptr_t>(static_cast<uint32_t>(static_cast<int32_t>(JSImmediate::rawValue(val)) >> ((JSImmediate::rawValue(shift) >> JSImmediate::IntegerPayloadShift) & 0x1f))) | JSImmediate::TagTypeNumber); #else return JSImmediate::makeValue((JSImmediate::rawValue(val) >> ((JSImmediate::rawValue(shift) >> JSImmediate::IntegerPayloadShift) & 0x1f)) | JSImmediate::TagTypeNumber); @@ -783,4 +718,6 @@ namespace JSC { } // namespace JSC +#endif // !USE(JSVALUE32_64) + #endif // JSImmediate_h diff --git a/JavaScriptCore/runtime/JSLock.cpp b/JavaScriptCore/runtime/JSLock.cpp index 7ece5da..8f056c8 100644 --- a/JavaScriptCore/runtime/JSLock.cpp +++ b/JavaScriptCore/runtime/JSLock.cpp @@ -60,23 +60,23 @@ static void setLockCount(intptr_t count) } JSLock::JSLock(ExecState* exec) - : m_lockingForReal(exec->globalData().isSharedInstance) + : m_lockBehavior(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly) { - lock(m_lockingForReal); + lock(m_lockBehavior); } -void JSLock::lock(bool lockForReal) +void JSLock::lock(JSLockBehavior lockBehavior) { #ifdef NDEBUG // Locking "not for real" is a debug-only feature. - if (!lockForReal) + if (lockBehavior == SilenceAssertionsOnly) return; #endif pthread_once(&createJSLockCountOnce, createJSLockCount); intptr_t currentLockCount = lockCount(); - if (!currentLockCount && lockForReal) { + if (!currentLockCount && lockBehavior == LockForReal) { int result; result = pthread_mutex_lock(&JSMutex); ASSERT(!result); @@ -84,19 +84,19 @@ void JSLock::lock(bool lockForReal) setLockCount(currentLockCount + 1); } -void JSLock::unlock(bool lockForReal) +void JSLock::unlock(JSLockBehavior lockBehavior) { ASSERT(lockCount()); #ifdef NDEBUG // Locking "not for real" is a debug-only feature. - if (!lockForReal) + if (lockBehavior == SilenceAssertionsOnly) return; #endif intptr_t newLockCount = lockCount() - 1; setLockCount(newLockCount); - if (!newLockCount && lockForReal) { + if (!newLockCount && lockBehavior == LockForReal) { int result; result = pthread_mutex_unlock(&JSMutex); ASSERT(!result); @@ -105,12 +105,12 @@ void JSLock::unlock(bool lockForReal) void JSLock::lock(ExecState* exec) { - lock(exec->globalData().isSharedInstance); + lock(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly); } void JSLock::unlock(ExecState* exec) { - unlock(exec->globalData().isSharedInstance); + unlock(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly); } bool JSLock::currentThreadIsHoldingLock() @@ -162,7 +162,7 @@ bool JSLock::currentThreadIsHoldingLock() static unsigned lockDropDepth = 0; JSLock::DropAllLocks::DropAllLocks(ExecState* exec) - : m_lockingForReal(exec->globalData().isSharedInstance) + : m_lockBehavior(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly) { pthread_once(&createJSLockCountOnce, createJSLockCount); @@ -173,11 +173,11 @@ JSLock::DropAllLocks::DropAllLocks(ExecState* exec) m_lockCount = JSLock::lockCount(); for (intptr_t i = 0; i < m_lockCount; i++) - JSLock::unlock(m_lockingForReal); + JSLock::unlock(m_lockBehavior); } -JSLock::DropAllLocks::DropAllLocks(bool lockingForReal) - : m_lockingForReal(lockingForReal) +JSLock::DropAllLocks::DropAllLocks(JSLockBehavior JSLockBehavior) + : m_lockBehavior(JSLockBehavior) { pthread_once(&createJSLockCountOnce, createJSLockCount); @@ -191,13 +191,13 @@ JSLock::DropAllLocks::DropAllLocks(bool lockingForReal) m_lockCount = JSLock::lockCount(); for (intptr_t i = 0; i < m_lockCount; i++) - JSLock::unlock(m_lockingForReal); + JSLock::unlock(m_lockBehavior); } JSLock::DropAllLocks::~DropAllLocks() { for (intptr_t i = 0; i < m_lockCount; i++) - JSLock::lock(m_lockingForReal); + JSLock::lock(m_lockBehavior); --lockDropDepth; } @@ -205,7 +205,7 @@ JSLock::DropAllLocks::~DropAllLocks() #else JSLock::JSLock(ExecState*) - : m_lockingForReal(false) + : m_lockBehavior(SilenceAssertionsOnly) { } @@ -221,11 +221,11 @@ bool JSLock::currentThreadIsHoldingLock() return true; } -void JSLock::lock(bool) +void JSLock::lock(JSLockBehavior) { } -void JSLock::unlock(bool) +void JSLock::unlock(JSLockBehavior) { } @@ -241,7 +241,7 @@ JSLock::DropAllLocks::DropAllLocks(ExecState*) { } -JSLock::DropAllLocks::DropAllLocks(bool) +JSLock::DropAllLocks::DropAllLocks(JSLockBehavior) { } diff --git a/JavaScriptCore/runtime/JSLock.h b/JavaScriptCore/runtime/JSLock.h index 3dde358..8b015c4 100644 --- a/JavaScriptCore/runtime/JSLock.h +++ b/JavaScriptCore/runtime/JSLock.h @@ -50,50 +50,52 @@ namespace JSC { class ExecState; - class JSLock : Noncopyable { + enum JSLockBehavior { SilenceAssertionsOnly, LockForReal }; + + class JSLock : public Noncopyable { public: JSLock(ExecState*); - JSLock(bool lockingForReal) - : m_lockingForReal(lockingForReal) + JSLock(JSLockBehavior lockBehavior) + : m_lockBehavior(lockBehavior) { #ifdef NDEBUG // Locking "not for real" is a debug-only feature. - if (!lockingForReal) + if (lockBehavior == SilenceAssertionsOnly) return; #endif - lock(lockingForReal); + lock(lockBehavior); } ~JSLock() { #ifdef NDEBUG // Locking "not for real" is a debug-only feature. - if (!m_lockingForReal) + if (m_lockBehavior == SilenceAssertionsOnly) return; #endif - unlock(m_lockingForReal); + unlock(m_lockBehavior); } - static void lock(bool); - static void unlock(bool); + static void lock(JSLockBehavior); + static void unlock(JSLockBehavior); static void lock(ExecState*); static void unlock(ExecState*); static intptr_t lockCount(); static bool currentThreadIsHoldingLock(); - bool m_lockingForReal; + JSLockBehavior m_lockBehavior; - class DropAllLocks : Noncopyable { + class DropAllLocks : public Noncopyable { public: DropAllLocks(ExecState* exec); - DropAllLocks(bool); + DropAllLocks(JSLockBehavior); ~DropAllLocks(); private: intptr_t m_lockCount; - bool m_lockingForReal; + JSLockBehavior m_lockBehavior; }; }; diff --git a/JavaScriptCore/runtime/JSNotAnObject.cpp b/JavaScriptCore/runtime/JSNotAnObject.cpp index 937dc2b..a542a9f 100644 --- a/JavaScriptCore/runtime/JSNotAnObject.cpp +++ b/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -74,11 +74,10 @@ JSObject* JSNotAnObject::toObject(ExecState* exec) const } // Marking -void JSNotAnObject::mark() +void JSNotAnObject::markChildren(MarkStack& markStack) { - JSCell::mark(); - if (!m_exception->marked()) - m_exception->mark(); + JSObject::markChildren(markStack); + markStack.append(m_exception); } // JSObject methods diff --git a/JavaScriptCore/runtime/JSNotAnObject.h b/JavaScriptCore/runtime/JSNotAnObject.h index a8e36bd..b65ff5f 100644 --- a/JavaScriptCore/runtime/JSNotAnObject.h +++ b/JavaScriptCore/runtime/JSNotAnObject.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -75,7 +75,7 @@ namespace JSC { virtual JSObject* toObject(ExecState*) const; // Marking - virtual void mark(); + virtual void markChildren(MarkStack&); // JSObject methods virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); diff --git a/JavaScriptCore/runtime/JSNumberCell.cpp b/JavaScriptCore/runtime/JSNumberCell.cpp index 669440b..0654da7 100644 --- a/JavaScriptCore/runtime/JSNumberCell.cpp +++ b/JavaScriptCore/runtime/JSNumberCell.cpp @@ -23,13 +23,13 @@ #include "config.h" #include "JSNumberCell.h" +#if USE(JSVALUE32) + #include "NumberObject.h" #include "UString.h" namespace JSC { -#if !USE(ALTERNATE_JSIMMEDIATE) - JSValue JSNumberCell::toPrimitive(ExecState*, PreferredPrimitiveType) const { return const_cast<JSNumberCell*>(this); @@ -82,22 +82,6 @@ bool JSNumberCell::getUInt32(uint32_t& uint32) const return uint32 == m_value; } -bool JSNumberCell::getTruncatedInt32(int32_t& int32) const -{ - if (!(m_value >= -2147483648.0 && m_value < 2147483648.0)) - return false; - int32 = static_cast<int32_t>(m_value); - return true; -} - -bool JSNumberCell::getTruncatedUInt32(uint32_t& uint32) const -{ - if (!(m_value >= 0.0 && m_value < 4294967296.0)) - return false; - uint32 = static_cast<uint32_t>(m_value); - return true; -} - JSValue JSNumberCell::getJSNumber() { return this; @@ -113,25 +97,21 @@ JSValue jsNumberCell(JSGlobalData* globalData, double d) return new (globalData) JSNumberCell(globalData, d); } -JSValue jsAPIMangledNumber(ExecState* exec, double d) -{ - return new (exec) JSNumberCell(JSNumberCell::APIMangled, d); -} +} // namespace JSC -#else +#else // USE(JSVALUE32) -JSValue jsNumberCell(ExecState*, double) -{ - ASSERT_NOT_REACHED(); - return JSValue(); -} +// Keep our exported symbols lists happy. +namespace JSC { + +JSValue jsNumberCell(ExecState*, double); -JSValue jsAPIMangledNumber(ExecState*, double) +JSValue jsNumberCell(ExecState*, double) { ASSERT_NOT_REACHED(); return JSValue(); } -#endif - } // namespace JSC + +#endif // USE(JSVALUE32) diff --git a/JavaScriptCore/runtime/JSNumberCell.h b/JavaScriptCore/runtime/JSNumberCell.h index a35e210..04cccef 100644 --- a/JavaScriptCore/runtime/JSNumberCell.h +++ b/JavaScriptCore/runtime/JSNumberCell.h @@ -35,10 +35,8 @@ namespace JSC { extern const double NaN; extern const double Inf; +#if USE(JSVALUE32) JSValue jsNumberCell(ExecState*, double); - JSValue jsAPIMangledNumber(ExecState*, double); - -#if !USE(ALTERNATE_JSIMMEDIATE) class Identifier; class JSCell; @@ -53,7 +51,7 @@ namespace JSC { friend class JIT; friend JSValue jsNumberCell(JSGlobalData*, double); friend JSValue jsNumberCell(ExecState*, double); - friend JSValue jsAPIMangledNumber(ExecState*, double); + public: double value() const { return m_value; } @@ -68,9 +66,6 @@ namespace JSC { virtual JSObject* toThisObject(ExecState*) const; virtual JSValue getJSNumber(); - static const uintptr_t JSAPIMangledMagicNumber = 0xbbadbeef; - bool isAPIMangledNumber() const { return m_structure == reinterpret_cast<Structure*>(JSAPIMangledMagicNumber); } - void* operator new(size_t size, ExecState* exec) { #ifdef JAVASCRIPTCORE_BUILDING_ALL_IN_ONE_FILE @@ -104,16 +99,7 @@ namespace JSC { { } - enum APIMangledTag { APIMangled }; - JSNumberCell(APIMangledTag, double value) - : JSCell(reinterpret_cast<Structure*>(JSAPIMangledMagicNumber)) - , m_value(value) - { - } - virtual bool getUInt32(uint32_t&) const; - virtual bool getTruncatedInt32(int32_t&) const; - virtual bool getTruncatedUInt32(uint32_t&) const; double m_value; }; @@ -131,7 +117,6 @@ namespace JSC { return static_cast<JSNumberCell*>(v.asCell()); } - inline JSValue::JSValue(ExecState* exec, double d) { JSValue v = JSImmediate::from(d); @@ -192,59 +177,30 @@ namespace JSC { *this = v ? v : jsNumberCell(globalData, i); } - inline JSValue::JSValue(JSGlobalData* globalData, long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(globalData, i); - } - - inline JSValue::JSValue(JSGlobalData* globalData, unsigned long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(globalData, i); - } - - inline JSValue::JSValue(JSGlobalData* globalData, long long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(globalData, static_cast<double>(i)); - } - - inline JSValue::JSValue(JSGlobalData* globalData, unsigned long long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(globalData, static_cast<double>(i)); - } - - inline bool JSValue::isDoubleNumber() const + inline bool JSValue::isDouble() const { return isNumberCell(asValue()); } - inline double JSValue::getDoubleNumber() const + inline double JSValue::asDouble() const { return asNumberCell(asValue())->value(); } inline bool JSValue::isNumber() const { - return JSImmediate::isNumber(asValue()) || isDoubleNumber(); + return JSImmediate::isNumber(asValue()) || isDouble(); } inline double JSValue::uncheckedGetNumber() const { ASSERT(isNumber()); - return JSImmediate::isImmediate(asValue()) ? JSImmediate::toDouble(asValue()) : getDoubleNumber(); + return JSImmediate::isImmediate(asValue()) ? JSImmediate::toDouble(asValue()) : asDouble(); } - inline bool JSValue::isAPIMangledNumber() - { - ASSERT(isNumber()); - return JSImmediate::isImmediate(asValue()) ? false : asNumberCell(asValue())->isAPIMangledNumber(); - } - -#else +#endif // USE(JSVALUE32) +#if USE(JSVALUE64) inline JSValue::JSValue(ExecState*, double d) { JSValue v = JSImmediate::from(d); @@ -315,40 +271,12 @@ namespace JSC { *this = v; } - inline JSValue::JSValue(JSGlobalData*, long i) - { - JSValue v = JSImmediate::from(i); - ASSERT(v); - *this = v; - } - - inline JSValue::JSValue(JSGlobalData*, unsigned long i) + inline bool JSValue::isDouble() const { - JSValue v = JSImmediate::from(i); - ASSERT(v); - *this = v; - } - - inline JSValue::JSValue(JSGlobalData*, long long i) - { - JSValue v = JSImmediate::from(static_cast<double>(i)); - ASSERT(v); - *this = v; + return JSImmediate::isDouble(asValue()); } - inline JSValue::JSValue(JSGlobalData*, unsigned long long i) - { - JSValue v = JSImmediate::from(static_cast<double>(i)); - ASSERT(v); - *this = v; - } - - inline bool JSValue::isDoubleNumber() const - { - return JSImmediate::isDoubleNumber(asValue()); - } - - inline double JSValue::getDoubleNumber() const + inline double JSValue::asDouble() const { return JSImmediate::doubleValue(asValue()); } @@ -364,7 +292,9 @@ namespace JSC { return JSImmediate::toDouble(asValue()); } -#endif +#endif // USE(JSVALUE64) + +#if USE(JSVALUE32) || USE(JSVALUE64) inline JSValue::JSValue(ExecState*, char i) { @@ -390,30 +320,6 @@ namespace JSC { *this = JSImmediate::from(i); } - inline JSValue::JSValue(JSGlobalData*, char i) - { - ASSERT(JSImmediate::from(i)); - *this = JSImmediate::from(i); - } - - inline JSValue::JSValue(JSGlobalData*, unsigned char i) - { - ASSERT(JSImmediate::from(i)); - *this = JSImmediate::from(i); - } - - inline JSValue::JSValue(JSGlobalData*, short i) - { - ASSERT(JSImmediate::from(i)); - *this = JSImmediate::from(i); - } - - inline JSValue::JSValue(JSGlobalData*, unsigned short i) - { - ASSERT(JSImmediate::from(i)); - *this = JSImmediate::from(i); - } - inline JSValue jsNaN(ExecState* exec) { return jsNumber(exec, NaN); @@ -433,23 +339,10 @@ namespace JSC { inline bool JSValue::getNumber(double &result) const { - if (isInt32Fast()) - result = getInt32Fast(); - else if (LIKELY(isDoubleNumber())) - result = getDoubleNumber(); - else { - ASSERT(!isNumber()); - return false; - } - return true; - } - - inline bool JSValue::numberToInt32(int32_t& arg) - { - if (isInt32Fast()) - arg = getInt32Fast(); - else if (LIKELY(isDoubleNumber())) - arg = JSC::toInt32(getDoubleNumber()); + if (isInt32()) + result = asInt32(); + else if (LIKELY(isDouble())) + result = asDouble(); else { ASSERT(!isNumber()); return false; @@ -457,23 +350,7 @@ namespace JSC { return true; } - inline bool JSValue::numberToUInt32(uint32_t& arg) - { - if (isUInt32Fast()) - arg = getUInt32Fast(); - else if (LIKELY(isDoubleNumber())) - arg = JSC::toUInt32(getDoubleNumber()); - else if (isInt32Fast()) { - // FIXME: I think this case can be merged with the uint case; toUInt32SlowCase - // on a negative value is equivalent to simple static_casting. - bool ignored; - arg = toUInt32SlowCase(getInt32Fast(), ignored); - } else { - ASSERT(!isNumber()); - return false; - } - return true; - } +#endif // USE(JSVALUE32) || USE(JSVALUE64) } // namespace JSC diff --git a/JavaScriptCore/runtime/JSONObject.cpp b/JavaScriptCore/runtime/JSONObject.cpp new file mode 100644 index 0000000..d643808 --- /dev/null +++ b/JavaScriptCore/runtime/JSONObject.cpp @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2009 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 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 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. + */ + +#include "config.h" +#include "JSONObject.h" + +#include "BooleanObject.h" +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "LiteralParser.h" +#include "PropertyNameArray.h" +#include <wtf/MathExtras.h> + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(JSONObject); + +static JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*, JSObject*, JSValue, const ArgList&); +static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSValue, const ArgList&); + +} + +#include "JSONObject.lut.h" + +namespace JSC { + +// PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked. +class PropertyNameForFunctionCall { +public: + PropertyNameForFunctionCall(const Identifier&); + PropertyNameForFunctionCall(unsigned); + + JSValue value(ExecState*) const; + +private: + const Identifier* m_identifier; + unsigned m_number; + mutable JSValue m_value; +}; + +class Stringifier : public Noncopyable { +public: + Stringifier(ExecState*, JSValue replacer, JSValue space); + ~Stringifier(); + JSValue stringify(JSValue); + + void markAggregate(MarkStack&); + +private: + typedef UString StringBuilder; + + class Holder { + public: + Holder(JSObject*); + + JSObject* object() const { return m_object; } + + bool appendNextProperty(Stringifier&, StringBuilder&); + + private: + JSObject* const m_object; + const bool m_isArray; + bool m_isJSArray; + unsigned m_index; + unsigned m_size; + RefPtr<PropertyNameArrayData> m_propertyNames; + }; + + friend class Holder; + + static void appendQuotedString(StringBuilder&, const UString&); + + JSValue toJSON(JSValue, const PropertyNameForFunctionCall&); + + enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedValue }; + StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&); + + bool willIndent() const; + void indent(); + void unindent(); + void startNewLine(StringBuilder&) const; + + Stringifier* const m_nextStringifierToMark; + ExecState* const m_exec; + const JSValue m_replacer; + bool m_usingArrayReplacer; + PropertyNameArray m_arrayReplacerPropertyNames; + CallType m_replacerCallType; + CallData m_replacerCallData; + const UString m_gap; + + HashSet<JSObject*> m_holderCycleDetector; + Vector<Holder, 16> m_holderStack; + UString m_repeatedGap; + UString m_indent; +}; + +// ------------------------------ helper functions -------------------------------- + +static inline JSValue unwrapBoxedPrimitive(JSValue value) +{ + if (!value.isObject()) + return value; + if (!asObject(value)->inherits(&NumberObject::info) && !asObject(value)->inherits(&StringObject::info) && !asObject(value)->inherits(&BooleanObject::info)) + return value; + return static_cast<JSWrapperObject*>(asObject(value))->internalValue(); +} + +static inline UString gap(JSValue space) +{ + space = unwrapBoxedPrimitive(space); + + // If the space value is a number, create a gap string with that number of spaces. + double spaceCount; + if (space.getNumber(spaceCount)) { + const int maxSpaceCount = 100; + int count; + if (spaceCount > maxSpaceCount) + count = maxSpaceCount; + else if (!(spaceCount > 0)) + count = 0; + else + count = static_cast<int>(spaceCount); + UChar spaces[maxSpaceCount]; + for (int i = 0; i < count; ++i) + spaces[i] = ' '; + return UString(spaces, count); + } + + // If the space value is a string, use it as the gap string, otherwise use no gap string. + return space.getString(); +} + +// ------------------------------ PropertyNameForFunctionCall -------------------------------- + +inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier) + : m_identifier(&identifier) +{ +} + +inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number) + : m_identifier(0) + , m_number(number) +{ +} + +JSValue PropertyNameForFunctionCall::value(ExecState* exec) const +{ + if (!m_value) { + if (m_identifier) + m_value = jsString(exec, m_identifier->ustring()); + else + m_value = jsNumber(exec, m_number); + } + return m_value; +} + +// ------------------------------ Stringifier -------------------------------- + +Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space) + : m_nextStringifierToMark(exec->globalData().firstStringifierToMark) + , m_exec(exec) + , m_replacer(replacer) + , m_usingArrayReplacer(false) + , m_arrayReplacerPropertyNames(exec) + , m_replacerCallType(CallTypeNone) + , m_gap(gap(space)) +{ + exec->globalData().firstStringifierToMark = this; + + if (!m_replacer.isObject()) + return; + + if (asObject(m_replacer)->inherits(&JSArray::info)) { + m_usingArrayReplacer = true; + JSObject* array = asObject(m_replacer); + unsigned length = array->get(exec, exec->globalData().propertyNames->length).toUInt32(exec); + for (unsigned i = 0; i < length; ++i) { + JSValue name = array->get(exec, i); + if (exec->hadException()) + break; + UString propertyName; + if (!name.getString(propertyName)) + continue; + if (exec->hadException()) + return; + m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); + } + return; + } + + m_replacerCallType = asObject(m_replacer)->getCallData(m_replacerCallData); +} + +Stringifier::~Stringifier() +{ + ASSERT(m_exec->globalData().firstStringifierToMark == this); + m_exec->globalData().firstStringifierToMark = m_nextStringifierToMark; +} + +void Stringifier::markAggregate(MarkStack& markStack) +{ + for (Stringifier* stringifier = this; stringifier; stringifier = stringifier->m_nextStringifierToMark) { + size_t size = m_holderStack.size(); + for (size_t i = 0; i < size; ++i) + markStack.append(m_holderStack[i].object()); + } +} + +JSValue Stringifier::stringify(JSValue value) +{ + JSObject* object = constructEmptyObject(m_exec); + if (m_exec->hadException()) + return jsNull(); + + PropertyNameForFunctionCall emptyPropertyName(m_exec->globalData().propertyNames->emptyIdentifier); + object->putDirect(m_exec->globalData().propertyNames->emptyIdentifier, value); + + StringBuilder result; + if (appendStringifiedValue(result, value, object, emptyPropertyName) != StringifySucceeded) + return jsUndefined(); + if (m_exec->hadException()) + return jsNull(); + + return jsString(m_exec, result); +} + +void Stringifier::appendQuotedString(StringBuilder& builder, const UString& value) +{ + int length = value.size(); + + // String length plus 2 for quote marks plus 8 so we can accomodate a few escaped characters. + builder.reserveCapacity(builder.size() + length + 2 + 8); + + builder.append('"'); + + const UChar* data = value.data(); + for (int i = 0; i < length; ++i) { + int start = i; + while (i < length && (data[i] > 0x1F && data[i] != '"' && data[i] != '\\')) + ++i; + builder.append(data + start, i - start); + if (i >= length) + break; + switch (data[i]) { + case '\t': + builder.append('\\'); + builder.append('t'); + break; + case '\r': + builder.append('\\'); + builder.append('r'); + break; + case '\n': + builder.append('\\'); + builder.append('n'); + break; + case '\f': + builder.append('\\'); + builder.append('f'); + break; + case '\b': + builder.append('\\'); + builder.append('b'); + break; + case '"': + builder.append('\\'); + builder.append('"'); + break; + case '\\': + builder.append('\\'); + builder.append('\\'); + break; + default: + static const char hexDigits[] = "0123456789abcdef"; + UChar ch = data[i]; + UChar hex[] = { '\\', 'u', hexDigits[(ch >> 12) & 0xF], hexDigits[(ch >> 8) & 0xF], hexDigits[(ch >> 4) & 0xF], hexDigits[ch & 0xF] }; + builder.append(hex, sizeof(hex) / sizeof(UChar)); + break; + } + } + + builder.append('"'); +} + +inline JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName) +{ + ASSERT(!m_exec->hadException()); + if (!value.isObject() || !asObject(value)->hasProperty(m_exec, m_exec->globalData().propertyNames->toJSON)) + return value; + + JSValue toJSONFunction = asObject(value)->get(m_exec, m_exec->globalData().propertyNames->toJSON); + if (m_exec->hadException()) + return jsNull(); + + if (!toJSONFunction.isObject()) + return value; + + JSObject* object = asObject(toJSONFunction); + CallData callData; + CallType callType = object->getCallData(callData); + if (callType == CallTypeNone) + return value; + + JSValue list[] = { propertyName.value(m_exec) }; + ArgList args(list, sizeof(list) / sizeof(JSValue)); + return call(m_exec, object, callType, callData, value, args); +} + +Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName) +{ + // Call the toJSON function. + value = toJSON(value, propertyName); + if (m_exec->hadException()) + return StringifyFailed; + + // Call the replacer function. + if (m_replacerCallType != CallTypeNone) { + JSValue list[] = { propertyName.value(m_exec), value }; + ArgList args(list, sizeof(list) / sizeof(JSValue)); + value = call(m_exec, m_replacer, m_replacerCallType, m_replacerCallData, holder, args); + if (m_exec->hadException()) + return StringifyFailed; + } + + if (value.isUndefined() && !holder->inherits(&JSArray::info)) + return StringifyFailedDueToUndefinedValue; + + if (value.isNull()) { + builder.append("null"); + return StringifySucceeded; + } + + value = unwrapBoxedPrimitive(value); + + if (value.isBoolean()) { + builder.append(value.getBoolean() ? "true" : "false"); + return StringifySucceeded; + } + + UString stringValue; + if (value.getString(stringValue)) { + appendQuotedString(builder, stringValue); + return StringifySucceeded; + } + + double numericValue; + if (value.getNumber(numericValue)) { + if (!isfinite(numericValue)) + builder.append("null"); + else + builder.append(UString::from(numericValue)); + return StringifySucceeded; + } + + if (!value.isObject()) + return StringifyFailed; + + JSObject* object = asObject(value); + + // Handle cycle detection, and put the holder on the stack. + if (!m_holderCycleDetector.add(object).second) { + throwError(m_exec, TypeError, "JSON.stringify cannot serialize cyclic structures."); + return StringifyFailed; + } + bool holderStackWasEmpty = m_holderStack.isEmpty(); + m_holderStack.append(object); + if (!holderStackWasEmpty) + return StringifySucceeded; + + // If this is the outermost call, then loop to handle everything on the holder stack. + TimeoutChecker localTimeoutChecker(m_exec->globalData().timeoutChecker); + localTimeoutChecker.reset(); + unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck(); + do { + while (m_holderStack.last().appendNextProperty(*this, builder)) { + if (m_exec->hadException()) + return StringifyFailed; + if (!--tickCount) { + if (localTimeoutChecker.didTimeOut(m_exec)) { + m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); + return StringifyFailed; + } + tickCount = localTimeoutChecker.ticksUntilNextCheck(); + } + } + m_holderCycleDetector.remove(m_holderStack.last().object()); + m_holderStack.removeLast(); + } while (!m_holderStack.isEmpty()); + return StringifySucceeded; +} + +inline bool Stringifier::willIndent() const +{ + return !m_gap.isEmpty(); +} + +inline void Stringifier::indent() +{ + // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent. + int newSize = m_indent.size() + m_gap.size(); + if (newSize > m_repeatedGap.size()) + m_repeatedGap.append(m_gap); + ASSERT(newSize <= m_repeatedGap.size()); + m_indent = m_repeatedGap.substr(0, newSize); +} + +inline void Stringifier::unindent() +{ + ASSERT(m_indent.size() >= m_gap.size()); + m_indent = m_repeatedGap.substr(0, m_indent.size() - m_gap.size()); +} + +inline void Stringifier::startNewLine(StringBuilder& builder) const +{ + if (m_gap.isEmpty()) + return; + builder.append('\n'); + builder.append(m_indent); +} + +inline Stringifier::Holder::Holder(JSObject* object) + : m_object(object) + , m_isArray(object->inherits(&JSArray::info)) + , m_index(0) +{ +} + +bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder) +{ + ASSERT(m_index <= m_size); + + ExecState* exec = stringifier.m_exec; + + // First time through, initialize. + if (!m_index) { + if (m_isArray) { + m_isJSArray = isJSArray(&exec->globalData(), m_object); + m_size = m_object->get(exec, exec->globalData().propertyNames->length).toUInt32(exec); + builder.append('['); + } else { + if (stringifier.m_usingArrayReplacer) + m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data(); + else { + PropertyNameArray objectPropertyNames(exec); + m_object->getPropertyNames(exec, objectPropertyNames); + m_propertyNames = objectPropertyNames.releaseData(); + } + m_size = m_propertyNames->propertyNameVector().size(); + builder.append('{'); + } + stringifier.indent(); + } + + // Last time through, finish up and return false. + if (m_index == m_size) { + stringifier.unindent(); + if (m_size && builder[builder.size() - 1] != '{') + stringifier.startNewLine(builder); + builder.append(m_isArray ? ']' : '}'); + return false; + } + + // Handle a single element of the array or object. + unsigned index = m_index++; + unsigned rollBackPoint = 0; + StringifyResult stringifyResult; + if (m_isArray) { + // Get the value. + JSValue value; + if (m_isJSArray && asArray(m_object)->canGetIndex(index)) + value = asArray(m_object)->getIndex(index); + else { + PropertySlot slot(m_object); + if (!m_object->getOwnPropertySlot(exec, index, slot)) + slot.setUndefined(); + if (exec->hadException()) + return false; + value = slot.getValue(exec, index); + } + + // Append the separator string. + if (index) + builder.append(','); + stringifier.startNewLine(builder); + + // Append the stringified value. + stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, index); + } else { + // Get the value. + PropertySlot slot(m_object); + Identifier& propertyName = m_propertyNames->propertyNameVector()[index]; + if (!m_object->getOwnPropertySlot(exec, propertyName, slot)) + return true; + JSValue value = slot.getValue(exec, propertyName); + if (exec->hadException()) + return false; + + rollBackPoint = builder.size(); + + // Append the separator string. + if (builder[rollBackPoint - 1] != '{') + builder.append(','); + stringifier.startNewLine(builder); + + // Append the property name. + appendQuotedString(builder, propertyName.ustring()); + builder.append(':'); + if (stringifier.willIndent()) + builder.append(' '); + + // Append the stringified value. + stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, propertyName); + } + + // From this point on, no access to the this pointer or to any members, because the + // Holder object may have moved if the call to stringify pushed a new Holder onto + // m_holderStack. + + switch (stringifyResult) { + case StringifyFailed: + builder.append("null"); + break; + case StringifySucceeded: + break; + case StringifyFailedDueToUndefinedValue: + // This only occurs when get an undefined value for an object property. + // In this case we don't want the separator and property name that we + // already appended, so roll back. + builder = builder.substr(0, rollBackPoint); + break; + } + + return true; +} + +// ------------------------------ JSONObject -------------------------------- + +const ClassInfo JSONObject::info = { "JSON", 0, 0, ExecState::jsonTable }; + +/* Source for JSONObject.lut.h +@begin jsonTable + parse JSONProtoFuncParse DontEnum|Function 1 + stringify JSONProtoFuncStringify DontEnum|Function 1 +@end +*/ + +// ECMA 15.8 + +bool JSONObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + const HashEntry* entry = ExecState::jsonTable(exec)->entry(exec, propertyName); + if (!entry) + return JSObject::getOwnPropertySlot(exec, propertyName, slot); + + ASSERT(entry->attributes() & Function); + setUpStaticFunctionSlot(exec, entry, this, propertyName, slot); + return true; +} + +void JSONObject::markStringifiers(MarkStack& markStack, Stringifier* stringifier) +{ + stringifier->markAggregate(markStack); +} + +class Walker { +public: + Walker(ExecState* exec, JSObject* function, CallType callType, CallData callData) + : m_exec(exec) + , m_function(function) + , m_callType(callType) + , m_callData(callData) + { + } + JSValue walk(JSValue unfiltered); +private: + JSValue callReviver(JSValue property, JSValue unfiltered) + { + JSValue args[] = { property, unfiltered }; + ArgList argList(args, 2); + return call(m_exec, m_function, m_callType, m_callData, jsNull(), argList); + } + + friend class Holder; + + ExecState* m_exec; + JSObject* m_function; + CallType m_callType; + CallData m_callData; +}; + +enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember, + ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember }; +NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) +{ + Vector<PropertyNameArray, 16> propertyStack; + Vector<uint32_t, 16> indexStack; + Vector<JSObject*, 16> objectStack; + Vector<JSArray*, 16> arrayStack; + + Vector<WalkerState, 16> stateStack; + WalkerState state = StateUnknown; + JSValue inValue = unfiltered; + JSValue outValue = jsNull(); + while (1) { + switch (state) { + arrayStartState: + case ArrayStartState: { + ASSERT(inValue.isObject()); + ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue))); + JSArray* array = asArray(inValue); + arrayStack.append(array); + indexStack.append(0); + // fallthrough + } + arrayStartVisitMember: + case ArrayStartVisitMember: { + JSArray* array = arrayStack.last(); + uint32_t index = indexStack.last(); + if (index == array->length()) { + outValue = array; + arrayStack.removeLast(); + indexStack.removeLast(); + break; + } + inValue = array->getIndex(index); + if (inValue.isObject()) { + stateStack.append(ArrayEndVisitMember); + goto stateUnknown; + } else + outValue = inValue; + // fallthrough + } + case ArrayEndVisitMember: { + JSArray* array = arrayStack.last(); + array->setIndex(indexStack.last(), callReviver(jsString(m_exec, UString::from(indexStack.last())), outValue)); + if (m_exec->hadException()) + return jsNull(); + indexStack.last()++; + goto arrayStartVisitMember; + } + objectStartState: + case ObjectStartState: { + ASSERT(inValue.isObject()); + ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue))); + JSObject* object = asObject(inValue); + objectStack.append(object); + indexStack.append(0); + propertyStack.append(PropertyNameArray(m_exec)); + object->getPropertyNames(m_exec, propertyStack.last()); + // fallthrough + } + objectStartVisitMember: + case ObjectStartVisitMember: { + JSObject* object = objectStack.last(); + uint32_t index = indexStack.last(); + PropertyNameArray& properties = propertyStack.last(); + if (index == properties.size()) { + outValue = object; + objectStack.removeLast(); + indexStack.removeLast(); + propertyStack.removeLast(); + break; + } + PropertySlot slot; + object->getOwnPropertySlot(m_exec, properties[index], slot); + inValue = slot.getValue(m_exec, properties[index]); + ASSERT(!m_exec->hadException()); + if (inValue.isObject()) { + stateStack.append(ObjectEndVisitMember); + goto stateUnknown; + } else + outValue = inValue; + // fallthrough + } + case ObjectEndVisitMember: { + JSObject* object = objectStack.last(); + Identifier prop = propertyStack.last()[indexStack.last()]; + PutPropertySlot slot; + object->put(m_exec, prop, callReviver(jsString(m_exec, prop.ustring()), outValue), slot); + if (m_exec->hadException()) + return jsNull(); + indexStack.last()++; + goto objectStartVisitMember; + } + stateUnknown: + case StateUnknown: + if (!inValue.isObject()) { + outValue = inValue; + break; + } + if (isJSArray(&m_exec->globalData(), asObject(inValue))) + goto arrayStartState; + goto objectStartState; + } + if (stateStack.isEmpty()) + break; + state = stateStack.last(); + stateStack.removeLast(); + } + return callReviver(jsEmptyString(m_exec), outValue); +} + +// ECMA-262 v5 15.12.2 +JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (args.isEmpty()) + return throwError(exec, GeneralError, "JSON.parse requires at least one parameter"); + JSValue value = args.at(0); + UString source = value.toString(exec); + if (exec->hadException()) + return jsNull(); + + LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON); + JSValue unfiltered = jsonParser.tryLiteralParse(); + if (!unfiltered) + return throwError(exec, SyntaxError, "Unable to parse JSON string"); + + if (args.size() < 2) + return unfiltered; + + JSValue function = args.at(1); + CallData callData; + CallType callType = function.getCallData(callData); + if (callType == CallTypeNone) + return unfiltered; + return Walker(exec, asObject(function), callType, callData).walk(unfiltered); +} + +// ECMA-262 v5 15.12.3 +JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (args.isEmpty()) + return throwError(exec, GeneralError, "No input to stringify"); + JSValue value = args.at(0); + JSValue replacer = args.at(1); + JSValue space = args.at(2); + return Stringifier(exec, replacer, space).stringify(value); +} + +} // namespace JSC diff --git a/JavaScriptCore/runtime/JSONObject.h b/JavaScriptCore/runtime/JSONObject.h new file mode 100644 index 0000000..faca7c7 --- /dev/null +++ b/JavaScriptCore/runtime/JSONObject.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 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 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 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 JSONObject_h +#define JSONObject_h + +#include "JSObject.h" + +namespace JSC { + + class Stringifier; + + class JSONObject : public JSObject { + public: + JSONObject(PassRefPtr<Structure> structure) + : JSObject(structure) + { + } + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType)); + } + + static void markStringifiers(MarkStack&, Stringifier*); + + private: + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + + virtual const ClassInfo* classInfo() const { return &info; } + static const ClassInfo info; + }; + +} // namespace JSC + +#endif // JSONObject_h diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp index 415c25d..419dfe9 100644 --- a/JavaScriptCore/runtime/JSObject.cpp +++ b/JavaScriptCore/runtime/JSObject.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel (eric@webkit.org) * * This library is free software; you can redistribute it and/or @@ -62,21 +62,16 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSObject); -void JSObject::mark() +void JSObject::markChildren(MarkStack& markStack) { JSOBJECT_MARK_BEGIN(); - JSCell::mark(); - m_structure->mark(); + JSCell::markChildren(markStack); + m_structure->markAggregate(markStack); PropertyStorage storage = propertyStorage(); - size_t storageSize = m_structure->propertyStorageSize(); - for (size_t i = 0; i < storageSize; ++i) { - JSValue v = JSValue::decode(storage[i]); - if (!v.marked()) - v.mark(); - } + markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize); JSOBJECT_MARK_END(); } @@ -310,7 +305,7 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO } PutPropertySlot slot; - GetterSetter* getterSetter = new (exec) GetterSetter; + GetterSetter* getterSetter = new (exec) GetterSetter(exec); putDirectInternal(exec->globalData(), propertyName, getterSetter, Getter, true, slot); // putDirect will change our Structure if we add a new property. For @@ -337,7 +332,7 @@ void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSO } PutPropertySlot slot; - GetterSetter* getterSetter = new (exec) GetterSetter; + GetterSetter* getterSetter = new (exec) GetterSetter(exec); putDirectInternal(exec->globalData(), propertyName, getterSetter, Setter, true, slot); // putDirect will change our Structure if we add a new property. For diff --git a/JavaScriptCore/runtime/JSObject.h b/JavaScriptCore/runtime/JSObject.h index 54805f2..decd5e9 100644 --- a/JavaScriptCore/runtime/JSObject.h +++ b/JavaScriptCore/runtime/JSObject.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 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. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 @@ -33,6 +33,7 @@ #include "ScopeChain.h" #include "Structure.h" #include "JSGlobalData.h" +#include <wtf/StdLibExtras.h> namespace JSC { @@ -42,11 +43,11 @@ namespace JSC { return value.asCell(); return 0; } - + + class HashEntry; class InternalFunction; class PropertyNameArray; class Structure; - struct HashEntry; struct HashTable; // ECMA 262-3 8.6.1 @@ -72,7 +73,7 @@ namespace JSC { public: explicit JSObject(PassRefPtr<Structure>); - virtual void mark(); + virtual void markChildren(MarkStack&); // The inline virtual destructor cannot be the first virtual function declared // in the class as it results in the vtable being generated as a weak symbol @@ -195,7 +196,7 @@ namespace JSC { void allocatePropertyStorageInline(size_t oldSize, size_t newSize); bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); } - static const size_t inlineStorageCapacity = 3; + static const size_t inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3; static const size_t nonInlineBaseStorageCapacity = 16; static PassRefPtr<Structure> createStructure(JSValue prototype) @@ -203,9 +204,6 @@ namespace JSC { return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot)); } - protected: - bool getOwnPropertySlotForWrite(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable); - private: ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } @@ -229,17 +227,15 @@ namespace JSC { const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; Structure* createInheritorID(); - RefPtr<Structure> m_inheritorID; - union { PropertyStorage m_externalStorage; EncodedJSValue m_inlineStorage[inlineStorageCapacity]; }; - }; - - JSObject* asObject(JSValue); - JSObject* constructEmptyObject(ExecState*); + RefPtr<Structure> m_inheritorID; + }; + +JSObject* constructEmptyObject(ExecState*); inline JSObject* asObject(JSValue value) { @@ -254,6 +250,9 @@ inline JSObject::JSObject(PassRefPtr<Structure> structure) ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); ASSERT(m_structure->isEmpty()); ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); +#if USE(JSVALUE64) || USE(JSVALUE32_64) + ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0); +#endif } inline JSObject::~JSObject() @@ -328,30 +327,6 @@ ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Ide return false; } -ALWAYS_INLINE bool JSObject::getOwnPropertySlotForWrite(ExecState* exec, const Identifier& propertyName, PropertySlot& slot, bool& slotIsWriteable) -{ - unsigned attributes; - if (JSValue* location = getDirectLocation(propertyName, attributes)) { - if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter()) { - slotIsWriteable = false; - fillGetterPropertySlot(slot, location); - } else { - slotIsWriteable = !(attributes & ReadOnly); - slot.setValueSlot(this, location, offsetForLocation(location)); - } - return true; - } - - // non-standard Netscape extension - if (propertyName == exec->propertyNames().underscoreProto) { - slot.setValue(prototype()); - slotIsWriteable = false; - return true; - } - - return false; -} - // It may seem crazy to inline a function this large, especially a virtual function, // but it makes a big difference to property lookup that derived classes can inline their // base class call to this. @@ -569,7 +544,7 @@ inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) con inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const { if (UNLIKELY(!isCell())) { - JSObject* prototype = JSImmediate::prototype(asValue(), exec); + JSObject* prototype = synthesizePrototype(exec); if (propertyName == exec->propertyNames().underscoreProto) return prototype; if (!prototype->getPropertySlot(exec, propertyName, slot)) @@ -597,7 +572,7 @@ inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const { if (UNLIKELY(!isCell())) { - JSObject* prototype = JSImmediate::prototype(asValue(), exec); + JSObject* prototype = synthesizePrototype(exec); if (!prototype->getPropertySlot(exec, propertyName, slot)) return jsUndefined(); return slot.getValue(exec, propertyName); @@ -617,7 +592,7 @@ inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { if (UNLIKELY(!isCell())) { - JSImmediate::toObject(asValue(), exec)->put(exec, propertyName, value, slot); + synthesizeObject(exec)->put(exec, propertyName, value, slot); return; } asCell()->put(exec, propertyName, value, slot); @@ -626,7 +601,7 @@ inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValu inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) { if (UNLIKELY(!isCell())) { - JSImmediate::toObject(asValue(), exec)->put(exec, propertyName, value); + synthesizeObject(exec)->put(exec, propertyName, value); return; } asCell()->put(exec, propertyName, value); diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp index 8c7b53d..dc0304f 100644 --- a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp +++ b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -73,11 +73,11 @@ JSObject* JSPropertyNameIterator::toObject(ExecState*) const return 0; } -void JSPropertyNameIterator::mark() +void JSPropertyNameIterator::markChildren(MarkStack& markStack) { - JSCell::mark(); - if (m_object && !m_object->marked()) - m_object->mark(); + JSCell::markChildren(markStack); + if (m_object) + markStack.append(m_object); } void JSPropertyNameIterator::invalidate() diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.h b/JavaScriptCore/runtime/JSPropertyNameIterator.h index 9817c07..4534528 100644 --- a/JavaScriptCore/runtime/JSPropertyNameIterator.h +++ b/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -51,14 +51,18 @@ namespace JSC { virtual UString toString(ExecState*) const; virtual JSObject* toObject(ExecState*) const; - virtual void mark(); + virtual void markChildren(MarkStack&); JSValue next(ExecState*); void invalidate(); - + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(CompoundType)); + } private: - JSPropertyNameIterator(); - JSPropertyNameIterator(JSObject*, PassRefPtr<PropertyNameArrayData> propertyNameArrayData); + JSPropertyNameIterator(ExecState*); + JSPropertyNameIterator(ExecState*, JSObject*, PassRefPtr<PropertyNameArrayData> propertyNameArrayData); JSObject* m_object; RefPtr<PropertyNameArrayData> m_data; @@ -66,16 +70,16 @@ namespace JSC { PropertyNameArrayData::const_iterator m_end; }; -inline JSPropertyNameIterator::JSPropertyNameIterator() - : JSCell(0) +inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec) + : JSCell(exec->globalData().propertyNameIteratorStructure.get()) , m_object(0) , m_position(0) , m_end(0) { } -inline JSPropertyNameIterator::JSPropertyNameIterator(JSObject* object, PassRefPtr<PropertyNameArrayData> propertyNameArrayData) - : JSCell(0) +inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec, JSObject* object, PassRefPtr<PropertyNameArrayData> propertyNameArrayData) + : JSCell(exec->globalData().propertyNameIteratorStructure.get()) , m_object(object) , m_data(propertyNameArrayData) , m_position(m_data->begin()) @@ -86,12 +90,12 @@ inline JSPropertyNameIterator::JSPropertyNameIterator(JSObject* object, PassRefP inline JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSValue v) { if (v.isUndefinedOrNull()) - return new (exec) JSPropertyNameIterator; + return new (exec) JSPropertyNameIterator(exec); JSObject* o = v.toObject(exec); PropertyNameArray propertyNames(exec); o->getPropertyNames(exec, propertyNames); - return new (exec) JSPropertyNameIterator(o, propertyNames.releaseData()); + return new (exec) JSPropertyNameIterator(exec, o, propertyNames.releaseData()); } inline JSValue JSPropertyNameIterator::next(ExecState* exec) diff --git a/JavaScriptCore/runtime/JSStaticScopeObject.cpp b/JavaScriptCore/runtime/JSStaticScopeObject.cpp index 0253fdd..a877ec6 100644 --- a/JavaScriptCore/runtime/JSStaticScopeObject.cpp +++ b/JavaScriptCore/runtime/JSStaticScopeObject.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,12 +31,10 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSStaticScopeObject); -void JSStaticScopeObject::mark() +void JSStaticScopeObject::markChildren(MarkStack& markStack) { - JSVariableObject::mark(); - - if (!d()->registerStore.marked()) - d()->registerStore.mark(); + JSVariableObject::markChildren(markStack); + markStack.append(d()->registerStore.jsValue()); } JSObject* JSStaticScopeObject::toThisObject(ExecState* exec) const @@ -76,9 +74,4 @@ inline bool JSStaticScopeObject::getOwnPropertySlot(ExecState*, const Identifier return symbolTableGet(propertyName, slot); } -inline bool JSStaticScopeObject::getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot& slot, bool& slotIsWriteable) -{ - return symbolTableGet(propertyName, slot, slotIsWriteable); -} - } diff --git a/JavaScriptCore/runtime/JSStaticScopeObject.h b/JavaScriptCore/runtime/JSStaticScopeObject.h index 7e7ce65..5eb0e4b 100644 --- a/JavaScriptCore/runtime/JSStaticScopeObject.h +++ b/JavaScriptCore/runtime/JSStaticScopeObject.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -50,11 +50,10 @@ namespace JSC{ symbolTable().add(ident.ustring().rep(), SymbolTableEntry(-1, attributes)); } virtual ~JSStaticScopeObject(); - virtual void mark(); + virtual void markChildren(MarkStack&); bool isDynamicScope() const; virtual JSObject* toThisObject(ExecState*) const; virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); - virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable); virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); void putWithAttributes(ExecState*, const Identifier&, JSValue, unsigned attributes); diff --git a/JavaScriptCore/runtime/JSString.h b/JavaScriptCore/runtime/JSString.h index 900c565..3daf58a 100644 --- a/JavaScriptCore/runtime/JSString.h +++ b/JavaScriptCore/runtime/JSString.h @@ -23,8 +23,8 @@ #ifndef JSString_h #define JSString_h -#include "CommonIdentifiers.h" #include "CallFrame.h" +#include "CommonIdentifiers.h" #include "Identifier.h" #include "JSNumberCell.h" #include "PropertySlot.h" @@ -60,7 +60,7 @@ namespace JSC { class JSString : public JSCell { friend class JIT; - friend class VPtrSet; + friend struct VPtrSet; public: JSString(JSGlobalData* globalData, const UString& value) @@ -208,7 +208,7 @@ namespace JSC { inline JSString* JSValue::toThisJSString(ExecState* exec) { - return JSImmediate::isImmediate(asValue()) ? jsString(exec, JSImmediate::toString(asValue())) : asCell()->toThisJSString(exec); + return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/JSType.h b/JavaScriptCore/runtime/JSType.h index 68f2890..a118b87 100644 --- a/JavaScriptCore/runtime/JSType.h +++ b/JavaScriptCore/runtime/JSType.h @@ -33,8 +33,11 @@ namespace JSC { NumberType = 3, NullType = 4, StringType = 5, - ObjectType = 6, - GetterSetterType = 7 + + // The CompoundType value must come before any JSType that may have children + CompoundType = 6, + ObjectType = 7, + GetterSetterType = 8 }; } // namespace JSC diff --git a/JavaScriptCore/runtime/JSValue.cpp b/JavaScriptCore/runtime/JSValue.cpp index 885914d..39a4093 100644 --- a/JavaScriptCore/runtime/JSValue.cpp +++ b/JavaScriptCore/runtime/JSValue.cpp @@ -23,8 +23,15 @@ #include "config.h" #include "JSValue.h" +#include "BooleanConstructor.h" +#include "BooleanPrototype.h" +#include "ExceptionHelpers.h" +#include "JSGlobalObject.h" #include "JSFunction.h" +#include "JSNotAnObject.h" +#include "NumberObject.h" #include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> namespace JSC { @@ -33,19 +40,97 @@ static const double D32 = 4294967296.0; // ECMA 9.4 double JSValue::toInteger(ExecState* exec) const { - if (isInt32Fast()) - return getInt32Fast(); + if (isInt32()) + return asInt32(); double d = toNumber(exec); return isnan(d) ? 0.0 : trunc(d); } double JSValue::toIntegerPreserveNaN(ExecState* exec) const { - if (isInt32Fast()) - return getInt32Fast(); + if (isInt32()) + return asInt32(); return trunc(toNumber(exec)); } +JSObject* JSValue::toObjectSlowCase(ExecState* exec) const +{ + ASSERT(!isCell()); + + if (isInt32() || isDouble()) + return constructNumber(exec, asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, asValue()); + ASSERT(isUndefinedOrNull()); + JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, isNull()); + exec->setException(exception); + return new (exec) JSNotAnObject(exec, exception); +} + +JSObject* JSValue::toThisObjectSlowCase(ExecState* exec) const +{ + ASSERT(!isCell()); + + if (isInt32() || isDouble()) + return constructNumber(exec, asValue()); + if (isTrue() || isFalse()) + return constructBooleanFromImmediateBoolean(exec, asValue()); + ASSERT(isUndefinedOrNull()); + return exec->globalThisValue(); +} + +JSObject* JSValue::synthesizeObject(ExecState* exec) const +{ + ASSERT(!isCell()); + if (isNumber()) + return constructNumber(exec, asValue()); + if (isBoolean()) + return constructBooleanFromImmediateBoolean(exec, asValue()); + + JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, isNull()); + exec->setException(exception); + return new (exec) JSNotAnObject(exec, exception); +} + +JSObject* JSValue::synthesizePrototype(ExecState* exec) const +{ + ASSERT(!isCell()); + if (isNumber()) + return exec->lexicalGlobalObject()->numberPrototype(); + if (isBoolean()) + return exec->lexicalGlobalObject()->booleanPrototype(); + + JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, isNull()); + exec->setException(exception); + return new (exec) JSNotAnObject(exec, exception); +} + +#ifndef NDEBUG +char* JSValue::description() +{ + static const size_t size = 32; + static char description[size]; + if (isInt32()) + snprintf(description, size, "Int32: %d", asInt32()); + else if (isDouble()) + snprintf(description, size, "Double: %lf", asDouble()); + else if (isCell()) + snprintf(description, size, "Cell: %p", asCell()); + else if (isTrue()) + snprintf(description, size, "True"); + else if (isFalse()) + snprintf(description, size, "False"); + else if (isNull()) + snprintf(description, size, "Null"); + else { + ASSERT(isUndefined()); + snprintf(description, size, "Undefined"); + } + + return description; +} +#endif + int32_t toInt32SlowCase(double d, bool& ok) { ok = true; @@ -84,4 +169,9 @@ uint32_t toUInt32SlowCase(double d, bool& ok) return static_cast<uint32_t>(d32); } +NEVER_INLINE double nonInlineNaN() +{ + return std::numeric_limits<double>::quiet_NaN(); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSValue.h b/JavaScriptCore/runtime/JSValue.h index 391425c..408c187 100644 --- a/JavaScriptCore/runtime/JSValue.h +++ b/JavaScriptCore/runtime/JSValue.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2004, 2005, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 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 @@ -28,8 +28,11 @@ #include "CallData.h" #include "ConstructData.h" -#include <wtf/HashTraits.h> +#include <math.h> #include <wtf/AlwaysInline.h> +#include <wtf/Assertions.h> +#include <wtf/HashTraits.h> +#include <wtf/MathExtras.h> namespace JSC { @@ -39,6 +42,7 @@ namespace JSC { class JSImmediate; class JSObject; class JSString; + class MarkStack; class PropertySlot; class PutPropertySlot; class UString; @@ -48,31 +52,37 @@ namespace JSC { enum PreferredPrimitiveType { NoPreference, PreferNumber, PreferString }; +#if USE(JSVALUE32_64) + typedef int64_t EncodedJSValue; +#else typedef void* EncodedJSValue; +#endif + + double nonInlineNaN(); + int32_t toInt32SlowCase(double, bool& ok); + uint32_t toUInt32SlowCase(double, bool& ok); class JSValue { friend class JSImmediate; - friend struct JSValueHashTraits; + friend struct EncodedJSValueHashTraits; + friend class JIT; + friend class JITStubs; + friend class JITStubCall; - static JSValue makeImmediate(intptr_t value) - { - return JSValue(reinterpret_cast<JSCell*>(value)); - } - - intptr_t immediateValue() - { - return reinterpret_cast<intptr_t>(m_ptr); - } - public: + static EncodedJSValue encode(JSValue value); + static JSValue decode(EncodedJSValue ptr); +#if !USE(JSVALUE32_64) + private: + static JSValue makeImmediate(intptr_t value); + intptr_t immediateValue(); + public: +#endif enum JSNullTag { JSNull }; enum JSUndefinedTag { JSUndefined }; enum JSTrueTag { JSTrue }; enum JSFalseTag { JSFalse }; - static EncodedJSValue encode(JSValue value); - static JSValue decode(EncodedJSValue ptr); - JSValue(); JSValue(JSNullTag); JSValue(JSUndefinedTag); @@ -94,20 +104,22 @@ namespace JSC { JSValue(ExecState*, long long); JSValue(ExecState*, unsigned long long); JSValue(JSGlobalData*, double); - JSValue(JSGlobalData*, char); - JSValue(JSGlobalData*, unsigned char); - JSValue(JSGlobalData*, short); - JSValue(JSGlobalData*, unsigned short); JSValue(JSGlobalData*, int); JSValue(JSGlobalData*, unsigned); - JSValue(JSGlobalData*, long); - JSValue(JSGlobalData*, unsigned long); - JSValue(JSGlobalData*, long long); - JSValue(JSGlobalData*, unsigned long long); operator bool() const; - bool operator==(const JSValue other) const; - bool operator!=(const JSValue other) const; + bool operator==(const JSValue& other) const; + bool operator!=(const JSValue& other) const; + + bool isInt32() const; + bool isUInt32() const; + bool isDouble() const; + bool isTrue() const; + bool isFalse() const; + + int32_t asInt32() const; + uint32_t asUInt32() const; + double asDouble() const; // Querying the type. bool isUndefined() const; @@ -134,8 +146,6 @@ namespace JSC { // Extracting integer values. bool getUInt32(uint32_t&) const; - bool getTruncatedInt32(int32_t&) const; - bool getTruncatedUInt32(uint32_t&) const; // Basic conversions. JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; @@ -151,37 +161,22 @@ namespace JSC { JSObject* toObject(ExecState*) const; // Integer conversions. - // 'x.numberToInt32(output)' is equivalent to 'x.isNumber() && x.toInt32(output)' double toInteger(ExecState*) const; double toIntegerPreserveNaN(ExecState*) const; int32_t toInt32(ExecState*) const; int32_t toInt32(ExecState*, bool& ok) const; - bool numberToInt32(int32_t& arg); uint32_t toUInt32(ExecState*) const; uint32_t toUInt32(ExecState*, bool& ok) const; - bool numberToUInt32(uint32_t& arg); - - // Fast integer operations; these values return results where the value is trivially available - // in a convenient form, for use in optimizations. No assumptions should be made based on the - // results of these operations, for example !isInt32Fast() does not necessarily indicate the - // result of getNumber will not be 0. - bool isInt32Fast() const; - int32_t getInt32Fast() const; - bool isUInt32Fast() const; - uint32_t getUInt32Fast() const; - static JSValue makeInt32Fast(int32_t); - static bool areBothInt32Fast(JSValue, JSValue); // Floating point conversions (this is a convenience method for webcore; // signle precision float is not a representation used in JS or JSC). float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); } - // API Mangled Numbers - bool isAPIMangledNumber(); - // Garbage collection. - void mark(); + void markChildren(MarkStack&); + bool hasChildren() const; bool marked() const; + void markDirect(); // Object operations, with the toObject operation included. JSValue get(ExecState*, const Identifier& propertyName) const; @@ -208,22 +203,72 @@ namespace JSC { bool isCell() const; JSCell* asCell() const; +#ifndef NDEBUG + char* description(); +#endif + private: enum HashTableDeletedValueTag { HashTableDeletedValue }; JSValue(HashTableDeletedValueTag); inline const JSValue asValue() const { return *this; } + JSObject* toObjectSlowCase(ExecState*) const; + JSObject* toThisObjectSlowCase(ExecState*) const; + + enum { Int32Tag = 0xffffffff }; + enum { CellTag = 0xfffffffe }; + enum { TrueTag = 0xfffffffd }; + enum { FalseTag = 0xfffffffc }; + enum { NullTag = 0xfffffffb }; + enum { UndefinedTag = 0xfffffffa }; + enum { DeletedValueTag = 0xfffffff9 }; + + enum { LowestTag = DeletedValueTag }; + + uint32_t tag() const; + int32_t payload() const; + + JSObject* synthesizePrototype(ExecState*) const; + JSObject* synthesizeObject(ExecState*) const; + +#if USE(JSVALUE32_64) + union { + EncodedJSValue asEncodedJSValue; + double asDouble; +#if PLATFORM(BIG_ENDIAN) + struct { + int32_t tag; + int32_t payload; + } asBits; +#else + struct { + int32_t payload; + int32_t tag; + } asBits; +#endif + } u; +#else // USE(JSVALUE32_64) + JSCell* m_ptr; +#endif // USE(JSVALUE32_64) + }; - bool isDoubleNumber() const; - double getDoubleNumber() const; +#if USE(JSVALUE32_64) + typedef IntHash<EncodedJSValue> EncodedJSValueHash; - JSCell* m_ptr; + struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> { + static const bool emptyValueIsZero = false; + static EncodedJSValue emptyValue() { return JSValue::encode(JSValue()); } + static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } + static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } }; +#else + typedef PtrHash<EncodedJSValue> EncodedJSValueHash; - struct JSValueHashTraits : HashTraits<EncodedJSValue> { + struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> { static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); } }; +#endif // Stand-alone helper functions. inline JSValue jsNull() @@ -301,61 +346,396 @@ namespace JSC { return JSValue(globalData, d); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, char i) + ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, int i) { return JSValue(globalData, i); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, unsigned char i) + ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, unsigned i) { return JSValue(globalData, i); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, short i) + inline bool operator==(const JSValue a, const JSCell* b) { return a == JSValue(b); } + inline bool operator==(const JSCell* a, const JSValue b) { return JSValue(a) == b; } + + inline bool operator!=(const JSValue a, const JSCell* b) { return a != JSValue(b); } + inline bool operator!=(const JSCell* a, const JSValue b) { return JSValue(a) != b; } + + inline int32_t toInt32(double val) { - return JSValue(globalData, i); + if (!(val >= -2147483648.0 && val < 2147483648.0)) { + bool ignored; + return toInt32SlowCase(val, ignored); + } + return static_cast<int32_t>(val); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, unsigned short i) + inline uint32_t toUInt32(double val) { - return JSValue(globalData, i); + if (!(val >= 0.0 && val < 4294967296.0)) { + bool ignored; + return toUInt32SlowCase(val, ignored); + } + return static_cast<uint32_t>(val); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, int i) + ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const { - return JSValue(globalData, i); + if (isInt32()) + return asInt32(); + bool ignored; + return toInt32SlowCase(toNumber(exec), ignored); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, unsigned i) + inline uint32_t JSValue::toUInt32(ExecState* exec) const { - return JSValue(globalData, i); + if (isUInt32()) + return asInt32(); + bool ignored; + return toUInt32SlowCase(toNumber(exec), ignored); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, long i) + inline int32_t JSValue::toInt32(ExecState* exec, bool& ok) const { - return JSValue(globalData, i); + if (isInt32()) { + ok = true; + return asInt32(); + } + return toInt32SlowCase(toNumber(exec), ok); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, unsigned long i) + inline uint32_t JSValue::toUInt32(ExecState* exec, bool& ok) const { - return JSValue(globalData, i); + if (isUInt32()) { + ok = true; + return asInt32(); + } + return toUInt32SlowCase(toNumber(exec), ok); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, long long i) +#if USE(JSVALUE32_64) + inline JSValue jsNaN(ExecState* exec) { - return JSValue(globalData, i); + return JSValue(exec, nonInlineNaN()); } - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, unsigned long long i) + // JSValue member functions. + inline EncodedJSValue JSValue::encode(JSValue value) { - return JSValue(globalData, i); + return value.u.asEncodedJSValue; } - inline bool operator==(const JSValue a, const JSCell* b) { return a == JSValue(b); } - inline bool operator==(const JSCell* a, const JSValue b) { return JSValue(a) == b; } + inline JSValue JSValue::decode(EncodedJSValue encodedJSValue) + { + JSValue v; + v.u.asEncodedJSValue = encodedJSValue; + return v; + } - inline bool operator!=(const JSValue a, const JSCell* b) { return a != JSValue(b); } - inline bool operator!=(const JSCell* a, const JSValue b) { return JSValue(a) != b; } + inline JSValue::JSValue() + { + u.asBits.tag = CellTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSNullTag) + { + u.asBits.tag = NullTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSUndefinedTag) + { + u.asBits.tag = UndefinedTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSTrueTag) + { + u.asBits.tag = TrueTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSFalseTag) + { + u.asBits.tag = FalseTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(HashTableDeletedValueTag) + { + u.asBits.tag = DeletedValueTag; + u.asBits.payload = 0; + } + + inline JSValue::JSValue(JSCell* ptr) + { + u.asBits.tag = CellTag; + u.asBits.payload = reinterpret_cast<int32_t>(ptr); + } + + inline JSValue::JSValue(const JSCell* ptr) + { + u.asBits.tag = CellTag; + u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); + } + + inline JSValue::operator bool() const + { + return u.asBits.payload || tag() != CellTag; + } + + inline bool JSValue::operator==(const JSValue& other) const + { + return u.asEncodedJSValue == other.u.asEncodedJSValue; + } + + inline bool JSValue::operator!=(const JSValue& other) const + { + return u.asEncodedJSValue != other.u.asEncodedJSValue; + } + + inline bool JSValue::isUndefined() const + { + return tag() == UndefinedTag; + } + + inline bool JSValue::isNull() const + { + return tag() == NullTag; + } + + inline bool JSValue::isUndefinedOrNull() const + { + return isUndefined() || isNull(); + } + + inline bool JSValue::isCell() const + { + return tag() == CellTag; + } + + inline bool JSValue::isInt32() const + { + return tag() == Int32Tag; + } + + inline bool JSValue::isUInt32() const + { + return tag() == Int32Tag && asInt32() > -1; + } + + inline bool JSValue::isDouble() const + { + return tag() < LowestTag; + } + + inline bool JSValue::isTrue() const + { + return tag() == TrueTag; + } + + inline bool JSValue::isFalse() const + { + return tag() == FalseTag; + } + + inline uint32_t JSValue::tag() const + { + return u.asBits.tag; + } + + inline int32_t JSValue::payload() const + { + return u.asBits.payload; + } + + inline int32_t JSValue::asInt32() const + { + ASSERT(isInt32()); + return u.asBits.payload; + } + + inline uint32_t JSValue::asUInt32() const + { + ASSERT(isUInt32()); + return u.asBits.payload; + } + + inline double JSValue::asDouble() const + { + ASSERT(isDouble()); + return u.asDouble; + } + + ALWAYS_INLINE JSCell* JSValue::asCell() const + { + ASSERT(isCell()); + return reinterpret_cast<JSCell*>(u.asBits.payload); + } + + inline JSValue::JSValue(ExecState* exec, double d) + { + const int32_t asInt32 = static_cast<int32_t>(d); + if (asInt32 != d || (!asInt32 && signbit(d))) { // true for -0.0 + u.asDouble = d; + return; + } + *this = JSValue(exec, static_cast<int32_t>(d)); + } + + inline JSValue::JSValue(ExecState* exec, char i) + { + *this = JSValue(exec, static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(ExecState* exec, unsigned char i) + { + *this = JSValue(exec, static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(ExecState* exec, short i) + { + *this = JSValue(exec, static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(ExecState* exec, unsigned short i) + { + *this = JSValue(exec, static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(ExecState*, int i) + { + u.asBits.tag = Int32Tag; + u.asBits.payload = i; + } + + inline JSValue::JSValue(ExecState* exec, unsigned i) + { + if (static_cast<int32_t>(i) < 0) { + *this = JSValue(exec, static_cast<double>(i)); + return; + } + *this = JSValue(exec, static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(ExecState* exec, long i) + { + if (static_cast<int32_t>(i) != i) { + *this = JSValue(exec, static_cast<double>(i)); + return; + } + *this = JSValue(exec, static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(ExecState* exec, unsigned long i) + { + if (static_cast<uint32_t>(i) != i) { + *this = JSValue(exec, static_cast<double>(i)); + return; + } + *this = JSValue(exec, static_cast<uint32_t>(i)); + } + + inline JSValue::JSValue(ExecState* exec, long long i) + { + if (static_cast<int32_t>(i) != i) { + *this = JSValue(exec, static_cast<double>(i)); + return; + } + *this = JSValue(exec, static_cast<int32_t>(i)); + } + + inline JSValue::JSValue(ExecState* exec, unsigned long long i) + { + if (static_cast<uint32_t>(i) != i) { + *this = JSValue(exec, static_cast<double>(i)); + return; + } + *this = JSValue(exec, static_cast<uint32_t>(i)); + } + + inline JSValue::JSValue(JSGlobalData* globalData, double d) + { + const int32_t asInt32 = static_cast<int32_t>(d); + if (asInt32 != d || (!asInt32 && signbit(d))) { // true for -0.0 + u.asDouble = d; + return; + } + *this = JSValue(globalData, static_cast<int32_t>(d)); + } + + inline JSValue::JSValue(JSGlobalData*, int i) + { + u.asBits.tag = Int32Tag; + u.asBits.payload = i; + } + + inline JSValue::JSValue(JSGlobalData* globalData, unsigned i) + { + if (static_cast<int32_t>(i) < 0) { + *this = JSValue(globalData, static_cast<double>(i)); + return; + } + *this = JSValue(globalData, static_cast<int32_t>(i)); + } + + inline bool JSValue::isNumber() const + { + return isInt32() || isDouble(); + } + + inline bool JSValue::isBoolean() const + { + return isTrue() || isFalse(); + } + + inline bool JSValue::getBoolean(bool& v) const + { + if (isTrue()) { + v = true; + return true; + } + if (isFalse()) { + v = false; + return true; + } + + return false; + } + + inline bool JSValue::getBoolean() const + { + ASSERT(isBoolean()); + return tag() == TrueTag; + } + + inline double JSValue::uncheckedGetNumber() const + { + ASSERT(isNumber()); + return isInt32() ? asInt32() : asDouble(); + } + + ALWAYS_INLINE JSValue JSValue::toJSNumber(ExecState* exec) const + { + return isNumber() ? asValue() : jsNumber(exec, this->toNumber(exec)); + } + + inline bool JSValue::getNumber(double& result) const + { + if (isInt32()) { + result = asInt32(); + return true; + } + if (isDouble()) { + result = asDouble(); + return true; + } + return false; + } + +#else // USE(JSVALUE32_64) // JSValue member functions. inline EncodedJSValue JSValue::encode(JSValue value) @@ -368,6 +748,16 @@ namespace JSC { return JSValue(reinterpret_cast<JSCell*>(ptr)); } + inline JSValue JSValue::makeImmediate(intptr_t value) + { + return JSValue(reinterpret_cast<JSCell*>(value)); + } + + inline intptr_t JSValue::immediateValue() + { + return reinterpret_cast<intptr_t>(m_ptr); + } + // 0x0 can never occur naturally because it has a tag of 00, indicating a pointer value, but a payload of 0x0, which is in the (invalid) zero page. inline JSValue::JSValue() : m_ptr(0) @@ -395,12 +785,12 @@ namespace JSC { return m_ptr; } - inline bool JSValue::operator==(const JSValue other) const + inline bool JSValue::operator==(const JSValue& other) const { return m_ptr == other.m_ptr; } - inline bool JSValue::operator!=(const JSValue other) const + inline bool JSValue::operator!=(const JSValue& other) const { return m_ptr != other.m_ptr; } @@ -414,6 +804,7 @@ namespace JSC { { return asValue() == jsNull(); } +#endif // USE(JSVALUE32_64) } // namespace JSC diff --git a/JavaScriptCore/runtime/JSWrapperObject.cpp b/JavaScriptCore/runtime/JSWrapperObject.cpp index fb57018..2c39f5c 100644 --- a/JavaScriptCore/runtime/JSWrapperObject.cpp +++ b/JavaScriptCore/runtime/JSWrapperObject.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Maks Orlovich - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006, 2009 Apple, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,11 +26,11 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSWrapperObject); -void JSWrapperObject::mark() +void JSWrapperObject::markChildren(MarkStack& markStack) { - JSObject::mark(); - if (m_internalValue && !m_internalValue.marked()) - m_internalValue.mark(); + JSObject::markChildren(markStack); + if (m_internalValue) + markStack.append(m_internalValue); } } // namespace JSC diff --git a/JavaScriptCore/runtime/JSWrapperObject.h b/JavaScriptCore/runtime/JSWrapperObject.h index 2a2e3c6..0b2c680 100644 --- a/JavaScriptCore/runtime/JSWrapperObject.h +++ b/JavaScriptCore/runtime/JSWrapperObject.h @@ -36,7 +36,7 @@ namespace JSC { JSValue internalValue() const { return m_internalValue; } void setInternalValue(JSValue); - virtual void mark(); + virtual void markChildren(MarkStack&); private: JSValue m_internalValue; diff --git a/JavaScriptCore/runtime/LiteralParser.cpp b/JavaScriptCore/runtime/LiteralParser.cpp index 10f9a13..17ec906 100644 --- a/JavaScriptCore/runtime/LiteralParser.cpp +++ b/JavaScriptCore/runtime/LiteralParser.cpp @@ -28,31 +28,12 @@ #include "JSArray.h" #include "JSString.h" +#include "Lexer.h" #include <wtf/ASCIICType.h> +#include <wtf/dtoa.h> namespace JSC { -class LiteralParser::StackGuard { -public: - StackGuard(LiteralParser* parser) - : m_parser(parser) - { - m_parser->m_depth++; - } - ~StackGuard() - { - m_parser->m_depth--; - } - bool isSafe() { return m_parser->m_depth < 10; } -private: - LiteralParser* m_parser; -}; - -static bool isSafeStringCharacter(UChar c) -{ - return (c >= ' ' && c <= 0xff && c != '\\') || c == '\t'; -} - LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) { while (m_ptr < m_end && isASCIISpace(*m_ptr)) @@ -100,8 +81,33 @@ LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) token.end = ++m_ptr; return TokColon; case '"': - return lexString(token); - + if (m_mode == StrictJSON) + return lexString<StrictJSON>(token); + return lexString<NonStrictJSON>(token); + case 't': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') { + m_ptr += 4; + token.type = TokTrue; + token.end = m_ptr; + return TokTrue; + } + break; + case 'f': + if (m_end - m_ptr >= 5 && m_ptr[1] == 'a' && m_ptr[2] == 'l' && m_ptr[3] == 's' && m_ptr[4] == 'e') { + m_ptr += 5; + token.type = TokFalse; + token.end = m_ptr; + return TokFalse; + } + break; + case 'n': + if (m_end - m_ptr >= 4 && m_ptr[1] == 'u' && m_ptr[2] == 'l' && m_ptr[3] == 'l') { + m_ptr += 4; + token.type = TokNull; + token.end = m_ptr; + return TokNull; + } + break; case '-': case '0': case '1': @@ -118,16 +124,81 @@ LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token) return TokError; } -LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token) +template <LiteralParser::ParserMode mode> static inline bool isSafeStringCharacter(UChar c) +{ + return (c >= ' ' && (mode == LiteralParser::StrictJSON || c <= 0xff) && c != '\\' && c != '"') || c == '\t'; +} + +// "inline" is required here to help WINSCW compiler resolve specialized argument in templated functions. +template <LiteralParser::ParserMode mode> inline LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token) { ++m_ptr; - while (m_ptr < m_end && isSafeStringCharacter(*m_ptr) && *m_ptr != '"') - ++m_ptr; - if (m_ptr >= m_end || *m_ptr != '"') { - token.type = TokError; - token.end = ++m_ptr; + const UChar* runStart; + token.stringToken = UString(); + do { + runStart = m_ptr; + while (m_ptr < m_end && isSafeStringCharacter<mode>(*m_ptr)) + ++m_ptr; + if (runStart < m_ptr) + token.stringToken.append(runStart, m_ptr - runStart); + if ((mode == StrictJSON) && m_ptr < m_end && *m_ptr == '\\') { + ++m_ptr; + if (m_ptr >= m_end) + return TokError; + switch (*m_ptr) { + case '"': + token.stringToken.append('"'); + m_ptr++; + break; + case '\\': + token.stringToken.append('\\'); + m_ptr++; + break; + case '/': + token.stringToken.append('/'); + m_ptr++; + break; + case 'b': + token.stringToken.append('\b'); + m_ptr++; + break; + case 'f': + token.stringToken.append('\f'); + m_ptr++; + break; + case 'n': + token.stringToken.append('\n'); + m_ptr++; + break; + case 'r': + token.stringToken.append('\r'); + m_ptr++; + break; + case 't': + token.stringToken.append('\t'); + m_ptr++; + break; + + case 'u': + if ((m_end - m_ptr) < 5) // uNNNN == 5 characters + return TokError; + for (int i = 1; i < 5; i++) { + if (!isASCIIHexDigit(m_ptr[i])) + return TokError; + } + token.stringToken.append(JSC::Lexer::convertUnicode(m_ptr[1], m_ptr[2], m_ptr[3], m_ptr[4])); + m_ptr += 5; + break; + + default: + return TokError; + } + } + } while ((mode == StrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != '"'); + + if (m_ptr >= m_end || *m_ptr != '"') return TokError; - } + token.type = TokString; token.end = ++m_ptr; return TokString; @@ -167,7 +238,7 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok if (m_ptr < m_end && *m_ptr == '.') { ++m_ptr; // [0-9]+ - if (m_ptr >= m_end && !isASCIIDigit(*m_ptr)) + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) return TokError; ++m_ptr; @@ -184,7 +255,7 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok ++m_ptr; // [0-9]+ - if (m_ptr >= m_end && !isASCIIDigit(*m_ptr)) + if (m_ptr >= m_end || !isASCIIDigit(*m_ptr)) return TokError; ++m_ptr; @@ -194,113 +265,186 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok token.type = TokNumber; token.end = m_ptr; + Vector<char, 64> buffer(token.end - token.start + 1); + int i; + for (i = 0; i < token.end - token.start; i++) { + ASSERT(static_cast<char>(token.start[i]) == token.start[i]); + buffer[i] = static_cast<char>(token.start[i]); + } + buffer[i] = 0; + char* end; + token.numberToken = WTF::strtod(buffer.data(), &end); + ASSERT(buffer.data() + (token.end - token.start) == end); return TokNumber; } -JSValue LiteralParser::parseStatement() +JSValue LiteralParser::parse(ParserState initialState) { - StackGuard guard(this); - if (!guard.isSafe()) - return abortParse(); + ParserState state = initialState; + MarkedArgumentBuffer objectStack; + JSValue lastValue; + Vector<ParserState, 16> stateStack; + Vector<Identifier, 16> identifierStack; + while (1) { + switch(state) { + startParseArray: + case StartParseArray: { + JSArray* array = constructEmptyArray(m_exec); + objectStack.append(array); + // fallthrough + } + doParseArrayStartExpression: + case DoParseArrayStartExpression: { + if (m_lexer.next() == TokRBracket) { + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } - switch (m_lexer.currentToken().type) { - case TokLBracket: - case TokNumber: - case TokString: - return parseExpression(); - case TokLParen: { - m_lexer.next(); - JSValue result = parseExpression(); - if (m_aborted || m_lexer.currentToken().type != TokRParen) - return abortParse(); - m_lexer.next(); - return result; - } - default: - return abortParse(); - } -} + stateStack.append(DoParseArrayEndExpression); + goto startParseExpression; + } + case DoParseArrayEndExpression: { + asArray(objectStack.last())->push(m_exec, lastValue); + + if (m_lexer.currentToken().type == TokComma) + goto doParseArrayStartExpression; -JSValue LiteralParser::parseExpression() -{ - StackGuard guard(this); - if (!guard.isSafe()) - return abortParse(); - switch (m_lexer.currentToken().type) { - case TokLBracket: - return parseArray(); - case TokLBrace: - return parseObject(); - case TokString: { - Lexer::LiteralParserToken stringToken = m_lexer.currentToken(); - m_lexer.next(); - return jsString(m_exec, UString(stringToken.start + 1, stringToken.end - stringToken.start - 2)); - } - case TokNumber: { - Lexer::LiteralParserToken numberToken = m_lexer.currentToken(); - m_lexer.next(); - return jsNumber(m_exec, UString(numberToken.start, numberToken.end - numberToken.start).toDouble()); - } - default: - return JSValue(); - } -} + if (m_lexer.currentToken().type != TokRBracket) + return JSValue(); + + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + startParseObject: + case StartParseObject: { + JSObject* object = constructEmptyObject(m_exec); + objectStack.append(object); -JSValue LiteralParser::parseArray() -{ - StackGuard guard(this); - if (!guard.isSafe()) - return abortParse(); - JSArray* array = constructEmptyArray(m_exec); - while (true) { - m_lexer.next(); - JSValue value = parseExpression(); - if (m_aborted) - return JSValue(); - if (!value) - break; - array->push(m_exec, value); + TokenType type = m_lexer.next(); + if (type == TokString) { + Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); - if (m_lexer.currentToken().type != TokComma) - break; - } - if (m_lexer.currentToken().type != TokRBracket) - return abortParse(); + // Check for colon + if (m_lexer.next() != TokColon) + return JSValue(); + + m_lexer.next(); + identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } else if (type != TokRBrace) + return JSValue(); + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + doParseObjectStartExpression: + case DoParseObjectStartExpression: { + TokenType type = m_lexer.next(); + if (type != TokString) + return JSValue(); + Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); - m_lexer.next(); - return array; -} + // Check for colon + if (m_lexer.next() != TokColon) + return JSValue(); -JSValue LiteralParser::parseObject() -{ - StackGuard guard(this); - if (!guard.isSafe()) - return abortParse(); - JSObject* object = constructEmptyObject(m_exec); - - while (m_lexer.next() == TokString) { - Lexer::LiteralParserToken identifierToken = m_lexer.currentToken(); - - // Check for colon - if (m_lexer.next() != TokColon) - return abortParse(); - m_lexer.next(); - - JSValue value = parseExpression(); - if (!value || m_aborted) - return abortParse(); - - Identifier ident(m_exec, identifierToken.start + 1, identifierToken.end - identifierToken.start - 2); - object->putDirect(ident, value); - - if (m_lexer.currentToken().type != TokComma) - break; + m_lexer.next(); + identifierStack.append(Identifier(m_exec, identifierToken.stringToken)); + stateStack.append(DoParseObjectEndExpression); + goto startParseExpression; + } + case DoParseObjectEndExpression: + { + asObject(objectStack.last())->putDirect(identifierStack.last(), lastValue); + identifierStack.removeLast(); + if (m_lexer.currentToken().type == TokComma) + goto doParseObjectStartExpression; + if (m_lexer.currentToken().type != TokRBrace) + return JSValue(); + m_lexer.next(); + lastValue = objectStack.last(); + objectStack.removeLast(); + break; + } + startParseExpression: + case StartParseExpression: { + switch (m_lexer.currentToken().type) { + case TokLBracket: + goto startParseArray; + case TokLBrace: + goto startParseObject; + case TokString: { + Lexer::LiteralParserToken stringToken = m_lexer.currentToken(); + m_lexer.next(); + lastValue = jsString(m_exec, stringToken.stringToken); + break; + } + case TokNumber: { + Lexer::LiteralParserToken numberToken = m_lexer.currentToken(); + m_lexer.next(); + lastValue = jsNumber(m_exec, numberToken.numberToken); + break; + } + case TokNull: + m_lexer.next(); + lastValue = jsNull(); + break; + + case TokTrue: + m_lexer.next(); + lastValue = jsBoolean(true); + break; + + case TokFalse: + m_lexer.next(); + lastValue = jsBoolean(false); + break; + + default: + // Error + return JSValue(); + } + break; + } + case StartParseStatement: { + switch (m_lexer.currentToken().type) { + case TokLBracket: + case TokNumber: + case TokString: + goto startParseExpression; + + case TokLParen: { + m_lexer.next(); + stateStack.append(StartParseStatementEndStatement); + goto startParseExpression; + } + default: + return JSValue(); + } + } + case StartParseStatementEndStatement: { + ASSERT(stateStack.isEmpty()); + if (m_lexer.currentToken().type != TokRParen) + return JSValue(); + if (m_lexer.next() == TokEnd) + return lastValue; + return JSValue(); + } + default: + ASSERT_NOT_REACHED(); + } + if (stateStack.isEmpty()) + return lastValue; + state = stateStack.last(); + stateStack.removeLast(); + continue; } - - if (m_lexer.currentToken().type != TokRBrace) - return abortParse(); - m_lexer.next(); - return object; } } diff --git a/JavaScriptCore/runtime/LiteralParser.h b/JavaScriptCore/runtime/LiteralParser.h index a72e3d0..bceee7c 100644 --- a/JavaScriptCore/runtime/LiteralParser.h +++ b/JavaScriptCore/runtime/LiteralParser.h @@ -34,27 +34,31 @@ namespace JSC { class LiteralParser { public: - LiteralParser(ExecState* exec, const UString& s) + typedef enum { StrictJSON, NonStrictJSON } ParserMode; + LiteralParser(ExecState* exec, const UString& s, ParserMode mode) : m_exec(exec) - , m_lexer(s) - , m_depth(0) - , m_aborted(false) + , m_lexer(s, mode) + , m_mode(mode) { } JSValue tryLiteralParse() { m_lexer.next(); - JSValue result = parseStatement(); - if (m_aborted || m_lexer.currentToken().type != TokEnd) + JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement); + if (m_lexer.currentToken().type != TokEnd) return JSValue(); return result; } private: - + enum ParserState { StartParseObject, StartParseArray, StartParseExpression, + StartParseStatement, StartParseStatementEndStatement, + DoParseObjectStartExpression, DoParseObjectEndExpression, + DoParseArrayStartExpression, DoParseArrayEndExpression }; enum TokenType { TokLBracket, TokRBracket, TokLBrace, TokRBrace, TokString, TokIdentifier, TokNumber, TokColon, - TokLParen, TokRParen, TokComma, TokEnd, TokError }; + TokLParen, TokRParen, TokComma, TokTrue, TokFalse, + TokNull, TokEnd, TokError }; class Lexer { public: @@ -62,9 +66,12 @@ namespace JSC { TokenType type; const UChar* start; const UChar* end; + UString stringToken; + double numberToken; }; - Lexer(const UString& s) + Lexer(const UString& s, ParserMode mode) : m_string(s) + , m_mode(mode) , m_ptr(s.data()) , m_end(s.data() + s.size()) { @@ -82,30 +89,21 @@ namespace JSC { private: TokenType lex(LiteralParserToken&); - TokenType lexString(LiteralParserToken&); + template <ParserMode parserMode> TokenType lexString(LiteralParserToken&); TokenType lexNumber(LiteralParserToken&); LiteralParserToken m_currentToken; UString m_string; + ParserMode m_mode; const UChar* m_ptr; const UChar* m_end; }; class StackGuard; - JSValue parseStatement(); - JSValue parseExpression(); - JSValue parseArray(); - JSValue parseObject(); - - JSValue abortParse() - { - m_aborted = true; - return JSValue(); - } + JSValue parse(ParserState); ExecState* m_exec; LiteralParser::Lexer m_lexer; - int m_depth; - bool m_aborted; + ParserMode m_mode; }; } diff --git a/JavaScriptCore/runtime/Lookup.h b/JavaScriptCore/runtime/Lookup.h index 3b7353d..167f2bc 100644 --- a/JavaScriptCore/runtime/Lookup.h +++ b/JavaScriptCore/runtime/Lookup.h @@ -29,6 +29,13 @@ #include <stdio.h> #include <wtf/Assertions.h> +// Bug #26843: Work around Metrowerks compiler bug +#if COMPILER(WINSCW) +#define JSC_CONST_HASHTABLE +#else +#define JSC_CONST_HASHTABLE const +#endif + namespace JSC { // Hash table generated by the create_hash_table script. diff --git a/JavaScriptCore/runtime/MarkStack.cpp b/JavaScriptCore/runtime/MarkStack.cpp new file mode 100644 index 0000000..80dbb17 --- /dev/null +++ b/JavaScriptCore/runtime/MarkStack.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 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 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 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. + */ + +#include "config.h" +#include "MarkStack.h" + +namespace JSC +{ + +size_t MarkStack::s_pageSize = 0; + +void MarkStack::compact() +{ + ASSERT(s_pageSize); + m_values.shrinkAllocation(s_pageSize); + m_markSets.shrinkAllocation(s_pageSize); +} + +} diff --git a/JavaScriptCore/runtime/MarkStack.h b/JavaScriptCore/runtime/MarkStack.h new file mode 100644 index 0000000..7a7b3af --- /dev/null +++ b/JavaScriptCore/runtime/MarkStack.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2009 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 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 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 MarkStack_h +#define MarkStack_h + +#include "JSValue.h" + +#include <wtf/Noncopyable.h> + +namespace JSC { + class Register; + + enum MarkSetProperties { MayContainNullValues, NoNullValues }; + + class MarkStack : Noncopyable { + public: + MarkStack() + : m_markSets() + , m_values() + { + } + + ALWAYS_INLINE void append(JSValue value) + { + ASSERT(value); + if (value.marked()) + return; + value.markDirect(); + if (value.hasChildren()) + m_values.append(value.asCell()); + } + + ALWAYS_INLINE void append(JSCell* cell); + + ALWAYS_INLINE void appendValues(Register* values, size_t count, MarkSetProperties properties = NoNullValues) + { + appendValues(reinterpret_cast<JSValue*>(values), count, properties); + } + + ALWAYS_INLINE void appendValues(JSValue* values, size_t count, MarkSetProperties properties = NoNullValues) + { + if (count) + m_markSets.append(MarkSet(values, values + count, properties)); + } + + inline void drain(); + void compact(); + + ~MarkStack() + { + ASSERT(m_markSets.isEmpty()); + ASSERT(m_values.isEmpty()); + } + + private: + struct MarkSet { + MarkSet(JSValue* values, JSValue* end, MarkSetProperties properties) + : m_values(values) + , m_end(end) + , m_properties(properties) + { + } + JSValue* m_values; + JSValue* m_end; + MarkSetProperties m_properties; + }; + + static void* allocateStack(size_t size); + static void releaseStack(void* addr, size_t size); + + static void initializePagesize(); + static size_t pageSize() + { + if (!s_pageSize) + initializePagesize(); + return s_pageSize; + } + + template <typename T> struct MarkStackArray { + MarkStackArray() + : m_top(0) + , m_allocated(MarkStack::pageSize()) + , m_capacity(m_allocated / sizeof(T)) + { + m_data = reinterpret_cast<T*>(allocateStack(m_allocated)); + } + + ~MarkStackArray() + { + releaseStack(m_data, m_allocated); + } + + void expand() + { + size_t oldAllocation = m_allocated; + m_allocated *= 2; + m_capacity = m_allocated / sizeof(T); + void* newData = allocateStack(m_allocated); + memcpy(newData, m_data, oldAllocation); + releaseStack(m_data, oldAllocation); + m_data = reinterpret_cast<T*>(newData); + } + + inline void append(const T& v) + { + if (m_top == m_capacity) + expand(); + m_data[m_top++] = v; + } + + inline T removeLast() + { + ASSERT(m_top); + return m_data[--m_top]; + } + + inline bool isEmpty() + { + return m_top == 0; + } + + inline size_t size() { return m_top; } + + inline void shrinkAllocation(size_t size) + { + ASSERT(size <= m_allocated); + ASSERT(0 == (size % MarkStack::pageSize())); + if (size == m_allocated) + return; + releaseStack(reinterpret_cast<char*>(m_data) + size, m_allocated - size); + m_allocated = size; + m_capacity = m_allocated / sizeof(T); + } + + private: + size_t m_top; + size_t m_allocated; + size_t m_capacity; + T* m_data; + }; + + MarkStackArray<MarkSet> m_markSets; + MarkStackArray<JSCell*> m_values; + static size_t s_pageSize; + }; +} + +#endif diff --git a/JavaScriptCore/runtime/MarkStackPosix.cpp b/JavaScriptCore/runtime/MarkStackPosix.cpp new file mode 100644 index 0000000..8e78ff3 --- /dev/null +++ b/JavaScriptCore/runtime/MarkStackPosix.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009 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 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 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. + */ + +#include "config.h" + + +#include "MarkStack.h" + +#include <unistd.h> +#include <sys/mman.h> + +namespace JSC { + +void MarkStack::initializePagesize() +{ + MarkStack::s_pageSize = getpagesize(); +} + +void* MarkStack::allocateStack(size_t size) +{ + return mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +} +void MarkStack::releaseStack(void* addr, size_t size) +{ + munmap(addr, size); +} + +} diff --git a/JavaScriptCore/runtime/MarkStackWin.cpp b/JavaScriptCore/runtime/MarkStackWin.cpp new file mode 100644 index 0000000..dbc3306 --- /dev/null +++ b/JavaScriptCore/runtime/MarkStackWin.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009 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 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 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. + */ + +#include "config.h" + + +#include "MarkStack.h" + +#include "windows.h" + +namespace JSC { + +void MarkStack::initializePagesize() +{ + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + MarkStack::s_pageSize = system_info.dwPageSize; +} + +void* MarkStack::allocateStack(size_t size) +{ + return VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +} +void MarkStack::releaseStack(void* addr, size_t size) +{ + VirtualFree(addr, size, MEM_RELEASE); +} + +} diff --git a/JavaScriptCore/runtime/ObjectConstructor.cpp b/JavaScriptCore/runtime/ObjectConstructor.cpp index cf1790f..70c7cd1 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -24,12 +24,15 @@ #include "JSFunction.h" #include "JSGlobalObject.h" #include "ObjectPrototype.h" +#include "PrototypeFunction.h" namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); -ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> structure, ObjectPrototype* objectPrototype) +static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&); + +ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) : InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object")) { // ECMA 15.2.3.1 @@ -37,6 +40,8 @@ ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> stru // no. of arguments for constructor putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); + + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum); } // ECMA 15.2.2 @@ -70,4 +75,11 @@ CallType ObjectConstructor::getCallData(CallData& callData) return CallTypeHost; } +JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec, JSObject*, JSValue, const ArgList& args) +{ + if (!args.at(0).isObject()) + return throwError(exec, TypeError, "Requested prototype of a value that is not an object."); + return asObject(args.at(0))->prototype(); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/ObjectConstructor.h b/JavaScriptCore/runtime/ObjectConstructor.h index f8c058a..9373781 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.h +++ b/JavaScriptCore/runtime/ObjectConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class ObjectConstructor : public InternalFunction { public: - ObjectConstructor(ExecState*, PassRefPtr<Structure>, ObjectPrototype*); + ObjectConstructor(ExecState*, PassRefPtr<Structure>, ObjectPrototype*, Structure* prototypeFunctionStructure); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h index acfc6c2..c4900d3 100644 --- a/JavaScriptCore/runtime/Operations.h +++ b/JavaScriptCore/runtime/Operations.h @@ -38,7 +38,7 @@ namespace JSC { // ECMA 11.9.3 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) { - if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2)) + if (v1.isInt32() && v2.isInt32()) return v1 == v2; return equalSlowCase(exec, v1, v2); @@ -46,8 +46,6 @@ namespace JSC { ALWAYS_INLINE bool JSValue::equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) { - ASSERT(!JSImmediate::areBothImmediateIntegerNumbers(v1, v2)); - do { if (v1.isNumber() && v2.isNumber()) return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); @@ -60,13 +58,13 @@ namespace JSC { if (v1.isUndefinedOrNull()) { if (v2.isUndefinedOrNull()) return true; - if (JSImmediate::isImmediate(v2)) + if (!v2.isCell()) return false; return v2.asCell()->structure()->typeInfo().masqueradesAsUndefined(); } if (v2.isUndefinedOrNull()) { - if (JSImmediate::isImmediate(v1)) + if (!v1.isCell()) return false; return v1.asCell()->structure()->typeInfo().masqueradesAsUndefined(); } @@ -78,7 +76,7 @@ namespace JSC { if (exec->hadException()) return false; v1 = p1; - if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2)) + if (v1.isInt32() && v2.isInt32()) return v1 == v2; continue; } @@ -88,7 +86,7 @@ namespace JSC { if (exec->hadException()) return false; v2 = p2; - if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2)) + if (v1.isInt32() && v2.isInt32()) return v1 == v2; continue; } @@ -114,7 +112,7 @@ namespace JSC { // ECMA 11.9.3 ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(JSValue v1, JSValue v2) { - ASSERT(!JSImmediate::isEitherImmediate(v1, v2)); + ASSERT(v1.isCell() && v2.isCell()); if (v1.asCell()->isString() && v2.asCell()->isString()) return asString(v1)->value() == asString(v2)->value(); @@ -124,13 +122,13 @@ namespace JSC { inline bool JSValue::strictEqual(JSValue v1, JSValue v2) { - if (JSImmediate::areBothImmediateIntegerNumbers(v1, v2)) + if (v1.isInt32() && v2.isInt32()) return v1 == v2; if (v1.isNumber() && v2.isNumber()) return v1.uncheckedGetNumber() == v2.uncheckedGetNumber(); - if (JSImmediate::isEitherImmediate(v1, v2)) + if (!v1.isCell() || !v2.isCell()) return v1 == v2; return strictEqualSlowCaseInline(v1, v2); @@ -138,8 +136,8 @@ namespace JSC { inline bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) { - if (JSValue::areBothInt32Fast(v1, v2)) - return v1.getInt32Fast() < v2.getInt32Fast(); + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() < v2.asInt32(); double n1; double n2; @@ -163,8 +161,8 @@ namespace JSC { inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) { - if (JSValue::areBothInt32Fast(v1, v2)) - return v1.getInt32Fast() <= v2.getInt32Fast(); + if (v1.isInt32() && v2.isInt32()) + return v1.asInt32() <= v2.asInt32(); double n1; double n2; @@ -213,8 +211,8 @@ namespace JSC { } if (rightIsNumber & leftIsString) { - RefPtr<UString::Rep> value = v2.isInt32Fast() ? - concatenate(asString(v1)->value().rep(), v2.getInt32Fast()) : + RefPtr<UString::Rep> value = v2.isInt32() ? + concatenate(asString(v1)->value().rep(), v2.asInt32()) : concatenate(asString(v1)->value().rep(), right); if (!value) @@ -315,8 +313,8 @@ namespace JSC { JSValue v = strings[i].jsValue(); if (LIKELY(v.isString())) result.append(asString(v)->value()); - else if (v.isInt32Fast()) - result.appendNumeric(v.getInt32Fast()); + else if (v.isInt32()) + result.appendNumeric(v.asInt32()); else { double d; if (v.getNumber(d)) diff --git a/JavaScriptCore/runtime/PropertySlot.h b/JavaScriptCore/runtime/PropertySlot.h index 7af60ce..15d9034 100644 --- a/JavaScriptCore/runtime/PropertySlot.h +++ b/JavaScriptCore/runtime/PropertySlot.h @@ -23,7 +23,6 @@ #include "Identifier.h" #include "JSValue.h" -#include "JSImmediate.h" #include "Register.h" #include <wtf/Assertions.h> #include <wtf/NotFound.h> @@ -39,16 +38,16 @@ namespace JSC { class PropertySlot { public: PropertySlot() - : m_offset(WTF::notFound) { clearBase(); + clearOffset(); clearValue(); } explicit PropertySlot(const JSValue base) : m_slotBase(base) - , m_offset(WTF::notFound) { + clearOffset(); clearValue(); } @@ -79,21 +78,12 @@ namespace JSC { return m_offset; } - void putValue(JSValue value) - { - if (m_getValue == JSC_VALUE_SLOT_MARKER) { - *m_data.valueSlot = value; - return; - } - ASSERT(m_getValue == JSC_REGISTER_SLOT_MARKER); - *m_data.registerSlot = JSValue(value); - } - void setValueSlot(JSValue* valueSlot) { ASSERT(valueSlot); - m_getValue = JSC_VALUE_SLOT_MARKER; clearBase(); + clearOffset(); + m_getValue = JSC_VALUE_SLOT_MARKER; m_data.valueSlot = valueSlot; } @@ -117,8 +107,9 @@ namespace JSC { void setValue(JSValue value) { ASSERT(value); - m_getValue = JSC_VALUE_SLOT_MARKER; clearBase(); + clearOffset(); + m_getValue = JSC_VALUE_SLOT_MARKER; m_value = value; m_data.valueSlot = &m_value; } @@ -126,8 +117,9 @@ namespace JSC { void setRegisterSlot(Register* registerSlot) { ASSERT(registerSlot); - m_getValue = JSC_REGISTER_SLOT_MARKER; clearBase(); + clearOffset(); + m_getValue = JSC_REGISTER_SLOT_MARKER; m_data.registerSlot = registerSlot; } @@ -157,13 +149,11 @@ namespace JSC { void setUndefined() { - clearBase(); setValue(jsUndefined()); } JSValue slotBase() const { - ASSERT(m_slotBase); return m_slotBase; } @@ -188,6 +178,13 @@ namespace JSC { #endif } + void clearOffset() + { + // Clear offset even in release builds, in case this PropertySlot has been used before. + // (For other data members, we don't need to clear anything because reuse would meaningfully overwrite them.) + m_offset = WTF::notFound; + } + unsigned index() const { return m_data.index; } private: diff --git a/JavaScriptCore/runtime/RegExp.cpp b/JavaScriptCore/runtime/RegExp.cpp index 857a316..7dd4a8f 100644 --- a/JavaScriptCore/runtime/RegExp.cpp +++ b/JavaScriptCore/runtime/RegExp.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org) * Copyright (c) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -110,7 +111,7 @@ void RegExp::compile(JSGlobalData* globalData) #endif } -int RegExp::match(const UString& s, int startOffset, OwnArrayPtr<int>* ovector) +int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) { if (startOffset < 0) startOffset = 0; @@ -126,16 +127,20 @@ int RegExp::match(const UString& s, int startOffset, OwnArrayPtr<int>* ovector) if (m_regExpBytecode) { #endif int offsetVectorSize = (m_numSubpatterns + 1) * 3; // FIXME: should be 2 - but adding temporary fallback to pcre. - int* offsetVector = new int [offsetVectorSize]; + int* offsetVector; + Vector<int, 32> nonReturnedOvector; + if (ovector) { + ovector->resize(offsetVectorSize); + offsetVector = ovector->data(); + } else { + nonReturnedOvector.resize(offsetVectorSize); + offsetVector = nonReturnedOvector.data(); + } + ASSERT(offsetVector); for (int j = 0; j < offsetVectorSize; ++j) offsetVector[j] = -1; - OwnArrayPtr<int> nonReturnedOvector; - if (!ovector) - nonReturnedOvector.set(offsetVector); - else - ovector->set(offsetVector); #if ENABLE(YARR_JIT) int result = Yarr::executeRegex(m_regExpJITCode, s.data(), startOffset, s.size(), offsetVector, offsetVectorSize); @@ -177,7 +182,7 @@ void RegExp::compile(JSGlobalData* globalData) m_regExp = jsRegExpCompile(reinterpret_cast<const UChar*>(m_pattern.data()), m_pattern.size(), ignoreCaseOption, multilineOption, &m_numSubpatterns, &m_constructionError); } -int RegExp::match(const UString& s, int startOffset, OwnArrayPtr<int>* ovector) +int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) { if (startOffset < 0) startOffset = 0; @@ -190,17 +195,19 @@ int RegExp::match(const UString& s, int startOffset, OwnArrayPtr<int>* ovector) #if ENABLE(WREC) if (m_wrecFunction) { int offsetVectorSize = (m_numSubpatterns + 1) * 2; - int* offsetVector = new int [offsetVectorSize]; + int* offsetVector; + Vector<int, 32> nonReturnedOvector; + if (ovector) { + ovector->resize(offsetVectorSize); + offsetVector = ovector->data(); + } else { + nonReturnedOvector.resize(offsetVectorSize); + offsetVector = nonReturnedOvector.data(); + } ASSERT(offsetVector); for (int j = 0; j < offsetVectorSize; ++j) offsetVector[j] = -1; - OwnArrayPtr<int> nonReturnedOvector; - if (!ovector) - nonReturnedOvector.set(offsetVector); - else - ovector->set(offsetVector); - int result = m_wrecFunction(s.data(), startOffset, s.size(), offsetVector); if (result < 0) { @@ -226,8 +233,8 @@ int RegExp::match(const UString& s, int startOffset, OwnArrayPtr<int>* ovector) offsetVector = fixedSizeOffsetVector; } else { offsetVectorSize = (m_numSubpatterns + 1) * 3; - offsetVector = new int [offsetVectorSize]; - ovector->set(offsetVector); + ovector->resize(offsetVectorSize); + offsetVector = ovector->data(); } int numMatches = jsRegExpExecute(m_regExp, reinterpret_cast<const UChar*>(s.data()), s.size(), startOffset, offsetVector, offsetVectorSize); diff --git a/JavaScriptCore/runtime/RegExp.h b/JavaScriptCore/runtime/RegExp.h index f3be656..24d4199 100644 --- a/JavaScriptCore/runtime/RegExp.h +++ b/JavaScriptCore/runtime/RegExp.h @@ -1,6 +1,7 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -53,7 +54,7 @@ namespace JSC { bool isValid() const { return !m_constructionError; } const char* errorMessage() const { return m_constructionError; } - int match(const UString&, int startOffset, OwnArrayPtr<int>* ovector = 0); + int match(const UString&, int startOffset, Vector<int, 32>* ovector = 0); unsigned numSubpatterns() const { return m_numSubpatterns; } private: diff --git a/JavaScriptCore/runtime/RegExpConstructor.cpp b/JavaScriptCore/runtime/RegExpConstructor.cpp index bcd0d07..6a8089d 100644 --- a/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -88,19 +89,26 @@ const ClassInfo RegExpConstructor::info = { "Function", &InternalFunction::info, @end */ -struct RegExpConstructorPrivate { +struct RegExpConstructorPrivate : FastAllocBase { // Global search cache / settings RegExpConstructorPrivate() : lastNumSubPatterns(0) , multiline(false) + , lastOvectorIndex(0) { } + const Vector<int, 32>& lastOvector() const { return ovector[lastOvectorIndex]; } + Vector<int, 32>& lastOvector() { return ovector[lastOvectorIndex]; } + Vector<int, 32>& tempOvector() { return ovector[lastOvectorIndex ? 0 : 1]; } + void changeLastOvector() { lastOvectorIndex = lastOvectorIndex ? 0 : 1; } + UString input; UString lastInput; - OwnArrayPtr<int> lastOvector; - unsigned lastNumSubPatterns : 31; + Vector<int, 32> ovector[2]; + unsigned lastNumSubPatterns : 30; bool multiline : 1; + unsigned lastOvectorIndex : 1; }; RegExpConstructor::RegExpConstructor(ExecState* exec, PassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) @@ -121,20 +129,19 @@ RegExpConstructor::RegExpConstructor(ExecState* exec, PassRefPtr<Structure> stru */ void RegExpConstructor::performMatch(RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector) { - OwnArrayPtr<int> tmpOvector; - position = r->match(s, startOffset, &tmpOvector); + position = r->match(s, startOffset, &d->tempOvector()); if (ovector) - *ovector = tmpOvector.get(); + *ovector = d->tempOvector().data(); if (position != -1) { - ASSERT(tmpOvector); + ASSERT(!d->tempOvector().isEmpty()); - length = tmpOvector[1] - tmpOvector[0]; + length = d->tempOvector()[1] - d->tempOvector()[0]; d->input = s; d->lastInput = s; - d->lastOvector.set(tmpOvector.release()); + d->changeLastOvector(); d->lastNumSubPatterns = r->numSubpatterns(); } } @@ -147,8 +154,8 @@ RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate d->lastInput = data->lastInput; d->lastNumSubPatterns = data->lastNumSubPatterns; unsigned offsetVectorSize = (data->lastNumSubPatterns + 1) * 2; // only copying the result part of the vector - d->lastOvector.set(new int[offsetVectorSize]); - memcpy(d->lastOvector.get(), data->lastOvector.get(), offsetVectorSize * sizeof(int)); + d->lastOvector().resize(offsetVectorSize); + memcpy(d->lastOvector().data(), data->lastOvector().data(), offsetVectorSize * sizeof(int)); // d->multiline is not needed, and remains uninitialized setLazyCreationData(d); @@ -167,13 +174,13 @@ void RegExpMatchesArray::fillArrayInstance(ExecState* exec) unsigned lastNumSubpatterns = d->lastNumSubPatterns; for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { - int start = d->lastOvector[2 * i]; + int start = d->lastOvector()[2 * i]; if (start >= 0) - JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start)); + JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start)); } PutPropertySlot slot; - JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector[0]), slot); + JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector()[0]), slot); JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->input), slot); delete d; @@ -187,10 +194,10 @@ JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) const { - if (d->lastOvector && i <= d->lastNumSubPatterns) { - int start = d->lastOvector[2 * i]; + if (!d->lastOvector().isEmpty() && i <= d->lastNumSubPatterns) { + int start = d->lastOvector()[2 * i]; if (start >= 0) - return jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start); + return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); } return jsEmptyString(exec); } @@ -199,25 +206,25 @@ JSValue RegExpConstructor::getLastParen(ExecState* exec) const { unsigned i = d->lastNumSubPatterns; if (i > 0) { - ASSERT(d->lastOvector); - int start = d->lastOvector[2 * i]; + ASSERT(!d->lastOvector().isEmpty()); + int start = d->lastOvector()[2 * i]; if (start >= 0) - return jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start); + return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); } return jsEmptyString(exec); } JSValue RegExpConstructor::getLeftContext(ExecState* exec) const { - if (d->lastOvector) - return jsSubstring(exec, d->lastInput, 0, d->lastOvector[0]); + if (!d->lastOvector().isEmpty()) + return jsSubstring(exec, d->lastInput, 0, d->lastOvector()[0]); return jsEmptyString(exec); } JSValue RegExpConstructor::getRightContext(ExecState* exec) const { - if (d->lastOvector) - return jsSubstring(exec, d->lastInput, d->lastOvector[1], d->lastInput.size() - d->lastOvector[1]); + if (!d->lastOvector().isEmpty()) + return jsSubstring(exec, d->lastInput, d->lastOvector()[1], d->lastInput.size() - d->lastOvector()[1]); return jsEmptyString(exec); } diff --git a/JavaScriptCore/runtime/RegExpObject.h b/JavaScriptCore/runtime/RegExpObject.h index fac9978..e83e0ac 100644 --- a/JavaScriptCore/runtime/RegExpObject.h +++ b/JavaScriptCore/runtime/RegExpObject.h @@ -56,7 +56,7 @@ namespace JSC { virtual CallType getCallData(CallData&); - struct RegExpObjectData { + struct RegExpObjectData : FastAllocBase { RegExpObjectData(PassRefPtr<RegExp> regExp, double lastIndex) : regExp(regExp) , lastIndex(lastIndex) diff --git a/JavaScriptCore/runtime/ScopeChain.h b/JavaScriptCore/runtime/ScopeChain.h index 6f1560a..c5e16c9 100644 --- a/JavaScriptCore/runtime/ScopeChain.h +++ b/JavaScriptCore/runtime/ScopeChain.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2008, 2009 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 @@ -21,16 +21,17 @@ #ifndef ScopeChain_h #define ScopeChain_h -#include <wtf/Assertions.h> +#include "FastAllocBase.h" namespace JSC { class JSGlobalData; class JSGlobalObject; class JSObject; + class MarkStack; class ScopeChainIterator; - class ScopeChainNode { + class ScopeChainNode : public FastAllocBase { public: ScopeChainNode(ScopeChainNode* next, JSObject* object, JSGlobalData* globalData, JSObject* globalThis) : next(next) @@ -204,7 +205,7 @@ namespace JSC { JSGlobalObject* globalObject() const { return m_node->globalObject(); } - void mark() const; + void markAggregate(MarkStack&) const; // Caution: this should only be used if the codeblock this is being used // with needs a full scope chain, otherwise this returns the depth of diff --git a/JavaScriptCore/runtime/ScopeChainMark.h b/JavaScriptCore/runtime/ScopeChainMark.h index b80b8ef..984d101 100644 --- a/JavaScriptCore/runtime/ScopeChainMark.h +++ b/JavaScriptCore/runtime/ScopeChainMark.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2009 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 @@ -25,13 +25,10 @@ namespace JSC { - inline void ScopeChain::mark() const + inline void ScopeChain::markAggregate(MarkStack& markStack) const { - for (ScopeChainNode* n = m_node; n; n = n->next) { - JSObject* o = n->object; - if (!o->marked()) - o->mark(); - } + for (ScopeChainNode* n = m_node; n; n = n->next) + markStack.append(n->object); } } // namespace JSC diff --git a/JavaScriptCore/runtime/SmallStrings.cpp b/JavaScriptCore/runtime/SmallStrings.cpp index 87b49f0..2f92cc1 100644 --- a/JavaScriptCore/runtime/SmallStrings.cpp +++ b/JavaScriptCore/runtime/SmallStrings.cpp @@ -34,7 +34,7 @@ namespace JSC { static const unsigned numCharactersToStore = 0x100; -class SmallStringsStorage : Noncopyable { +class SmallStringsStorage : public Noncopyable { public: SmallStringsStorage(); @@ -85,10 +85,10 @@ SmallStrings::~SmallStrings() void SmallStrings::mark() { if (m_emptyString && !m_emptyString->marked()) - m_emptyString->mark(); + m_emptyString->markCellDirect(); for (unsigned i = 0; i < numCharactersToStore; ++i) { if (m_singleCharacterStrings[i] && !m_singleCharacterStrings[i]->marked()) - m_singleCharacterStrings[i]->mark(); + m_singleCharacterStrings[i]->markCellDirect(); } } diff --git a/JavaScriptCore/runtime/SmallStrings.h b/JavaScriptCore/runtime/SmallStrings.h index e7f1170..f0dd8df 100644 --- a/JavaScriptCore/runtime/SmallStrings.h +++ b/JavaScriptCore/runtime/SmallStrings.h @@ -36,7 +36,7 @@ namespace JSC { class SmallStringsStorage; - class SmallStrings : Noncopyable { + class SmallStrings : public Noncopyable { public: SmallStrings(); ~SmallStrings(); diff --git a/JavaScriptCore/runtime/StringPrototype.cpp b/JavaScriptCore/runtime/StringPrototype.cpp index d6939cb..531a302 100644 --- a/JavaScriptCore/runtime/StringPrototype.cpp +++ b/JavaScriptCore/runtime/StringPrototype.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -375,8 +376,8 @@ JSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec, JSObject*, JSValue UString s = thisValue.toThisString(exec); unsigned len = s.size(); JSValue a0 = args.at(0); - if (a0.isUInt32Fast()) { - uint32_t i = a0.getUInt32Fast(); + if (a0.isUInt32()) { + uint32_t i = a0.asUInt32(); if (i < len) return jsSingleCharacterSubstring(exec, s, i); return jsEmptyString(exec); @@ -392,8 +393,8 @@ JSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec, JSObject*, JSVa UString s = thisValue.toThisString(exec); unsigned len = s.size(); JSValue a0 = args.at(0); - if (a0.isUInt32Fast()) { - uint32_t i = a0.getUInt32Fast(); + if (a0.isUInt32()) { + uint32_t i = a0.asUInt32(); if (i < len) return jsNumber(exec, s.data()[i]); return jsNaN(exec); @@ -425,8 +426,8 @@ JSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue int pos; if (a1.isUndefined()) pos = 0; - else if (a1.isUInt32Fast()) - pos = min<uint32_t>(a1.getUInt32Fast(), len); + else if (a1.isUInt32()) + pos = min<uint32_t>(a1.asUInt32(), len); else { double dpos = a1.toInteger(exec); if (dpos < 0) @@ -575,7 +576,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue t } int pos = 0; while (i != limit && pos < s.size()) { - OwnArrayPtr<int> ovector; + Vector<int, 32> ovector; int mpos = reg->match(s, pos, &ovector); if (mpos < 0) break; diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp index 3597a5c..5dfd919 100644 --- a/JavaScriptCore/runtime/Structure.cpp +++ b/JavaScriptCore/runtime/Structure.cpp @@ -306,8 +306,11 @@ void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& p } if (shouldCache) { + StructureChain* protoChain = prototypeChain(exec); m_cachedPropertyNameArrayData = propertyNames.data(); - m_cachedPropertyNameArrayData->setCachedPrototypeChain(prototypeChain(exec)); + if (!protoChain->isCacheable()) + return; + m_cachedPropertyNameArrayData->setCachedPrototypeChain(protoChain); m_cachedPropertyNameArrayData->setCachedStructure(this); } } @@ -407,6 +410,7 @@ PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, con if (structure->transitionCount() > s_maxTransitionLength) { RefPtr<Structure> transition = toDictionaryTransition(structure); + ASSERT(structure != transition); offset = transition->put(propertyName, attributes, specificValue); if (transition->propertyStorageSize() > transition->propertyStorageCapacity()) transition->growPropertyStorageCapacity(); diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h index 866999d..f3a0c7c 100644 --- a/JavaScriptCore/runtime/Structure.h +++ b/JavaScriptCore/runtime/Structure.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,6 +29,7 @@ #include "Identifier.h" #include "JSType.h" #include "JSValue.h" +#include "MarkStack.h" #include "PropertyMapHashTable.h" #include "StructureChain.h" #include "StructureTransitionTable.h" @@ -72,10 +73,9 @@ namespace JSC { ~Structure(); - void mark() + void markAggregate(MarkStack& markStack) { - if (!m_prototype.marked()) - m_prototype.mark(); + markStack.append(m_prototype); } // These should be used with caution. diff --git a/JavaScriptCore/runtime/StructureChain.cpp b/JavaScriptCore/runtime/StructureChain.cpp index 085876c..85049b1 100644 --- a/JavaScriptCore/runtime/StructureChain.cpp +++ b/JavaScriptCore/runtime/StructureChain.cpp @@ -46,4 +46,15 @@ StructureChain::StructureChain(Structure* head) m_vector[i] = 0; } +bool StructureChain::isCacheable() const +{ + uint32_t i = 0; + + while (m_vector[i]) { + if (m_vector[i++]->isDictionary()) + return false; + } + return true; +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/StructureChain.h b/JavaScriptCore/runtime/StructureChain.h index 795e649..c48749d 100644 --- a/JavaScriptCore/runtime/StructureChain.h +++ b/JavaScriptCore/runtime/StructureChain.h @@ -39,6 +39,7 @@ namespace JSC { public: static PassRefPtr<StructureChain> create(Structure* head) { return adoptRef(new StructureChain(head)); } RefPtr<Structure>* head() { return m_vector.get(); } + bool isCacheable() const; private: StructureChain(Structure* head); diff --git a/JavaScriptCore/runtime/UString.cpp b/JavaScriptCore/runtime/UString.cpp index 0eb46da..118751e 100644 --- a/JavaScriptCore/runtime/UString.cpp +++ b/JavaScriptCore/runtime/UString.cpp @@ -63,7 +63,7 @@ extern const double NaN; extern const double Inf; // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. -static const int minLengthToShare = 30; +static const int minLengthToShare = 10; 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); } @@ -243,6 +243,15 @@ PassRefPtr<UString::Rep> UString::Rep::create(UChar* string, int length, PassRef return rep; } +UString::SharedUChar* UString::Rep::sharedBuffer() +{ + UString::BaseString* base = baseString(); + if (len < minLengthToShare) + return 0; + + return base->sharedBuffer(); +} + void UString::Rep::destroy() { checkConsistency(); @@ -385,10 +394,6 @@ void UString::Rep::checkConsistency() const UString::SharedUChar* UString::BaseString::sharedBuffer() { - - if (len < minLengthToShare) - return 0; - if (!m_sharedBuffer) setSharedBuffer(SharedUChar::create(new OwnFastMallocPtr<UChar>(buf))); return m_sharedBuffer; diff --git a/JavaScriptCore/runtime/UString.h b/JavaScriptCore/runtime/UString.h index 6852d91..d01b75d 100644 --- a/JavaScriptCore/runtime/UString.h +++ b/JavaScriptCore/runtime/UString.h @@ -107,6 +107,7 @@ namespace JSC { // Uses SharedUChar to have joint ownership over the UChar*. static PassRefPtr<Rep> create(UChar*, int, PassRefPtr<SharedUChar>); + SharedUChar* sharedBuffer(); void destroy(); bool baseIsSelf() const { return m_identifierTableAndFlags.isFlagSet(BaseStringFlag); } @@ -192,7 +193,6 @@ namespace JSC { struct BaseString : public Rep { bool isShared() { return rc != 1 || isBufferReadOnly(); } void setSharedBuffer(PassRefPtr<SharedUChar>); - SharedUChar* sharedBuffer(); bool isBufferReadOnly() { @@ -224,6 +224,7 @@ namespace JSC { checkConsistency(); } + SharedUChar* sharedBuffer(); bool slowIsBufferReadOnly(); friend struct Rep; |