diff options
author | Steve Block <steveblock@google.com> | 2009-12-15 10:12:09 +0000 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2009-12-17 17:41:10 +0000 |
commit | 643ca7872b450ea4efacab6188849e5aac2ba161 (patch) | |
tree | 6982576c228bcd1a7efe98afed544d840751094c /JavaScriptCore/runtime | |
parent | d026980fde6eb3b01c1fe49441174e89cd1be298 (diff) | |
download | external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.zip external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.tar.gz external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.tar.bz2 |
Merge webkit.org at r51976 : Initial merge by git.
Change-Id: Ib0e7e2f0fb4bee5a186610272edf3186f0986b43
Diffstat (limited to 'JavaScriptCore/runtime')
50 files changed, 1245 insertions, 534 deletions
diff --git a/JavaScriptCore/runtime/ArgList.h b/JavaScriptCore/runtime/ArgList.h index 3227770..8e1fdbe 100644 --- a/JavaScriptCore/runtime/ArgList.h +++ b/JavaScriptCore/runtime/ArgList.h @@ -104,7 +104,11 @@ namespace JSC { void append(JSValue v) { ASSERT(!m_isReadOnly); - + +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif + if (m_isUsingInlineBuffer && m_size < inlineCapacity) { m_vector.uncheckedAppend(v); ++m_size; @@ -187,6 +191,10 @@ namespace JSC { : m_args(args) , m_argCount(argCount) { +#if ENABLE(JSC_ZOMBIES) + for (size_t i = 0; i < argCount; i++) + ASSERT(!m_args[i].isZombie()); +#endif } ArgList(Register* args, int argCount) diff --git a/JavaScriptCore/runtime/ArrayPrototype.cpp b/JavaScriptCore/runtime/ArrayPrototype.cpp index 7a89447..5b359e7 100644 --- a/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -745,8 +745,8 @@ JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue th cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(exec, k)); cachedCall.setArgument(2, thisObj); - - if (!cachedCall.call().toBoolean(exec)) + JSValue result = cachedCall.call(); + if (!result.toBoolean(cachedCall.newCallFrame(exec))) return jsBoolean(false); } } @@ -846,8 +846,8 @@ JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thi cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(exec, k)); cachedCall.setArgument(2, thisObj); - - if (cachedCall.call().toBoolean(exec)) + JSValue result = cachedCall.call(); + if (result.toBoolean(cachedCall.newCallFrame(exec))) return jsBoolean(true); } } @@ -1034,7 +1034,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue JSValue e = getProperty(exec, thisObj, index); if (!e) continue; - if (JSValue::strictEqual(searchElement, e)) + if (JSValue::strictEqual(exec, searchElement, e)) return jsNumber(exec, index); } @@ -1065,7 +1065,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSVa JSValue e = getProperty(exec, thisObj, index); if (!e) continue; - if (JSValue::strictEqual(searchElement, e)) + if (JSValue::strictEqual(exec, searchElement, e)) return jsNumber(exec, index); } diff --git a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h index 929a5e7..74089a5 100644 --- a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h +++ b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -43,7 +43,7 @@ namespace JSC { ~BatchedTransitionOptimizer() { - m_object->setStructure(Structure::fromDictionaryTransition(m_object->structure())); + m_object->flattenDictionaryObject(); } private: diff --git a/JavaScriptCore/runtime/Collector.cpp b/JavaScriptCore/runtime/Collector.cpp index 1b238d9..1630a58 100644 --- a/JavaScriptCore/runtime/Collector.cpp +++ b/JavaScriptCore/runtime/Collector.cpp @@ -32,6 +32,7 @@ #include "JSONObject.h" #include "JSString.h" #include "JSValue.h" +#include "JSZombie.h" #include "MarkStack.h" #include "Nodes.h" #include "Tracing.h" @@ -118,11 +119,7 @@ static RHeap* userChunk = 0; #if PLATFORM(DARWIN) typedef mach_port_t PlatformThread; #elif PLATFORM(WIN_OS) -struct PlatformThread { - PlatformThread(DWORD _id, HANDLE _handle) : id(_id), handle(_handle) {} - DWORD id; - HANDLE handle; -}; +typedef HANDLE PlatformThread; #endif class Heap::Thread { @@ -198,9 +195,11 @@ void Heap::destroy() sweep<PrimaryHeap>(); // No need to sweep number heap, because the JSNumber destructor doesn't do anything. - +#if ENABLE(JSC_ZOMBIES) + ASSERT(primaryHeap.numLiveObjects == primaryHeap.numZombies); +#else ASSERT(!primaryHeap.numLiveObjects); - +#endif freeBlocks(&primaryHeap); freeBlocks(&numberHeap); @@ -646,8 +645,7 @@ static inline PlatformThread getCurrentPlatformThread() #if PLATFORM(DARWIN) return pthread_mach_thread_np(pthread_self()); #elif PLATFORM(WIN_OS) - HANDLE threadHandle = pthread_getw32threadhandle_np(pthread_self()); - return PlatformThread(GetCurrentThreadId(), threadHandle); + return pthread_getw32threadhandle_np(pthread_self()); #endif } @@ -811,7 +809,7 @@ static inline void suspendThread(const PlatformThread& platformThread) #if PLATFORM(DARWIN) thread_suspend(platformThread); #elif PLATFORM(WIN_OS) - SuspendThread(platformThread.handle); + SuspendThread(platformThread); #else #error Need a way to suspend threads on this platform #endif @@ -822,7 +820,7 @@ static inline void resumeThread(const PlatformThread& platformThread) #if PLATFORM(DARWIN) thread_resume(platformThread); #elif PLATFORM(WIN_OS) - ResumeThread(platformThread.handle); + ResumeThread(platformThread); #else #error Need a way to resume threads on this platform #endif @@ -886,7 +884,7 @@ static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, P #elif PLATFORM(WIN_OS) && PLATFORM(X86) regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; - GetThreadContext(platformThread.handle, ®s); + GetThreadContext(platformThread, ®s); return sizeof(CONTEXT); #else #error Need a way to get thread registers on this platform @@ -1041,17 +1039,26 @@ template <HeapType heapType> size_t Heap::sweep() // assumes the object has a valid vptr.) if (cell->u.freeCell.zeroIfFree == 0) continue; - +#if ENABLE(JSC_ZOMBIES) + if (!imp->isZombie()) { + const ClassInfo* info = imp->classInfo(); + imp->~JSCell(); + new (imp) JSZombie(info, JSZombie::leakedZombieStructure()); + heap.numZombies++; + } +#else imp->~JSCell(); +#endif } - - --usedCells; --numLiveObjects; +#if !ENABLE(JSC_ZOMBIES) + --usedCells; // put cell on the free list cell->u.freeCell.zeroIfFree = 0; cell->u.freeCell.next = freeList - (cell + 1); freeList = cell; +#endif } } } else { @@ -1064,8 +1071,18 @@ template <HeapType heapType> size_t Heap::sweep() if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) { if (heapType != NumberHeap) { JSCell* imp = reinterpret_cast<JSCell*>(cell); +#if ENABLE(JSC_ZOMBIES) + if (!imp->isZombie()) { + const ClassInfo* info = imp->classInfo(); + imp->~JSCell(); + new (imp) JSZombie(info, JSZombie::leakedZombieStructure()); + heap.numZombies++; + } +#else imp->~JSCell(); +#endif } +#if !ENABLE(JSC_ZOMBIES) --usedCells; --numLiveObjects; @@ -1073,6 +1090,7 @@ template <HeapType heapType> size_t Heap::sweep() cell->u.freeCell.zeroIfFree = 0; cell->u.freeCell.next = freeList - (cell + 1); freeList = cell; +#endif } } } diff --git a/JavaScriptCore/runtime/Collector.h b/JavaScriptCore/runtime/Collector.h index 9ca9d18..9128701 100644 --- a/JavaScriptCore/runtime/Collector.h +++ b/JavaScriptCore/runtime/Collector.h @@ -60,6 +60,9 @@ namespace JSC { size_t numLiveObjects; size_t numLiveObjectsAtLastCollect; size_t extraCost; +#if ENABLE(JSC_ZOMBIES) + size_t numZombies; +#endif OperationInProgress operationInProgress; }; diff --git a/JavaScriptCore/runtime/DateConstructor.cpp b/JavaScriptCore/runtime/DateConstructor.cpp index 9908fef..61ec4c5 100644 --- a/JavaScriptCore/runtime/DateConstructor.cpp +++ b/JavaScriptCore/runtime/DateConstructor.cpp @@ -77,14 +77,14 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) double value; if (numArgs == 0) // new Date() ECMA 15.9.3.3 - value = getCurrentUTCTime(); + value = jsCurrentTime(); else if (numArgs == 1) { if (args.at(0).inherits(&DateInstance::info)) value = asDateInstance(args.at(0))->internalNumber(); else { JSValue primitive = args.at(0).toPrimitive(exec); if (primitive.isString()) - value = parseDate(primitive.getString()); + value = parseDate(exec, primitive.getString(exec)); else value = primitive.toNumber(exec); } @@ -108,7 +108,7 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) 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); + value = gregorianDateTimeToMS(exec, t, ms, false); } } @@ -132,8 +132,8 @@ static JSValue JSC_HOST_CALL callDate(ExecState* exec, JSObject*, JSValue, const time_t localTime = time(0); tm localTM; getLocalTime(&localTime, &localTM); - GregorianDateTime ts(localTM); - return jsNontrivialString(exec, formatDate(ts) + " " + formatTime(ts, false)); + GregorianDateTime ts(exec, localTM); + return jsNontrivialString(exec, formatDate(ts) + " " + formatTime(ts)); } CallType DateConstructor::getCallData(CallData& callData) @@ -144,12 +144,12 @@ CallType DateConstructor::getCallData(CallData& callData) static JSValue JSC_HOST_CALL dateParse(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, parseDate(args.at(0).toString(exec))); + return jsNumber(exec, parseDate(exec, args.at(0).toString(exec))); } static JSValue JSC_HOST_CALL dateNow(ExecState* exec, JSObject*, JSValue, const ArgList&) { - return jsNumber(exec, getCurrentUTCTime()); + return jsNumber(exec, jsCurrentTime()); } static JSValue JSC_HOST_CALL dateUTC(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -173,7 +173,7 @@ static JSValue JSC_HOST_CALL dateUTC(ExecState* exec, JSObject*, JSValue, const t.minute = args.at(4).toInt32(exec); t.second = args.at(5).toInt32(exec); double ms = (n >= 7) ? args.at(6).toNumber(exec) : 0; - return jsNumber(exec, gregorianDateTimeToMS(t, ms, true)); + return jsNumber(exec, gregorianDateTimeToMS(exec, t, ms, true)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/DateConversion.cpp b/JavaScriptCore/runtime/DateConversion.cpp index a725478..b9d0e02 100644 --- a/JavaScriptCore/runtime/DateConversion.cpp +++ b/JavaScriptCore/runtime/DateConversion.cpp @@ -43,6 +43,7 @@ #include "config.h" #include "DateConversion.h" +#include "CallFrame.h" #include "UString.h" #include <wtf/DateMath.h> #include <wtf/StringExtras.h> @@ -51,9 +52,14 @@ using namespace WTF; namespace JSC { -double parseDate(const UString &date) +double parseDate(ExecState* exec, const UString &date) { - return parseDateFromNullTerminatedCharacters(date.UTF8String().c_str()); + if (date == exec->globalData().cachedDateString) + return exec->globalData().cachedDateStringValue; + double value = parseDateFromNullTerminatedCharacters(exec, date.UTF8String().c_str()); + exec->globalData().cachedDateString = date; + exec->globalData().cachedDateStringValue = value; + return value; } UString formatDate(const GregorianDateTime &t) @@ -74,28 +80,31 @@ UString formatDateUTCVariant(const GregorianDateTime &t) return buffer; } -UString formatTime(const GregorianDateTime &t, bool utc) +UString formatTime(const GregorianDateTime &t) { char buffer[100]; - if (utc) { - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.hour, t.minute, t.second); - } else { - int offset = abs(gmtoffset(t)); - char timeZoneName[70]; - struct tm gtm = t; - strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m); + int offset = abs(gmtoffset(t)); + char timeZoneName[70]; + struct tm gtm = t; + strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m); - if (timeZoneName[0]) { - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d (%s)", - t.hour, t.minute, t.second, - gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, timeZoneName); - } else { - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d", - t.hour, t.minute, t.second, - gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60); - } + if (timeZoneName[0]) { + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d (%s)", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, timeZoneName); + } else { + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60); } return UString(buffer); } +UString formatTimeUTC(const GregorianDateTime &t) +{ + char buffer[100]; + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.hour, t.minute, t.second); + return UString(buffer); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/DateConversion.h b/JavaScriptCore/runtime/DateConversion.h index 0d12815..dc980d3 100644 --- a/JavaScriptCore/runtime/DateConversion.h +++ b/JavaScriptCore/runtime/DateConversion.h @@ -42,18 +42,17 @@ #ifndef DateConversion_h #define DateConversion_h -namespace WTF { - struct GregorianDateTime; -} - namespace JSC { +class ExecState; class UString; +struct GregorianDateTime; -double parseDate(const UString&); -UString formatDate(const WTF::GregorianDateTime&); -UString formatDateUTCVariant(const WTF::GregorianDateTime&); -UString formatTime(const WTF::GregorianDateTime&, bool inputIsUTC); +double parseDate(ExecState* exec, const UString&); +UString formatDate(const GregorianDateTime&); +UString formatDateUTCVariant(const GregorianDateTime&); +UString formatTime(const GregorianDateTime&); +UString formatTimeUTC(const GregorianDateTime&); } // namespace JSC diff --git a/JavaScriptCore/runtime/DateInstance.cpp b/JavaScriptCore/runtime/DateInstance.cpp index d4c9ef7..77a92be 100644 --- a/JavaScriptCore/runtime/DateInstance.cpp +++ b/JavaScriptCore/runtime/DateInstance.cpp @@ -46,30 +46,36 @@ DateInstance::DateInstance(ExecState* exec, double time) setInternalValue(jsNumber(exec, timeClip(time))); } -bool DateInstance::getGregorianDateTime(ExecState* exec, bool outputIsUTC, GregorianDateTime& t) const +const GregorianDateTime* DateInstance::calculateGregorianDateTime(ExecState* exec) const { double milli = internalNumber(); if (isnan(milli)) - return false; + return 0; if (!m_data) m_data = exec->globalData().dateInstanceCache.add(milli); - if (outputIsUTC) { - if (m_data->m_gregorianDateTimeUTCCachedForMS != milli) { - WTF::msToGregorianDateTime(internalNumber(), true, m_data->m_cachedGregorianDateTimeUTC); - m_data->m_gregorianDateTimeUTCCachedForMS = milli; - } - t.copyFrom(m_data->m_cachedGregorianDateTimeUTC); - } else { - if (m_data->m_gregorianDateTimeCachedForMS != milli) { - WTF::msToGregorianDateTime(internalNumber(), false, m_data->m_cachedGregorianDateTime); - m_data->m_gregorianDateTimeCachedForMS = milli; - } - t.copyFrom(m_data->m_cachedGregorianDateTime); + if (m_data->m_gregorianDateTimeCachedForMS != milli) { + msToGregorianDateTime(exec, milli, false, m_data->m_cachedGregorianDateTime); + m_data->m_gregorianDateTimeCachedForMS = milli; } + return &m_data->m_cachedGregorianDateTime; +} + +const GregorianDateTime* DateInstance::calculateGregorianDateTimeUTC(ExecState* exec) const +{ + double milli = internalNumber(); + if (isnan(milli)) + return 0; - return true; + if (!m_data) + m_data = exec->globalData().dateInstanceCache.add(milli); + + if (m_data->m_gregorianDateTimeUTCCachedForMS != milli) { + msToGregorianDateTime(exec, milli, true, m_data->m_cachedGregorianDateTimeUTC); + m_data->m_gregorianDateTimeUTCCachedForMS = milli; + } + return &m_data->m_cachedGregorianDateTimeUTC; } } // namespace JSC diff --git a/JavaScriptCore/runtime/DateInstance.h b/JavaScriptCore/runtime/DateInstance.h index 38b321c..44b7521 100644 --- a/JavaScriptCore/runtime/DateInstance.h +++ b/JavaScriptCore/runtime/DateInstance.h @@ -38,7 +38,19 @@ namespace JSC { static JS_EXPORTDATA const ClassInfo info; - bool getGregorianDateTime(ExecState*, bool outputIsUTC, WTF::GregorianDateTime&) const; + const GregorianDateTime* gregorianDateTime(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTime; + return calculateGregorianDateTime(exec); + } + + const GregorianDateTime* gregorianDateTimeUTC(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeUTCCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTimeUTC; + return calculateGregorianDateTimeUTC(exec); + } static PassRefPtr<Structure> createStructure(JSValue prototype) { @@ -49,6 +61,8 @@ namespace JSC { static const unsigned StructureFlags = OverridesMarkChildren | JSWrapperObject::StructureFlags; private: + const GregorianDateTime* calculateGregorianDateTime(ExecState*) const; + const GregorianDateTime* calculateGregorianDateTimeUTC(ExecState*) const; virtual const ClassInfo* classInfo() const { return &info; } mutable RefPtr<DateInstanceData> m_data; diff --git a/JavaScriptCore/runtime/DateInstanceCache.h b/JavaScriptCore/runtime/DateInstanceCache.h index b626c1d..d208580 100644 --- a/JavaScriptCore/runtime/DateInstanceCache.h +++ b/JavaScriptCore/runtime/DateInstanceCache.h @@ -40,9 +40,9 @@ namespace JSC { static PassRefPtr<DateInstanceData> create() { return adoptRef(new DateInstanceData); } double m_gregorianDateTimeCachedForMS; - WTF::GregorianDateTime m_cachedGregorianDateTime; + GregorianDateTime m_cachedGregorianDateTime; double m_gregorianDateTimeUTCCachedForMS; - WTF::GregorianDateTime m_cachedGregorianDateTimeUTC; + GregorianDateTime m_cachedGregorianDateTimeUTC; private: DateInstanceData() @@ -56,6 +56,11 @@ namespace JSC { public: DateInstanceCache() { + reset(); + } + + void reset() + { for (size_t i = 0; i < cacheSize; ++i) m_cache[i].key = NaN; } @@ -72,7 +77,7 @@ namespace JSC { } private: - static const size_t cacheSize = 64; + static const size_t cacheSize = 16; struct CacheEntry { double key; diff --git a/JavaScriptCore/runtime/DatePrototype.cpp b/JavaScriptCore/runtime/DatePrototype.cpp index 3f3e1f9..7a2e802 100644 --- a/JavaScriptCore/runtime/DatePrototype.cpp +++ b/JavaScriptCore/runtime/DatePrototype.cpp @@ -28,7 +28,6 @@ #include "JSString.h" #include "ObjectPrototype.h" #include "DateInstance.h" -#include <float.h> #if !PLATFORM(MAC) && HAVE(LANGINFO_H) #include <langinfo.h> @@ -253,11 +252,10 @@ static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, L static JSCell* formatLocaleDate(ExecState* exec, DateInstance* dateObject, double, LocaleDateTimeFormat format, const ArgList&) { - GregorianDateTime gregorianDateTime; - const bool outputIsUTC = false; - if (!dateObject->getGregorianDateTime(exec, outputIsUTC, gregorianDateTime)) + const GregorianDateTime* gregorianDateTime = dateObject->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return formatLocaleDate(exec, gregorianDateTime, format); + return formatLocaleDate(exec, *gregorianDateTime, format); } #endif // !PLATFORM(MAC) @@ -420,14 +418,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDate(t) + " " + formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatDate(*gregorianDateTime) + " " + formatTime(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -435,14 +431,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDateUTCVariant(t) + " " + formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatDateUTCVariant(*gregorianDateTime) + " " + formatTimeUTC(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -450,17 +444,15 @@ JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); // Maximum amount of space we need in buffer: 6 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds) // 6 for formatting and one for null termination = 27. We add one extra character to allow us to force null termination. char buffer[28]; - snprintf(buffer, sizeof(buffer) - 1, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + t.year, t.month + 1, t.monthDay, t.hour, t.minute, t.second, static_cast<int>(fmod(thisDateObj->internalNumber(), 1000))); + snprintf(buffer, sizeof(buffer) - 1, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + gregorianDateTime->year, gregorianDateTime->month + 1, gregorianDateTime->monthDay, gregorianDateTime->hour, gregorianDateTime->minute, gregorianDateTime->second, static_cast<int>(fmod(thisDateObj->internalNumber(), 1000))); buffer[sizeof(buffer) - 1] = 0; return jsNontrivialString(exec, buffer); } @@ -470,14 +462,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec, JSObject*, JSVa if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDate(t)); + return jsNontrivialString(exec, formatDate(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -485,14 +475,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec, JSObject*, JSVa if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatTime(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) @@ -535,14 +523,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, 1900 + t.year); + return jsNumber(exec, 1900 + gregorianDateTime->year); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -550,14 +536,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject*, JS if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, 1900 + t.year); + return jsNumber(exec, 1900 + gregorianDateTime->year); } JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -565,14 +549,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDateUTCVariant(t) + " " + formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatDateUTCVariant(*gregorianDateTime) + " " + formatTimeUTC(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -580,14 +562,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.month); + return jsNumber(exec, gregorianDateTime->month); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -595,14 +575,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.month); + return jsNumber(exec, gregorianDateTime->month); } JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -610,14 +588,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec, JSObject*, JSValue t if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.monthDay); + return jsNumber(exec, gregorianDateTime->monthDay); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -625,14 +601,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec, JSObject*, JSValu if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.monthDay); + return jsNumber(exec, gregorianDateTime->monthDay); } JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -640,14 +614,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec, JSObject*, JSValue th if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.weekDay); + return jsNumber(exec, gregorianDateTime->weekDay); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -655,14 +627,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.weekDay); + return jsNumber(exec, gregorianDateTime->weekDay); } JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -670,14 +640,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.hour); + return jsNumber(exec, gregorianDateTime->hour); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -685,14 +653,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.hour); + return jsNumber(exec, gregorianDateTime->hour); } JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -700,14 +666,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec, JSObject*, JSValu if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.minute); + return jsNumber(exec, gregorianDateTime->minute); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -715,14 +679,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject*, JSV if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.minute); + return jsNumber(exec, gregorianDateTime->minute); } JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -730,14 +692,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec, JSObject*, JSValu if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.second); + return jsNumber(exec, gregorianDateTime->second); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -745,14 +705,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject*, JSV if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.second); + return jsNumber(exec, gregorianDateTime->second); } JSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -790,14 +748,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec, JSObject*, if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, -gmtoffset(t) / minutesPerHour); + return jsNumber(exec, -gregorianDateTime->utcOffset / minutesPerHour); } JSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) @@ -830,16 +786,21 @@ static JSValue setNewValueFromTimeArgs(ExecState* exec, JSValue thisValue, const double secs = floor(milli / msPerSecond); double ms = milli - secs * msPerSecond; - GregorianDateTime t; - thisDateObj->getGregorianDateTime(exec, inputIsUTC, t); + const GregorianDateTime* other = inputIsUTC + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return jsNaN(exec); - if (!fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &t)) { + GregorianDateTime gregorianDateTime; + gregorianDateTime.copyFrom(*other); + if (!fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &gregorianDateTime)) { JSValue result = jsNaN(exec); thisDateObj->setInternalValue(result); return result; } - JSValue result = jsNumber(exec, gregorianDateTimeToMS(t, ms, inputIsUTC)); + JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); thisDateObj->setInternalValue(result); return result; } @@ -857,26 +818,28 @@ static JSValue setNewValueFromDateArgs(ExecState* exec, JSValue thisValue, const } double milli = thisDateObj->internalNumber(); - double ms = 0; - - GregorianDateTime t; - if (numArgsToUse == 3 && isnan(milli)) - // Based on ECMA 262 15.9.5.40 - .41 (set[UTC]FullYear) - // the time must be reset to +0 if it is NaN. - WTF::msToGregorianDateTime(0, true, t); - else { - double secs = floor(milli / msPerSecond); - ms = milli - secs * msPerSecond; - thisDateObj->getGregorianDateTime(exec, inputIsUTC, t); + double ms = 0; + + GregorianDateTime gregorianDateTime; + if (numArgsToUse == 3 && isnan(milli)) + msToGregorianDateTime(exec, 0, true, gregorianDateTime); + else { + ms = milli - floor(milli / msPerSecond) * msPerSecond; + const GregorianDateTime* other = inputIsUTC + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return jsNaN(exec); + gregorianDateTime.copyFrom(*other); } - if (!fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &t)) { + if (!fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &gregorianDateTime)) { JSValue result = jsNaN(exec); thisDateObj->setInternalValue(result); return result; } - JSValue result = jsNumber(exec, gregorianDateTimeToMS(t, ms, inputIsUTC)); + JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); thisDateObj->setInternalValue(result); return result; } @@ -970,8 +933,6 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); if (args.isEmpty()) { JSValue result = jsNaN(exec); @@ -982,15 +943,16 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t double milli = thisDateObj->internalNumber(); double ms = 0; - GregorianDateTime t; + GregorianDateTime gregorianDateTime; if (isnan(milli)) // Based on ECMA 262 B.2.5 (setYear) // the time must be reset to +0 if it is NaN. - WTF::msToGregorianDateTime(0, true, t); + msToGregorianDateTime(exec, 0, true, gregorianDateTime); else { double secs = floor(milli / msPerSecond); ms = milli - secs * msPerSecond; - thisDateObj->getGregorianDateTime(exec, outputIsUTC, t); + if (const GregorianDateTime* other = thisDateObj->gregorianDateTime(exec)) + gregorianDateTime.copyFrom(*other); } bool ok = true; @@ -1001,8 +963,8 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t return result; } - t.year = (year > 99 || year < 0) ? year - 1900 : year; - JSValue result = jsNumber(exec, gregorianDateTimeToMS(t, ms, outputIsUTC)); + gregorianDateTime.year = (year > 99 || year < 0) ? year - 1900 : year; + JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, false)); thisDateObj->setInternalValue(result); return result; } @@ -1012,16 +974,14 @@ JSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec, JSObject*, JSValue t if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); // NOTE: IE returns the full year even in getYear. - return jsNumber(exec, t.year); + return jsNumber(exec, gregorianDateTime->year); } JSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) diff --git a/JavaScriptCore/runtime/FunctionPrototype.cpp b/JavaScriptCore/runtime/FunctionPrototype.cpp index 45f17b1..a3a7479 100644 --- a/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -90,13 +90,13 @@ JSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec, JSObject*, JSVa FunctionExecutable* executable = function->jsExecutable(); UString sourceString = executable->source().toString(); insertSemicolonIfNeeded(sourceString); - return jsString(exec, "function " + function->name(&exec->globalData()) + "(" + executable->paramString() + ") " + sourceString); + return jsString(exec, "function " + function->name(exec) + "(" + executable->paramString() + ") " + sourceString); } } if (thisValue.inherits(&InternalFunction::info)) { InternalFunction* function = asInternalFunction(thisValue); - return jsString(exec, "function " + function->name(&exec->globalData()) + "() {\n [native code]\n}"); + return jsString(exec, "function " + function->name(exec) + "() {\n [native code]\n}"); } return throwError(exec, TypeError); diff --git a/JavaScriptCore/runtime/InitializeThreading.cpp b/JavaScriptCore/runtime/InitializeThreading.cpp index fea89f8..aad1af8 100644 --- a/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/JavaScriptCore/runtime/InitializeThreading.cpp @@ -51,7 +51,7 @@ static void initializeThreadingOnce() initializeUString(); #if ENABLE(JSC_MULTIPLE_THREADS) s_dtoaP5Mutex = new Mutex; - WTF::initializeDates(); + initializeDates(); #endif } diff --git a/JavaScriptCore/runtime/InternalFunction.cpp b/JavaScriptCore/runtime/InternalFunction.cpp index 2ba2984..c48d628 100644 --- a/JavaScriptCore/runtime/InternalFunction.cpp +++ b/JavaScriptCore/runtime/InternalFunction.cpp @@ -43,29 +43,29 @@ InternalFunction::InternalFunction(JSGlobalData* globalData, NonNullPassRefPtr<S putDirect(globalData->propertyNames->name, jsString(globalData, name.ustring()), DontDelete | ReadOnly | DontEnum); } -const UString& InternalFunction::name(JSGlobalData* globalData) +const UString& InternalFunction::name(ExecState* exec) { - return asString(getDirect(globalData->propertyNames->name))->value(); + return asString(getDirect(exec->globalData().propertyNames->name))->value(exec); } -const UString InternalFunction::displayName(JSGlobalData* globalData) +const UString InternalFunction::displayName(ExecState* exec) { - JSValue displayName = getDirect(globalData->propertyNames->displayName); + JSValue displayName = getDirect(exec->globalData().propertyNames->displayName); - if (displayName && isJSString(globalData, displayName)) - return asString(displayName)->value(); + if (displayName && isJSString(&exec->globalData(), displayName)) + return asString(displayName)->value(exec); return UString::null(); } -const UString InternalFunction::calculatedDisplayName(JSGlobalData* globalData) +const UString InternalFunction::calculatedDisplayName(ExecState* exec) { - const UString explicitName = displayName(globalData); + const UString explicitName = displayName(exec); if (!explicitName.isEmpty()) return explicitName; - return name(globalData); + return name(exec); } } // namespace JSC diff --git a/JavaScriptCore/runtime/InternalFunction.h b/JavaScriptCore/runtime/InternalFunction.h index de9a1d6..fa1e5aa 100644 --- a/JavaScriptCore/runtime/InternalFunction.h +++ b/JavaScriptCore/runtime/InternalFunction.h @@ -36,9 +36,9 @@ namespace JSC { virtual const ClassInfo* classInfo() const; static JS_EXPORTDATA const ClassInfo info; - const UString& name(JSGlobalData*); - const UString displayName(JSGlobalData*); - const UString calculatedDisplayName(JSGlobalData*); + const UString& name(ExecState*); + const UString displayName(ExecState*); + const UString calculatedDisplayName(ExecState*); static PassRefPtr<Structure> createStructure(JSValue proto) { diff --git a/JavaScriptCore/runtime/JSArray.cpp b/JavaScriptCore/runtime/JSArray.cpp index fd9e7b2..b16d3fa 100644 --- a/JavaScriptCore/runtime/JSArray.cpp +++ b/JavaScriptCore/runtime/JSArray.cpp @@ -785,7 +785,7 @@ struct AVLTreeAbstractorForArrayCompare { m_cachedCall->setThis(m_globalThisValue); m_cachedCall->setArgument(0, va); m_cachedCall->setArgument(1, vb); - compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame()); + compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec)); } else { MarkedArgumentBuffer arguments; arguments.append(va); diff --git a/JavaScriptCore/runtime/JSCell.cpp b/JavaScriptCore/runtime/JSCell.cpp index fae056e..17410e2 100644 --- a/JavaScriptCore/runtime/JSCell.cpp +++ b/JavaScriptCore/runtime/JSCell.cpp @@ -86,17 +86,17 @@ bool JSCell::getUInt32(uint32_t&) const return false; } -bool JSCell::getString(UString&stringValue) const +bool JSCell::getString(ExecState* exec, UString&stringValue) const { if (!isString()) return false; - stringValue = static_cast<const JSString*>(this)->value(); + stringValue = static_cast<const JSString*>(this)->value(exec); return true; } -UString JSCell::getString() const +UString JSCell::getString(ExecState* exec) const { - return isString() ? static_cast<const JSString*>(this)->value() : UString(); + return isString() ? static_cast<const JSString*>(this)->value(exec) : UString(); } JSObject* JSCell::getObject() diff --git a/JavaScriptCore/runtime/JSCell.h b/JavaScriptCore/runtime/JSCell.h index 722ae33..c8ba2b8 100644 --- a/JavaScriptCore/runtime/JSCell.h +++ b/JavaScriptCore/runtime/JSCell.h @@ -42,6 +42,7 @@ namespace JSC { friend class JSString; friend class JSValue; friend class JSAPIValueWrapper; + friend class JSZombie; friend struct VPtrSet; private: @@ -64,8 +65,8 @@ namespace JSC { Structure* structure() const; // Extracting the value. - bool getString(UString&) const; - UString getString() const; // null string if not a string + bool getString(ExecState* exec, UString&) const; + UString getString(ExecState* exec) const; // null string if not a string JSObject* getObject(); // NULL if not an object const JSObject* getObject() const; // NULL if not an object @@ -90,6 +91,9 @@ namespace JSC { void* operator new(size_t, void* placementNewDestination) { return placementNewDestination; } virtual void markChildren(MarkStack&); +#if ENABLE(JSC_ZOMBIES) + virtual bool isZombie() const { return false; } +#endif // Object operations, with the toObject operation included. virtual const ClassInfo* classInfo() const; @@ -175,14 +179,14 @@ namespace JSC { return isCell() && asCell()->isObject(); } - inline bool JSValue::getString(UString& s) const + inline bool JSValue::getString(ExecState* exec, UString& s) const { - return isCell() && asCell()->getString(s); + return isCell() && asCell()->getString(exec, s); } - inline UString JSValue::getString() const + inline UString JSValue::getString(ExecState* exec) const { - return isCell() ? asCell()->getString() : UString(); + return isCell() ? asCell()->getString(exec) : UString(); } inline JSObject* JSValue::getObject() const @@ -342,7 +346,13 @@ namespace JSC { { return cellBlock(c)->heap; } - + +#if ENABLE(JSC_ZOMBIES) + inline bool JSValue::isZombie() const + { + return isCell() && asCell() && asCell()->isZombie(); + } +#endif } // namespace JSC #endif // JSCell_h diff --git a/JavaScriptCore/runtime/JSGlobalData.cpp b/JavaScriptCore/runtime/JSGlobalData.cpp index 1221ef2..234449f 100644 --- a/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/JavaScriptCore/runtime/JSGlobalData.cpp @@ -82,26 +82,28 @@ struct VPtrSet { VPtrSet::VPtrSet() { - // Bizarrely, calling fastMalloc here is faster than allocating space on the stack. - void* storage = fastMalloc(sizeof(CollectorBlock)); + CollectorCell cell; + void* storage = &cell; + COMPILE_ASSERT(sizeof(JSArray) <= sizeof(CollectorCell), sizeof_JSArray_must_be_less_than_CollectorCell); JSCell* jsArray = new (storage) JSArray(JSArray::createStructure(jsNull())); jsArrayVPtr = jsArray->vptr(); jsArray->~JSCell(); + COMPILE_ASSERT(sizeof(JSByteArray) <= sizeof(CollectorCell), sizeof_JSByteArray_must_be_less_than_CollectorCell); JSCell* jsByteArray = new (storage) JSByteArray(JSByteArray::VPtrStealingHack); jsByteArrayVPtr = jsByteArray->vptr(); jsByteArray->~JSCell(); + COMPILE_ASSERT(sizeof(JSString) <= sizeof(CollectorCell), sizeof_JSString_must_be_less_than_CollectorCell); JSCell* jsString = new (storage) JSString(JSString::VPtrStealingHack); jsStringVPtr = jsString->vptr(); jsString->~JSCell(); + COMPILE_ASSERT(sizeof(JSFunction) <= sizeof(CollectorCell), sizeof_JSFunction_must_be_less_than_CollectorCell); JSCell* jsFunction = new (storage) JSFunction(JSFunction::createStructure(jsNull())); jsFunctionVPtr = jsFunction->vptr(); jsFunction->~JSCell(); - - fastFree(storage); } JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) @@ -147,6 +149,8 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) , functionCodeBlockBeingReparsed(0) , firstStringifierToMark(0) , markStack(vptrSet.jsArrayVPtr) + , cachedUTCOffset(NaN) + , weakRandom(static_cast<int>(currentTime())) #ifndef NDEBUG , mainThreadOnly(false) #endif @@ -251,6 +255,14 @@ JSGlobalData::ClientData::~ClientData() { } +void JSGlobalData::resetDateCache() +{ + cachedUTCOffset = NaN; + dstOffsetCache.reset(); + cachedDateString = UString(); + dateInstanceCache.reset(); +} + void JSGlobalData::startSampling() { interpreter->startSampling(); diff --git a/JavaScriptCore/runtime/JSGlobalData.h b/JavaScriptCore/runtime/JSGlobalData.h index d2aa2da..f0c1b5c 100644 --- a/JavaScriptCore/runtime/JSGlobalData.h +++ b/JavaScriptCore/runtime/JSGlobalData.h @@ -38,6 +38,7 @@ #include "NumericStrings.h" #include "SmallStrings.h" #include "TimeoutChecker.h" +#include "WeakRandom.h" #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/RefCounted.h> @@ -63,6 +64,26 @@ namespace JSC { struct Instruction; struct VPtrSet; + struct DSTOffsetCache { + DSTOffsetCache() + { + reset(); + } + + void reset() + { + offset = 0.0; + start = 0.0; + end = -1.0; + increment = 0.0; + } + + double offset; + double start; + double end; + double increment; + }; + class JSGlobalData : public RefCounted<JSGlobalData> { public: struct ClientData { @@ -153,10 +174,20 @@ namespace JSC { MarkStack markStack; + double cachedUTCOffset; + DSTOffsetCache dstOffsetCache; + + UString cachedDateString; + double cachedDateStringValue; + + WeakRandom weakRandom; + #ifndef NDEBUG bool mainThreadOnly; #endif + void resetDateCache(); + void startSampling(); void stopSampling(); void dumpSampleData(ExecState* exec); @@ -165,7 +196,7 @@ namespace JSC { static JSGlobalData*& sharedInstanceInternal(); void createNativeThunk(); }; - + } // namespace JSC #endif // JSGlobalData_h diff --git a/JavaScriptCore/runtime/JSGlobalObject.h b/JavaScriptCore/runtime/JSGlobalObject.h index 720d3a5..9e4ef49 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.h +++ b/JavaScriptCore/runtime/JSGlobalObject.h @@ -442,7 +442,13 @@ namespace JSC { : m_dynamicGlobalObjectSlot(callFrame->globalData().dynamicGlobalObject) , m_savedDynamicGlobalObject(m_dynamicGlobalObjectSlot) { - m_dynamicGlobalObjectSlot = dynamicGlobalObject; + if (!m_dynamicGlobalObjectSlot) { + m_dynamicGlobalObjectSlot = dynamicGlobalObject; + + // Reset the date cache between JS invocations to force the VM + // to observe time zone changes. + callFrame->globalData().resetDateCache(); + } } ~DynamicGlobalObjectScope() diff --git a/JavaScriptCore/runtime/JSNumberCell.h b/JavaScriptCore/runtime/JSNumberCell.h index 309488f..e9e2470 100644 --- a/JavaScriptCore/runtime/JSNumberCell.h +++ b/JavaScriptCore/runtime/JSNumberCell.h @@ -109,6 +109,11 @@ namespace JSC { return static_cast<JSNumberCell*>(v.asCell()); } + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState* exec, double d) + { + *this = jsNumberCell(exec, d); + } + inline JSValue::JSValue(ExecState* exec, double d) { JSValue v = JSImmediate::from(d); @@ -193,6 +198,11 @@ namespace JSC { #endif // USE(JSVALUE32) #if USE(JSVALUE64) + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState*, double d) + { + *this = JSImmediate::fromNumberOutsideIntegerRange(d); + } + inline JSValue::JSValue(ExecState*, double d) { JSValue v = JSImmediate::from(d); diff --git a/JavaScriptCore/runtime/JSONObject.cpp b/JavaScriptCore/runtime/JSONObject.cpp index 297d457..cc7f6d9 100644 --- a/JavaScriptCore/runtime/JSONObject.cpp +++ b/JavaScriptCore/runtime/JSONObject.cpp @@ -70,7 +70,23 @@ public: void markAggregate(MarkStack&); private: - typedef UString StringBuilder; + class StringBuilder : public Vector<UChar> { + public: + using Vector<UChar>::append; + + inline void append(const char* str) + { + size_t len = strlen(str); + reserveCapacity(size() + len); + for (size_t i = 0; i < len; i++) + Vector<UChar>::append(str[i]); + } + + inline void append(const UString& str) + { + append(str.data(), str.size()); + } + }; class Holder { public: @@ -156,7 +172,7 @@ static inline UString gap(ExecState* exec, JSValue space) } // If the space value is a string, use it as the gap string, otherwise use no gap string. - UString spaces = space.getString(); + UString spaces = space.getString(exec); if (spaces.size() > maxGapLength) { spaces = spaces.substr(0, maxGapLength); } @@ -213,7 +229,7 @@ Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space) break; UString propertyName; - if (name.getString(propertyName)) { + if (name.getString(exec, propertyName)) { m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); continue; } @@ -269,7 +285,9 @@ JSValue Stringifier::stringify(JSValue value) if (m_exec->hadException()) return jsNull(); - return jsString(m_exec, result); + result.shrinkToFit(); + size_t length = result.size(); + return jsString(m_exec, UString(result.releaseBuffer(), length, false)); } void Stringifier::appendQuotedString(StringBuilder& builder, const UString& value) @@ -389,7 +407,7 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& } UString stringValue; - if (value.getString(stringValue)) { + if (value.getString(m_exec, stringValue)) { appendQuotedString(builder, stringValue); return StringifySucceeded; } @@ -586,7 +604,7 @@ bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBui // 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); + builder.resize(rollBackPoint); break; } diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp index 6932ded..ed9fdc2 100644 --- a/JavaScriptCore/runtime/JSObject.cpp +++ b/JavaScriptCore/runtime/JSObject.cpp @@ -406,26 +406,10 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const { - unsigned attributes; - if (!getPropertyAttributes(exec, propertyName, attributes)) + PropertyDescriptor descriptor; + if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor)) return false; - return !(attributes & DontEnum); -} - -bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const -{ - JSCell* specificValue; - if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) - return true; - - // Look in the static hashtable of properties - const HashEntry* entry = findPropertyHashEntry(exec, propertyName); - if (entry) { - attributes = entry->attributes(); - return true; - } - - return false; + return descriptor.enumerable(); } bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const @@ -522,12 +506,12 @@ void JSObject::removeDirect(const Identifier& propertyName) void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) { - putDirectFunction(Identifier(exec, function->name(&exec->globalData())), function, attr); + putDirectFunction(Identifier(exec, function->name(exec)), function, attr); } void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) { - putDirectFunctionWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr); + putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr); } NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) @@ -599,7 +583,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName if (descriptor.isEmpty()) return true; - if (current.equalTo(descriptor)) + if (current.equalTo(exec, descriptor)) return true; // Filter out invalid changes @@ -645,7 +629,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName return false; } if (!current.writable()) { - if (descriptor.value() || !JSValue::strictEqual(current.value(), descriptor.value())) { + if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) { if (throwException) throwError(exec, TypeError, "Attempting to change value of a readonly property."); return false; @@ -667,12 +651,12 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName // Changing the accessor functions of an existing accessor property ASSERT(descriptor.isAccessorDescriptor()); if (!current.configurable()) { - if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(current.setter(), descriptor.setter()))) { + if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) { if (throwException) throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property."); return false; } - if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(current.getter(), descriptor.getter()))) { + if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { if (throwException) throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property."); return false; diff --git a/JavaScriptCore/runtime/JSObject.h b/JavaScriptCore/runtime/JSObject.h index 5a89c40..a5da267 100644 --- a/JavaScriptCore/runtime/JSObject.h +++ b/JavaScriptCore/runtime/JSObject.h @@ -135,7 +135,6 @@ namespace JSC { virtual JSObject* toThisObject(ExecState*) const; virtual JSObject* unwrappedObject(); - virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const; bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; // This get function only looks at the property map. @@ -210,6 +209,11 @@ namespace JSC { return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags)); } + void flattenDictionaryObject() + { + m_structure->flattenDictionaryStructure(this); + } + protected: static const unsigned StructureFlags = 0; @@ -229,7 +233,7 @@ namespace JSC { using JSCell::isGetterSetter; using JSCell::toObject; void getObject(); - void getString(); + void getString(ExecState* exec); void isObject(); void isString(); #if USE(JSVALUE32) diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp index 6fd0344..d3dcb83 100644 --- a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp +++ b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -45,7 +45,8 @@ JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSObject o->getPropertyNames(exec, propertyNames); size_t numCacheableSlots = 0; if (!o->structure()->hasNonEnumerableProperties() && !o->structure()->hasAnonymousSlots() && - !o->structure()->isUncacheableDictionary() && !o->structure()->typeInfo().overridesGetPropertyNames()) + !o->structure()->hasGetterSetterProperties() && !o->structure()->isUncacheableDictionary() && + !o->structure()->typeInfo().overridesGetPropertyNames()) numCacheableSlots = o->structure()->propertyStorageSize(); JSPropertyNameIterator* jsPropertyNameIterator = new (exec) JSPropertyNameIterator(exec, propertyNames.data(), numCacheableSlots); @@ -76,7 +77,7 @@ JSValue JSPropertyNameIterator::get(ExecState* exec, JSObject* base, size_t i) if (m_cachedStructure == base->structure() && m_cachedPrototypeChain == base->structure()->prototypeChain(exec)) return identifier; - if (!base->hasProperty(exec, Identifier(exec, asString(identifier)->value()))) + if (!base->hasProperty(exec, Identifier(exec, asString(identifier)->value(exec)))) return JSValue(); return identifier; } diff --git a/JavaScriptCore/runtime/JSString.cpp b/JavaScriptCore/runtime/JSString.cpp index 20ba868..28ae6f7 100644 --- a/JavaScriptCore/runtime/JSString.cpp +++ b/JavaScriptCore/runtime/JSString.cpp @@ -25,41 +25,160 @@ #include "JSGlobalObject.h" #include "JSObject.h" +#include "Operations.h" #include "StringObject.h" #include "StringPrototype.h" namespace JSC { +void JSString::Rope::destructNonRecursive() +{ + Vector<Rope*, 32> workQueue; + Rope* rope = this; + + while (true) { + unsigned length = rope->ropeLength(); + for (unsigned i = 0; i < length; ++i) { + Fiber& fiber = rope->fibers(i); + if (fiber.isString()) + fiber.string()->deref(); + else { + Rope* nextRope = fiber.rope(); + if (nextRope->hasOneRef()) + workQueue.append(nextRope); + else + nextRope->deref(); + } + } + if (rope != this) + fastFree(rope); + + if (workQueue.isEmpty()) + return; + + rope = workQueue.last(); + workQueue.removeLast(); + } +} + +JSString::Rope::~Rope() +{ + destructNonRecursive(); +} + +#define ROPE_COPY_CHARS_INLINE_CUTOFF 20 + +static inline void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) +{ +#ifdef ROPE_COPY_CHARS_INLINE_CUTOFF + if (numCharacters <= ROPE_COPY_CHARS_INLINE_CUTOFF) { + for (unsigned i = 0; i < numCharacters; ++i) + destination[i] = source[i]; + return; + } +#endif + memcpy(destination, source, numCharacters * sizeof(UChar)); +} + +// Overview: this methods converts a JSString from holding a string in rope form +// down to a simple UString representation. It does so by building up the string +// backwards, since we want to avoid recursion, we expect that the tree structure +// representing the rope is likely imbalanced with more nodes down the left side +// (since appending to the string is likely more common) - and as such resolving +// in this fashion should minimize work queue size. (If we built the queue forwards +// we would likely have to place all of the constituent UString::Reps into the +// Vector before performing any concatenation, but by working backwards we likely +// only fill the queue with the number of substrings at any given level in a +// rope-of-ropes.) +void JSString::resolveRope(ExecState* exec) const +{ + ASSERT(isRope()); + + // Allocate the buffer to hold the final string, position initially points to the end. + UChar* buffer; + if (!tryFastMalloc(m_stringLength * sizeof(UChar)).getValue(buffer)) { + for (unsigned i = 0; i < m_ropeLength; ++i) + m_fibers[i].deref(); + m_ropeLength = 0; + ASSERT(!isRope()); + ASSERT(m_value == UString()); + + throwOutOfMemoryError(exec); + return; + } + UChar* position = buffer + m_stringLength; + + // Start with the current Rope. + Vector<Rope::Fiber, 32> workQueue; + Rope::Fiber currentFiber; + for (unsigned i = 0; i < (m_ropeLength - 1); ++i) + workQueue.append(m_fibers[i]); + currentFiber = m_fibers[m_ropeLength - 1]; + while (true) { + if (currentFiber.isRope()) { + Rope* rope = currentFiber.rope(); + // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber' + // (we will be working backwards over the rope). + unsigned ropeLengthMinusOne = rope->ropeLength() - 1; + for (unsigned i = 0; i < ropeLengthMinusOne; ++i) + workQueue.append(rope->fibers(i)); + currentFiber = rope->fibers(ropeLengthMinusOne); + } else { + UString::Rep* string = currentFiber.string(); + unsigned length = string->len; + position -= length; + copyChars(position, string->data(), length); + + // Was this the last item in the work queue? + if (workQueue.isEmpty()) { + // Create a string from the UChar buffer, clear the rope RefPtr. + ASSERT(buffer == position); + m_value = UString::Rep::create(buffer, m_stringLength); + for (unsigned i = 0; i < m_ropeLength; ++i) + m_fibers[i].deref(); + m_ropeLength = 0; + + ASSERT(!isRope()); + return; + } + + // No! - set the next item up to process. + currentFiber = workQueue.last(); + workQueue.removeLast(); + } + } +} + JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const { return const_cast<JSString*>(this); } -bool JSString::getPrimitiveNumber(ExecState*, double& number, JSValue& value) +bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) { - value = this; - number = m_value.toDouble(); + result = this; + number = value(exec).toDouble(); return false; } bool JSString::toBoolean(ExecState*) const { - return !m_value.isEmpty(); + return m_stringLength; } -double JSString::toNumber(ExecState*) const +double JSString::toNumber(ExecState* exec) const { - return m_value.toDouble(); + return value(exec).toDouble(); } -UString JSString::toString(ExecState*) const +UString JSString::toString(ExecState* exec) const { - return m_value; + return value(exec); } -UString JSString::toThisString(ExecState*) const +UString JSString::toThisString(ExecState* exec) const { - return m_value; + return value(exec); } JSString* JSString::toThisJSString(ExecState*) @@ -106,14 +225,14 @@ bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyNam bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { if (propertyName == exec->propertyNames().length) { - descriptor.setDescriptor(jsNumber(exec, m_value.size()), DontEnum | DontDelete | ReadOnly); + descriptor.setDescriptor(jsNumber(exec, m_stringLength), DontEnum | DontDelete | ReadOnly); return true; } bool isStrictUInt32; unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < static_cast<unsigned>(m_value.size())) { - descriptor.setDescriptor(jsSingleCharacterSubstring(exec, m_value, i), DontDelete | ReadOnly); + if (isStrictUInt32 && i < m_stringLength) { + descriptor.setDescriptor(jsSingleCharacterSubstring(exec, value(exec), i), DontDelete | ReadOnly); return true; } diff --git a/JavaScriptCore/runtime/JSString.h b/JavaScriptCore/runtime/JSString.h index 39dfe75..1867b17 100644 --- a/JavaScriptCore/runtime/JSString.h +++ b/JavaScriptCore/runtime/JSString.h @@ -60,13 +60,110 @@ namespace JSC { JSString* jsOwnedString(ExecState*, const UString&); class JSString : public JSCell { + public: friend class JIT; friend struct VPtrSet; - public: + // A Rope is a string composed of a set of substrings. + class Rope : public RefCounted<Rope> { + public: + // A Rope is composed from a set of smaller strings called Fibers. + // Each Fiber in a rope is either UString::Rep or another Rope. + class Fiber { + public: + Fiber() {} + Fiber(UString::Rep* string) : m_value(reinterpret_cast<intptr_t>(string)) {} + Fiber(Rope* rope) : m_value(reinterpret_cast<intptr_t>(rope) | 1) {} + + void deref() + { + if (isRope()) + rope()->deref(); + else + string()->deref(); + } + + Fiber& ref() + { + if (isString()) + string()->ref(); + else + rope()->ref(); + return *this; + } + + unsigned refAndGetLength() + { + if (isString()) { + UString::Rep* rep = string(); + return rep->ref()->len; + } else { + Rope* r = rope(); + r->ref(); + return r->stringLength(); + } + } + + bool isRope() { return m_value & 1; } + Rope* rope() { return reinterpret_cast<Rope*>(m_value & ~1); } + bool isString() { return !isRope(); } + UString::Rep* string() { return reinterpret_cast<UString::Rep*>(m_value); } + + private: + intptr_t m_value; + }; + + // Creates a Rope comprising of 'ropeLength' Fibers. + // The Rope is constructed in an uninitialized state - initialize must be called for each Fiber in the Rope. + static PassRefPtr<Rope> createOrNull(unsigned ropeLength) + { + void* allocation; + if (tryFastMalloc(sizeof(Rope) + (ropeLength - 1) * sizeof(Fiber)).getValue(allocation)) + return adoptRef(new (allocation) Rope(ropeLength)); + return 0; + } + + ~Rope(); + void destructNonRecursive(); + + void append(unsigned &index, Fiber& fiber) + { + m_fibers[index++] = fiber; + m_stringLength += fiber.refAndGetLength(); + } + void append(unsigned &index, const UString& string) + { + UString::Rep* rep = string.rep(); + m_fibers[index++] = Fiber(rep); + m_stringLength += rep->ref()->len; + } + void append(unsigned& index, JSString* jsString) + { + if (jsString->isRope()) { + for (unsigned i = 0; i < jsString->m_ropeLength; ++i) + append(index, jsString->m_fibers[i]); + } else + append(index, jsString->string()); + } + + unsigned ropeLength() { return m_ropeLength; } + unsigned stringLength() { return m_stringLength; } + Fiber& fibers(unsigned index) { return m_fibers[index]; } + + private: + Rope(unsigned ropeLength) : m_ropeLength(ropeLength), m_stringLength(0) {} + void* operator new(size_t, void* inPlace) { return inPlace; } + + unsigned m_ropeLength; + unsigned m_stringLength; + Fiber m_fibers[1]; + }; + JSString(JSGlobalData* globalData, const UString& value) : JSCell(globalData->stringStructure.get()) + , m_stringLength(value.size()) , m_value(value) + , m_ropeLength(0) { Heap::heap(this)->reportExtraMemoryCost(value.cost()); } @@ -74,23 +171,106 @@ namespace JSC { enum HasOtherOwnerType { HasOtherOwner }; JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) + , m_stringLength(value.size()) , m_value(value) + , m_ropeLength(0) { } JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) + , m_stringLength(value->size()) , m_value(value) + , m_ropeLength(0) { } - - const UString& value() const { return m_value; } + JSString(JSGlobalData* globalData, PassRefPtr<JSString::Rope> rope) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(rope->stringLength()) + , m_ropeLength(1) + { + m_fibers[0] = rope.releaseRef(); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with ropeLength <= 3. + JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, JSString* s2) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(s1->length() + s2->length()) + , m_ropeLength(ropeLength) + { + ASSERT(ropeLength <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, s1); + appendStringInConstruct(index, s2); + ASSERT(ropeLength == index); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with ropeLength <= 3. + JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, const UString& u2) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(s1->length() + u2.size()) + , m_ropeLength(ropeLength) + { + ASSERT(ropeLength <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, s1); + appendStringInConstruct(index, u2); + ASSERT(ropeLength == index); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with ropeLength <= 3. + JSString(JSGlobalData* globalData, unsigned ropeLength, const UString& u1, JSString* s2) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(u1.size() + s2->length()) + , m_ropeLength(ropeLength) + { + ASSERT(ropeLength <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, u1); + appendStringInConstruct(index, s2); + ASSERT(ropeLength == index); + } + // This constructor constructs a new string by concatenating v1, v2 & v3. + // This should only be called with ropeLength <= 3 ... which since every + // value must require a ropeLength of at least one implies that the length + // for each value must be exactly 1! + JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3) + : JSCell(exec->globalData().stringStructure.get()) + , m_stringLength(0) + , m_ropeLength(s_maxInternalRopeLength) + { + unsigned index = 0; + appendValueInConstructAndIncrementLength(exec, index, v1); + appendValueInConstructAndIncrementLength(exec, index, v2); + appendValueInConstructAndIncrementLength(exec, index, v3); + ASSERT(index == s_maxInternalRopeLength); + } + + ~JSString() + { + for (unsigned i = 0; i < m_ropeLength; ++i) + m_fibers[i].deref(); + } + + const UString& value(ExecState* exec) const + { + if (isRope()) + resolveRope(exec); + return m_value; + } + const UString tryGetValue() const + { + if (isRope()) + UString(); + return m_value; + } + unsigned length() { return m_stringLength; } bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); - bool canGetIndex(unsigned i) { return i < static_cast<unsigned>(m_value.size()); } - JSString* getIndex(JSGlobalData*, unsigned); + bool canGetIndex(unsigned i) { return i < m_stringLength; } + JSString* getIndex(ExecState*, unsigned); static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion)); } @@ -98,7 +278,39 @@ namespace JSC { enum VPtrStealingHackType { VPtrStealingHack }; JSString(VPtrStealingHackType) : JSCell(0) + , m_ropeLength(0) + { + } + + void resolveRope(ExecState*) const; + + void appendStringInConstruct(unsigned& index, const UString& string) + { + m_fibers[index++] = Rope::Fiber(string.rep()->ref()); + } + + void appendStringInConstruct(unsigned& index, JSString* jsString) + { + if (jsString->isRope()) { + for (unsigned i = 0; i < jsString->m_ropeLength; ++i) + m_fibers[index++] = jsString->m_fibers[i].ref(); + } else + appendStringInConstruct(index, jsString->string()); + } + + void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v) { + if (v.isString()) { + ASSERT(asCell(v)->isString()); + JSString* s = static_cast<JSString*>(asCell(v)); + ASSERT(s->ropeLength() == 1); + appendStringInConstruct(index, s); + m_stringLength += s->length(); + } else { + UString u(v.toString(exec)); + m_fibers[index++] = Rope::Fiber(u.rep()->ref()); + m_stringLength += u.size(); + } } virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; @@ -117,7 +329,22 @@ namespace JSC { virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); - UString m_value; + static const unsigned s_maxInternalRopeLength = 3; + + // A string is represented either by a UString or a Rope. + unsigned m_stringLength; + mutable UString m_value; + mutable unsigned m_ropeLength; + mutable Rope::Fiber m_fibers[s_maxInternalRopeLength]; + + bool isRope() const { return m_ropeLength; } + UString& string() { ASSERT(!isRope()); return m_value; } + unsigned ropeLength() { return m_ropeLength ? m_ropeLength : 1; } + + friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2); + friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2); + friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2); + friend JSValue jsString(ExecState* exec, Register* strings, unsigned count); }; JSString* asString(JSValue); @@ -146,7 +373,7 @@ namespace JSC { UChar c = s.data()[offset]; if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); - return new (globalData) JSString(globalData, UString::Rep::create(s.rep(), offset, 1)); + return new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1))); } inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s) @@ -163,10 +390,10 @@ namespace JSC { return new (globalData) JSString(globalData, s); } - inline JSString* JSString::getIndex(JSGlobalData* globalData, unsigned i) + inline JSString* JSString::getIndex(ExecState* exec, unsigned i) { ASSERT(canGetIndex(i)); - return jsSingleCharacterSubstring(globalData, m_value, i); + return jsSingleCharacterSubstring(&exec->globalData(), value(exec), i); } inline JSString* jsString(JSGlobalData* globalData, const UString& s) @@ -181,7 +408,7 @@ namespace JSC { } return new (globalData) JSString(globalData, s); } - + inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length) { ASSERT(offset <= static_cast<unsigned>(s.size())); @@ -194,7 +421,7 @@ namespace JSC { if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); } - return new (globalData) JSString(globalData, UString::Rep::create(s.rep(), offset, length)); + return new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length))); } inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s) @@ -222,14 +449,14 @@ namespace JSC { ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { if (propertyName == exec->propertyNames().length) { - slot.setValue(jsNumber(exec, m_value.size())); + slot.setValue(jsNumber(exec, m_stringLength)); return true; } bool isStrictUInt32; unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < static_cast<unsigned>(m_value.size())) { - slot.setValue(jsSingleCharacterSubstring(exec, m_value, i)); + if (isStrictUInt32 && i < m_stringLength) { + slot.setValue(jsSingleCharacterSubstring(exec, value(exec), i)); return true; } @@ -238,8 +465,8 @@ namespace JSC { ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { - if (propertyName < static_cast<unsigned>(m_value.size())) { - slot.setValue(jsSingleCharacterSubstring(exec, m_value, propertyName)); + if (propertyName < m_stringLength) { + slot.setValue(jsSingleCharacterSubstring(exec, value(exec), propertyName)); return true; } @@ -258,7 +485,7 @@ namespace JSC { inline UString JSValue::toString(ExecState* exec) const { if (isString()) - return static_cast<JSString*>(asCell())->value(); + return static_cast<JSString*>(asCell())->value(exec); if (isInt32()) return exec->globalData().numericStrings.add(asInt32()); if (isDouble()) diff --git a/JavaScriptCore/runtime/JSValue.h b/JavaScriptCore/runtime/JSValue.h index 1063cdc..fa5b5c0 100644 --- a/JavaScriptCore/runtime/JSValue.h +++ b/JavaScriptCore/runtime/JSValue.h @@ -80,6 +80,7 @@ namespace JSC { enum JSUndefinedTag { JSUndefined }; enum JSTrueTag { JSTrue }; enum JSFalseTag { JSFalse }; + enum EncodeAsDoubleTag { EncodeAsDouble }; JSValue(); JSValue(JSNullTag); @@ -90,6 +91,7 @@ namespace JSC { JSValue(const JSCell* ptr); // Numbers + JSValue(EncodeAsDoubleTag, ExecState*, double); JSValue(ExecState*, double); JSValue(ExecState*, char); JSValue(ExecState*, unsigned char); @@ -135,8 +137,8 @@ namespace JSC { bool getBoolean() const; // false if not a boolean bool getNumber(double&) const; double uncheckedGetNumber() const; - bool getString(UString&) const; - UString getString() const; // null string if not a string + bool getString(ExecState* exec, UString&) const; + UString getString(ExecState* exec) const; // null string if not a string JSObject* getObject() const; // 0 if not an object CallType getCallData(CallData&); @@ -166,6 +168,10 @@ namespace JSC { uint32_t toUInt32(ExecState*) const; uint32_t toUInt32(ExecState*, bool& ok) const; +#if ENABLE(JSC_ZOMBIES) + bool isZombie() const; +#endif + // 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)); } @@ -186,9 +192,9 @@ namespace JSC { static bool equal(ExecState* exec, JSValue v1, JSValue v2); static bool equalSlowCase(ExecState* exec, JSValue v1, JSValue v2); static bool equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2); - static bool strictEqual(JSValue v1, JSValue v2); - static bool strictEqualSlowCase(JSValue v1, JSValue v2); - static bool strictEqualSlowCaseInline(JSValue v1, JSValue v2); + static bool strictEqual(ExecState* exec, JSValue v1, JSValue v2); + static bool strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2); + static bool strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2); JSValue getJSNumber(); // JSValue() if this is not a JSNumber or number object @@ -279,6 +285,11 @@ namespace JSC { return b ? JSValue(JSValue::JSTrue) : JSValue(JSValue::JSFalse); } + ALWAYS_INLINE JSValue jsDoubleNumber(ExecState* exec, double d) + { + return JSValue(JSValue::EncodeAsDouble, exec, d); + } + ALWAYS_INLINE JSValue jsNumber(ExecState* exec, double d) { return JSValue(exec, d); @@ -431,6 +442,9 @@ namespace JSC { { JSValue v; v.u.asEncodedJSValue = encodedJSValue; +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif return v; } @@ -477,6 +491,9 @@ namespace JSC { else u.asBits.tag = EmptyValueTag; u.asBits.payload = reinterpret_cast<int32_t>(ptr); +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::JSValue(const JSCell* ptr) @@ -486,6 +503,9 @@ namespace JSC { else u.asBits.tag = EmptyValueTag; u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::operator bool() const @@ -583,6 +603,11 @@ namespace JSC { return reinterpret_cast<JSCell*>(u.asBits.payload); } + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState*, double d) + { + u.asDouble = d; + } + inline JSValue::JSValue(ExecState* exec, double d) { const int32_t asInt32 = static_cast<int32_t>(d); @@ -781,11 +806,17 @@ namespace JSC { inline JSValue::JSValue(JSCell* ptr) : m_ptr(ptr) { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::JSValue(const JSCell* ptr) : m_ptr(const_cast<JSCell*>(ptr)) { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::operator bool() const diff --git a/JavaScriptCore/runtime/JSVariableObject.cpp b/JavaScriptCore/runtime/JSVariableObject.cpp index 6586393..3aa9e62 100644 --- a/JavaScriptCore/runtime/JSVariableObject.cpp +++ b/JavaScriptCore/runtime/JSVariableObject.cpp @@ -53,16 +53,6 @@ void JSVariableObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& p JSObject::getOwnPropertyNames(exec, propertyNames); } -bool JSVariableObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const -{ - SymbolTableEntry entry = symbolTable().get(propertyName.ustring().rep()); - if (!entry.isNull()) { - attributes = entry.getAttributes() | DontDelete; - return true; - } - return JSObject::getPropertyAttributes(exec, propertyName, attributes); -} - bool JSVariableObject::isVariableObject() const { return true; diff --git a/JavaScriptCore/runtime/JSVariableObject.h b/JavaScriptCore/runtime/JSVariableObject.h index d8b1479..15d6ff5 100644 --- a/JavaScriptCore/runtime/JSVariableObject.h +++ b/JavaScriptCore/runtime/JSVariableObject.h @@ -54,8 +54,6 @@ namespace JSC { virtual bool isVariableObject() const; virtual bool isDynamicScope() const = 0; - virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const; - Register& registerAt(int index) const { return d->registers[index]; } static PassRefPtr<Structure> createStructure(JSValue prototype) diff --git a/JavaScriptCore/runtime/JSZombie.cpp b/JavaScriptCore/runtime/JSZombie.cpp new file mode 100644 index 0000000..072d29b --- /dev/null +++ b/JavaScriptCore/runtime/JSZombie.cpp @@ -0,0 +1,48 @@ +/* + * 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 "JSZombie.h" +#include "ClassInfo.h" + +#if ENABLE(JSC_ZOMBIES) + +namespace JSC { + +const ClassInfo JSZombie::s_info = { "Zombie", 0, 0, 0 }; + +Structure* JSZombie::leakedZombieStructure() { + static Structure* structure = 0; + if (!structure) { + Structure::startIgnoringLeaks(); + structure = Structure::create(jsNull(), TypeInfo(UnspecifiedType)).releaseRef(); + Structure::stopIgnoringLeaks(); + } + return structure; +} + +} + +#endif // ENABLE(JSC_ZOMBIES) diff --git a/JavaScriptCore/runtime/JSZombie.h b/JavaScriptCore/runtime/JSZombie.h new file mode 100644 index 0000000..8b33ea6 --- /dev/null +++ b/JavaScriptCore/runtime/JSZombie.h @@ -0,0 +1,78 @@ +/* + * 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 JSZombie_h +#define JSZombie_h + +#include "JSCell.h" + +#if ENABLE(JSC_ZOMBIES) +namespace JSC { + +class JSZombie : public JSCell { +public: + JSZombie(const ClassInfo* oldInfo, Structure* structure) + : JSCell(structure) + , m_oldInfo(oldInfo) + { + } + virtual bool isZombie() const { return true; } + virtual const ClassInfo* classInfo() const { return &s_info; } + static Structure* leakedZombieStructure(); + + virtual bool isGetterSetter() const { ASSERT_NOT_REACHED(); return false; } + virtual bool isAPIValueWrapper() const { ASSERT_NOT_REACHED(); return false; } + virtual bool isPropertyNameIterator() const { ASSERT_NOT_REACHED(); return false; } + virtual CallType getCallData(CallData&) { ASSERT_NOT_REACHED(); return CallTypeNone; } + virtual ConstructType getConstructData(ConstructData&) { ASSERT_NOT_REACHED(); return ConstructTypeNone; } + virtual bool getUInt32(uint32_t&) const { ASSERT_NOT_REACHED(); return false; } + virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const { ASSERT_NOT_REACHED(); return jsNull(); } + virtual bool getPrimitiveNumber(ExecState*, double&, JSValue&) { ASSERT_NOT_REACHED(); return false; } + virtual bool toBoolean(ExecState*) const { ASSERT_NOT_REACHED(); return false; } + virtual double toNumber(ExecState*) const { ASSERT_NOT_REACHED(); return 0.0; } + virtual UString toString(ExecState*) const { ASSERT_NOT_REACHED(); return ""; } + virtual JSObject* toObject(ExecState*) const { ASSERT_NOT_REACHED(); return 0; } + virtual void markChildren(MarkStack&) { ASSERT_NOT_REACHED(); } + virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&) { ASSERT_NOT_REACHED(); } + virtual void put(ExecState*, unsigned, JSValue) { ASSERT_NOT_REACHED(); } + virtual bool deleteProperty(ExecState*, const Identifier&) { ASSERT_NOT_REACHED(); return false; } + virtual bool deleteProperty(ExecState*, unsigned) { ASSERT_NOT_REACHED(); return false; } + virtual JSObject* toThisObject(ExecState*) const { ASSERT_NOT_REACHED(); return 0; } + virtual UString toThisString(ExecState*) const { ASSERT_NOT_REACHED(); return ""; } + virtual JSString* toThisJSString(ExecState*) { ASSERT_NOT_REACHED(); return 0; } + virtual JSValue getJSNumber() { ASSERT_NOT_REACHED(); return jsNull(); } + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&) { ASSERT_NOT_REACHED(); return false; } + virtual bool getOwnPropertySlot(ExecState*, unsigned, PropertySlot&) { ASSERT_NOT_REACHED(); return false; } + + static const ClassInfo s_info; +private: + const ClassInfo* m_oldInfo; +}; + +} + +#endif // ENABLE(JSC_ZOMBIES) + +#endif // JSZombie_h diff --git a/JavaScriptCore/runtime/MarkStack.h b/JavaScriptCore/runtime/MarkStack.h index ea09f54..a114ae0 100644 --- a/JavaScriptCore/runtime/MarkStack.h +++ b/JavaScriptCore/runtime/MarkStack.h @@ -153,7 +153,7 @@ namespace JSC { ASSERT(0 == (size % MarkStack::pageSize())); if (size == m_allocated) return; -#if PLATFORM(WIN) || PLATFORM(SYMBIAN) +#if PLATFORM(WIN_OS) || PLATFORM(SYMBIAN) // We cannot release a part of a region with VirtualFree. To get around this, // we'll release the entire region and reallocate the size that we want. releaseStack(m_data, m_allocated); diff --git a/JavaScriptCore/runtime/MathObject.cpp b/JavaScriptCore/runtime/MathObject.cpp index e8b7b97..98ff3ba 100644 --- a/JavaScriptCore/runtime/MathObject.cpp +++ b/JavaScriptCore/runtime/MathObject.cpp @@ -96,7 +96,6 @@ MathObject::MathObject(ExecState* exec, NonNullPassRefPtr<Structure> structure) putDirectWithoutTransition(Identifier(exec, "PI"), jsNumber(exec, piDouble), DontDelete | DontEnum | ReadOnly); putDirectWithoutTransition(Identifier(exec, "SQRT1_2"), jsNumber(exec, sqrt(0.5)), DontDelete | DontEnum | ReadOnly); putDirectWithoutTransition(Identifier(exec, "SQRT2"), jsNumber(exec, sqrt(2.0)), DontDelete | DontEnum | ReadOnly); - WTF::initializeWeakRandomNumberGenerator(); } // ECMA 15.8 @@ -120,22 +119,22 @@ JSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState* exec, JSObject*, JSValue, cons JSValue JSC_HOST_CALL mathProtoFuncACos(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, acos(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, acos(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncASin(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, asin(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, asin(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncATan(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, atan(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, atan(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, atan2(args.at(0).toNumber(exec), args.at(1).toNumber(exec))); + return jsDoubleNumber(exec, atan2(args.at(0).toNumber(exec), args.at(1).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -145,12 +144,12 @@ JSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec, JSObject*, JSValue, con JSValue JSC_HOST_CALL mathProtoFuncCos(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, cos(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, cos(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncExp(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, exp(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, exp(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -160,7 +159,7 @@ JSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec, JSObject*, JSValue, co JSValue JSC_HOST_CALL mathProtoFuncLog(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, log(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, log(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -211,7 +210,7 @@ JSValue JSC_HOST_CALL mathProtoFuncPow(ExecState* exec, JSObject*, JSValue, cons JSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState* exec, JSObject*, JSValue, const ArgList&) { - return jsNumber(exec, WTF::weakRandomNumber()); + return jsDoubleNumber(exec, exec->globalData().weakRandom.get()); } JSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -224,17 +223,17 @@ JSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec, JSObject*, JSValue, co JSValue JSC_HOST_CALL mathProtoFuncSin(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, sin(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, sin(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, sqrt(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, sqrt(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncTan(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, tan(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, tan(args.at(0).toNumber(exec))); } } // namespace JSC diff --git a/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/JavaScriptCore/runtime/NativeErrorConstructor.cpp index c655fae..403fc7e 100644 --- a/JavaScriptCore/runtime/NativeErrorConstructor.cpp +++ b/JavaScriptCore/runtime/NativeErrorConstructor.cpp @@ -33,7 +33,7 @@ ASSERT_CLASS_FITS_IN_CELL(NativeErrorConstructor); const ClassInfo NativeErrorConstructor::info = { "Function", &InternalFunction::info, 0, 0 }; NativeErrorConstructor::NativeErrorConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, NativeErrorPrototype* nativeErrorPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, nativeErrorPrototype->getDirect(exec->propertyNames().name).getString())) + : InternalFunction(&exec->globalData(), structure, Identifier(exec, nativeErrorPrototype->getDirect(exec->propertyNames().name).getString(exec))) , m_errorStructure(ErrorInstance::createStructure(nativeErrorPrototype)) { putDirect(exec->propertyNames().length, jsNumber(exec, 1), DontDelete | ReadOnly | DontEnum); // ECMA 15.11.7.5 diff --git a/JavaScriptCore/runtime/ObjectConstructor.cpp b/JavaScriptCore/runtime/ObjectConstructor.cpp index 837d5a6..693efc3 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -148,14 +148,14 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor } JSObject* description = asObject(in); - PropertySlot enumerableSlot; + PropertySlot enumerableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); if (exec->hadException()) return false; } - PropertySlot configurableSlot; + PropertySlot configurableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); if (exec->hadException()) @@ -163,21 +163,21 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor } JSValue value; - PropertySlot valueSlot; + PropertySlot valueSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); if (exec->hadException()) return false; } - PropertySlot writableSlot; + PropertySlot writableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); if (exec->hadException()) return false; } - PropertySlot getSlot; + PropertySlot getSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { JSValue get = getSlot.getValue(exec, exec->propertyNames().get); if (exec->hadException()) @@ -193,7 +193,7 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor desc.setGetter(get); } - PropertySlot setSlot; + PropertySlot setSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { JSValue set = setSlot.getValue(exec, exec->propertyNames().set); if (exec->hadException()) diff --git a/JavaScriptCore/runtime/Operations.cpp b/JavaScriptCore/runtime/Operations.cpp index 093bbec..0e1887c 100644 --- a/JavaScriptCore/runtime/Operations.cpp +++ b/JavaScriptCore/runtime/Operations.cpp @@ -29,10 +29,6 @@ #include <stdio.h> #include <wtf/MathExtras.h> -#if HAVE(FLOAT_H) -#include <float.h> -#endif - namespace JSC { bool JSValue::equalSlowCase(ExecState* exec, JSValue v1, JSValue v2) @@ -40,9 +36,9 @@ bool JSValue::equalSlowCase(ExecState* exec, JSValue v1, JSValue v2) return equalSlowCaseInline(exec, v1, v2); } -bool JSValue::strictEqualSlowCase(JSValue v1, JSValue v2) +bool JSValue::strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2) { - return strictEqualSlowCaseInline(v1, v2); + return strictEqualSlowCaseInline(exec, v1, v2); } NEVER_INLINE JSValue throwOutOfMemoryError(ExecState* exec) @@ -58,12 +54,13 @@ NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2) JSValue p1 = v1.toPrimitive(callFrame); JSValue p2 = v2.toPrimitive(callFrame); - if (p1.isString() || p2.isString()) { - RefPtr<UString::Rep> value = concatenate(p1.toString(callFrame).rep(), p2.toString(callFrame).rep()); - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); + if (p1.isString()) { + return p2.isString() + ? jsString(callFrame, asString(p1), asString(p2)) + : jsString(callFrame, asString(p1), p2.toString(callFrame)); } + if (p2.isString()) + return jsString(callFrame, p1.toString(callFrame), asString(p2)); return jsNumber(callFrame, p1.toNumber(callFrame) + p2.toNumber(callFrame)); } diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h index 1aa68b3..0289ac2 100644 --- a/JavaScriptCore/runtime/Operations.h +++ b/JavaScriptCore/runtime/Operations.h @@ -35,6 +35,99 @@ namespace JSC { bool jsIsObjectType(JSValue); bool jsIsFunctionType(JSValue); + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) + { + if (!s1->length()) + return s2; + if (!s2->length()) + return s1; + + unsigned ropeLength = s1->ropeLength() + s2->ropeLength(); + JSGlobalData* globalData = &exec->globalData(); + + if (ropeLength <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, ropeLength, s1, s2); + + unsigned index = 0; + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + rope->append(index, s1); + rope->append(index, s2); + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2) + { + unsigned ropeLength = 1 + s2->ropeLength(); + JSGlobalData* globalData = &exec->globalData(); + + if (ropeLength <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, ropeLength, u1, s2); + + unsigned index = 0; + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + rope->append(index, u1); + rope->append(index, s2); + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2) + { + unsigned ropeLength = s1->ropeLength() + 1; + JSGlobalData* globalData = &exec->globalData(); + + if (ropeLength <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, ropeLength, s1, u2); + + unsigned index = 0; + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + rope->append(index, s1); + rope->append(index, u2); + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) + { + ASSERT(count >= 3); + + unsigned ropeLength = 0; + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + if (LIKELY(v.isString())) + ropeLength += asString(v)->ropeLength(); + else + ++ropeLength; + } + + JSGlobalData* globalData = &exec->globalData(); + if (ropeLength == 3) + return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue()); + + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + + unsigned index = 0; + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + if (LIKELY(v.isString())) + rope->append(index, asString(v)); + else + rope->append(index, v.toString(exec)); + } + + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + // ECMA 11.9.3 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) { @@ -53,7 +146,7 @@ namespace JSC { bool s1 = v1.isString(); bool s2 = v2.isString(); if (s1 && s2) - return asString(v1)->value() == asString(v2)->value(); + return asString(v1)->value(exec) == asString(v2)->value(exec); if (v1.isUndefinedOrNull()) { if (v2.isUndefinedOrNull()) @@ -110,17 +203,17 @@ namespace JSC { } // ECMA 11.9.3 - ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(JSValue v1, JSValue v2) + ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) { ASSERT(v1.isCell() && v2.isCell()); if (v1.asCell()->isString() && v2.asCell()->isString()) - return asString(v1)->value() == asString(v2)->value(); + return asString(v1)->value(exec) == asString(v2)->value(exec); return v1 == v2; } - inline bool JSValue::strictEqual(JSValue v1, JSValue v2) + inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) { if (v1.isInt32() && v2.isInt32()) return v1 == v2; @@ -131,7 +224,7 @@ namespace JSC { if (!v1.isCell() || !v2.isCell()) return v1 == v2; - return strictEqualSlowCaseInline(v1, v2); + return strictEqualSlowCaseInline(exec, v1, v2); } inline bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) @@ -146,7 +239,7 @@ namespace JSC { JSGlobalData* globalData = &callFrame->globalData(); if (isJSString(globalData, v1) && isJSString(globalData, v2)) - return asString(v1)->value() < asString(v2)->value(); + return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); JSValue p1; JSValue p2; @@ -156,7 +249,7 @@ namespace JSC { if (wasNotString1 | wasNotString2) return n1 < n2; - return asString(p1)->value() < asString(p2)->value(); + return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); } inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) @@ -171,7 +264,7 @@ namespace JSC { JSGlobalData* globalData = &callFrame->globalData(); if (isJSString(globalData, v1) && isJSString(globalData, v2)) - return !(asString(v2)->value() < asString(v1)->value()); + return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); JSValue p1; JSValue p2; @@ -181,7 +274,7 @@ namespace JSC { if (wasNotString1 | wasNotString2) return n1 <= n2; - return !(asString(p2)->value() < asString(p1)->value()); + return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame)); } // Fast-path choices here are based on frequency data from SunSpider: @@ -195,29 +288,14 @@ namespace JSC { ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) { - double left; - double right = 0.0; - - bool rightIsNumber = v2.getNumber(right); - if (rightIsNumber && v1.getNumber(left)) + double left = 0.0, right; + if (v1.getNumber(left), v2.getNumber(right)) return jsNumber(callFrame, left + right); - bool leftIsString = v1.isString(); - if (leftIsString && v2.isString()) { - RefPtr<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep()); - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); - } - - if (rightIsNumber & leftIsString) { - RefPtr<UString::Rep> value = v2.isInt32() ? - concatenate(asString(v1)->value().rep(), v2.asInt32()) : - concatenate(asString(v1)->value().rep(), right); - - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); + if (v1.isString()) { + return v2.isString() + ? jsString(callFrame, asString(v1), asString(v2)) + : jsString(callFrame, asString(v1), v2.toString(callFrame)); } // All other cases are pretty uncommon @@ -243,7 +321,7 @@ namespace JSC { // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (cell->structure()->isDictionary()) - asObject(cell)->setStructure(Structure::fromDictionaryTransition(cell->structure())); + asObject(cell)->flattenDictionaryObject(); ++count; } @@ -265,7 +343,7 @@ namespace JSC { // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (base->structure()->isDictionary()) - asObject(base)->setStructure(Structure::fromDictionaryTransition(base->structure())); + asObject(base)->flattenDictionaryObject(); ++count; } @@ -293,52 +371,6 @@ namespace JSC { ASSERT_NOT_REACHED(); return JSValue(); } - - ALWAYS_INLINE JSValue concatenateStrings(CallFrame* callFrame, Register* strings, unsigned count) - { - ASSERT(count >= 3); - - // Estimate the amount of space required to hold the entire string. If all - // arguments are strings, we can easily calculate the exact amount of space - // required. For any other arguments, for now let's assume they may require - // 11 UChars of storage. This is enouch to hold any int, and likely is also - // reasonable for the other immediates. We may want to come back and tune - // this value at some point. - unsigned bufferSize = 0; - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - bufferSize += asString(v)->value().size(); - else - bufferSize += 11; - } - - // Allocate an output string to store the result. - // If the first argument is a String, and if it has the capacity (or can grow - // its capacity) to hold the entire result then use this as a base to concatenate - // onto. Otherwise, allocate a new empty output buffer. - JSValue firstValue = strings[0].jsValue(); - RefPtr<UString::Rep> resultRep; - if (firstValue.isString() && (resultRep = asString(firstValue)->value().rep())->reserveCapacity(bufferSize)) { - // We're going to concatenate onto the first string - remove it from the list of items to be appended. - ++strings; - --count; - } else - resultRep = UString::Rep::createEmptyBuffer(bufferSize); - UString result(resultRep); - - // Loop over the operands, writing them into the output buffer. - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - result.append(asString(v)->value()); - else - result.append(v.toString(callFrame)); - } - - return jsString(callFrame, result); - } - } // namespace JSC #endif // Operations_h diff --git a/JavaScriptCore/runtime/PropertyDescriptor.cpp b/JavaScriptCore/runtime/PropertyDescriptor.cpp index 4db814f..558ae28 100644 --- a/JavaScriptCore/runtime/PropertyDescriptor.cpp +++ b/JavaScriptCore/runtime/PropertyDescriptor.cpp @@ -153,15 +153,15 @@ void PropertyDescriptor::setGetter(JSValue getter) m_attributes &= ~ReadOnly; } -bool PropertyDescriptor::equalTo(const PropertyDescriptor& other) const +bool PropertyDescriptor::equalTo(ExecState* exec, const PropertyDescriptor& other) const { if (!other.m_value == m_value || !other.m_getter == m_getter || !other.m_setter == m_setter) return false; - return (!m_value || JSValue::strictEqual(other.m_value, m_value)) && - (!m_getter || JSValue::strictEqual(other.m_getter, m_getter)) && - (!m_setter || JSValue::strictEqual(other.m_setter, m_setter)) && + return (!m_value || JSValue::strictEqual(exec, other.m_value, m_value)) && + (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter)) && + (!m_setter || JSValue::strictEqual(exec, other.m_setter, m_setter)) && attributesEqual(other); } diff --git a/JavaScriptCore/runtime/PropertyDescriptor.h b/JavaScriptCore/runtime/PropertyDescriptor.h index 40bec86..ff9f160 100644 --- a/JavaScriptCore/runtime/PropertyDescriptor.h +++ b/JavaScriptCore/runtime/PropertyDescriptor.h @@ -61,7 +61,7 @@ namespace JSC { bool configurablePresent() const { return m_seenAttributes & ConfigurablePresent; } bool setterPresent() const { return m_setter; } bool getterPresent() const { return m_getter; } - bool equalTo(const PropertyDescriptor& other) const; + bool equalTo(ExecState* exec, const PropertyDescriptor& other) const; bool attributesEqual(const PropertyDescriptor& other) const; unsigned attributesWithOverride(const PropertyDescriptor& other) const; private: diff --git a/JavaScriptCore/runtime/RegExpConstructor.cpp b/JavaScriptCore/runtime/RegExpConstructor.cpp index c609e08..e5c6909 100644 --- a/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -132,6 +132,8 @@ void RegExpMatchesArray::fillArrayInstance(ExecState* exec) int start = d->lastOvector()[2 * i]; if (start >= 0) JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start)); + else + JSArray::put(exec, i, jsUndefined()); } PutPropertySlot slot; diff --git a/JavaScriptCore/runtime/StringObject.cpp b/JavaScriptCore/runtime/StringObject.cpp index 7216d3a..f23a20d 100644 --- a/JavaScriptCore/runtime/StringObject.cpp +++ b/JavaScriptCore/runtime/StringObject.cpp @@ -79,12 +79,16 @@ bool StringObject::deleteProperty(ExecState* exec, const Identifier& propertyNam { if (propertyName == exec->propertyNames().length) return false; + bool isStrictUInt32; + unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); + if (isStrictUInt32 && internalValue()->canGetIndex(i)) + return false; return JSObject::deleteProperty(exec, propertyName); } void StringObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) { - int size = internalValue()->value().size(); + int size = internalValue()->length(); for (int i = 0; i < size; ++i) propertyNames.add(Identifier(exec, UString::from(i))); return JSObject::getOwnPropertyNames(exec, propertyNames); diff --git a/JavaScriptCore/runtime/StringPrototype.cpp b/JavaScriptCore/runtime/StringPrototype.cpp index a0713b8..32f9e6b 100644 --- a/JavaScriptCore/runtime/StringPrototype.cpp +++ b/JavaScriptCore/runtime/StringPrototype.cpp @@ -224,7 +224,7 @@ static inline int localeCompare(const UString& a, const UString& b) JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { JSString* sourceVal = thisValue.toThisJSString(exec); - const UString& source = sourceVal->value(); + const UString& source = sourceVal->value(exec); JSValue pattern = args.at(0); @@ -281,7 +281,8 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue cachedCall.setArgument(i++, sourceVal); cachedCall.setThis(exec->globalThisValue()); - replacements.append(cachedCall.call().toString(cachedCall.newCallFrame())); + JSValue result = cachedCall.call(); + replacements.append(result.toString(cachedCall.newCallFrame(exec))); if (exec->hadException()) break; @@ -469,6 +470,11 @@ JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSV dpos = 0; else if (!(dpos <= len)) // true for NaN dpos = len; +#if PLATFORM(SYMBIAN) + // Work around for broken NaN compare operator + else if (isnan(dpos)) + dpos = len; +#endif return jsNumber(exec, s.rfind(u2, static_cast<int>(dpos))); } @@ -691,7 +697,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec, JSObject*, JSVal JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { JSString* sVal = thisValue.toThisJSString(exec); - const UString& s = sVal->value(); + const UString& s = sVal->value(exec); int sSize = s.size(); if (!sSize) @@ -725,7 +731,7 @@ JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSV JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { JSString* sVal = thisValue.toThisJSString(exec); - const UString& s = sVal->value(); + const UString& s = sVal->value(exec); int sSize = s.size(); if (!sSize) diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp index 65b62f9..e4c9ac3 100644 --- a/JavaScriptCore/runtime/Structure.cpp +++ b/JavaScriptCore/runtime/Structure.cpp @@ -77,6 +77,8 @@ static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>); static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); #endif +static int comparePropertyMapEntryIndices(const void* a, const void* b); + void Structure::dumpStatistics() { #if DUMP_STRUCTURE_ID_STATISTICS @@ -534,20 +536,47 @@ PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* st return toDictionaryTransition(structure, UncachedDictionaryKind); } -PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure) +PassRefPtr<Structure> Structure::flattenDictionaryStructure(JSObject* object) { - ASSERT(structure->isDictionary()); - - // Since dictionary Structures are not shared, and no opcodes specialize - // for them, we don't need to allocate a new Structure when transitioning - // to non-dictionary status. - - // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the - // deleted offsets vector) before transitioning from dictionary. - if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty()) - structure->m_dictionaryKind = NoneDictionaryKind; + ASSERT(isDictionary()); + if (isUncacheableDictionary()) { + ASSERT(m_propertyTable); + Vector<PropertyMapEntry*> sortedPropertyEntries(m_propertyTable->keyCount); + PropertyMapEntry** p = sortedPropertyEntries.data(); + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (m_propertyTable->entries()[i].key) + *p++ = &m_propertyTable->entries()[i]; + } + size_t propertyCount = p - sortedPropertyEntries.data(); + qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); + sortedPropertyEntries.resize(propertyCount); + + // We now have the properties currently defined on this object + // in the order that they are expected to be in, but we need to + // reorder the storage, so we have to copy the current values out + Vector<JSValue> values(propertyCount); + unsigned anonymousSlotCount = m_propertyTable->anonymousSlotCount; + for (unsigned i = 0; i < propertyCount; i++) { + PropertyMapEntry* entry = sortedPropertyEntries[i]; + values[i] = object->getDirectOffset(entry->offset); + // Update property table to have the new property offsets + entry->offset = anonymousSlotCount + i; + entry->index = i; + } + + // Copy the original property values into their final locations + for (unsigned i = 0; i < propertyCount; i++) + object->putDirectOffset(anonymousSlotCount + i, values[i]); + + if (m_propertyTable->deletedOffsets) { + delete m_propertyTable->deletedOffsets; + m_propertyTable->deletedOffsets = 0; + } + } - return structure; + m_dictionaryKind = NoneDictionaryKind; + return this; } size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) @@ -556,8 +585,6 @@ size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, u materializePropertyMapIfNecessary(); m_isPinnedPropertyTable = true; - if (attributes & DontEnum) - m_hasNonEnumerableProperties = true; size_t offset = put(propertyName, attributes, specificValue); if (propertyStorageSize() > propertyStorageCapacity()) @@ -739,6 +766,9 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCel checkConsistency(); + if (attributes & DontEnum) + m_hasNonEnumerableProperties = true; + UString::Rep* rep = propertyName._ustring.rep(); if (!m_propertyTable) @@ -1003,7 +1033,7 @@ void Structure::rehashPropertyMapHashTable(unsigned newTableSize) checkConsistency(); } -static int comparePropertyMapEntryIndices(const void* a, const void* b) +int comparePropertyMapEntryIndices(const void* a, const void* b) { unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index; unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index; @@ -1025,6 +1055,7 @@ void Structure::getEnumerablePropertyNames(PropertyNameArray& propertyNames) int i = 0; unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; for (unsigned k = 1; k <= entryCount; k++) { + ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[k].attributes & DontEnum)); if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) { PropertyMapEntry* value = &m_propertyTable->entries()[k]; int j; @@ -1112,6 +1143,7 @@ void Structure::checkConsistency() unsigned nonEmptyEntryCount = 0; for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) { + ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[c].attributes & DontEnum)); UString::Rep* rep = m_propertyTable->entries()[c].key; if (!rep) continue; diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h index f355c53..c6b7d91 100644 --- a/JavaScriptCore/runtime/Structure.h +++ b/JavaScriptCore/runtime/Structure.h @@ -74,7 +74,8 @@ namespace JSC { static PassRefPtr<Structure> getterSetterTransition(Structure*); static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*); static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*); - static PassRefPtr<Structure> fromDictionaryTransition(Structure*); + + PassRefPtr<Structure> flattenDictionaryStructure(JSObject*); ~Structure(); @@ -306,7 +307,7 @@ namespace JSC { TransitionTable* transitionTable = new TransitionTable; setTransitionTable(transitionTable); if (existingTransition) - add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); + add(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); } } // namespace JSC diff --git a/JavaScriptCore/runtime/UString.cpp b/JavaScriptCore/runtime/UString.cpp index e66ca93..50d23c4 100644 --- a/JavaScriptCore/runtime/UString.cpp +++ b/JavaScriptCore/runtime/UString.cpp @@ -30,12 +30,12 @@ #include "Identifier.h" #include "Operations.h" #include <ctype.h> -#include <float.h> #include <limits.h> #include <limits> #include <math.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <wtf/ASCIICType.h> #include <wtf/Assertions.h> #include <wtf/MathExtras.h> @@ -44,9 +44,6 @@ #include <wtf/unicode/UTF8.h> #include <wtf/StringExtras.h> -#if HAVE(STRING_H) -#include <string.h> -#endif #if HAVE(STRINGS_H) #include <strings.h> #endif @@ -578,11 +575,33 @@ static PassRefPtr<UString::Rep> createRep(const char* c) } +static inline PassRefPtr<UString::Rep> createRep(const char* c, int length) +{ + if (!c) + return &UString::Rep::null(); + + if (!length) + return &UString::Rep::empty(); + + UChar* d; + if (!allocChars(length).getValue(d)) + return &UString::Rep::null(); + + for (int i = 0; i < length; i++) + d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend + return UString::Rep::create(d, length); +} + UString::UString(const char* c) : m_rep(createRep(c)) { } +UString::UString(const char* c, int length) + : m_rep(createRep(c, length)) +{ +} + UString::UString(const UChar* c, int length) { if (length == 0) @@ -1025,69 +1044,10 @@ UString UString::from(long l) UString UString::from(double d) { - // avoid ever printing -NaN, in JS conceptually there is only one NaN value - if (isnan(d)) - return "NaN"; - if (!d) - return "0"; // -0 -> "0" - - char buf[80]; - int decimalPoint; - int sign; - - char result[80]; - WTF::dtoa(result, d, 0, &decimalPoint, &sign, NULL); - int length = static_cast<int>(strlen(result)); - - int i = 0; - if (sign) - buf[i++] = '-'; - - if (decimalPoint <= 0 && decimalPoint > -6) { - buf[i++] = '0'; - buf[i++] = '.'; - for (int j = decimalPoint; j < 0; j++) - buf[i++] = '0'; - strcpy(buf + i, result); - } else if (decimalPoint <= 21 && decimalPoint > 0) { - if (length <= decimalPoint) { - strcpy(buf + i, result); - i += length; - for (int j = 0; j < decimalPoint - length; j++) - buf[i++] = '0'; - buf[i] = '\0'; - } else { - strncpy(buf + i, result, decimalPoint); - i += decimalPoint; - buf[i++] = '.'; - strcpy(buf + i, result + decimalPoint); - } - } else if (result[0] < '0' || result[0] > '9') - strcpy(buf + i, result); - else { - buf[i++] = result[0]; - if (length > 1) { - buf[i++] = '.'; - strcpy(buf + i, result + 1); - i += length - 1; - } - - buf[i++] = 'e'; - buf[i++] = (decimalPoint >= 0) ? '+' : '-'; - // decimalPoint can't be more than 3 digits decimal given the - // nature of float representation - int exponential = decimalPoint - 1; - if (exponential < 0) - exponential = -exponential; - if (exponential >= 100) - buf[i++] = static_cast<char>('0' + exponential / 100); - if (exponential >= 10) - buf[i++] = static_cast<char>('0' + (exponential % 100) / 10); - buf[i++] = static_cast<char>('0' + exponential % 10); - buf[i++] = '\0'; - } - - return UString(buf); + DtoaBuffer buffer; + unsigned length; + doubleToStringInJavaScriptFormat(d, buffer, &length); + return UString(buffer, length); } UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const diff --git a/JavaScriptCore/runtime/UString.h b/JavaScriptCore/runtime/UString.h index c4dad2a..9b046f2 100644 --- a/JavaScriptCore/runtime/UString.h +++ b/JavaScriptCore/runtime/UString.h @@ -235,7 +235,10 @@ namespace JSC { public: UString(); + // Constructor for null-terminated ASCII string. UString(const char*); + // Constructor for non-null-terminated ASCII string. + UString(const char*, int length); UString(const UChar*, int length); UString(UChar*, int length, bool copy); diff --git a/JavaScriptCore/runtime/WeakRandom.h b/JavaScriptCore/runtime/WeakRandom.h new file mode 100644 index 0000000..ff3995e --- /dev/null +++ b/JavaScriptCore/runtime/WeakRandom.h @@ -0,0 +1,86 @@ +/* + * 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. + * + * + * Copyright (c) 2009 Ian C. Bullard + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef WeakRandom_h +#define WeakRandom_h + +#include <limits.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + +class WeakRandom { +public: + WeakRandom(unsigned seed) + : m_low(seed ^ 0x49616E42) + , m_high(seed) + { + } + + double get() + { + return advance() / (UINT_MAX + 1.0); + } + +private: + unsigned advance() + { + m_high = (m_high << 16) + (m_high >> 16); + m_high += m_low; + m_low += m_high; + return m_high; + } + + unsigned m_low; + unsigned m_high; +}; + +} // namespace JSC + +#endif // WeakRandom_h |