diff options
Diffstat (limited to 'JavaScriptCore/runtime')
174 files changed, 7835 insertions, 6559 deletions
diff --git a/JavaScriptCore/runtime/AlignedMemoryAllocator.h b/JavaScriptCore/runtime/AlignedMemoryAllocator.h new file mode 100644 index 0000000..e682eb3 --- /dev/null +++ b/JavaScriptCore/runtime/AlignedMemoryAllocator.h @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef AlignedMemoryAllocator_h +#define AlignedMemoryAllocator_h + +#include <wtf/Bitmap.h> +#include <wtf/PageReservation.h> + +namespace JSC { + +struct AlignedMemoryAllocatorConstants { +// Set sane defaults if -D<flagname=value> wasn't provided via compiler args +#if defined(JSCCOLLECTOR_VIRTUALMEM_RESERVATION) + // Keep backwards compatibility with symbian build system + static const size_t virtualMemoryReservation = JSCCOLLECTOR_VIRTUALMEM_RESERVATION; +#elif defined(__WINS__) + // Emulator has limited virtual address space + static const size_t virtualMemoryReservation = 0x400000; +#else + // HW has plenty of virtual addresses + static const size_t virtualMemoryReservation = 0x8000000; +#endif +}; + +template<size_t blockSize> class AlignedMemory; +template<size_t blockSize> class AlignedMemoryAllocator; + +#if HAVE(PAGE_ALLOCATE_ALIGNED) + +template<size_t blockSize> +class AlignedMemoryAllocator; + +template<size_t blockSize> +class AlignedMemory { +public: + void deallocate(); + void* base(); + +private: + friend class AlignedMemoryAllocator<blockSize>; + + AlignedMemory(PageAllocation); + + PageAllocation m_allocation; +}; + +template<size_t blockSize> +class AlignedMemoryAllocator { +public: + void destroy(); + AlignedMemory<blockSize> allocate(); +}; + +template<size_t blockSize> +inline void AlignedMemoryAllocator<blockSize>::destroy() +{ +} + +template<size_t blockSize> +inline AlignedMemory<blockSize> AlignedMemoryAllocator<blockSize>::allocate() +{ + return AlignedMemory<blockSize>(PageAllocation::allocateAligned(blockSize, PageAllocation::JSGCHeapPages)); +} + +template<size_t blockSize> +inline void AlignedMemory<blockSize>::deallocate() +{ + m_allocation.deallocate(); +} + +template<size_t blockSize> +inline void* AlignedMemory<blockSize>::base() +{ + return m_allocation.base(); +} + +template<size_t blockSize> +inline AlignedMemory<blockSize>::AlignedMemory(PageAllocation allocation) + : m_allocation(allocation) +{ +} + +#else + +template<size_t blockSize> +class AlignedMemory { +public: + void deallocate(); + void* base(); + +private: + friend class AlignedMemoryAllocator<blockSize>; + + AlignedMemory(void* base, AlignedMemoryAllocator<blockSize>* allocator); + + void* m_base; + AlignedMemoryAllocator<blockSize>* m_allocator; +}; + +template<size_t blockSize> +class AlignedMemoryAllocator { +public: + AlignedMemoryAllocator(); + ~AlignedMemoryAllocator(); + + void destroy(); + AlignedMemory<blockSize> allocate(); + void free(AlignedMemory<blockSize>); + +private: + static const size_t reservationSize = AlignedMemoryAllocatorConstants::virtualMemoryReservation; + static const size_t bitmapSize = reservationSize / blockSize; + + PageReservation m_reservation; + size_t m_nextFree; + uintptr_t m_reservationBase; + WTF::Bitmap<bitmapSize> m_bitmap; +}; + +template<size_t blockSize> +AlignedMemoryAllocator<blockSize>::AlignedMemoryAllocator() + : m_reservation(PageReservation::reserve(reservationSize + blockSize, PageAllocation::JSGCHeapPages)) + , m_nextFree(0) +{ + // check that blockSize and reservationSize are powers of two + ASSERT(!(blockSize & (blockSize - 1))); + ASSERT(!(reservationSize & (reservationSize - 1))); + + // check that blockSize is a multiple of pageSize and that + // reservationSize is a multiple of blockSize + ASSERT(!(blockSize & (PageAllocation::pageSize() - 1))); + ASSERT(!(reservationSize & (blockSize - 1))); + + ASSERT(m_reservation); + + m_reservationBase = reinterpret_cast<uintptr_t>(m_reservation.base()); + m_reservationBase = (m_reservationBase + blockSize) & ~(blockSize - 1); +} + +template<size_t blockSize> +AlignedMemoryAllocator<blockSize>::~AlignedMemoryAllocator() +{ + destroy(); + m_reservation.deallocate(); +} + +template<size_t blockSize> +inline void AlignedMemoryAllocator<blockSize>::destroy() +{ + for (unsigned i = 0; i < bitmapSize; ++i) { + if (m_bitmap.get(i)) { + void* blockAddress = reinterpret_cast<void*>(m_reservationBase + m_nextFree * blockSize); + m_reservation.decommit(blockAddress, blockSize); + + m_bitmap.clear(i); + } + } +} + +template<size_t blockSize> +AlignedMemory<blockSize> AlignedMemoryAllocator<blockSize>::allocate() +{ + while (m_nextFree < bitmapSize) { + if (!m_bitmap.get(m_nextFree)) { + void* blockAddress = reinterpret_cast<void*>(m_reservationBase + m_nextFree * blockSize); + m_reservation.commit(blockAddress, blockSize); + + m_bitmap.set(m_nextFree); + ++m_nextFree; + + return AlignedMemory<blockSize>(blockAddress, this); + } + m_bitmap.advanceToNextFreeBit(m_nextFree); + } + + if (m_bitmap.isFull()) + return AlignedMemory<blockSize>(0, this); + + m_nextFree = 0; + + return allocate(); +} + +template<size_t blockSize> +void AlignedMemoryAllocator<blockSize>::free(AlignedMemory<blockSize> allocation) +{ + ASSERT(allocation.base()); + m_reservation.decommit(allocation.base(), blockSize); + + size_t diff = (reinterpret_cast<uintptr_t>(allocation.base()) - m_reservationBase); + ASSERT(!(diff & (blockSize - 1))); + + size_t i = diff / blockSize; + ASSERT(m_bitmap.get(i)); + + m_bitmap.clear(i); +} + +template<size_t blockSize> +inline void AlignedMemory<blockSize>::deallocate() +{ + m_allocator->free(*this); +} + +template<size_t blockSize> +inline void* AlignedMemory<blockSize>::base() +{ + return m_base; +} + +template<size_t blockSize> +AlignedMemory<blockSize>::AlignedMemory(void* base, AlignedMemoryAllocator<blockSize>* allocator) + : m_base(base) + , m_allocator(allocator) +{ +} + +#endif + +} + +#endif diff --git a/JavaScriptCore/runtime/ArgList.h b/JavaScriptCore/runtime/ArgList.h index 8e1fdbe..cd563a2 100644 --- a/JavaScriptCore/runtime/ArgList.h +++ b/JavaScriptCore/runtime/ArgList.h @@ -22,6 +22,7 @@ #ifndef ArgList_h #define ArgList_h +#include "CallFrame.h" #include "Register.h" #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> @@ -187,6 +188,12 @@ namespace JSC { { } + ArgList(ExecState* exec) + : m_args(reinterpret_cast<JSValue*>(&exec[exec->hostThisRegister() + 1])) + , m_argCount(exec->argumentCount()) + { + } + ArgList(JSValue* args, unsigned argCount) : m_args(args) , m_argCount(argCount) diff --git a/JavaScriptCore/runtime/Arguments.cpp b/JavaScriptCore/runtime/Arguments.cpp index bb30e3b..39886a8 100644 --- a/JavaScriptCore/runtime/Arguments.cpp +++ b/JavaScriptCore/runtime/Arguments.cpp @@ -151,13 +151,37 @@ bool Arguments::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& sl return true; } - return JSObject::getOwnPropertySlot(exec, Identifier(exec, UString::from(i)), slot); + return JSObject::getOwnPropertySlot(exec, Identifier(exec, UString::number(i)), slot); +} + +void Arguments::createStrictModeCallerIfNecessary(ExecState* exec) +{ + if (d->overrodeCaller) + return; + + d->overrodeCaller = true; + PropertyDescriptor descriptor; + JSValue thrower = createTypeErrorFunction(exec, "Unable to access caller of strict mode function"); + descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter); + defineOwnProperty(exec, exec->propertyNames().caller, descriptor, false); +} + +void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec) +{ + if (d->overrodeCallee) + return; + + d->overrodeCallee = true; + PropertyDescriptor descriptor; + JSValue thrower = createTypeErrorFunction(exec, "Unable to access callee of strict mode function"); + descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter); + defineOwnProperty(exec, exec->propertyNames().callee, descriptor, false); } bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { if (i < d->numParameters) { slot.setRegisterSlot(&d->registers[d->firstParameterIndex + i]); @@ -167,22 +191,28 @@ bool Arguments::getOwnPropertySlot(ExecState* exec, const Identifier& propertyNa } if (propertyName == exec->propertyNames().length && LIKELY(!d->overrodeLength)) { - slot.setValue(jsNumber(exec, d->numArguments)); + slot.setValue(jsNumber(d->numArguments)); return true; } if (propertyName == exec->propertyNames().callee && LIKELY(!d->overrodeCallee)) { - slot.setValue(d->callee); - return true; + if (!d->isStrictMode) { + slot.setValue(d->callee); + return true; + } + createStrictModeCalleeIfNecessary(exec); } + if (propertyName == exec->propertyNames().caller && d->isStrictMode) + createStrictModeCallerIfNecessary(exec); + return JSObject::getOwnPropertySlot(exec, propertyName, slot); } bool Arguments::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { if (i < d->numParameters) { descriptor.setDescriptor(d->registers[d->firstParameterIndex + i].jsValue(), DontEnum); @@ -192,14 +222,20 @@ bool Arguments::getOwnPropertyDescriptor(ExecState* exec, const Identifier& prop } if (propertyName == exec->propertyNames().length && LIKELY(!d->overrodeLength)) { - descriptor.setDescriptor(jsNumber(exec, d->numArguments), DontEnum); + descriptor.setDescriptor(jsNumber(d->numArguments), DontEnum); return true; } if (propertyName == exec->propertyNames().callee && LIKELY(!d->overrodeCallee)) { - descriptor.setDescriptor(d->callee, DontEnum); - return true; + if (!d->isStrictMode) { + descriptor.setDescriptor(d->callee, DontEnum); + return true; + } + createStrictModeCalleeIfNecessary(exec); } + + if (propertyName == exec->propertyNames().caller && d->isStrictMode) + createStrictModeCallerIfNecessary(exec); return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); } @@ -209,7 +245,7 @@ void Arguments::getOwnPropertyNames(ExecState* exec, PropertyNameArray& property if (mode == IncludeDontEnumProperties) { for (unsigned i = 0; i < d->numArguments; ++i) { if (!d->deletedArguments || !d->deletedArguments[i]) - propertyNames.add(Identifier(exec, UString::from(i))); + propertyNames.add(Identifier(exec, UString::number(i))); } propertyNames.add(exec->propertyNames().callee); propertyNames.add(exec->propertyNames().length); @@ -227,13 +263,13 @@ void Arguments::put(ExecState* exec, unsigned i, JSValue value, PutPropertySlot& return; } - JSObject::put(exec, Identifier(exec, UString::from(i)), value, slot); + JSObject::put(exec, Identifier(exec, UString::number(i)), value, slot); } void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex && i < d->numArguments && (!d->deletedArguments || !d->deletedArguments[i])) { if (i < d->numParameters) d->registers[d->firstParameterIndex + i] = JSValue(value); @@ -249,11 +285,17 @@ void Arguments::put(ExecState* exec, const Identifier& propertyName, JSValue val } if (propertyName == exec->propertyNames().callee && !d->overrodeCallee) { - d->overrodeCallee = true; - putDirect(propertyName, value, DontEnum); - return; + if (!d->isStrictMode) { + d->overrodeCallee = true; + putDirect(propertyName, value, DontEnum); + return; + } + createStrictModeCalleeIfNecessary(exec); } + if (propertyName == exec->propertyNames().caller && d->isStrictMode) + createStrictModeCallerIfNecessary(exec); + JSObject::put(exec, propertyName, value, slot); } @@ -270,13 +312,13 @@ bool Arguments::deleteProperty(ExecState* exec, unsigned i) } } - return JSObject::deleteProperty(exec, Identifier(exec, UString::from(i))); + return JSObject::deleteProperty(exec, Identifier(exec, UString::number(i))); } bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName) { bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex && i < d->numArguments) { if (!d->deletedArguments) { d->deletedArguments.set(new bool[d->numArguments]); @@ -294,9 +336,15 @@ bool Arguments::deleteProperty(ExecState* exec, const Identifier& propertyName) } if (propertyName == exec->propertyNames().callee && !d->overrodeCallee) { - d->overrodeCallee = true; - return true; + if (!d->isStrictMode) { + d->overrodeCallee = true; + return true; + } + createStrictModeCalleeIfNecessary(exec); } + + if (propertyName == exec->propertyNames().caller && !d->isStrictMode) + createStrictModeCallerIfNecessary(exec); return JSObject::deleteProperty(exec, propertyName); } diff --git a/JavaScriptCore/runtime/Arguments.h b/JavaScriptCore/runtime/Arguments.h index 9797e08..715a2ac 100644 --- a/JavaScriptCore/runtime/Arguments.h +++ b/JavaScriptCore/runtime/Arguments.h @@ -50,11 +50,17 @@ namespace JSC { JSFunction* callee; bool overrodeLength : 1; bool overrodeCallee : 1; + bool overrodeCaller : 1; + bool isStrictMode : 1; }; class Arguments : public JSObject { public: + // Use an enum because otherwise gcc insists on doing a memory + // read. + enum { MaxArguments = 0x10000 }; + enum NoParametersType { NoParameters }; Arguments(CallFrame*); @@ -101,6 +107,8 @@ namespace JSC { virtual void put(ExecState*, unsigned propertyName, JSValue, PutPropertySlot&); virtual bool deleteProperty(ExecState*, const Identifier& propertyName); virtual bool deleteProperty(ExecState*, unsigned propertyName); + void createStrictModeCallerIfNecessary(ExecState*); + void createStrictModeCalleeIfNecessary(ExecState*); virtual const ClassInfo* classInfo() const { return &info; } @@ -119,10 +127,10 @@ namespace JSC { ALWAYS_INLINE void Arguments::getArgumentsData(CallFrame* callFrame, JSFunction*& function, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc) { - function = callFrame->callee(); + function = asFunction(callFrame->callee()); int numParameters = function->jsExecutable()->parameterCount(); - argc = callFrame->argumentCount(); + argc = callFrame->argumentCountIncludingThis(); if (argc <= numParameters) argv = callFrame->registers() - RegisterFile::CallFrameHeaderSize - numParameters; @@ -135,7 +143,7 @@ namespace JSC { inline Arguments::Arguments(CallFrame* callFrame) : JSObject(callFrame->lexicalGlobalObject()->argumentsStructure()) - , d(new ArgumentsData) + , d(adoptPtr(new ArgumentsData)) { JSFunction* callee; ptrdiff_t firstParameterIndex; @@ -168,15 +176,19 @@ namespace JSC { d->callee = callee; d->overrodeLength = false; d->overrodeCallee = false; + d->overrodeCaller = false; + d->isStrictMode = callFrame->codeBlock()->isStrictMode(); + if (d->isStrictMode) + copyRegisters(); } inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) : JSObject(callFrame->lexicalGlobalObject()->argumentsStructure()) - , d(new ArgumentsData) + , d(adoptPtr(new ArgumentsData)) { - ASSERT(!callFrame->callee()->jsExecutable()->parameterCount()); + ASSERT(!asFunction(callFrame->callee())->jsExecutable()->parameterCount()); - unsigned numArguments = callFrame->argumentCount() - 1; + unsigned numArguments = callFrame->argumentCount(); d->numParameters = 0; d->numArguments = numArguments; @@ -194,9 +206,13 @@ namespace JSC { d->extraArguments = extraArguments; - d->callee = callFrame->callee(); + d->callee = asFunction(callFrame->callee()); d->overrodeLength = false; d->overrodeCallee = false; + d->overrodeCaller = false; + d->isStrictMode = callFrame->codeBlock()->isStrictMode(); + if (d->isStrictMode) + copyRegisters(); } inline void Arguments::copyRegisters() @@ -216,12 +232,12 @@ namespace JSC { } // This JSActivation function is defined here so it can get at Arguments::setRegisters. - inline void JSActivation::copyRegisters(Arguments* arguments) + inline void JSActivation::copyRegisters() { ASSERT(!d()->registerArray); - size_t numParametersMinusThis = d()->functionExecutable->generatedBytecode().m_numParameters - 1; - size_t numVars = d()->functionExecutable->generatedBytecode().m_numVars; + size_t numParametersMinusThis = d()->functionExecutable->parameterCount(); + size_t numVars = d()->functionExecutable->capturedVariableCount(); size_t numLocals = numVars + numParametersMinusThis; if (!numLocals) @@ -232,17 +248,7 @@ namespace JSC { Register* registerArray = copyRegisterArray(d()->registers - registerOffset, registerArraySize); setRegisters(registerArray + registerOffset, registerArray); - if (arguments && !arguments->isTornOff()) - static_cast<Arguments*>(arguments)->setActivation(this); - } - - ALWAYS_INLINE Arguments* Register::arguments() const - { - if (jsValue() == JSValue()) - return 0; - return asArguments(jsValue()); } - } // namespace JSC diff --git a/JavaScriptCore/runtime/ArrayConstructor.cpp b/JavaScriptCore/runtime/ArrayConstructor.cpp index fb44494..632d466 100644 --- a/JavaScriptCore/runtime/ArrayConstructor.cpp +++ b/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -26,6 +26,7 @@ #include "ArrayPrototype.h" #include "Error.h" +#include "ExceptionHelpers.h" #include "JSArray.h" #include "JSFunction.h" #include "Lookup.h" @@ -35,19 +36,19 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ArrayConstructor); -static JSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*); -ArrayConstructor::ArrayConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ArrayPrototype* arrayPrototype, Structure* prototypeFunctionStructure) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, arrayPrototype->classInfo()->className)) +ArrayConstructor::ArrayConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ArrayPrototype* arrayPrototype, Structure* prototypeFunctionStructure) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, arrayPrototype->classInfo()->className)) { // ECMA 15.4.3.1 Array.prototype putDirectWithoutTransition(exec->propertyNames().prototype, arrayPrototype, DontEnum | DontDelete | ReadOnly); // no. of arguments for constructor - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); // ES5 - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().isArray, arrayConstructorIsArray), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().isArray, arrayConstructorIsArray), DontEnum); } static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args) @@ -56,17 +57,18 @@ static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgLi if (args.size() == 1 && args.at(0).isNumber()) { uint32_t n = args.at(0).toUInt32(exec); if (n != args.at(0).toNumber(exec)) - return throwError(exec, RangeError, "Array size is not a small enough positive integer."); - return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), n); + return throwError(exec, createRangeError(exec, "Array size is not a small enough positive integer.")); + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), n, CreateInitialized); } // otherwise the array is constructed with the arguments in it return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), args); } -static JSObject* constructWithArrayConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithArrayConstructor(ExecState* exec) { - return constructArrayWithSizeQuirk(exec, args); + ArgList args(exec); + return JSValue::encode(constructArrayWithSizeQuirk(exec, args)); } // ECMA 15.4.2 @@ -76,9 +78,10 @@ ConstructType ArrayConstructor::getConstructData(ConstructData& constructData) return ConstructTypeHost; } -static JSValue JSC_HOST_CALL callArrayConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callArrayConstructor(ExecState* exec) { - return constructArrayWithSizeQuirk(exec, args); + ArgList args(exec); + return JSValue::encode(constructArrayWithSizeQuirk(exec, args)); } // ECMA 15.6.1 @@ -89,9 +92,9 @@ CallType ArrayConstructor::getCallData(CallData& callData) return CallTypeHost; } -JSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState* exec) { - return jsBoolean(args.at(0).inherits(&JSArray::info)); + return JSValue::encode(jsBoolean(exec->argument(0).inherits(&JSArray::info))); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ArrayConstructor.h b/JavaScriptCore/runtime/ArrayConstructor.h index 6d25400..5e1408f 100644 --- a/JavaScriptCore/runtime/ArrayConstructor.h +++ b/JavaScriptCore/runtime/ArrayConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class ArrayConstructor : public InternalFunction { public: - ArrayConstructor(ExecState*, NonNullPassRefPtr<Structure>, ArrayPrototype*, Structure*); + ArrayConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, ArrayPrototype*, Structure*); virtual ConstructType getConstructData(ConstructData&); virtual CallType getCallData(CallData&); diff --git a/JavaScriptCore/runtime/ArrayPrototype.cpp b/JavaScriptCore/runtime/ArrayPrototype.cpp index b64abad..ab0c3d4 100644 --- a/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -24,13 +24,13 @@ #include "config.h" #include "ArrayPrototype.h" -#include "CodeBlock.h" #include "CachedCall.h" +#include "CodeBlock.h" #include "Interpreter.h" #include "JIT.h" #include "JSStringBuilder.h" -#include "ObjectPrototype.h" #include "Lookup.h" +#include "ObjectPrototype.h" #include "Operations.h" #include <algorithm> #include <wtf/Assertions.h> @@ -40,27 +40,27 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype); -static JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState*); +static EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState*); } @@ -73,16 +73,13 @@ static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, if (callType != CallTypeJS) return false; -#if ENABLE(JIT) - // If the JIT is enabled then we need to preserve the invariant that every - // function with a CodeBlock also has JIT code. - callData.js.functionExecutable->jitCode(exec, callData.js.scopeChain); - CodeBlock& codeBlock = callData.js.functionExecutable->generatedBytecode(); -#else - CodeBlock& codeBlock = callData.js.functionExecutable->bytecode(exec, callData.js.scopeChain); -#endif + FunctionExecutable* executable = callData.js.functionExecutable; - return codeBlock.isNumericCompareFunction(); + JSObject* error = executable->compileForCall(exec, callData.js.scopeChain); + if (error) + return false; + + return executable->generatedBytecodeForCall().isNumericCompareFunction(); } // ------------------------------ ArrayPrototype ---------------------------- @@ -116,9 +113,10 @@ const ClassInfo ArrayPrototype::info = {"Array", &JSArray::info, 0, ExecState::a */ // ECMA 15.4.4 -ArrayPrototype::ArrayPrototype(NonNullPassRefPtr<Structure> structure) +ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) : JSArray(structure) { + putAnonymousValue(0, globalObject); } bool ArrayPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -148,26 +146,48 @@ static void putProperty(ExecState* exec, JSObject* obj, const Identifier& proper obj->put(exec, propertyName, value, slot); } -JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0) { + JSValue value = exec->argument(argument); + if (value.isUndefined()) + return undefinedValue; + + double indexDouble = value.toInteger(exec); + if (indexDouble < 0) { + indexDouble += length; + return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble); + } + return indexDouble > length ? length : static_cast<unsigned>(indexDouble); +} + +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); bool isRealArray = isJSArray(&exec->globalData(), thisValue); if (!isRealArray && !thisValue.inherits(&JSArray::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); JSArray* thisObj = asArray(thisValue); HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; - if (arrayVisitedElements.size() >= MaxSecondaryThreadReentryDepth) { - if (!isMainThread() || arrayVisitedElements.size() >= MaxMainThreadReentryDepth) - return throwError(exec, RangeError, "Maximum call stack size exceeded."); + if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { + if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) + return throwVMError(exec, createStackOverflowError(exec)); } bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; if (alreadyVisited) - return jsEmptyString(exec); // return an empty string, avoiding infinite recursion. + return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoiding infinite recursion. unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned totalSize = length ? length - 1 : 0; - Vector<RefPtr<UString::Rep>, 256> strBuffer(length); +#if OS(SYMBIAN) + // Symbian has very limited stack size available. + // This function could be called recursively and allocating 1K on stack here cause + // stack overflow on Symbian devices. + Vector<RefPtr<StringImpl> > strBuffer(length); +#else + Vector<RefPtr<StringImpl>, 256> strBuffer(length); +#endif for (unsigned k = 0; k < length; k++) { JSValue element; if (isRealArray && thisObj->canGetIndex(k)) @@ -179,8 +199,8 @@ JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue continue; UString str = element.toString(exec); - strBuffer[k] = str.rep(); - totalSize += str.size(); + strBuffer[k] = str.impl(); + totalSize += str.length(); if (!strBuffer.data()) { throwOutOfMemoryError(exec); @@ -191,37 +211,38 @@ JSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec, JSObject*, JSValue } arrayVisitedElements.remove(thisObj); if (!totalSize) - return jsEmptyString(exec); + return JSValue::encode(jsEmptyString(exec)); Vector<UChar> buffer; buffer.reserveCapacity(totalSize); if (!buffer.data()) - return throwOutOfMemoryError(exec); + return JSValue::encode(throwOutOfMemoryError(exec)); for (unsigned i = 0; i < length; i++) { if (i) buffer.append(','); - if (RefPtr<UString::Rep> rep = strBuffer[i]) - buffer.append(rep->data(), rep->size()); + if (RefPtr<StringImpl> rep = strBuffer[i]) + buffer.append(rep->characters(), rep->length()); } ASSERT(buffer.size() == totalSize); - return jsString(exec, UString::adopt(buffer)); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); } -JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&JSArray::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); JSObject* thisObj = asArray(thisValue); HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; - if (arrayVisitedElements.size() >= MaxSecondaryThreadReentryDepth) { - if (!isMainThread() || arrayVisitedElements.size() >= MaxMainThreadReentryDepth) - return throwError(exec, RangeError, "Maximum call stack size exceeded."); + if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { + if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) + return throwVMError(exec, createStackOverflowError(exec)); } bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; if (alreadyVisited) - return jsEmptyString(exec); // return an empty string, avoding infinite recursion. + return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion. JSStringBuilder strBuffer; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); @@ -235,7 +256,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec, JSObject*, J JSValue conversionFunction = o->get(exec, exec->propertyNames().toLocaleString); UString str; CallData callData; - CallType callType = conversionFunction.getCallData(callData); + CallType callType = getCallData(conversionFunction, callData); if (callType != CallTypeNone) str = call(exec, conversionFunction, callType, callData, element, exec->emptyList()).toString(exec); else @@ -244,31 +265,65 @@ JSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec, JSObject*, J } } arrayVisitedElements.remove(thisObj); - return strBuffer.build(exec); + return JSValue::encode(strBuffer.build(exec)); } -JSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements; - if (arrayVisitedElements.size() >= MaxSecondaryThreadReentryDepth) { - if (!isMainThread() || arrayVisitedElements.size() >= MaxMainThreadReentryDepth) - return throwError(exec, RangeError, "Maximum call stack size exceeded."); + if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) { + if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth) + return throwVMError(exec, createStackOverflowError(exec)); } bool alreadyVisited = !arrayVisitedElements.add(thisObj).second; if (alreadyVisited) - return jsEmptyString(exec); // return an empty string, avoding infinite recursion. + return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion. JSStringBuilder strBuffer; UString separator; - if (!args.at(0).isUndefined()) - separator = args.at(0).toString(exec); + if (!exec->argument(0).isUndefined()) + separator = exec->argument(0).toString(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - for (unsigned k = 0; k < length; k++) { + unsigned k = 0; + if (isJSArray(&exec->globalData(), thisObj)) { + JSArray* array = asArray(thisObj); + + if (length) { + if (!array->canGetIndex(k)) + goto skipFirstLoop; + JSValue element = array->getIndex(k); + if (!element.isUndefinedOrNull()) + strBuffer.append(element.toString(exec)); + k++; + } + + if (separator.isNull()) { + for (; k < length; k++) { + if (!array->canGetIndex(k)) + break; + strBuffer.append(','); + JSValue element = array->getIndex(k); + if (!element.isUndefinedOrNull()) + strBuffer.append(element.toString(exec)); + } + } else { + for (; k < length; k++) { + if (!array->canGetIndex(k)) + break; + strBuffer.append(separator); + JSValue element = array->getIndex(k); + if (!element.isUndefinedOrNull()) + strBuffer.append(element.toString(exec)); + } + } + } + skipFirstLoop: + for (; k < length; k++) { if (k >= 1) { if (separator.isNull()) strBuffer.append(','); @@ -281,16 +336,17 @@ JSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec, JSObject*, JSValue thi strBuffer.append(element.toString(exec)); } arrayVisitedElements.remove(thisObj); - return strBuffer.build(exec); + return JSValue::encode(strBuffer.build(exec)); } -JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); JSArray* arr = constructEmptyArray(exec); - int n = 0; + unsigned n = 0; JSValue curArg = thisValue.toThisObject(exec); - ArgList::const_iterator it = args.begin(); - ArgList::const_iterator end = args.end(); + size_t i = 0; + size_t argCount = exec->argumentCount(); while (1) { if (curArg.inherits(&JSArray::info)) { unsigned length = curArg.get(exec, exec->propertyNames().length).toUInt32(exec); @@ -304,54 +360,56 @@ JSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec, JSObject*, JSValue t arr->put(exec, n, curArg); n++; } - if (it == end) + if (i == argCount) break; - curArg = (*it); - ++it; + curArg = (exec->argument(i)); + ++i; } arr->setLength(n); - return arr; + return JSValue::encode(arr); } -JSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (isJSArray(&exec->globalData(), thisValue)) - return asArray(thisValue)->pop(); + return JSValue::encode(asArray(thisValue)->pop()); JSObject* thisObj = thisValue.toThisObject(exec); JSValue result; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (length == 0) { - putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length)); + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); result = jsUndefined(); } else { result = thisObj->get(exec, length - 1); thisObj->deleteProperty(exec, length - 1); - putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1)); + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); } - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) { - if (isJSArray(&exec->globalData(), thisValue) && args.size() == 1) { + JSValue thisValue = exec->hostThisValue(); + if (isJSArray(&exec->globalData(), thisValue) && exec->argumentCount() == 1) { JSArray* array = asArray(thisValue); - array->push(exec, *args.begin()); - return jsNumber(exec, array->length()); + array->push(exec, exec->argument(0)); + return JSValue::encode(jsNumber(array->length())); } JSObject* thisObj = thisValue.toThisObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - for (unsigned n = 0; n < args.size(); n++) - thisObj->put(exec, length + n, args.at(n)); - length += args.size(); - putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length)); - return jsNumber(exec, length); + for (unsigned n = 0; n < exec->argumentCount(); n++) + thisObj->put(exec, length + n, exec->argument(n)); + length += exec->argumentCount(); + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); + return JSValue::encode(jsNumber(length)); } -JSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned middle = length / 2; @@ -370,84 +428,65 @@ JSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec, JSObject*, JSValue else thisObj->deleteProperty(exec, lk1); } - return thisObj; + return JSValue::encode(thisObj); } -JSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue result; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (length == 0) { - putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length)); + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length)); result = jsUndefined(); } else { result = thisObj->get(exec, 0); - for (unsigned k = 1; k < length; k++) { - if (JSValue obj = getProperty(exec, thisObj, k)) - thisObj->put(exec, k - 1, obj); - else - thisObj->deleteProperty(exec, k - 1); + if (isJSArray(&exec->globalData(), thisObj)) + ((JSArray *)thisObj)->shiftCount(exec, 1); + else { + for (unsigned k = 1; k < length; k++) { + if (JSValue obj = getProperty(exec, thisObj, k)) + thisObj->put(exec, k - 1, obj); + else + thisObj->deleteProperty(exec, k - 1); + } + thisObj->deleteProperty(exec, length - 1); } - thisObj->deleteProperty(exec, length - 1); - putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1)); + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); } - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) { // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10 - - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); // We return a new array JSArray* resObj = constructEmptyArray(exec); JSValue result = resObj; - double begin = args.at(0).toInteger(exec); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - if (begin >= 0) { - if (begin > length) - begin = length; - } else { - begin += length; - if (begin < 0) - begin = 0; - } - double end; - if (args.at(1).isUndefined()) - end = length; - else { - end = args.at(1).toInteger(exec); - if (end < 0) { - end += length; - if (end < 0) - end = 0; - } else { - if (end > length) - end = length; - } - } + unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); + unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length); - int n = 0; - int b = static_cast<int>(begin); - int e = static_cast<int>(end); - for (int k = b; k < e; k++, n++) { + unsigned n = 0; + for (unsigned k = begin; k < end; k++, n++) { if (JSValue v = getProperty(exec, thisObj, k)) resObj->put(exec, n, v); } resObj->setLength(n); - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (thisObj->classInfo() == &JSArray::info) { if (isNumericCompareFunction(exec, callType, callData)) @@ -456,13 +495,13 @@ JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec, JSObject*, JSValue thi asArray(thisObj)->sort(exec, function, callType, callData); else asArray(thisObj)->sort(exec); - return thisObj; + return JSValue::encode(thisObj); } unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (!length) - return thisObj; + return JSValue::encode(thisObj); // "Min" sort. Not the fastest, but definitely less code than heapsort // or quicksort, and much less swapping than bubblesort/insertionsort. @@ -496,97 +535,112 @@ JSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec, JSObject*, JSValue thi thisObj->put(exec, themin, iObj); } } - return thisObj; + return JSValue::encode(thisObj); } -JSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); // 15.4.4.12 - JSArray* resObj = constructEmptyArray(exec); - JSValue result = resObj; + + if (!exec->argumentCount()) + return JSValue::encode(constructEmptyArray(exec)); + unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - if (!args.size()) - return jsUndefined(); - int begin = args.at(0).toUInt32(exec); - if (begin < 0) - begin = std::max<int>(begin + length, 0); - else - begin = std::min<int>(begin, length); - - unsigned deleteCount; - if (args.size() > 1) - deleteCount = std::min<int>(std::max<int>(args.at(1).toUInt32(exec), 0), length - begin); - else - deleteCount = length - begin; - - for (unsigned k = 0; k < deleteCount; k++) { - if (JSValue v = getProperty(exec, thisObj, k + begin)) - resObj->put(exec, k, v); + unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); + + unsigned deleteCount = length - begin; + if (exec->argumentCount() > 1) { + double deleteDouble = exec->argument(1).toInteger(exec); + if (deleteDouble < 0) + deleteCount = 0; + else if (deleteDouble > length - begin) + deleteCount = length - begin; + else + deleteCount = static_cast<unsigned>(deleteDouble); } + + JSArray* resObj = new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), deleteCount, CreateCompact); + JSValue result = resObj; + + for (unsigned k = 0; k < deleteCount; k++) + resObj->uncheckedSetIndex(k, getProperty(exec, thisObj, k + begin)); + resObj->setLength(deleteCount); - unsigned additionalArgs = std::max<int>(args.size() - 2, 0); + unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0); if (additionalArgs != deleteCount) { if (additionalArgs < deleteCount) { - for (unsigned k = begin; k < length - deleteCount; ++k) { - if (JSValue v = getProperty(exec, thisObj, k + deleteCount)) - thisObj->put(exec, k + additionalArgs, v); - else - thisObj->deleteProperty(exec, k + additionalArgs); + if ((!begin) && (isJSArray(&exec->globalData(), thisObj))) + ((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs); + else { + for (unsigned k = begin; k < length - deleteCount; ++k) { + if (JSValue v = getProperty(exec, thisObj, k + deleteCount)) + thisObj->put(exec, k + additionalArgs, v); + else + thisObj->deleteProperty(exec, k + additionalArgs); + } + for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) + thisObj->deleteProperty(exec, k - 1); } - for (unsigned k = length; k > length - deleteCount + additionalArgs; --k) - thisObj->deleteProperty(exec, k - 1); } else { - for (unsigned k = length - deleteCount; (int)k > begin; --k) { - if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1)) - thisObj->put(exec, k + additionalArgs - 1, obj); - else - thisObj->deleteProperty(exec, k + additionalArgs - 1); + if ((!begin) && (isJSArray(&exec->globalData(), thisObj))) + ((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount); + else { + for (unsigned k = length - deleteCount; k > begin; --k) { + if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1)) + thisObj->put(exec, k + additionalArgs - 1, obj); + else + thisObj->deleteProperty(exec, k + additionalArgs - 1); + } } } } for (unsigned k = 0; k < additionalArgs; ++k) - thisObj->put(exec, k + begin, args.at(k + 2)); + thisObj->put(exec, k + begin, exec->argument(k + 2)); - putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - deleteCount + additionalArgs)); - return result; + putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - deleteCount + additionalArgs)); + return JSValue::encode(result); } -JSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); // 15.4.4.13 unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - unsigned nrArgs = args.size(); - if (nrArgs) { - for (unsigned k = length; k > 0; --k) { - if (JSValue v = getProperty(exec, thisObj, k - 1)) - thisObj->put(exec, k + nrArgs - 1, v); - else - thisObj->deleteProperty(exec, k + nrArgs - 1); + unsigned nrArgs = exec->argumentCount(); + if ((nrArgs) && (length)) { + if (isJSArray(&exec->globalData(), thisObj)) + ((JSArray *)thisObj)->unshiftCount(exec, nrArgs); + else { + for (unsigned k = length; k > 0; --k) { + if (JSValue v = getProperty(exec, thisObj, k - 1)) + thisObj->put(exec, k + nrArgs - 1, v); + else + thisObj->deleteProperty(exec, k + nrArgs - 1); + } } } for (unsigned k = 0; k < nrArgs; ++k) - thisObj->put(exec, k, args.at(k)); - JSValue result = jsNumber(exec, length + nrArgs); + thisObj->put(exec, k, exec->argument(k)); + JSValue result = jsNumber(length + nrArgs); putProperty(exec, thisObj, exec->propertyNames().length, result); - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec); + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); JSArray* resultArray = constructEmptyArray(exec); unsigned filterIndex = 0; @@ -595,14 +649,14 @@ JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValue t if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); - CachedCall cachedCall(exec, f, 3, exec->exceptionSlot()); + CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (!array->canGetIndex(k)) break; JSValue v = array->getIndex(k); cachedCall.setThis(applyThis); cachedCall.setArgument(0, v); - cachedCall.setArgument(1, jsNumber(exec, k)); + cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); JSValue result = cachedCall.call(); @@ -610,7 +664,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValue t resultArray->put(exec, filterIndex++, v); } if (k == length) - return resultArray; + return JSValue::encode(resultArray); } for (; k < length && !exec->hadException(); ++k) { PropertySlot slot(thisObj); @@ -623,7 +677,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValue t MarkedArgumentBuffer eachArguments; eachArguments.append(v); - eachArguments.append(jsNumber(exec, k)); + eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); @@ -631,20 +685,20 @@ JSValue JSC_HOST_CALL arrayProtoFuncFilter(ExecState* exec, JSObject*, JSValue t if (result.toBoolean(exec)) resultArray->put(exec, filterIndex++, v); } - return resultArray; + return JSValue::encode(resultArray); } -JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec); + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); @@ -653,14 +707,14 @@ JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec, JSObject*, JSValue this if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); - CachedCall cachedCall(exec, f, 3, exec->exceptionSlot()); + CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); - cachedCall.setArgument(1, jsNumber(exec, k)); + cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); resultArray->JSArray::put(exec, k, cachedCall.call()); @@ -676,14 +730,14 @@ JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec, JSObject*, JSValue this MarkedArgumentBuffer eachArguments; eachArguments.append(v); - eachArguments.append(jsNumber(exec, k)); + eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); JSValue result = call(exec, function, callType, callData, applyThis, eachArguments); resultArray->put(exec, k, result); } - return resultArray; + return JSValue::encode(resultArray); } // Documentation for these three is available at: @@ -691,17 +745,17 @@ JSValue JSC_HOST_CALL arrayProtoFuncMap(ExecState* exec, JSObject*, JSValue this // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach // http://developer-test.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:some -JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec); + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); JSValue result = jsBoolean(true); @@ -710,18 +764,18 @@ JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue th if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); - CachedCall cachedCall(exec, f, 3, exec->exceptionSlot()); + CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); - cachedCall.setArgument(1, jsNumber(exec, k)); + cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); JSValue result = cachedCall.call(); if (!result.toBoolean(cachedCall.newCallFrame(exec))) - return jsBoolean(false); + return JSValue::encode(jsBoolean(false)); } } for (; k < length && !exec->hadException(); ++k) { @@ -733,7 +787,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue th MarkedArgumentBuffer eachArguments; eachArguments.append(slot.getValue(exec, k)); - eachArguments.append(jsNumber(exec, k)); + eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); @@ -744,34 +798,34 @@ JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue th } } - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec); + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); unsigned k = 0; if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); - CachedCall cachedCall(exec, f, 3, exec->exceptionSlot()); + CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); - cachedCall.setArgument(1, jsNumber(exec, k)); + cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); cachedCall.call(); @@ -784,25 +838,25 @@ JSValue JSC_HOST_CALL arrayProtoFuncForEach(ExecState* exec, JSObject*, JSValue MarkedArgumentBuffer eachArguments; eachArguments.append(slot.getValue(exec, k)); - eachArguments.append(jsNumber(exec, k)); + eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); call(exec, function, callType, callData, applyThis, eachArguments); } - return jsUndefined(); + return JSValue::encode(jsUndefined()); } -JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - JSObject* applyThis = args.at(1).isUndefinedOrNull() ? exec->globalThisValue() : args.at(1).toObject(exec); + JSObject* applyThis = exec->argument(1).isUndefinedOrNull() ? exec->globalThisValue() : exec->argument(1).toObject(exec); JSValue result = jsBoolean(false); @@ -811,18 +865,18 @@ JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thi if (callType == CallTypeJS && isJSArray(&exec->globalData(), thisObj)) { JSFunction* f = asFunction(function); JSArray* array = asArray(thisObj); - CachedCall cachedCall(exec, f, 3, exec->exceptionSlot()); + CachedCall cachedCall(exec, f, 3); for (; k < length && !exec->hadException(); ++k) { if (UNLIKELY(!array->canGetIndex(k))) break; cachedCall.setThis(applyThis); cachedCall.setArgument(0, array->getIndex(k)); - cachedCall.setArgument(1, jsNumber(exec, k)); + cachedCall.setArgument(1, jsNumber(k)); cachedCall.setArgument(2, thisObj); JSValue result = cachedCall.call(); if (result.toBoolean(cachedCall.newCallFrame(exec))) - return jsBoolean(true); + return JSValue::encode(jsBoolean(true)); } } for (; k < length && !exec->hadException(); ++k) { @@ -832,7 +886,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thi MarkedArgumentBuffer eachArguments; eachArguments.append(slot.getValue(exec, k)); - eachArguments.append(jsNumber(exec, k)); + eachArguments.append(jsNumber(k)); eachArguments.append(thisObj); bool predicateResult = call(exec, function, callType, callData, applyThis, eachArguments).toBoolean(exec); @@ -842,30 +896,30 @@ JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thi break; } } - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); unsigned i = 0; JSValue rv; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - if (!length && args.size() == 1) - return throwError(exec, TypeError); + if (!length && exec->argumentCount() == 1) + return throwVMTypeError(exec); JSArray* array = 0; if (isJSArray(&exec->globalData(), thisObj)) array = asArray(thisObj); - if (args.size() >= 2) - rv = args.at(1); + if (exec->argumentCount() >= 2) + rv = exec->argument(1); else if (array && array->canGetIndex(0)){ rv = array->getIndex(0); i = 1; @@ -876,12 +930,12 @@ JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec, JSObject*, JSValue t break; } if (!rv) - return throwError(exec, TypeError); + return throwVMTypeError(exec); i++; } if (callType == CallTypeJS && array) { - CachedCall cachedCall(exec, asFunction(function), 4, exec->exceptionSlot()); + CachedCall cachedCall(exec, asFunction(function), 4); for (; i < length && !exec->hadException(); ++i) { cachedCall.setThis(jsNull()); cachedCall.setArgument(0, rv); @@ -891,12 +945,12 @@ JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec, JSObject*, JSValue t else break; // length has been made unsafe while we enumerate fallback to slow path cachedCall.setArgument(1, v); - cachedCall.setArgument(2, jsNumber(exec, i)); + cachedCall.setArgument(2, jsNumber(i)); cachedCall.setArgument(3, array); rv = cachedCall.call(); } if (i == length) // only return if we reached the end of the array - return rv; + return JSValue::encode(rv); } for (; i < length && !exec->hadException(); ++i) { @@ -907,35 +961,35 @@ JSValue JSC_HOST_CALL arrayProtoFuncReduce(ExecState* exec, JSObject*, JSValue t MarkedArgumentBuffer eachArguments; eachArguments.append(rv); eachArguments.append(prop); - eachArguments.append(jsNumber(exec, i)); + eachArguments.append(jsNumber(i)); eachArguments.append(thisObj); rv = call(exec, function, callType, callData, jsNull(), eachArguments); } - return rv; + return JSValue::encode(rv); } -JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSValue function = args.at(0); + JSValue function = exec->argument(0); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); unsigned i = 0; JSValue rv; unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - if (!length && args.size() == 1) - return throwError(exec, TypeError); + if (!length && exec->argumentCount() == 1) + return throwVMTypeError(exec); JSArray* array = 0; if (isJSArray(&exec->globalData(), thisObj)) array = asArray(thisObj); - if (args.size() >= 2) - rv = args.at(1); + if (exec->argumentCount() >= 2) + rv = exec->argument(1); else if (array && array->canGetIndex(length - 1)){ rv = array->getIndex(length - 1); i = 1; @@ -946,12 +1000,12 @@ JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec, JSObject*, JSVa break; } if (!rv) - return throwError(exec, TypeError); + return throwVMTypeError(exec); i++; } if (callType == CallTypeJS && array) { - CachedCall cachedCall(exec, asFunction(function), 4, exec->exceptionSlot()); + CachedCall cachedCall(exec, asFunction(function), 4); for (; i < length && !exec->hadException(); ++i) { unsigned idx = length - i - 1; cachedCall.setThis(jsNull()); @@ -959,12 +1013,12 @@ JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec, JSObject*, JSVa if (UNLIKELY(!array->canGetIndex(idx))) break; // length has been made unsafe while we enumerate fallback to slow path cachedCall.setArgument(1, array->getIndex(idx)); - cachedCall.setArgument(2, jsNumber(exec, idx)); + cachedCall.setArgument(2, jsNumber(idx)); cachedCall.setArgument(3, array); rv = cachedCall.call(); } if (i == length) // only return if we reached the end of the array - return rv; + return JSValue::encode(rv); } for (; i < length && !exec->hadException(); ++i) { @@ -976,74 +1030,69 @@ JSValue JSC_HOST_CALL arrayProtoFuncReduceRight(ExecState* exec, JSObject*, JSVa MarkedArgumentBuffer eachArguments; eachArguments.append(rv); eachArguments.append(prop); - eachArguments.append(jsNumber(exec, idx)); + eachArguments.append(jsNumber(idx)); eachArguments.append(thisObj); rv = call(exec, function, callType, callData, jsNull(), eachArguments); } - return rv; + return JSValue::encode(rv); } -JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec) { // JavaScript 1.5 Extension by Mozilla // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); - JSObject* thisObj = thisValue.toThisObject(exec); - - unsigned index = 0; - double d = args.at(1).toInteger(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - if (d < 0) - d += length; - if (d > 0) { - if (d > length) - index = length; - else - index = static_cast<unsigned>(d); - } + unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); - JSValue searchElement = args.at(0); + JSValue searchElement = exec->argument(0); for (; index < length; ++index) { JSValue e = getProperty(exec, thisObj, index); if (!e) continue; if (JSValue::strictEqual(exec, searchElement, e)) - return jsNumber(exec, index); + return JSValue::encode(jsNumber(index)); } - return jsNumber(exec, -1); + return JSValue::encode(jsNumber(-1)); } -JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec) { // JavaScript 1.6 Extension by Mozilla // Documentation: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf - - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); - int index = length - 1; - double d = args.at(1).toIntegerPreserveNaN(exec); - - if (d < 0) { - d += length; - if (d < 0) - return jsNumber(exec, -1); + if (!length) + return JSValue::encode(jsNumber(-1)); + + unsigned index = length - 1; + JSValue fromValue = exec->argument(1); + if (!fromValue.isUndefined()) { + double fromDouble = fromValue.toInteger(exec); + if (fromDouble < 0) { + fromDouble += length; + if (fromDouble < 0) + return JSValue::encode(jsNumber(-1)); + } + if (fromDouble < length) + index = static_cast<unsigned>(fromDouble); } - if (d < length) - index = static_cast<int>(d); - JSValue searchElement = args.at(0); - for (; index >= 0; --index) { + JSValue searchElement = exec->argument(0); + do { + ASSERT(index < length); JSValue e = getProperty(exec, thisObj, index); if (!e) continue; if (JSValue::strictEqual(exec, searchElement, e)) - return jsNumber(exec, index); - } + return JSValue::encode(jsNumber(index)); + } while (index--); - return jsNumber(exec, -1); + return JSValue::encode(jsNumber(-1)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ArrayPrototype.h b/JavaScriptCore/runtime/ArrayPrototype.h index e52914c..42665e3 100644 --- a/JavaScriptCore/runtime/ArrayPrototype.h +++ b/JavaScriptCore/runtime/ArrayPrototype.h @@ -28,13 +28,21 @@ namespace JSC { class ArrayPrototype : public JSArray { public: - explicit ArrayPrototype(NonNullPassRefPtr<Structure>); + explicit ArrayPrototype(JSGlobalObject*, NonNullPassRefPtr<Structure>); bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; + + static PassRefPtr<Structure> createStructure(JSValue prototype) + { + return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + protected: + static const unsigned AnonymousSlotCount = JSArray::AnonymousSlotCount + 1; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/BooleanConstructor.cpp b/JavaScriptCore/runtime/BooleanConstructor.cpp index b0d8df3..0167e03 100644 --- a/JavaScriptCore/runtime/BooleanConstructor.cpp +++ b/JavaScriptCore/runtime/BooleanConstructor.cpp @@ -28,13 +28,13 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(BooleanConstructor); -BooleanConstructor::BooleanConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, BooleanPrototype* booleanPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, booleanPrototype->classInfo()->className)) +BooleanConstructor::BooleanConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, BooleanPrototype* booleanPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, booleanPrototype->classInfo()->className)) { putDirectWithoutTransition(exec->propertyNames().prototype, booleanPrototype, DontEnum | DontDelete | ReadOnly); // no. of arguments for constructor - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontDelete | DontEnum); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); } // ECMA 15.6.2 @@ -45,9 +45,10 @@ JSObject* constructBoolean(ExecState* exec, const ArgList& args) return obj; } -static JSObject* constructWithBooleanConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithBooleanConstructor(ExecState* exec) { - return constructBoolean(exec, args); + ArgList args(exec); + return JSValue::encode(constructBoolean(exec, args)); } ConstructType BooleanConstructor::getConstructData(ConstructData& constructData) @@ -57,9 +58,9 @@ ConstructType BooleanConstructor::getConstructData(ConstructData& constructData) } // ECMA 15.6.1 -static JSValue JSC_HOST_CALL callBooleanConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callBooleanConstructor(ExecState* exec) { - return jsBoolean(args.at(0).toBoolean(exec)); + return JSValue::encode(jsBoolean(exec->argument(0).toBoolean(exec))); } CallType BooleanConstructor::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/BooleanConstructor.h b/JavaScriptCore/runtime/BooleanConstructor.h index 1d8a26a..0f3efa7 100644 --- a/JavaScriptCore/runtime/BooleanConstructor.h +++ b/JavaScriptCore/runtime/BooleanConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class BooleanConstructor : public InternalFunction { public: - BooleanConstructor(ExecState*, NonNullPassRefPtr<Structure>, BooleanPrototype*); + BooleanConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, BooleanPrototype*); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/BooleanPrototype.cpp b/JavaScriptCore/runtime/BooleanPrototype.cpp index 8d338f9..7ffd095 100644 --- a/JavaScriptCore/runtime/BooleanPrototype.cpp +++ b/JavaScriptCore/runtime/BooleanPrototype.cpp @@ -22,6 +22,7 @@ #include "BooleanPrototype.h" #include "Error.h" +#include "ExceptionHelpers.h" #include "JSFunction.h" #include "JSString.h" #include "ObjectPrototype.h" @@ -32,18 +33,18 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(BooleanPrototype); // Functions -static JSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState*); // ECMA 15.6.4 -BooleanPrototype::BooleanPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) +BooleanPrototype::BooleanPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) : BooleanObject(structure) { setInternalValue(jsBoolean(false)); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, booleanProtoFuncToString), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, booleanProtoFuncValueOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, booleanProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, booleanProtoFuncValueOf), DontEnum); } @@ -51,33 +52,35 @@ BooleanPrototype::BooleanPrototype(ExecState* exec, NonNullPassRefPtr<Structure> // ECMA 15.6.4.2 + 15.6.4.3 -JSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL booleanProtoFuncToString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (thisValue == jsBoolean(false)) - return jsNontrivialString(exec, "false"); + return JSValue::encode(jsNontrivialString(exec, "false")); if (thisValue == jsBoolean(true)) - return jsNontrivialString(exec, "true"); + return JSValue::encode(jsNontrivialString(exec, "true")); if (!thisValue.inherits(&BooleanObject::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); if (asBooleanObject(thisValue)->internalValue() == jsBoolean(false)) - return jsNontrivialString(exec, "false"); + return JSValue::encode(jsNontrivialString(exec, "false")); ASSERT(asBooleanObject(thisValue)->internalValue() == jsBoolean(true)); - return jsNontrivialString(exec, "true"); + return JSValue::encode(jsNontrivialString(exec, "true")); } -JSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL booleanProtoFuncValueOf(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (thisValue.isBoolean()) - return thisValue; + return JSValue::encode(thisValue); if (!thisValue.inherits(&BooleanObject::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - return asBooleanObject(thisValue)->internalValue(); + return JSValue::encode(asBooleanObject(thisValue)->internalValue()); } } // namespace JSC diff --git a/JavaScriptCore/runtime/BooleanPrototype.h b/JavaScriptCore/runtime/BooleanPrototype.h index cc69b3f..ddadc43 100644 --- a/JavaScriptCore/runtime/BooleanPrototype.h +++ b/JavaScriptCore/runtime/BooleanPrototype.h @@ -27,7 +27,7 @@ namespace JSC { class BooleanPrototype : public BooleanObject { public: - BooleanPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + BooleanPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/CachedTranscendentalFunction.h b/JavaScriptCore/runtime/CachedTranscendentalFunction.h new file mode 100644 index 0000000..67c7af8 --- /dev/null +++ b/JavaScriptCore/runtime/CachedTranscendentalFunction.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 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 CachedTranscendentalFunction_h +#define CachedTranscendentalFunction_h + +#include "JSValue.h" + +namespace JSC { + +extern const double NaN; + +typedef double (*TranscendentalFunctionPtr)(double); + +// CachedTranscendentalFunction provides a generic mechanism to cache results +// for pure functions with the signature "double func(double)", and where NaN +// maps to NaN. +template<TranscendentalFunctionPtr orignalFunction> +class CachedTranscendentalFunction { + struct CacheEntry { + double operand; + double result; + }; + +public: + CachedTranscendentalFunction() + : m_cache(0) + { + } + + ~CachedTranscendentalFunction() + { + if (m_cache) + fastFree(m_cache); + } + + JSValue operator() (double operand) + { + if (UNLIKELY(!m_cache)) + initialize(); + CacheEntry* entry = &m_cache[hash(operand)]; + + if (entry->operand == operand) + return jsDoubleNumber(entry->result); + double result = orignalFunction(operand); + entry->operand = operand; + entry->result = result; + return jsDoubleNumber(result); + } + +private: + void initialize() + { + // Lazily allocate the table, populate with NaN->NaN mapping. + m_cache = static_cast<CacheEntry*>(fastMalloc(s_cacheSize * sizeof(CacheEntry))); + for (unsigned x = 0; x < s_cacheSize; ++x) { + m_cache[x].operand = NaN; + m_cache[x].result = NaN; + } + } + + static unsigned hash(double d) + { + union doubleAndUInt64 { + double d; + uint32_t is[2]; + } u; + u.d = d; + + unsigned x = u.is[0] ^ u.is[1]; + x = (x >> 20) ^ (x >> 8); + return x & (s_cacheSize - 1); + } + + static const unsigned s_cacheSize = 0x1000; + CacheEntry* m_cache; +}; + +} + +#endif // CachedTranscendentalFunction_h diff --git a/JavaScriptCore/runtime/CallData.cpp b/JavaScriptCore/runtime/CallData.cpp index 62e42fe..018e2ca 100644 --- a/JavaScriptCore/runtime/CallData.cpp +++ b/JavaScriptCore/runtime/CallData.cpp @@ -26,17 +26,16 @@ #include "config.h" #include "CallData.h" +#include "Executable.h" +#include "Interpreter.h" #include "JSFunction.h" namespace JSC { JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) { - if (callType == CallTypeHost) - return callData.native.function(exec, asObject(functionObject), thisValue, args); - ASSERT(callType == CallTypeJS); - // FIXME: Can this be done more efficiently using the callData? - return asFunction(functionObject)->call(exec, thisValue, args); + ASSERT(callType == CallTypeJS || callType == CallTypeHost); + return exec->interpreter()->executeCall(exec, asObject(functionObject), callType, callData, thisValue, args); } } // namespace JSC diff --git a/JavaScriptCore/runtime/CallData.h b/JavaScriptCore/runtime/CallData.h index 24c19f9..32e1094 100644 --- a/JavaScriptCore/runtime/CallData.h +++ b/JavaScriptCore/runtime/CallData.h @@ -29,6 +29,7 @@ #ifndef CallData_h #define CallData_h +#include "JSValue.h" #include "NativeFunctionWrapper.h" namespace JSC { @@ -37,7 +38,6 @@ namespace JSC { class ExecState; class FunctionExecutable; class JSObject; - class JSValue; class ScopeChainNode; enum CallType { @@ -46,7 +46,7 @@ namespace JSC { CallTypeJS }; - typedef JSValue (JSC_HOST_CALL *NativeFunction)(ExecState*, JSObject*, JSValue thisValue, const ArgList&); + typedef EncodedJSValue (JSC_HOST_CALL *NativeFunction)(ExecState*); union CallData { struct { diff --git a/JavaScriptCore/runtime/Collector.cpp b/JavaScriptCore/runtime/Collector.cpp index 2873e0b..3fbd278 100644 --- a/JavaScriptCore/runtime/Collector.cpp +++ b/JavaScriptCore/runtime/Collector.cpp @@ -25,6 +25,7 @@ #include "CallFrame.h" #include "CodeBlock.h" #include "CollectorHeapIterator.h" +#include "GCActivityCallback.h" #include "Interpreter.h" #include "JSArray.h" #include "JSGlobalObject.h" @@ -42,6 +43,7 @@ #include <stdlib.h> #include <wtf/FastMalloc.h> #include <wtf/HashCountedSet.h> +#include <wtf/WTFThreadData.h> #include <wtf/UnusedParam.h> #include <wtf/VMTags.h> @@ -53,11 +55,6 @@ #include <mach/thread_act.h> #include <mach/vm_map.h> -#elif OS(SYMBIAN) -#include <e32std.h> -#include <e32cmn.h> -#include <unistd.h> - #elif OS(WINDOWS) #include <windows.h> @@ -109,11 +106,6 @@ const size_t ALLOCATIONS_PER_COLLECTION = 3600; // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. #define MIN_ARRAY_SIZE (static_cast<size_t>(14)) -#if OS(SYMBIAN) -const size_t MAX_NUM_BLOCKS = 256; // Max size of collector heap set to 16 MB -static RHeap* userChunk = 0; -#endif - #if ENABLE(JSC_MULTIPLE_THREADS) #if OS(DARWIN) @@ -148,29 +140,10 @@ Heap::Heap(JSGlobalData* globalData) , m_globalData(globalData) { ASSERT(globalData); - -#if OS(SYMBIAN) - // Symbian OpenC supports mmap but currently not the MAP_ANON flag. - // Using fastMalloc() does not properly align blocks on 64k boundaries - // and previous implementation was flawed/incomplete. - // UserHeap::ChunkHeap allows allocation of continuous memory and specification - // of alignment value for (symbian) cells within that heap. - // - // Clarification and mapping of terminology: - // RHeap (created by UserHeap::ChunkHeap below) is continuos memory chunk, - // which can dynamically grow up to 8 MB, - // that holds all CollectorBlocks of this session (static). - // Each symbian cell within RHeap maps to a 64kb aligned CollectorBlock. - // JSCell objects are maintained as usual within CollectorBlocks. - if (!userChunk) { - userChunk = UserHeap::ChunkHeap(0, 0, MAX_NUM_BLOCKS * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); - if (!userChunk) - CRASH(); - } -#endif // OS(SYMBIAN) - memset(&m_heap, 0, sizeof(CollectorHeap)); allocateBlock(); + m_activityCallback = DefaultGCActivityCallback::create(this); + (*m_activityCallback)(); } Heap::~Heap() @@ -198,6 +171,9 @@ void Heap::destroy() freeBlocks(); + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + m_weakGCHandlePools[i].deallocate(); + #if ENABLE(JSC_MULTIPLE_THREADS) if (m_currentThreadRegistrar) { int error = pthread_key_delete(m_currentThreadRegistrar); @@ -211,82 +187,38 @@ void Heap::destroy() t = next; } #endif - + m_blockallocator.destroy(); m_globalData = 0; } NEVER_INLINE CollectorBlock* Heap::allocateBlock() { -#if OS(DARWIN) - vm_address_t address = 0; - vm_map(current_task(), &address, BLOCK_SIZE, BLOCK_OFFSET_MASK, VM_FLAGS_ANYWHERE | VM_TAG_FOR_COLLECTOR_MEMORY, MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT); -#elif OS(SYMBIAN) - // Allocate a 64 kb aligned CollectorBlock - unsigned char* mask = reinterpret_cast<unsigned char*>(userChunk->Alloc(BLOCK_SIZE)); - if (!mask) + AlignedCollectorBlock allocation = m_blockallocator.allocate(); + CollectorBlock* block = static_cast<CollectorBlock*>(allocation.base()); + if (!block) CRASH(); - uintptr_t address = reinterpret_cast<uintptr_t>(mask); -#elif OS(WINCE) - void* address = VirtualAlloc(NULL, BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -#elif OS(WINDOWS) -#if COMPILER(MINGW) - void* address = __mingw_aligned_malloc(BLOCK_SIZE, BLOCK_SIZE); -#else - void* address = _aligned_malloc(BLOCK_SIZE, BLOCK_SIZE); -#endif - memset(address, 0, BLOCK_SIZE); -#elif HAVE(POSIX_MEMALIGN) - void* address; - posix_memalign(&address, BLOCK_SIZE, BLOCK_SIZE); -#else - -#if ENABLE(JSC_MULTIPLE_THREADS) -#error Need to initialize pagesize safely. -#endif - static size_t pagesize = getpagesize(); - - size_t extra = 0; - if (BLOCK_SIZE > pagesize) - extra = BLOCK_SIZE - pagesize; - - void* mmapResult = mmap(NULL, BLOCK_SIZE + extra, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - uintptr_t address = reinterpret_cast<uintptr_t>(mmapResult); - - size_t adjust = 0; - if ((address & BLOCK_OFFSET_MASK) != 0) - adjust = BLOCK_SIZE - (address & BLOCK_OFFSET_MASK); - - if (adjust > 0) - munmap(reinterpret_cast<char*>(address), adjust); - - if (adjust < extra) - munmap(reinterpret_cast<char*>(address + adjust + BLOCK_SIZE), extra - adjust); - - address += adjust; -#endif // Initialize block. - CollectorBlock* block = reinterpret_cast<CollectorBlock*>(address); block->heap = this; clearMarkBits(block); Structure* dummyMarkableCellStructure = m_globalData->dummyMarkableCellStructure.get(); for (size_t i = 0; i < HeapConstants::cellsPerBlock; ++i) - new (block->cells + i) JSCell(dummyMarkableCellStructure); + new (&block->cells[i]) JSCell(dummyMarkableCellStructure); // Add block to blocks vector. size_t numBlocks = m_heap.numBlocks; if (m_heap.usedBlocks == numBlocks) { - static const size_t maxNumBlocks = ULONG_MAX / sizeof(CollectorBlock*) / GROWTH_FACTOR; + static const size_t maxNumBlocks = ULONG_MAX / sizeof(AlignedCollectorBlock) / GROWTH_FACTOR; if (numBlocks > maxNumBlocks) CRASH(); numBlocks = max(MIN_ARRAY_SIZE, numBlocks * GROWTH_FACTOR); m_heap.numBlocks = numBlocks; - m_heap.blocks = static_cast<CollectorBlock**>(fastRealloc(m_heap.blocks, numBlocks * sizeof(CollectorBlock*))); + m_heap.blocks = static_cast<AlignedCollectorBlock*>(fastRealloc(m_heap.blocks, numBlocks * sizeof(AlignedCollectorBlock))); } - m_heap.blocks[m_heap.usedBlocks++] = block; + m_heap.blocks[m_heap.usedBlocks++] = allocation; return block; } @@ -299,7 +231,7 @@ NEVER_INLINE void Heap::freeBlock(size_t block) ObjectIterator end(m_heap, block + 1); for ( ; it != end; ++it) (*it)->~JSCell(); - freeBlockPtr(m_heap.blocks[block]); + m_heap.blocks[block].deallocate(); // swap with the last block so we compact as we go m_heap.blocks[block] = m_heap.blocks[m_heap.usedBlocks - 1]; @@ -307,31 +239,10 @@ NEVER_INLINE void Heap::freeBlock(size_t block) if (m_heap.numBlocks > MIN_ARRAY_SIZE && m_heap.usedBlocks < m_heap.numBlocks / LOW_WATER_FACTOR) { m_heap.numBlocks = m_heap.numBlocks / GROWTH_FACTOR; - m_heap.blocks = static_cast<CollectorBlock**>(fastRealloc(m_heap.blocks, m_heap.numBlocks * sizeof(CollectorBlock*))); + m_heap.blocks = static_cast<AlignedCollectorBlock*>(fastRealloc(m_heap.blocks, m_heap.numBlocks * sizeof(AlignedCollectorBlock))); } } -NEVER_INLINE void Heap::freeBlockPtr(CollectorBlock* block) -{ -#if OS(DARWIN) - vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(block), BLOCK_SIZE); -#elif OS(SYMBIAN) - userChunk->Free(reinterpret_cast<TAny*>(block)); -#elif OS(WINCE) - VirtualFree(block, 0, MEM_RELEASE); -#elif OS(WINDOWS) -#if COMPILER(MINGW) - __mingw_aligned_free(block); -#else - _aligned_free(block); -#endif -#elif HAVE(POSIX_MEMALIGN) - free(block); -#else - munmap(reinterpret_cast<char*>(block), BLOCK_SIZE); -#endif -} - void Heap::freeBlocks() { ProtectCountSet protectedValuesCopy = m_protectedValues; @@ -355,7 +266,7 @@ void Heap::freeBlocks() it->first->~JSCell(); for (size_t block = 0; block < m_heap.usedBlocks; ++block) - freeBlockPtr(m_heap.blocks[block]); + m_heap.blocks[block].deallocate(); fastFree(m_heap.blocks); @@ -388,6 +299,7 @@ void Heap::recordExtraCost(size_t cost) void* Heap::allocate(size_t s) { + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); typedef HeapConstants::Block Block; typedef HeapConstants::Cell Cell; @@ -408,11 +320,11 @@ allocate: do { ASSERT(m_heap.nextBlock < m_heap.usedBlocks); - Block* block = reinterpret_cast<Block*>(m_heap.blocks[m_heap.nextBlock]); + Block* block = m_heap.collectorBlock(m_heap.nextBlock); do { ASSERT(m_heap.nextCell < HeapConstants::cellsPerBlock); if (!block->marked.get(m_heap.nextCell)) { // Always false for the last cell in the block - Cell* cell = block->cells + m_heap.nextCell; + Cell* cell = &block->cells[m_heap.nextCell]; m_heap.operationInProgress = Allocation; JSCell* imp = reinterpret_cast<JSCell*>(cell); @@ -422,7 +334,8 @@ allocate: ++m_heap.nextCell; return cell; } - } while (++m_heap.nextCell != HeapConstants::cellsPerBlock); + block->marked.advanceToNextPossibleFreeCell(m_heap.nextCell); + } while (m_heap.nextCell != HeapConstants::cellsPerBlock); m_heap.nextCell = 0; } while (++m_heap.nextBlock != m_heap.usedBlocks); @@ -462,10 +375,10 @@ void Heap::shrinkBlocks(size_t neededBlocks) // Clear the always-on last bit, so isEmpty() isn't fooled by it. for (size_t i = 0; i < m_heap.usedBlocks; ++i) - m_heap.blocks[i]->marked.clear(HeapConstants::cellsPerBlock - 1); + m_heap.collectorBlock(i)->marked.clear(HeapConstants::cellsPerBlock - 1); for (size_t i = 0; i != m_heap.usedBlocks && m_heap.usedBlocks != neededBlocks; ) { - if (m_heap.blocks[i]->marked.isEmpty()) { + if (m_heap.collectorBlock(i)->marked.isEmpty()) { freeBlock(i); } else ++i; @@ -473,11 +386,11 @@ void Heap::shrinkBlocks(size_t neededBlocks) // Reset the always-on last bit. for (size_t i = 0; i < m_heap.usedBlocks; ++i) - m_heap.blocks[i]->marked.set(HeapConstants::cellsPerBlock - 1); + m_heap.collectorBlock(i)->marked.set(HeapConstants::cellsPerBlock - 1); } #if OS(WINCE) -void* g_stackBase = 0; +JS_EXPORTDATA void* g_stackBase = 0; inline bool isPageWritable(void* page) { @@ -574,10 +487,6 @@ static inline void* currentThreadStackBase() MOV pTib, EAX } return static_cast<void*>(pTib->StackBase); -#elif OS(WINDOWS) && CPU(X86_64) && COMPILER(MSVC) - // FIXME: why only for MSVC? - PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb()); - return reinterpret_cast<void*>(pTib->StackBase); #elif OS(WINDOWS) && CPU(X86) && COMPILER(GCC) // offset 0x18 from the FS segment register gives a pointer to // the thread information block for the current thread @@ -586,7 +495,12 @@ static inline void* currentThreadStackBase() : "=r" (pTib) ); return static_cast<void*>(pTib->StackBase); +#elif OS(WINDOWS) && CPU(X86_64) + PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb()); + return reinterpret_cast<void*>(pTib->StackBase); #elif OS(QNX) + AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); + MutexLocker locker(mutex); return currentThreadStackBaseQNX(); #elif OS(SOLARIS) stack_t s; @@ -598,19 +512,17 @@ static inline void* currentThreadStackBase() pthread_stackseg_np(thread, &stack); return stack.ss_sp; #elif OS(SYMBIAN) - static void* stackBase = 0; - if (stackBase == 0) { - TThreadStackInfo info; - RThread thread; - thread.StackInfo(info); - stackBase = (void*)info.iBase; - } - return (void*)stackBase; + TThreadStackInfo info; + RThread thread; + thread.StackInfo(info); + return (void*)info.iBase; #elif OS(HAIKU) thread_info threadInfo; get_thread_info(find_thread(NULL), &threadInfo); return threadInfo.stack_end; #elif OS(UNIX) + AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); + MutexLocker locker(mutex); static void* stackBase = 0; static size_t stackSize = 0; static pthread_t stackThread; @@ -633,6 +545,8 @@ static inline void* currentThreadStackBase() } return static_cast<char*>(stackBase) + stackSize; #elif OS(WINCE) + AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); + MutexLocker locker(mutex); if (g_stackBase) return g_stackBase; else { @@ -667,7 +581,7 @@ void Heap::makeUsableFromMultipleThreads() void Heap::registerThread() { - ASSERT(!m_globalData->mainThreadOnly || isMainThread()); + ASSERT(!m_globalData->exclusiveThread || m_globalData->exclusiveThread == currentThread()); if (!m_currentThreadRegistrar || pthread_getspecific(m_currentThreadRegistrar)) return; @@ -728,19 +642,6 @@ inline bool isPointerAligned(void* p) // Cell size needs to be a power of two for isPossibleCell to be valid. COMPILE_ASSERT(sizeof(CollectorCell) % 2 == 0, Collector_cell_size_is_power_of_two); -#if USE(JSVALUE32) -static bool isHalfCellAligned(void *p) -{ - return (((intptr_t)(p) & (CELL_MASK >> 1)) == 0); -} - -static inline bool isPossibleCell(void* p) -{ - return isHalfCellAligned(p) && p; -} - -#else - static inline bool isCellAligned(void *p) { return (((intptr_t)(p) & CELL_MASK) == 0); @@ -750,7 +651,6 @@ static inline bool isPossibleCell(void* p) { return isCellAligned(p) && p; } -#endif // USE(JSVALUE32) void Heap::markConservatively(MarkStack& markStack, void* start, void* end) { @@ -767,7 +667,6 @@ void Heap::markConservatively(MarkStack& markStack, void* start, void* end) char** p = static_cast<char**>(start); char** e = static_cast<char**>(end); - CollectorBlock** blocks = m_heap.blocks; while (p != e) { char* x = *p++; if (isPossibleCell(x)) { @@ -783,7 +682,7 @@ void Heap::markConservatively(MarkStack& markStack, void* start, void* end) CollectorBlock* blockAddr = reinterpret_cast<CollectorBlock*>(xAsBits - offset); usedBlocks = m_heap.usedBlocks; for (size_t block = 0; block < usedBlocks; block++) { - if (blocks[block] != blockAddr) + if (m_heap.collectorBlock(block) != blockAddr) continue; markStack.append(reinterpret_cast<JSCell*>(xAsBits)); markStack.drain(); @@ -998,10 +897,40 @@ void Heap::markStackObjectsConservatively(MarkStack& markStack) #endif } +void Heap::updateWeakGCHandles() +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + weakGCHandlePool(i)->update(); +} + +void WeakGCHandlePool::update() +{ + for (unsigned i = 1; i < WeakGCHandlePool::numPoolEntries; ++i) { + if (m_entries[i].isValidPtr()) { + JSCell* cell = m_entries[i].get(); + if (!cell || !Heap::isCellMarked(cell)) + m_entries[i].invalidate(); + } + } +} + +WeakGCHandle* Heap::addWeakGCHandle(JSCell* ptr) +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + if (!weakGCHandlePool(i)->isFull()) + return weakGCHandlePool(i)->allocate(ptr); + + AlignedMemory<WeakGCHandlePool::poolSize> allocation = m_weakGCHandlePoolAllocator.allocate(); + m_weakGCHandlePools.append(allocation); + + WeakGCHandlePool* pool = new (allocation) WeakGCHandlePool(); + return pool->allocate(ptr); +} + void Heap::protect(JSValue k) { ASSERT(k); - ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); if (!k.isCell()) return; @@ -1009,15 +938,15 @@ void Heap::protect(JSValue k) m_protectedValues.add(k.asCell()); } -void Heap::unprotect(JSValue k) +bool Heap::unprotect(JSValue k) { ASSERT(k); - ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); if (!k.isCell()) - return; + return false; - m_protectedValues.remove(k.asCell()); + return m_protectedValues.remove(k.asCell()); } void Heap::markProtectedObjects(MarkStack& markStack) @@ -1032,7 +961,7 @@ void Heap::markProtectedObjects(MarkStack& markStack) void Heap::clearMarkBits() { for (size_t i = 0; i < m_heap.usedBlocks; ++i) - clearMarkBits(m_heap.blocks[i]); + clearMarkBits(m_heap.collectorBlock(i)); } void Heap::clearMarkBits(CollectorBlock* block) @@ -1051,9 +980,9 @@ size_t Heap::markedCells(size_t startBlock, size_t startCell) const return 0; size_t result = 0; - result += m_heap.blocks[startBlock]->marked.count(startCell); + result += m_heap.collectorBlock(startBlock)->marked.count(startCell); for (size_t i = startBlock + 1; i < m_heap.usedBlocks; ++i) - result += m_heap.blocks[i]->marked.count(); + result += m_heap.collectorBlock(i)->marked.count(); return result; } @@ -1093,7 +1022,7 @@ void Heap::sweep() void Heap::markRoots() { #ifndef NDEBUG - if (m_globalData->isSharedInstance) { + if (m_globalData->isSharedInstance()) { ASSERT(JSLock::lockCount() > 0); ASSERT(JSLock::currentThreadIsHoldingLock()); } @@ -1122,8 +1051,6 @@ void Heap::markRoots() MarkedArgumentBuffer::markLists(markStack, *m_markListSet); if (m_globalData->exception) markStack.append(m_globalData->exception); - if (m_globalData->functionCodeBlockBeingReparsed) - m_globalData->functionCodeBlockBeingReparsed->markAggregate(markStack); if (m_globalData->firstStringifierToMark) JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark); @@ -1134,6 +1061,8 @@ void Heap::markRoots() markStack.drain(); markStack.compact(); + updateWeakGCHandles(); + m_heap.operationInProgress = NoOperation; } @@ -1158,6 +1087,11 @@ Heap::Statistics Heap::statistics() const return statistics; } +size_t Heap::size() const +{ + return m_heap.usedBlocks * BLOCK_SIZE; +} + size_t Heap::globalObjectCount() { size_t count = 0; @@ -1195,10 +1129,6 @@ static const char* typeName(JSCell* cell) { if (cell->isString()) return "string"; -#if USE(JSVALUE32) - if (cell->isNumber()) - return "number"; -#endif if (cell->isGetterSetter()) return "Getter-Setter"; if (cell->isAPIValueWrapper()) @@ -1241,6 +1171,7 @@ bool Heap::isBusy() void Heap::reset() { + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); JAVASCRIPTCORE_GC_BEGIN(); markRoots(); @@ -1257,10 +1188,13 @@ void Heap::reset() resizeBlocks(); JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); } void Heap::collectAllGarbage() { + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); JAVASCRIPTCORE_GC_BEGIN(); // If the last iteration through the heap deallocated blocks, we need @@ -1293,4 +1227,9 @@ LiveObjectIterator Heap::primaryHeapEnd() return LiveObjectIterator(m_heap, m_heap.usedBlocks); } +void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback) +{ + m_activityCallback = activityCallback; +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/Collector.h b/JavaScriptCore/runtime/Collector.h index 82aa8a1..237c139 100644 --- a/JavaScriptCore/runtime/Collector.h +++ b/JavaScriptCore/runtime/Collector.h @@ -22,12 +22,18 @@ #ifndef Collector_h #define Collector_h +#include "AlignedMemoryAllocator.h" +#include "GCHandle.h" #include <stddef.h> #include <string.h> +#include <wtf/Bitmap.h> +#include <wtf/FixedArray.h> #include <wtf/HashCountedSet.h> #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> #include <wtf/OwnPtr.h> +#include <wtf/PageAllocation.h> +#include <wtf/PassOwnPtr.h> #include <wtf/StdLibExtras.h> #include <wtf/Threading.h> @@ -40,6 +46,7 @@ namespace JSC { class CollectorBlock; + class GCActivityCallback; class JSCell; class JSGlobalData; class JSValue; @@ -50,10 +57,19 @@ namespace JSC { class LiveObjectIterator; +#if OS(WINCE) || OS(SYMBIAN) || PLATFORM(BREWMP) + const size_t BLOCK_SIZE = 64 * 1024; // 64k +#else + const size_t BLOCK_SIZE = 256 * 1024; // 256k +#endif + + typedef AlignedMemoryAllocator<BLOCK_SIZE> CollectorBlockAllocator; + typedef AlignedMemory<BLOCK_SIZE> AlignedCollectorBlock; + struct CollectorHeap { size_t nextBlock; size_t nextCell; - CollectorBlock** blocks; + AlignedCollectorBlock* blocks; void* nextNumber; @@ -64,6 +80,11 @@ namespace JSC { bool didShrink; OperationInProgress operationInProgress; + + CollectorBlock* collectorBlock(size_t index) const + { + return static_cast<CollectorBlock*>(blocks[index].base()); + } }; class Heap : public Noncopyable { @@ -77,6 +98,7 @@ namespace JSC { bool isBusy(); // true if an allocation or collection is in progress void collectAllGarbage(); + void setActivityCallback(PassOwnPtr<GCActivityCallback>); static const size_t minExtraCost = 256; static const size_t maxExtraCost = 1024 * 1024; @@ -89,9 +111,12 @@ namespace JSC { size_t free; }; Statistics statistics() const; + size_t size() const; void protect(JSValue); - void unprotect(JSValue); + // Returns true if the value is no longer protected by any protect pointers + // (though it may still be alive due to heap/stack references). + bool unprotect(JSValue); static Heap* heap(JSValue); // 0 for immediate values static Heap* heap(JSCell*); @@ -105,8 +130,11 @@ namespace JSC { void registerThread(); // Only needs to be called by clients that can use the same heap from multiple threads. static bool isCellMarked(const JSCell*); + static bool checkMarkCell(const JSCell*); static void markCell(JSCell*); + WeakGCHandle* addWeakGCHandle(JSCell*); + void markConservatively(MarkStack&, void* start, void* end); HashSet<MarkedArgumentBuffer*>& markListSet() { if (!m_markListSet) m_markListSet = new HashSet<MarkedArgumentBuffer*>; return *m_markListSet; } @@ -129,7 +157,6 @@ namespace JSC { NEVER_INLINE CollectorBlock* allocateBlock(); NEVER_INLINE void freeBlock(size_t); - NEVER_INLINE void freeBlockPtr(CollectorBlock*); void freeBlocks(); void resizeBlocks(); void growBlocks(size_t neededBlocks); @@ -149,14 +176,20 @@ namespace JSC { void markOtherThreadConservatively(MarkStack&, Thread*); void markStackObjectsConservatively(MarkStack&); + void updateWeakGCHandles(); + WeakGCHandlePool* weakGCHandlePool(size_t index); + typedef HashCountedSet<JSCell*> ProtectCountSet; CollectorHeap m_heap; ProtectCountSet m_protectedValues; + WTF::Vector<AlignedMemory<WeakGCHandlePool::poolSize> > m_weakGCHandlePools; HashSet<MarkedArgumentBuffer*>* m_markListSet; + OwnPtr<GCActivityCallback> m_activityCallback; + #if ENABLE(JSC_MULTIPLE_THREADS) void makeUsableFromMultipleThreads(); @@ -168,30 +201,18 @@ namespace JSC { pthread_key_t m_currentThreadRegistrar; #endif + // Allocates collector blocks with correct alignment + CollectorBlockAllocator m_blockallocator; + WeakGCHandlePool::Allocator m_weakGCHandlePoolAllocator; + JSGlobalData* m_globalData; }; // tunable parameters - template<size_t bytesPerWord> struct CellSize; - - // cell size needs to be a power of two for certain optimizations in collector.cpp -#if USE(JSVALUE32) - template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 32; }; -#else - template<> struct CellSize<sizeof(uint32_t)> { static const size_t m_value = 64; }; -#endif - template<> struct CellSize<sizeof(uint64_t)> { static const size_t m_value = 64; }; - -#if OS(WINCE) || OS(SYMBIAN) - const size_t BLOCK_SIZE = 64 * 1024; // 64k -#else - const size_t BLOCK_SIZE = 64 * 4096; // 256k -#endif - // derived constants const size_t BLOCK_OFFSET_MASK = BLOCK_SIZE - 1; const size_t BLOCK_MASK = ~BLOCK_OFFSET_MASK; - const size_t MINIMUM_CELL_SIZE = CellSize<sizeof(void*)>::m_value; + const size_t MINIMUM_CELL_SIZE = 64; const size_t CELL_ARRAY_LENGTH = (MINIMUM_CELL_SIZE / sizeof(double)) + (MINIMUM_CELL_SIZE % sizeof(double) != 0 ? sizeof(double) : 0); const size_t CELL_SIZE = CELL_ARRAY_LENGTH * sizeof(double); const size_t SMALL_CELL_SIZE = CELL_SIZE / 2; @@ -203,11 +224,26 @@ namespace JSC { const size_t BITMAP_WORDS = (BITMAP_SIZE + 3) / sizeof(uint32_t); struct CollectorBitmap { - uint32_t bits[BITMAP_WORDS]; + FixedArray<uint32_t, BITMAP_WORDS> bits; bool get(size_t n) const { return !!(bits[n >> 5] & (1 << (n & 0x1F))); } void set(size_t n) { bits[n >> 5] |= (1 << (n & 0x1F)); } + bool getset(size_t n) + { + unsigned i = (1 << (n & 0x1F)); + uint32_t& b = bits[n >> 5]; + bool r = !!(b & i); + b |= i; + return r; + } void clear(size_t n) { bits[n >> 5] &= ~(1 << (n & 0x1F)); } - void clearAll() { memset(bits, 0, sizeof(bits)); } + void clearAll() { memset(bits.data(), 0, sizeof(bits)); } + ALWAYS_INLINE void advanceToNextPossibleFreeCell(size_t& startCell) + { + if (!~bits[startCell >> 5]) + startCell = (startCell & (~0x1F)) + 32; + else + ++startCell; + } size_t count(size_t startCell = 0) { size_t result = 0; @@ -229,12 +265,12 @@ namespace JSC { }; struct CollectorCell { - double memory[CELL_ARRAY_LENGTH]; + FixedArray<double, CELL_ARRAY_LENGTH> memory; }; class CollectorBlock { public: - CollectorCell cells[CELLS_PER_BLOCK]; + FixedArray<CollectorCell, CELLS_PER_BLOCK> cells; CollectorBitmap marked; Heap* heap; }; @@ -261,6 +297,11 @@ namespace JSC { return cellBlock(cell)->marked.get(cellOffset(cell)); } + inline bool Heap::checkMarkCell(const JSCell* cell) + { + return cellBlock(cell)->marked.getset(cellOffset(cell)); + } + inline void Heap::markCell(JSCell* cell) { cellBlock(cell)->marked.set(cellOffset(cell)); @@ -284,6 +325,11 @@ namespace JSC { return result; } + + inline WeakGCHandlePool* Heap::weakGCHandlePool(size_t index) + { + return static_cast<WeakGCHandlePool*>(m_weakGCHandlePools[index].base()); + } } // namespace JSC #endif /* Collector_h */ diff --git a/JavaScriptCore/runtime/CollectorHeapIterator.h b/JavaScriptCore/runtime/CollectorHeapIterator.h index be6f3c9..9d107b7 100644 --- a/JavaScriptCore/runtime/CollectorHeapIterator.h +++ b/JavaScriptCore/runtime/CollectorHeapIterator.h @@ -77,7 +77,7 @@ namespace JSC { inline JSCell* CollectorHeapIterator::operator*() const { - return reinterpret_cast<JSCell*>(m_heap.blocks[m_block]->cells + m_cell); + return reinterpret_cast<JSCell*>(&m_heap.collectorBlock(m_block)->cells[m_cell]); } // Iterators advance up to the next-to-last -- and not the last -- cell in a @@ -103,7 +103,7 @@ namespace JSC { if (m_block < m_heap.nextBlock || (m_block == m_heap.nextBlock && m_cell < m_heap.nextCell)) return *this; - while (m_block < m_heap.usedBlocks && !m_heap.blocks[m_block]->marked.get(m_cell)) + while (m_block < m_heap.usedBlocks && !m_heap.collectorBlock(m_block)->marked.get(m_cell)) advance(HeapConstants::cellsPerBlock - 1); return *this; } @@ -119,7 +119,7 @@ namespace JSC { do { advance(HeapConstants::cellsPerBlock - 1); ASSERT(m_block > m_heap.nextBlock || (m_block == m_heap.nextBlock && m_cell >= m_heap.nextCell)); - } while (m_block < m_heap.usedBlocks && m_heap.blocks[m_block]->marked.get(m_cell)); + } while (m_block < m_heap.usedBlocks && m_heap.collectorBlock(m_block)->marked.get(m_cell)); return *this; } diff --git a/JavaScriptCore/runtime/CommonIdentifiers.cpp b/JavaScriptCore/runtime/CommonIdentifiers.cpp index ed5e304..1561102 100644 --- a/JavaScriptCore/runtime/CommonIdentifiers.cpp +++ b/JavaScriptCore/runtime/CommonIdentifiers.cpp @@ -28,9 +28,11 @@ static const char* const nullCString = 0; #define INITIALIZE_PROPERTY_NAME(name) , name(globalData, #name) CommonIdentifiers::CommonIdentifiers(JSGlobalData* globalData) - : emptyIdentifier(globalData, "") + : nullIdentifier(globalData, nullCString) + , emptyIdentifier(globalData, "") , underscoreProto(globalData, "__proto__") , thisIdentifier(globalData, "this") + , useStrictIdentifier(globalData, "use strict") JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME) { } diff --git a/JavaScriptCore/runtime/CommonIdentifiers.h b/JavaScriptCore/runtime/CommonIdentifiers.h index 0a3d774..1e22b6a 100644 --- a/JavaScriptCore/runtime/CommonIdentifiers.h +++ b/JavaScriptCore/runtime/CommonIdentifiers.h @@ -90,9 +90,11 @@ namespace JSC { friend class JSGlobalData; public: + const Identifier nullIdentifier; const Identifier emptyIdentifier; const Identifier underscoreProto; const Identifier thisIdentifier; + const Identifier useStrictIdentifier; #define JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL(name) const Identifier name; JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(JSC_IDENTIFIER_DECLARE_PROPERTY_NAME_GLOBAL) diff --git a/JavaScriptCore/runtime/Completion.cpp b/JavaScriptCore/runtime/Completion.cpp index 2f88df9..eeb8b0d 100644 --- a/JavaScriptCore/runtime/Completion.cpp +++ b/JavaScriptCore/runtime/Completion.cpp @@ -29,6 +29,7 @@ #include "Interpreter.h" #include "Parser.h" #include "Debugger.h" +#include "WTFThreadData.h" #include <stdio.h> namespace JSC { @@ -36,7 +37,7 @@ namespace JSC { Completion checkSyntax(ExecState* exec, const SourceCode& source) { JSLock lock(exec); - ASSERT(exec->globalData().identifierTable == currentIdentifierTable()); + ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable()); RefPtr<ProgramExecutable> program = ProgramExecutable::create(exec, source); JSObject* error = program->checkSyntax(exec); @@ -49,7 +50,7 @@ Completion checkSyntax(ExecState* exec, const SourceCode& source) Completion evaluate(ExecState* exec, ScopeChain& scopeChain, const SourceCode& source, JSValue thisValue) { JSLock lock(exec); - ASSERT(exec->globalData().identifierTable == currentIdentifierTable()); + ASSERT(exec->globalData().identifierTable == wtfThreadData().currentIdentifierTable()); RefPtr<ProgramExecutable> program = ProgramExecutable::create(exec, source); JSObject* error = program->compile(exec, scopeChain.node()); @@ -58,13 +59,16 @@ Completion evaluate(ExecState* exec, ScopeChain& scopeChain, const SourceCode& s JSObject* thisObj = (!thisValue || thisValue.isUndefinedOrNull()) ? exec->dynamicGlobalObject() : thisValue.toObject(exec); - JSValue exception; - JSValue result = exec->interpreter()->execute(program.get(), exec, scopeChain.node(), thisObj, &exception); + JSValue result = exec->interpreter()->execute(program.get(), exec, scopeChain.node(), thisObj); - if (exception) { - if (exception.isObject() && asObject(exception)->isWatchdogException()) - return Completion(Interrupted, exception); - return Completion(Throw, exception); + if (exec->hadException()) { + JSValue exception = exec->exception(); + exec->clearException(); + + ComplType exceptionType = Throw; + if (exception.isObject()) + exceptionType = asObject(exception)->exceptionType(); + return Completion(exceptionType, exception); } return Completion(Normal, result); } diff --git a/JavaScriptCore/runtime/Completion.h b/JavaScriptCore/runtime/Completion.h index 41c9a64..63b315e 100644 --- a/JavaScriptCore/runtime/Completion.h +++ b/JavaScriptCore/runtime/Completion.h @@ -31,7 +31,7 @@ namespace JSC { class ScopeChain; class SourceCode; - enum ComplType { Normal, Break, Continue, ReturnValue, Throw, Interrupted }; + enum ComplType { Normal, Break, Continue, ReturnValue, Throw, Interrupted, Terminated }; /* * Completion objects are used to convey the return status and value diff --git a/JavaScriptCore/runtime/ConstructData.cpp b/JavaScriptCore/runtime/ConstructData.cpp index 7ee59d7..5da2a91 100644 --- a/JavaScriptCore/runtime/ConstructData.cpp +++ b/JavaScriptCore/runtime/ConstructData.cpp @@ -26,17 +26,17 @@ #include "config.h" #include "ConstructData.h" +#include "Executable.h" +#include "Interpreter.h" #include "JSFunction.h" +#include "JSGlobalObject.h" namespace JSC { -JSObject* construct(ExecState* exec, JSValue object, ConstructType constructType, const ConstructData& constructData, const ArgList& args) +JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args) { - if (constructType == ConstructTypeHost) - return constructData.native.function(exec, asObject(object), args); - ASSERT(constructType == ConstructTypeJS); - // FIXME: Can this be done more efficiently using the constructData? - return asFunction(object)->construct(exec, args); + ASSERT(constructType == ConstructTypeJS || constructType == ConstructTypeHost); + return exec->interpreter()->executeConstruct(exec, asObject(constructorObject), constructType, constructData, args); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ConstructData.h b/JavaScriptCore/runtime/ConstructData.h index 6b954a6..3d5f732 100644 --- a/JavaScriptCore/runtime/ConstructData.h +++ b/JavaScriptCore/runtime/ConstructData.h @@ -29,13 +29,14 @@ #ifndef ConstructData_h #define ConstructData_h +#include "JSValue.h" + namespace JSC { class ArgList; class ExecState; class FunctionExecutable; class JSObject; - class JSValue; class ScopeChainNode; enum ConstructType { @@ -44,7 +45,7 @@ namespace JSC { ConstructTypeJS }; - typedef JSObject* (*NativeConstructor)(ExecState*, JSObject*, const ArgList&); + typedef EncodedJSValue (JSC_HOST_CALL *NativeConstructor)(ExecState*); union ConstructData { struct { diff --git a/JavaScriptCore/runtime/DateConstructor.cpp b/JavaScriptCore/runtime/DateConstructor.cpp index ab95d06..dcbe12d 100644 --- a/JavaScriptCore/runtime/DateConstructor.cpp +++ b/JavaScriptCore/runtime/DateConstructor.cpp @@ -54,20 +54,20 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(DateConstructor); -static JSValue JSC_HOST_CALL dateParse(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateNow(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateUTC(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL dateParse(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateNow(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateUTC(ExecState*); -DateConstructor::DateConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, DatePrototype* datePrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, datePrototype->classInfo()->className)) +DateConstructor::DateConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, DatePrototype* datePrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, datePrototype->classInfo()->className)) { putDirectWithoutTransition(exec->propertyNames().prototype, datePrototype, DontEnum|DontDelete|ReadOnly); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().parse, dateParse), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 7, exec->propertyNames().UTC, dateUTC), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().now, dateNow), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().parse, dateParse), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 7, exec->propertyNames().UTC, dateUTC), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().now, dateNow), DontEnum); - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 7), ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(7), ReadOnly | DontEnum | DontDelete); } // ECMA 15.9.3 @@ -90,25 +90,34 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) value = primitive.toNumber(exec); } } else { - if (isnan(args.at(0).toNumber(exec)) - || isnan(args.at(1).toNumber(exec)) - || (numArgs >= 3 && isnan(args.at(2).toNumber(exec))) - || (numArgs >= 4 && isnan(args.at(3).toNumber(exec))) - || (numArgs >= 5 && isnan(args.at(4).toNumber(exec))) - || (numArgs >= 6 && isnan(args.at(5).toNumber(exec))) - || (numArgs >= 7 && isnan(args.at(6).toNumber(exec)))) + double doubleArguments[7] = { + args.at(0).toNumber(exec), + args.at(1).toNumber(exec), + args.at(2).toNumber(exec), + args.at(3).toNumber(exec), + args.at(4).toNumber(exec), + args.at(5).toNumber(exec), + args.at(6).toNumber(exec) + }; + if (isnan(doubleArguments[0]) + || isnan(doubleArguments[1]) + || (numArgs >= 3 && isnan(doubleArguments[2])) + || (numArgs >= 4 && isnan(doubleArguments[3])) + || (numArgs >= 5 && isnan(doubleArguments[4])) + || (numArgs >= 6 && isnan(doubleArguments[5])) + || (numArgs >= 7 && isnan(doubleArguments[6]))) value = NaN; else { GregorianDateTime t; - int year = args.at(0).toInt32(exec); + int year = JSC::toInt32(doubleArguments[0]); t.year = (year >= 0 && year <= 99) ? year : year - 1900; - t.month = args.at(1).toInt32(exec); - t.monthDay = (numArgs >= 3) ? args.at(2).toInt32(exec) : 1; - t.hour = args.at(3).toInt32(exec); - t.minute = args.at(4).toInt32(exec); - t.second = args.at(5).toInt32(exec); + t.month = JSC::toInt32(doubleArguments[1]); + t.monthDay = (numArgs >= 3) ? JSC::toInt32(doubleArguments[2]) : 1; + t.hour = JSC::toInt32(doubleArguments[3]); + t.minute = JSC::toInt32(doubleArguments[4]); + t.second = JSC::toInt32(doubleArguments[5]); t.isDST = -1; - double ms = (numArgs >= 7) ? args.at(6).toNumber(exec) : 0; + double ms = (numArgs >= 7) ? doubleArguments[6] : 0; value = gregorianDateTimeToMS(exec, t, ms, false); } } @@ -116,9 +125,10 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) return new (exec) DateInstance(exec, value); } -static JSObject* constructWithDateConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithDateConstructor(ExecState* exec) { - return constructDate(exec, args); + ArgList args(exec); + return JSValue::encode(constructDate(exec, args)); } ConstructType DateConstructor::getConstructData(ConstructData& constructData) @@ -128,7 +138,7 @@ ConstructType DateConstructor::getConstructData(ConstructData& constructData) } // ECMA 15.9.2 -static JSValue JSC_HOST_CALL callDate(ExecState* exec, JSObject*, JSValue, const ArgList&) +static EncodedJSValue JSC_HOST_CALL callDate(ExecState* exec) { time_t localTime = time(0); tm localTM; @@ -138,7 +148,7 @@ static JSValue JSC_HOST_CALL callDate(ExecState* exec, JSObject*, JSValue, const DateConversionBuffer time; formatDate(ts, date); formatTime(ts, time); - return jsMakeNontrivialString(exec, date, " ", time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); } CallType DateConstructor::getCallData(CallData& callData) @@ -147,38 +157,47 @@ CallType DateConstructor::getCallData(CallData& callData) return CallTypeHost; } -static JSValue JSC_HOST_CALL dateParse(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL dateParse(ExecState* exec) { - return jsNumber(exec, parseDate(exec, args.at(0).toString(exec))); + return JSValue::encode(jsNumber(parseDate(exec, exec->argument(0).toString(exec)))); } -static JSValue JSC_HOST_CALL dateNow(ExecState* exec, JSObject*, JSValue, const ArgList&) +static EncodedJSValue JSC_HOST_CALL dateNow(ExecState*) { - return jsNumber(exec, jsCurrentTime()); + return JSValue::encode(jsNumber(jsCurrentTime())); } -static JSValue JSC_HOST_CALL dateUTC(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL dateUTC(ExecState* exec) { - int n = args.size(); - if (isnan(args.at(0).toNumber(exec)) - || isnan(args.at(1).toNumber(exec)) - || (n >= 3 && isnan(args.at(2).toNumber(exec))) - || (n >= 4 && isnan(args.at(3).toNumber(exec))) - || (n >= 5 && isnan(args.at(4).toNumber(exec))) - || (n >= 6 && isnan(args.at(5).toNumber(exec))) - || (n >= 7 && isnan(args.at(6).toNumber(exec)))) - return jsNaN(exec); + double doubleArguments[7] = { + exec->argument(0).toNumber(exec), + exec->argument(1).toNumber(exec), + exec->argument(2).toNumber(exec), + exec->argument(3).toNumber(exec), + exec->argument(4).toNumber(exec), + exec->argument(5).toNumber(exec), + exec->argument(6).toNumber(exec) + }; + int n = exec->argumentCount(); + if (isnan(doubleArguments[0]) + || isnan(doubleArguments[1]) + || (n >= 3 && isnan(doubleArguments[2])) + || (n >= 4 && isnan(doubleArguments[3])) + || (n >= 5 && isnan(doubleArguments[4])) + || (n >= 6 && isnan(doubleArguments[5])) + || (n >= 7 && isnan(doubleArguments[6]))) + return JSValue::encode(jsNaN()); GregorianDateTime t; - int year = args.at(0).toInt32(exec); + int year = JSC::toInt32(doubleArguments[0]); t.year = (year >= 0 && year <= 99) ? year : year - 1900; - t.month = args.at(1).toInt32(exec); - t.monthDay = (n >= 3) ? args.at(2).toInt32(exec) : 1; - t.hour = args.at(3).toInt32(exec); - t.minute = args.at(4).toInt32(exec); - t.second = args.at(5).toInt32(exec); - double ms = (n >= 7) ? args.at(6).toNumber(exec) : 0; - return jsNumber(exec, timeClip(gregorianDateTimeToMS(exec, t, ms, true))); + t.month = JSC::toInt32(doubleArguments[1]); + t.monthDay = (n >= 3) ? JSC::toInt32(doubleArguments[2]) : 1; + t.hour = JSC::toInt32(doubleArguments[3]); + t.minute = JSC::toInt32(doubleArguments[4]); + t.second = JSC::toInt32(doubleArguments[5]); + double ms = (n >= 7) ? doubleArguments[6] : 0; + return JSValue::encode(jsNumber(timeClip(gregorianDateTimeToMS(exec, t, ms, true)))); } } // namespace JSC diff --git a/JavaScriptCore/runtime/DateConstructor.h b/JavaScriptCore/runtime/DateConstructor.h index 10e450e..c8ca456 100644 --- a/JavaScriptCore/runtime/DateConstructor.h +++ b/JavaScriptCore/runtime/DateConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class DateConstructor : public InternalFunction { public: - DateConstructor(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, DatePrototype*); + DateConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, DatePrototype*); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/DateConversion.cpp b/JavaScriptCore/runtime/DateConversion.cpp index f129407..d4b8232 100644 --- a/JavaScriptCore/runtime/DateConversion.cpp +++ b/JavaScriptCore/runtime/DateConversion.cpp @@ -47,6 +47,7 @@ #include "UString.h" #include <wtf/DateMath.h> #include <wtf/StringExtras.h> +#include <wtf/text/CString.h> using namespace WTF; @@ -56,7 +57,9 @@ double parseDate(ExecState* exec, const UString &date) { if (date == exec->globalData().cachedDateString) return exec->globalData().cachedDateStringValue; - double value = parseDateFromNullTerminatedCharacters(exec, date.UTF8String().c_str()); + double value = parseES5DateFromNullTerminatedCharacters(date.utf8().data()); + if (isnan(value)) + value = parseDateFromNullTerminatedCharacters(exec, date.utf8().data()); exec->globalData().cachedDateString = date; exec->globalData().cachedDateStringValue = value; return value; diff --git a/JavaScriptCore/runtime/DateInstance.cpp b/JavaScriptCore/runtime/DateInstance.cpp index b43b183..8562e2d 100644 --- a/JavaScriptCore/runtime/DateInstance.cpp +++ b/JavaScriptCore/runtime/DateInstance.cpp @@ -34,22 +34,22 @@ namespace JSC { const ClassInfo DateInstance::info = {"Date", 0, 0, 0}; -DateInstance::DateInstance(ExecState* exec, NonNullPassRefPtr<Structure> structure) +DateInstance::DateInstance(ExecState*, NonNullPassRefPtr<Structure> structure) : JSWrapperObject(structure) { - setInternalValue(jsNaN(exec)); + setInternalValue(jsNaN()); } -DateInstance::DateInstance(ExecState* exec, NonNullPassRefPtr<Structure> structure, double time) +DateInstance::DateInstance(ExecState*, NonNullPassRefPtr<Structure> structure, double time) : JSWrapperObject(structure) { - setInternalValue(jsNumber(exec, timeClip(time))); + setInternalValue(jsNumber(timeClip(time))); } DateInstance::DateInstance(ExecState* exec, double time) : JSWrapperObject(exec->lexicalGlobalObject()->dateStructure()) { - setInternalValue(jsNumber(exec, timeClip(time))); + setInternalValue(jsNumber(timeClip(time))); } const GregorianDateTime* DateInstance::calculateGregorianDateTime(ExecState* exec) const diff --git a/JavaScriptCore/runtime/DateInstanceCache.h b/JavaScriptCore/runtime/DateInstanceCache.h index d208580..b60c29a 100644 --- a/JavaScriptCore/runtime/DateInstanceCache.h +++ b/JavaScriptCore/runtime/DateInstanceCache.h @@ -86,7 +86,7 @@ namespace JSC { CacheEntry& lookup(double d) { return m_cache[WTF::FloatHash<double>::hash(d) & (cacheSize - 1)]; } - CacheEntry m_cache[cacheSize]; + FixedArray<CacheEntry, cacheSize> m_cache; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/DatePrototype.cpp b/JavaScriptCore/runtime/DatePrototype.cpp index 25b0ac4..085cb33 100644 --- a/JavaScriptCore/runtime/DatePrototype.cpp +++ b/JavaScriptCore/runtime/DatePrototype.cpp @@ -2,6 +2,7 @@ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Torch Mobile, Inc. All rights reserved. + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,11 +25,12 @@ #include "DatePrototype.h" #include "DateConversion.h" +#include "DateInstance.h" #include "Error.h" #include "JSString.h" #include "JSStringBuilder.h" +#include "Lookup.h" #include "ObjectPrototype.h" -#include "DateInstance.h" #if !PLATFORM(MAC) && HAVE(LANGINFO_H) #include <langinfo.h> @@ -37,6 +39,7 @@ #include <limits.h> #include <locale.h> #include <math.h> +#include <stdlib.h> #include <time.h> #include <wtf/Assertions.h> #include <wtf/DateMath.h> @@ -70,52 +73,51 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(DatePrototype); -static JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState*, JSObject*, JSValue, const ArgList&); - -static JSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState*); +static EncodedJSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState*); } @@ -143,7 +145,7 @@ static CFDateFormatterStyle styleFromArgString(const UString& string, CFDateForm return defaultStyle; } -static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMilliseconds, LocaleDateTimeFormat format, const ArgList& args) +static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMilliseconds, LocaleDateTimeFormat format) { CFDateFormatterStyle dateStyle = (format != LocaleTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); CFDateFormatterStyle timeStyle = (format != LocaleDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle); @@ -151,16 +153,16 @@ static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMil bool useCustomFormat = false; UString customFormatString; - UString arg0String = args.at(0).toString(exec); - if (arg0String == "custom" && !args.at(1).isUndefined()) { + UString arg0String = exec->argument(0).toString(exec); + if (arg0String == "custom" && !exec->argument(1).isUndefined()) { useCustomFormat = true; - customFormatString = args.at(1).toString(exec); - } else if (format == LocaleDateAndTime && !args.at(1).isUndefined()) { + customFormatString = exec->argument(1).toString(exec); + } else if (format == LocaleDateAndTime && !exec->argument(1).isUndefined()) { dateStyle = styleFromArgString(arg0String, dateStyle); - timeStyle = styleFromArgString(args.at(1).toString(exec), timeStyle); - } else if (format != LocaleTime && !args.at(0).isUndefined()) + timeStyle = styleFromArgString(exec->argument(1).toString(exec), timeStyle); + } else if (format != LocaleTime && !exec->argument(0).isUndefined()) dateStyle = styleFromArgString(arg0String, dateStyle); - else if (format != LocaleDate && !args.at(0).isUndefined()) + else if (format != LocaleDate && !exec->argument(0).isUndefined()) timeStyle = styleFromArgString(arg0String, timeStyle); CFLocaleRef locale = CFLocaleCopyCurrent(); @@ -168,7 +170,7 @@ static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMil CFRelease(locale); if (useCustomFormat) { - CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, customFormatString.data(), customFormatString.size()); + CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, customFormatString.characters(), customFormatString.length()); CFDateFormatterSetFormat(formatter, customFormatCFString); CFRelease(customFormatCFString); } @@ -180,7 +182,7 @@ static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMil // We truncate the string returned from CFDateFormatter if it's absurdly long (> 200 characters). // That's not great error handling, but it just won't happen so it doesn't matter. UChar buffer[200]; - const size_t bufferLength = sizeof(buffer) / sizeof(buffer[0]); + const size_t bufferLength = WTF_ARRAY_LENGTH(buffer); size_t length = CFStringGetLength(string); ASSERT(length <= bufferLength); if (length > bufferLength) @@ -247,11 +249,31 @@ static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, L strncpy(yearLocation, yearString, yearLen - 1); } - + + // Convert multi-byte result to UNICODE. + // If __STDC_ISO_10646__ is defined, wide character represents + // UTF-16 (or UTF-32) code point. In most modern Unix like system + // (e.g. Linux with glibc 2.2 and above) the macro is defined, + // and wide character represents UTF-32 code point. + // Here we static_cast potential UTF-32 to UTF-16, it should be + // safe because date and (or) time related characters in different languages + // should be in UNICODE BMP. If mbstowcs fails, we just fall + // back on using multi-byte result as-is. +#ifdef __STDC_ISO_10646__ + UChar buffer[bufsize]; + wchar_t tempbuffer[bufsize]; + size_t length = mbstowcs(tempbuffer, timebuffer, bufsize - 1); + if (length != static_cast<size_t>(-1)) { + for (size_t i = 0; i < length; ++i) + buffer[i] = static_cast<UChar>(tempbuffer[i]); + return jsNontrivialString(exec, UString(buffer, length)); + } +#endif + return jsNontrivialString(exec, timebuffer); } -static JSCell* formatLocaleDate(ExecState* exec, DateInstance* dateObject, double, LocaleDateTimeFormat format, const ArgList&) +static JSCell* formatLocaleDate(ExecState* exec, DateInstance* dateObject, double, LocaleDateTimeFormat format) { const GregorianDateTime* gregorianDateTime = dateObject->gregorianDateTime(exec); if (!gregorianDateTime) @@ -265,12 +287,12 @@ static JSCell* formatLocaleDate(ExecState* exec, DateInstance* dateObject, doubl // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. // // Format of member function: f([hour,] [min,] [sec,] [ms]) -static bool fillStructuresUsingTimeArgs(ExecState* exec, const ArgList& args, int maxArgs, double* ms, GregorianDateTime* t) +static bool fillStructuresUsingTimeArgs(ExecState* exec, int maxArgs, double* ms, GregorianDateTime* t) { double milliseconds = 0; bool ok = true; int idx = 0; - int numArgs = args.size(); + int numArgs = exec->argumentCount(); // JS allows extra trailing arguments -- ignore them if (numArgs > maxArgs) @@ -279,19 +301,25 @@ static bool fillStructuresUsingTimeArgs(ExecState* exec, const ArgList& args, in // hours if (maxArgs >= 4 && idx < numArgs) { t->hour = 0; - milliseconds += args.at(idx++).toInt32(exec, ok) * msPerHour; + double hours = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(hours); + milliseconds += hours * msPerHour; } // minutes if (maxArgs >= 3 && idx < numArgs && ok) { t->minute = 0; - milliseconds += args.at(idx++).toInt32(exec, ok) * msPerMinute; + double minutes = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(minutes); + milliseconds += minutes * msPerMinute; } // seconds if (maxArgs >= 2 && idx < numArgs && ok) { t->second = 0; - milliseconds += args.at(idx++).toInt32(exec, ok) * msPerSecond; + double seconds = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(seconds); + milliseconds += seconds * msPerSecond; } if (!ok) @@ -299,7 +327,7 @@ static bool fillStructuresUsingTimeArgs(ExecState* exec, const ArgList& args, in // milliseconds if (idx < numArgs) { - double millis = args.at(idx).toNumber(exec); + double millis = exec->argument(idx).toIntegerPreserveNaN(exec); ok = isfinite(millis); milliseconds += millis; } else @@ -313,28 +341,34 @@ static bool fillStructuresUsingTimeArgs(ExecState* exec, const ArgList& args, in // ms (representing milliseconds) and t (representing the rest of the date structure) appropriately. // // Format of member function: f([years,] [months,] [days]) -static bool fillStructuresUsingDateArgs(ExecState *exec, const ArgList& args, int maxArgs, double *ms, GregorianDateTime *t) +static bool fillStructuresUsingDateArgs(ExecState *exec, int maxArgs, double *ms, GregorianDateTime *t) { int idx = 0; bool ok = true; - int numArgs = args.size(); + int numArgs = exec->argumentCount(); // JS allows extra trailing arguments -- ignore them if (numArgs > maxArgs) numArgs = maxArgs; // years - if (maxArgs >= 3 && idx < numArgs) - t->year = args.at(idx++).toInt32(exec, ok) - 1900; - + if (maxArgs >= 3 && idx < numArgs) { + double years = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(years); + t->year = toInt32(years - 1900); + } // months - if (maxArgs >= 2 && idx < numArgs && ok) - t->month = args.at(idx++).toInt32(exec, ok); - + if (maxArgs >= 2 && idx < numArgs && ok) { + double months = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(months); + t->month = toInt32(months); + } // days - if (idx < numArgs && ok) { + if (idx < numArgs && ok) { + double days = exec->argument(idx++).toIntegerPreserveNaN(exec); + ok = isfinite(days); t->monthDay = 0; - *ms += args.at(idx).toInt32(exec, ok) * msPerDay; + *ms += days * msPerDay; } return ok; @@ -389,16 +423,17 @@ const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, 0, ExecState setUTCFullYear dateProtoFuncSetUTCFullYear DontEnum|Function 3 setYear dateProtoFuncSetYear DontEnum|Function 1 getYear dateProtoFuncGetYear DontEnum|Function 0 - toJSON dateProtoFuncToJSON DontEnum|Function 0 + toJSON dateProtoFuncToJSON DontEnum|Function 1 @end */ // ECMA 15.9.4 -DatePrototype::DatePrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure) +DatePrototype::DatePrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) : DateInstance(exec, structure) { // The constructor will be added later, after DateConstructor has been built. + putAnonymousValue(0, globalObject); } bool DatePrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -414,390 +449,419 @@ bool DatePrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& // Functions -JSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNontrivialString(exec, "Invalid Date"); + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); DateConversionBuffer date; DateConversionBuffer time; formatDate(*gregorianDateTime, date); formatTime(*gregorianDateTime, time); - return jsMakeNontrivialString(exec, date, " ", time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); } -JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNontrivialString(exec, "Invalid Date"); + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); DateConversionBuffer date; DateConversionBuffer time; formatDateUTCVariant(*gregorianDateTime, date); formatTimeUTC(*gregorianDateTime, time); - return jsMakeNontrivialString(exec, date, " ", time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); } -JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNontrivialString(exec, "Invalid Date"); + return JSValue::encode(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 + 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); + return JSValue::encode(jsNontrivialString(exec, buffer)); } -JSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNontrivialString(exec, "Invalid Date"); + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); DateConversionBuffer date; formatDate(*gregorianDateTime, date); - return jsNontrivialString(exec, date); + return JSValue::encode(jsNontrivialString(exec, date)); } -JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNontrivialString(exec, "Invalid Date"); + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); DateConversionBuffer time; formatTime(*gregorianDateTime, time); - return jsNontrivialString(exec, time); + return JSValue::encode(jsNontrivialString(exec, time)); } -JSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); - return formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDateAndTime, args); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDateAndTime)); } -JSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); - return formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDate, args); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleDate)); } -JSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); - return formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleTime, args); + return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleTime)); } -JSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - return asDateInstance(thisValue)->internalValue(); + return JSValue::encode(asDateInstance(thisValue)->internalValue()); } -JSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, 1900 + gregorianDateTime->year); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(1900 + gregorianDateTime->year)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, 1900 + gregorianDateTime->year); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(1900 + gregorianDateTime->year)); } -JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNontrivialString(exec, "Invalid Date"); + return JSValue::encode(jsNontrivialString(exec, "Invalid Date")); DateConversionBuffer date; DateConversionBuffer time; formatDateUTCVariant(*gregorianDateTime, date); formatTimeUTC(*gregorianDateTime, time); - return jsMakeNontrivialString(exec, date, " ", time); + return JSValue::encode(jsMakeNontrivialString(exec, date, " ", time)); } -JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->month); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->month)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->month); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->month)); } -JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->monthDay); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->monthDay)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->monthDay); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->monthDay)); } -JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->weekDay); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->weekDay)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->weekDay); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->weekDay)); } -JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->hour); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->hour)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->hour); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->hour)); } -JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->minute); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->minute)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->minute); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->minute)); } -JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->second); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->second)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, gregorianDateTime->second); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(gregorianDateTime->second)); } -JSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); double milli = thisDateObj->internalNumber(); if (isnan(milli)) - return jsNaN(exec); + return JSValue::encode(jsNaN()); double secs = floor(milli / msPerSecond); double ms = milli - secs * msPerSecond; - return jsNumber(exec, ms); + return JSValue::encode(jsNumber(ms)); } -JSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetUTCMilliseconds(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); double milli = thisDateObj->internalNumber(); if (isnan(milli)) - return jsNaN(exec); + return JSValue::encode(jsNaN()); double secs = floor(milli / msPerSecond); double ms = milli - secs * msPerSecond; - return jsNumber(exec, ms); + return JSValue::encode(jsNumber(ms)); } -JSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); - return jsNumber(exec, -gregorianDateTime->utcOffset / minutesPerHour); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(-gregorianDateTime->utcOffset / minutesPerHour)); } -JSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); - double milli = timeClip(args.at(0).toNumber(exec)); - JSValue result = jsNumber(exec, milli); + double milli = timeClip(exec->argument(0).toNumber(exec)); + JSValue result = jsNumber(milli); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } -static JSValue setNewValueFromTimeArgs(ExecState* exec, JSValue thisValue, const ArgList& args, int numArgsToUse, bool inputIsUTC) +static EncodedJSValue setNewValueFromTimeArgs(ExecState* exec, int numArgsToUse, bool inputIsUTC) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); double milli = thisDateObj->internalNumber(); - if (args.isEmpty() || isnan(milli)) { - JSValue result = jsNaN(exec); + if (!exec->argumentCount() || isnan(milli)) { + JSValue result = jsNaN(); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } double secs = floor(milli / msPerSecond); @@ -807,31 +871,32 @@ static JSValue setNewValueFromTimeArgs(ExecState* exec, JSValue thisValue, const ? thisDateObj->gregorianDateTimeUTC(exec) : thisDateObj->gregorianDateTime(exec); if (!other) - return jsNaN(exec); + return JSValue::encode(jsNaN()); GregorianDateTime gregorianDateTime; gregorianDateTime.copyFrom(*other); - if (!fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &gregorianDateTime)) { - JSValue result = jsNaN(exec); + if (!fillStructuresUsingTimeArgs(exec, numArgsToUse, &ms, &gregorianDateTime)) { + JSValue result = jsNaN(); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } - JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } -static JSValue setNewValueFromDateArgs(ExecState* exec, JSValue thisValue, const ArgList& args, int numArgsToUse, bool inputIsUTC) +static EncodedJSValue setNewValueFromDateArgs(ExecState* exec, int numArgsToUse, bool inputIsUTC) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); - if (args.isEmpty()) { - JSValue result = jsNaN(exec); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } double milli = thisDateObj->internalNumber(); @@ -846,115 +911,116 @@ static JSValue setNewValueFromDateArgs(ExecState* exec, JSValue thisValue, const ? thisDateObj->gregorianDateTimeUTC(exec) : thisDateObj->gregorianDateTime(exec); if (!other) - return jsNaN(exec); + return JSValue::encode(jsNaN()); gregorianDateTime.copyFrom(*other); } - if (!fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &gregorianDateTime)) { - JSValue result = jsNaN(exec); + if (!fillStructuresUsingDateArgs(exec, numArgsToUse, &ms, &gregorianDateTime)) { + JSValue result = jsNaN(); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } - JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMilliSeconds(ExecState* exec) { const bool inputIsUTC = false; - return setNewValueFromTimeArgs(exec, thisValue, args, 1, inputIsUTC); + return setNewValueFromTimeArgs(exec, 1, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMilliseconds(ExecState* exec) { const bool inputIsUTC = true; - return setNewValueFromTimeArgs(exec, thisValue, args, 1, inputIsUTC); + return setNewValueFromTimeArgs(exec, 1, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetSeconds(ExecState* exec) { const bool inputIsUTC = false; - return setNewValueFromTimeArgs(exec, thisValue, args, 2, inputIsUTC); + return setNewValueFromTimeArgs(exec, 2, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCSeconds(ExecState* exec) { const bool inputIsUTC = true; - return setNewValueFromTimeArgs(exec, thisValue, args, 2, inputIsUTC); + return setNewValueFromTimeArgs(exec, 2, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMinutes(ExecState* exec) { const bool inputIsUTC = false; - return setNewValueFromTimeArgs(exec, thisValue, args, 3, inputIsUTC); + return setNewValueFromTimeArgs(exec, 3, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMinutes(ExecState* exec) { const bool inputIsUTC = true; - return setNewValueFromTimeArgs(exec, thisValue, args, 3, inputIsUTC); + return setNewValueFromTimeArgs(exec, 3, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetHours(ExecState* exec) { const bool inputIsUTC = false; - return setNewValueFromTimeArgs(exec, thisValue, args, 4, inputIsUTC); + return setNewValueFromTimeArgs(exec, 4, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCHours(ExecState* exec) { const bool inputIsUTC = true; - return setNewValueFromTimeArgs(exec, thisValue, args, 4, inputIsUTC); + return setNewValueFromTimeArgs(exec, 4, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetDate(ExecState* exec) { const bool inputIsUTC = false; - return setNewValueFromDateArgs(exec, thisValue, args, 1, inputIsUTC); + return setNewValueFromDateArgs(exec, 1, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCDate(ExecState* exec) { const bool inputIsUTC = true; - return setNewValueFromDateArgs(exec, thisValue, args, 1, inputIsUTC); + return setNewValueFromDateArgs(exec, 1, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetMonth(ExecState* exec) { const bool inputIsUTC = false; - return setNewValueFromDateArgs(exec, thisValue, args, 2, inputIsUTC); + return setNewValueFromDateArgs(exec, 2, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCMonth(ExecState* exec) { const bool inputIsUTC = true; - return setNewValueFromDateArgs(exec, thisValue, args, 2, inputIsUTC); + return setNewValueFromDateArgs(exec, 2, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetFullYear(ExecState* exec) { const bool inputIsUTC = false; - return setNewValueFromDateArgs(exec, thisValue, args, 3, inputIsUTC); + return setNewValueFromDateArgs(exec, 3, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetUTCFullYear(ExecState* exec) { const bool inputIsUTC = true; - return setNewValueFromDateArgs(exec, thisValue, args, 3, inputIsUTC); + return setNewValueFromDateArgs(exec, 3, inputIsUTC); } -JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); - if (args.isEmpty()) { - JSValue result = jsNaN(exec); + if (!exec->argumentCount()) { + JSValue result = jsNaN(); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } double milli = thisDateObj->internalNumber(); @@ -972,56 +1038,57 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t gregorianDateTime.copyFrom(*other); } - bool ok = true; - int32_t year = args.at(0).toInt32(exec, ok); - if (!ok) { - JSValue result = jsNaN(exec); + double year = exec->argument(0).toIntegerPreserveNaN(exec); + if (!isfinite(year)) { + JSValue result = jsNaN(); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } - gregorianDateTime.year = (year > 99 || year < 0) ? year - 1900 : year; - JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, false)); + gregorianDateTime.year = toInt32((year > 99 || year < 0) ? year - 1900 : year); + JSValue result = jsNumber(gregorianDateTimeToMS(exec, gregorianDateTime, ms, false)); thisDateObj->setInternalValue(result); - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&DateInstance::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); if (!gregorianDateTime) - return jsNaN(exec); + return JSValue::encode(jsNaN()); // NOTE: IE returns the full year even in getYear. - return jsNumber(exec, gregorianDateTime->year); + return JSValue::encode(jsNumber(gregorianDateTime->year)); } -JSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); JSObject* object = thisValue.toThisObject(exec); if (exec->hadException()) - return jsNull(); + return JSValue::encode(jsNull()); JSValue toISOValue = object->get(exec, exec->globalData().propertyNames->toISOString); if (exec->hadException()) - return jsNull(); + return JSValue::encode(jsNull()); CallData callData; - CallType callType = toISOValue.getCallData(callData); + CallType callType = getCallData(toISOValue, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError, "toISOString is not a function"); + return throwVMError(exec, createTypeError(exec, "toISOString is not a function")); JSValue result = call(exec, asObject(toISOValue), callType, callData, object, exec->emptyList()); if (exec->hadException()) - return jsNull(); + return JSValue::encode(jsNull()); if (result.isObject()) - return throwError(exec, TypeError, "toISOString did not return a primitive value"); - return result; + return throwVMError(exec, createTypeError(exec, "toISOString did not return a primitive value")); + return JSValue::encode(result); } } // namespace JSC diff --git a/JavaScriptCore/runtime/DatePrototype.h b/JavaScriptCore/runtime/DatePrototype.h index 612ca06..e3672aa 100644 --- a/JavaScriptCore/runtime/DatePrototype.h +++ b/JavaScriptCore/runtime/DatePrototype.h @@ -29,7 +29,7 @@ namespace JSC { class DatePrototype : public DateInstance { public: - DatePrototype(ExecState*, NonNullPassRefPtr<Structure>); + DatePrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); diff --git a/JavaScriptCore/runtime/Error.cpp b/JavaScriptCore/runtime/Error.cpp index 69464b7..227e9ec 100644 --- a/JavaScriptCore/runtime/Error.cpp +++ b/JavaScriptCore/runtime/Error.cpp @@ -31,104 +31,181 @@ #include "JSObject.h" #include "JSString.h" #include "NativeErrorConstructor.h" +#include "SourceCode.h" namespace JSC { -const char* expressionBeginOffsetPropertyName = "expressionBeginOffset"; -const char* expressionCaretOffsetPropertyName = "expressionCaretOffset"; -const char* expressionEndOffsetPropertyName = "expressionEndOffset"; - -JSObject* Error::create(ExecState* exec, ErrorType type, const UString& message, int lineNumber, intptr_t sourceID, const UString& sourceURL) -{ - JSObject* constructor; - const char* name; - switch (type) { - case EvalError: - constructor = exec->lexicalGlobalObject()->evalErrorConstructor(); - name = "Evaluation error"; - break; - case RangeError: - constructor = exec->lexicalGlobalObject()->rangeErrorConstructor(); - name = "Range error"; - break; - case ReferenceError: - constructor = exec->lexicalGlobalObject()->referenceErrorConstructor(); - name = "Reference error"; - break; - case SyntaxError: - constructor = exec->lexicalGlobalObject()->syntaxErrorConstructor(); - name = "Syntax error"; - break; - case TypeError: - constructor = exec->lexicalGlobalObject()->typeErrorConstructor(); - name = "Type error"; - break; - case URIError: - constructor = exec->lexicalGlobalObject()->URIErrorConstructor(); - name = "URI error"; - break; - default: - constructor = exec->lexicalGlobalObject()->errorConstructor(); - name = "Error"; - break; - } +static const char* linePropertyName = "line"; +static const char* sourceIdPropertyName = "sourceId"; +static const char* sourceURLPropertyName = "sourceURL"; + +JSObject* createError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->errorStructure(), message); +} + +JSObject* createEvalError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->evalErrorConstructor()->errorStructure(), message); +} + +JSObject* createRangeError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->rangeErrorConstructor()->errorStructure(), message); +} + +JSObject* createReferenceError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->referenceErrorConstructor()->errorStructure(), message); +} + +JSObject* createSyntaxError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->syntaxErrorConstructor()->errorStructure(), message); +} + +JSObject* createTypeError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->typeErrorConstructor()->errorStructure(), message); +} + +JSObject* createURIError(JSGlobalObject* globalObject, const UString& message) +{ + ASSERT(!message.isEmpty()); + return ErrorInstance::create(&globalObject->globalData(), globalObject->URIErrorConstructor()->errorStructure(), message); +} + +JSObject* createError(ExecState* exec, const UString& message) +{ + return createError(exec->lexicalGlobalObject(), message); +} + +JSObject* createEvalError(ExecState* exec, const UString& message) +{ + return createEvalError(exec->lexicalGlobalObject(), message); +} + +JSObject* createRangeError(ExecState* exec, const UString& message) +{ + return createRangeError(exec->lexicalGlobalObject(), message); +} - MarkedArgumentBuffer args; - if (message.isEmpty()) - args.append(jsString(exec, name)); - else - args.append(jsString(exec, message)); - ConstructData constructData; - ConstructType constructType = constructor->getConstructData(constructData); - JSObject* error = construct(exec, constructor, constructType, constructData, args); - - if (lineNumber != -1) - error->putWithAttributes(exec, Identifier(exec, "line"), jsNumber(exec, lineNumber), ReadOnly | DontDelete); +JSObject* createReferenceError(ExecState* exec, const UString& message) +{ + return createReferenceError(exec->lexicalGlobalObject(), message); +} + +JSObject* createSyntaxError(ExecState* exec, const UString& message) +{ + return createSyntaxError(exec->lexicalGlobalObject(), message); +} + +JSObject* createTypeError(ExecState* exec, const UString& message) +{ + return createTypeError(exec->lexicalGlobalObject(), message); +} + +JSObject* createURIError(ExecState* exec, const UString& message) +{ + return createURIError(exec->lexicalGlobalObject(), message); +} + +JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source) +{ + intptr_t sourceID = source.provider()->asID(); + const UString& sourceURL = source.provider()->url(); + + if (line != -1) + error->putWithAttributes(globalData, Identifier(globalData, linePropertyName), jsNumber(line), ReadOnly | DontDelete); if (sourceID != -1) - error->putWithAttributes(exec, Identifier(exec, "sourceId"), jsNumber(exec, sourceID), ReadOnly | DontDelete); + error->putWithAttributes(globalData, Identifier(globalData, sourceIdPropertyName), jsNumber((double)sourceID), ReadOnly | DontDelete); if (!sourceURL.isNull()) - error->putWithAttributes(exec, Identifier(exec, "sourceURL"), jsString(exec, sourceURL), ReadOnly | DontDelete); + error->putWithAttributes(globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete); return error; } -JSObject* Error::create(ExecState* exec, ErrorType type, const char* message) +JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source) { - return create(exec, type, message, -1, -1, UString()); + return addErrorInfo(&exec->globalData(), error, line, source); } -JSObject* throwError(ExecState* exec, JSObject* error) +bool hasErrorInfo(ExecState* exec, JSObject* error) { - exec->setException(error); - return error; + return error->hasProperty(exec, Identifier(exec, linePropertyName)) + || error->hasProperty(exec, Identifier(exec, sourceIdPropertyName)) + || error->hasProperty(exec, Identifier(exec, sourceURLPropertyName)); } -JSObject* throwError(ExecState* exec, ErrorType type) +JSValue throwError(ExecState* exec, JSValue error) { - JSObject* error = Error::create(exec, type, UString(), -1, -1, UString()); - exec->setException(error); + exec->globalData().exception = error; return error; } -JSObject* throwError(ExecState* exec, ErrorType type, const UString& message) +JSObject* throwError(ExecState* exec, JSObject* error) { - JSObject* error = Error::create(exec, type, message, -1, -1, UString()); - exec->setException(error); + exec->globalData().exception = error; return error; } -JSObject* throwError(ExecState* exec, ErrorType type, const char* message) +JSObject* throwTypeError(ExecState* exec) { - JSObject* error = Error::create(exec, type, message, -1, -1, UString()); - exec->setException(error); - return error; + return throwError(exec, createTypeError(exec, "Type error")); } -JSObject* throwError(ExecState* exec, ErrorType type, const UString& message, int line, intptr_t sourceID, const UString& sourceURL) +JSObject* throwSyntaxError(ExecState* exec) { - JSObject* error = Error::create(exec, type, message, line, sourceID, sourceURL); - exec->setException(error); - return error; + return throwError(exec, createSyntaxError(exec, "Syntax error")); +} + +class StrictModeTypeErrorFunction : public InternalFunction { +public: + StrictModeTypeErrorFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const UString& message) + : InternalFunction(&exec->globalData(), globalObject, structure, exec->globalData().propertyNames->emptyIdentifier) + , m_message(message) + { + } + + static EncodedJSValue JSC_HOST_CALL constructThrowTypeError(ExecState* exec) + { + throwTypeError(exec, static_cast<StrictModeTypeErrorFunction*>(exec->callee())->m_message); + return JSValue::encode(jsNull()); + } + + ConstructType getConstructData(ConstructData& constructData) + { + constructData.native.function = constructThrowTypeError; + return ConstructTypeHost; + } + + static EncodedJSValue JSC_HOST_CALL callThrowTypeError(ExecState* exec) + { + throwTypeError(exec, static_cast<StrictModeTypeErrorFunction*>(exec->callee())->m_message); + return JSValue::encode(jsNull()); + } + + CallType getCallData(CallData& callData) + { + callData.native.function = callThrowTypeError; + return CallTypeHost; + } + +private: + UString m_message; +}; + +COMPILE_ASSERT(sizeof(StrictModeTypeErrorFunction) <= sizeof(CollectorCell), sizeof_StrictModeTypeErrorFunction_must_be_less_than_CollectorCell); + +JSValue createTypeErrorFunction(ExecState* exec, const UString& message) +{ + return new (exec) StrictModeTypeErrorFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->internalFunctionStructure(), message); } } // namespace JSC diff --git a/JavaScriptCore/runtime/Error.h b/JavaScriptCore/runtime/Error.h index e959cff..c0f9d32 100644 --- a/JavaScriptCore/runtime/Error.h +++ b/JavaScriptCore/runtime/Error.h @@ -23,44 +23,56 @@ #ifndef Error_h #define Error_h +#include "JSObject.h" #include <stdint.h> namespace JSC { class ExecState; + class JSGlobalData; + class JSGlobalObject; class JSObject; + class SourceCode; + class Structure; class UString; - /** - * Types of Native Errors available. For custom errors, GeneralError - * should be used. - */ - enum ErrorType { - GeneralError = 0, - EvalError = 1, - RangeError = 2, - ReferenceError = 3, - SyntaxError = 4, - TypeError = 5, - URIError = 6 - }; - - extern const char* expressionBeginOffsetPropertyName; - extern const char* expressionCaretOffsetPropertyName; - extern const char* expressionEndOffsetPropertyName; - - class Error { - public: - static JSObject* create(ExecState*, ErrorType, const UString& message, int lineNumber, intptr_t sourceID, const UString& sourceURL); - static JSObject* create(ExecState*, ErrorType, const char* message); - }; + // Methods to create a range of internal errors. + JSObject* createError(JSGlobalObject*, const UString&); + JSObject* createEvalError(JSGlobalObject*, const UString&); + JSObject* createRangeError(JSGlobalObject*, const UString&); + JSObject* createReferenceError(JSGlobalObject*, const UString&); + JSObject* createSyntaxError(JSGlobalObject*, const UString&); + JSObject* createTypeError(JSGlobalObject*, const UString&); + JSObject* createURIError(JSGlobalObject*, const UString&); + // ExecState wrappers. + JSObject* createError(ExecState*, const UString&); + JSObject* createEvalError(ExecState*, const UString&); + JSObject* createRangeError(ExecState*, const UString&); + JSObject* createReferenceError(ExecState*, const UString&); + JSObject* createSyntaxError(ExecState*, const UString&); + JSObject* createTypeError(ExecState*, const UString&); + JSObject* createURIError(ExecState*, const UString&); + + // Methods to add + bool hasErrorInfo(ExecState*, JSObject* error); + JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&); + // ExecState wrappers. + JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&); - JSObject* throwError(ExecState*, ErrorType, const UString& message, int lineNumber, intptr_t sourceID, const UString& sourceURL); - JSObject* throwError(ExecState*, ErrorType, const UString& message); - JSObject* throwError(ExecState*, ErrorType, const char* message); - JSObject* throwError(ExecState*, ErrorType); + // Methods to throw Errors. + JSValue throwError(ExecState*, JSValue); JSObject* throwError(ExecState*, JSObject*); + // Convenience wrappers, create an throw an exception with a default message. + JSObject* throwTypeError(ExecState*); + JSObject* throwSyntaxError(ExecState*); + + // Convenience wrappers, wrap result as an EncodedJSValue. + inline EncodedJSValue throwVMError(ExecState* exec, JSValue error) { return JSValue::encode(throwError(exec, error)); } + inline EncodedJSValue throwVMTypeError(ExecState* exec) { return JSValue::encode(throwTypeError(exec)); } + + JSValue createTypeErrorFunction(ExecState* exec, const UString& message); + } // namespace JSC #endif // Error_h diff --git a/JavaScriptCore/runtime/ErrorConstructor.cpp b/JavaScriptCore/runtime/ErrorConstructor.cpp index b9c3f58..4326a4d 100644 --- a/JavaScriptCore/runtime/ErrorConstructor.cpp +++ b/JavaScriptCore/runtime/ErrorConstructor.cpp @@ -29,26 +29,21 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ErrorConstructor); -ErrorConstructor::ErrorConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ErrorPrototype* errorPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, errorPrototype->classInfo()->className)) +ErrorConstructor::ErrorConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ErrorPrototype* errorPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, errorPrototype->classInfo()->className)) { // ECMA 15.11.3.1 Error.prototype putDirectWithoutTransition(exec->propertyNames().prototype, errorPrototype, DontEnum | DontDelete | ReadOnly); - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), DontDelete | ReadOnly | DontEnum); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), DontDelete | ReadOnly | DontEnum); } // ECMA 15.9.3 -ErrorInstance* constructError(ExecState* exec, const ArgList& args) -{ - ErrorInstance* obj = new (exec) ErrorInstance(exec->lexicalGlobalObject()->errorStructure()); - if (!args.at(0).isUndefined()) - obj->putDirect(exec->propertyNames().message, jsString(exec, args.at(0).toString(exec))); - return obj; -} -static JSObject* constructWithErrorConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithErrorConstructor(ExecState* exec) { - return constructError(exec, args); + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = exec->lexicalGlobalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); } ConstructType ErrorConstructor::getConstructData(ConstructData& constructData) @@ -57,11 +52,11 @@ ConstructType ErrorConstructor::getConstructData(ConstructData& constructData) return ConstructTypeHost; } -// ECMA 15.9.2 -static JSValue JSC_HOST_CALL callErrorConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callErrorConstructor(ExecState* exec) { - // "Error()" gives the sames result as "new Error()" - return constructError(exec, args); + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = exec->lexicalGlobalObject()->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); } CallType ErrorConstructor::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/ErrorConstructor.h b/JavaScriptCore/runtime/ErrorConstructor.h index e3d789b..3d0d706 100644 --- a/JavaScriptCore/runtime/ErrorConstructor.h +++ b/JavaScriptCore/runtime/ErrorConstructor.h @@ -30,15 +30,13 @@ namespace JSC { class ErrorConstructor : public InternalFunction { public: - ErrorConstructor(ExecState*, NonNullPassRefPtr<Structure>, ErrorPrototype*); + ErrorConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, ErrorPrototype*); private: virtual ConstructType getConstructData(ConstructData&); virtual CallType getCallData(CallData&); }; - ErrorInstance* constructError(ExecState*, const ArgList&); - } // namespace JSC #endif // ErrorConstructor_h diff --git a/JavaScriptCore/runtime/ErrorInstance.cpp b/JavaScriptCore/runtime/ErrorInstance.cpp index 1cdb87a..0f3153c 100644 --- a/JavaScriptCore/runtime/ErrorInstance.cpp +++ b/JavaScriptCore/runtime/ErrorInstance.cpp @@ -25,9 +25,30 @@ namespace JSC { const ClassInfo ErrorInstance::info = { "Error", 0, 0, 0 }; -ErrorInstance::ErrorInstance(NonNullPassRefPtr<Structure> structure) +ErrorInstance::ErrorInstance(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure) : JSObject(structure) + , m_appendSourceToMessage(false) { + putDirect(globalData->propertyNames->message, jsString(globalData, "")); +} + +ErrorInstance::ErrorInstance(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure, const UString& message) + : JSObject(structure) + , m_appendSourceToMessage(false) +{ + putDirect(globalData->propertyNames->message, jsString(globalData, message)); +} + +ErrorInstance* ErrorInstance::create(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure, const UString& message) +{ + return new (globalData) ErrorInstance(globalData, structure, message); +} + +ErrorInstance* ErrorInstance::create(ExecState* exec, NonNullPassRefPtr<Structure> structure, JSValue message) +{ + if (message.isUndefined()) + return new (exec) ErrorInstance(&exec->globalData(), structure); + return new (exec) ErrorInstance(&exec->globalData(), structure, message.toString(exec)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ErrorInstance.h b/JavaScriptCore/runtime/ErrorInstance.h index 9f53b51..b3bebec 100644 --- a/JavaScriptCore/runtime/ErrorInstance.h +++ b/JavaScriptCore/runtime/ErrorInstance.h @@ -27,10 +27,25 @@ namespace JSC { class ErrorInstance : public JSObject { public: - explicit ErrorInstance(NonNullPassRefPtr<Structure>); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; + + static ErrorInstance* create(JSGlobalData*, NonNullPassRefPtr<Structure>, const UString&); + static ErrorInstance* create(ExecState* exec, NonNullPassRefPtr<Structure>, JSValue message); + + + bool appendSourceToMessage() { return m_appendSourceToMessage; } + void setAppendSourceToMessage() { m_appendSourceToMessage = true; } + void clearAppendSourceToMessage() { m_appendSourceToMessage = false; } + + virtual bool isErrorInstance() const { return true; } + + protected: + explicit ErrorInstance(JSGlobalData*, NonNullPassRefPtr<Structure>); + explicit ErrorInstance(JSGlobalData*, NonNullPassRefPtr<Structure>, const UString&); + + bool m_appendSourceToMessage; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/ErrorPrototype.cpp b/JavaScriptCore/runtime/ErrorPrototype.cpp index eb35733..d18e7d8 100644 --- a/JavaScriptCore/runtime/ErrorPrototype.cpp +++ b/JavaScriptCore/runtime/ErrorPrototype.cpp @@ -32,23 +32,21 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ErrorPrototype); -static JSValue JSC_HOST_CALL errorProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState*); // ECMA 15.9.4 -ErrorPrototype::ErrorPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) - : ErrorInstance(structure) +ErrorPrototype::ErrorPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) + : ErrorInstance(&exec->globalData(), structure) { // The constructor will be added later in ErrorConstructor's constructor putDirectWithoutTransition(exec->propertyNames().name, jsNontrivialString(exec, "Error"), DontEnum); - putDirectWithoutTransition(exec->propertyNames().message, jsNontrivialString(exec, "Unknown error"), DontEnum); - - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, errorProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, errorProtoFuncToString), DontEnum); } -JSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec) { - JSObject* thisObj = thisValue.toThisObject(exec); + JSObject* thisObj = exec->hostThisValue().toThisObject(exec); JSValue name = thisObj->get(exec, exec->propertyNames().name); JSValue message = thisObj->get(exec, exec->propertyNames().message); @@ -56,12 +54,12 @@ JSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec, JSObject*, JSValue if (!name.isUndefined()) { if (!message.isUndefined()) - return jsMakeNontrivialString(exec, name.toString(exec), ": ", message.toString(exec)); - return jsNontrivialString(exec, name.toString(exec)); + return JSValue::encode(jsMakeNontrivialString(exec, name.toString(exec), ": ", message.toString(exec))); + return JSValue::encode(jsNontrivialString(exec, name.toString(exec))); } if (!message.isUndefined()) - return jsMakeNontrivialString(exec, "Error: ", message.toString(exec)); - return jsNontrivialString(exec, "Error"); + return JSValue::encode(jsMakeNontrivialString(exec, "Error: ", message.toString(exec))); + return JSValue::encode(jsNontrivialString(exec, "Error")); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ErrorPrototype.h b/JavaScriptCore/runtime/ErrorPrototype.h index a561590..fce2742 100644 --- a/JavaScriptCore/runtime/ErrorPrototype.h +++ b/JavaScriptCore/runtime/ErrorPrototype.h @@ -29,7 +29,7 @@ namespace JSC { class ErrorPrototype : public ErrorInstance { public: - ErrorPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + ErrorPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/ExceptionHelpers.cpp b/JavaScriptCore/runtime/ExceptionHelpers.cpp index b9c6319..1ef264c 100644 --- a/JavaScriptCore/runtime/ExceptionHelpers.cpp +++ b/JavaScriptCore/runtime/ExceptionHelpers.cpp @@ -31,11 +31,13 @@ #include "CodeBlock.h" #include "CallFrame.h" +#include "ErrorInstance.h" #include "JSGlobalObjectFunctions.h" #include "JSObject.h" #include "JSNotAnObject.h" #include "Interpreter.h" #include "Nodes.h" +#include "UStringConcatenate.h" namespace JSC { @@ -46,151 +48,98 @@ public: { } - virtual bool isWatchdogException() const { return true; } + virtual ComplType exceptionType() const { return Interrupted; } virtual UString toString(ExecState*) const { return "JavaScript execution exceeded timeout."; } }; -JSValue createInterruptedExecutionException(JSGlobalData* globalData) +JSObject* createInterruptedExecutionException(JSGlobalData* globalData) { return new (globalData) InterruptedExecutionError(globalData); } -static JSValue createError(ExecState* exec, ErrorType e, const char* msg) +class TerminatedExecutionError : public JSObject { +public: + TerminatedExecutionError(JSGlobalData* globalData) + : JSObject(globalData->terminatedExecutionErrorStructure) + { + } + + virtual ComplType exceptionType() const { return Terminated; } + + virtual UString toString(ExecState*) const { return "JavaScript execution terminated."; } +}; + +JSObject* createTerminatedExecutionException(JSGlobalData* globalData) { - return Error::create(exec, e, msg, -1, -1, UString()); + return new (globalData) TerminatedExecutionError(globalData); } -JSValue createStackOverflowError(ExecState* exec) +JSObject* createStackOverflowError(ExecState* exec) { - return createError(exec, RangeError, "Maximum call stack size exceeded."); + return createRangeError(exec, "Maximum call stack size exceeded."); } -JSValue createTypeError(ExecState* exec, const char* message) +JSObject* createStackOverflowError(JSGlobalObject* globalObject) { - return createError(exec, TypeError, message); + return createRangeError(globalObject, "Maximum call stack size exceeded."); } -JSValue createUndefinedVariableError(ExecState* exec, const Identifier& ident, unsigned bytecodeOffset, CodeBlock* codeBlock) +JSObject* createUndefinedVariableError(ExecState* exec, const Identifier& ident) { - int startOffset = 0; - int endOffset = 0; - int divotPoint = 0; - int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); - JSObject* exception = Error::create(exec, ReferenceError, makeString("Can't find variable: ", ident.ustring()), line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); - exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); - return exception; + UString message(makeUString("Can't find variable: ", ident.ustring())); + return createReferenceError(exec, message); } -static UString createErrorMessage(ExecState* exec, CodeBlock* codeBlock, int, int expressionStart, int expressionStop, JSValue value, UString error) +JSObject* createInvalidParamError(ExecState* exec, const char* op, JSValue value) { - if (!expressionStop || expressionStart > codeBlock->source()->length()) - return makeString(value.toString(exec), " is ", error); - if (expressionStart < expressionStop) - return makeString("Result of expression '", codeBlock->source()->getRange(expressionStart, expressionStop), "' [", value.toString(exec), "] is ", error, "."); - - // No range information, so give a few characters of context - const UChar* data = codeBlock->source()->data(); - int dataLength = codeBlock->source()->length(); - int start = expressionStart; - int stop = expressionStart; - // Get up to 20 characters of context to the left and right of the divot, clamping to the line. - // then strip whitespace. - while (start > 0 && (expressionStart - start < 20) && data[start - 1] != '\n') - start--; - while (start < (expressionStart - 1) && isStrWhiteSpace(data[start])) - start++; - while (stop < dataLength && (stop - expressionStart < 20) && data[stop] != '\n') - stop++; - while (stop > expressionStart && isStrWhiteSpace(data[stop])) - stop--; - return makeString("Result of expression near '...", codeBlock->source()->getRange(start, stop), "...' [", value.toString(exec), "] is ", error, "."); + UString errorMessage = makeUString("'", value.toString(exec), "' is not a valid argument for '", op, "'"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); + return exception; } -JSObject* createInvalidParamError(ExecState* exec, const char* op, JSValue value, unsigned bytecodeOffset, CodeBlock* codeBlock) +JSObject* createNotAConstructorError(ExecState* exec, JSValue value) { - int startOffset = 0; - int endOffset = 0; - int divotPoint = 0; - int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); - UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint, divotPoint + endOffset, value, makeString("not a valid argument for '", op, "'")); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); - exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); + UString errorMessage = makeUString("'", value.toString(exec), "' is not a constructor"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); return exception; } -JSObject* createNotAConstructorError(ExecState* exec, JSValue value, unsigned bytecodeOffset, CodeBlock* codeBlock) +JSObject* createNotAFunctionError(ExecState* exec, JSValue value) { - int startOffset = 0; - int endOffset = 0; - int divotPoint = 0; - int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); - - // We're in a "new" expression, so we need to skip over the "new.." part - int startPoint = divotPoint - (startOffset ? startOffset - 4 : 0); // -4 for "new " - const UChar* data = codeBlock->source()->data(); - while (startPoint < divotPoint && isStrWhiteSpace(data[startPoint])) - startPoint++; - - UString errorMessage = createErrorMessage(exec, codeBlock, line, startPoint, divotPoint, value, "not a constructor"); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); - exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); + UString errorMessage = makeUString("'", value.toString(exec), "' is not a function"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); return exception; } -JSValue createNotAFunctionError(ExecState* exec, JSValue value, unsigned bytecodeOffset, CodeBlock* codeBlock) +JSObject* createNotAnObjectError(ExecState* exec, JSValue value) { - int startOffset = 0; - int endOffset = 0; - int divotPoint = 0; - int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); - UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint - startOffset, divotPoint, value, "not a function"); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); - exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); + UString errorMessage = makeUString("'", value.toString(exec), "' is not an object"); + JSObject* exception = createTypeError(exec, errorMessage); + ASSERT(exception->isErrorInstance()); + static_cast<ErrorInstance*>(exception)->setAppendSourceToMessage(); return exception; } -JSNotAnObjectErrorStub* createNotAnObjectErrorStub(ExecState* exec, bool isNull) +JSObject* createErrorForInvalidGlobalAssignment(ExecState* exec, const UString& propertyName) { - return new (exec) JSNotAnObjectErrorStub(exec, isNull); -} + return createReferenceError(exec, makeUString("Strict mode forbids implicit creation of global property '", propertyName, "'")); +} -JSObject* createNotAnObjectError(ExecState* exec, JSNotAnObjectErrorStub* error, unsigned bytecodeOffset, CodeBlock* codeBlock) +JSObject* throwOutOfMemoryError(ExecState* exec) { - // Both op_construct and op_instanceof require a use of op_get_by_id to get - // the prototype property from an object. The exception messages for exceptions - // thrown by these instances op_get_by_id need to reflect this. - OpcodeID followingOpcodeID; - if (codeBlock->getByIdExceptionInfoForBytecodeOffset(exec, bytecodeOffset, followingOpcodeID)) { - ASSERT(followingOpcodeID == op_construct || followingOpcodeID == op_instanceof); - if (followingOpcodeID == op_construct) - return createNotAConstructorError(exec, error->isNull() ? jsNull() : jsUndefined(), bytecodeOffset, codeBlock); - return createInvalidParamError(exec, "instanceof", error->isNull() ? jsNull() : jsUndefined(), bytecodeOffset, codeBlock); - } - - int startOffset = 0; - int endOffset = 0; - int divotPoint = 0; - int line = codeBlock->expressionRangeForBytecodeOffset(exec, bytecodeOffset, divotPoint, startOffset, endOffset); - UString errorMessage = createErrorMessage(exec, codeBlock, line, divotPoint - startOffset, divotPoint, error->isNull() ? jsNull() : jsUndefined(), "not an object"); - JSObject* exception = Error::create(exec, TypeError, errorMessage, line, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->sourceURL()); - exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionCaretOffsetPropertyName), jsNumber(exec, divotPoint), ReadOnly | DontDelete); - exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete); - return exception; + return throwError(exec, createError(exec, "Out of memory")); } -JSValue throwOutOfMemoryError(ExecState* exec) +JSObject* throwStackOverflowError(ExecState* exec) { - return throwError(exec, GeneralError, "Out of memory"); + return throwError(exec, createStackOverflowError(exec)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ExceptionHelpers.h b/JavaScriptCore/runtime/ExceptionHelpers.h index b6e7373..7edffad 100644 --- a/JavaScriptCore/runtime/ExceptionHelpers.h +++ b/JavaScriptCore/runtime/ExceptionHelpers.h @@ -29,6 +29,7 @@ #ifndef ExceptionHelpers_h #define ExceptionHelpers_h +#include "JSValue.h" namespace JSC { @@ -36,22 +37,25 @@ namespace JSC { class ExecState; class Identifier; class JSGlobalData; + class JSGlobalObject; class JSNotAnObjectErrorStub; class JSObject; - class JSValue; class Node; struct Instruction; - JSValue createInterruptedExecutionException(JSGlobalData*); - JSValue createStackOverflowError(ExecState*); - JSValue createTypeError(ExecState*, const char* message); - JSValue createUndefinedVariableError(ExecState*, const Identifier&, unsigned bytecodeOffset, CodeBlock*); - JSNotAnObjectErrorStub* createNotAnObjectErrorStub(ExecState*, bool isNull); - JSObject* createInvalidParamError(ExecState*, const char* op, JSValue, unsigned bytecodeOffset, CodeBlock*); - JSObject* createNotAConstructorError(ExecState*, JSValue, unsigned bytecodeOffset, CodeBlock*); - JSValue createNotAFunctionError(ExecState*, JSValue, unsigned bytecodeOffset, CodeBlock*); - JSObject* createNotAnObjectError(ExecState*, JSNotAnObjectErrorStub*, unsigned bytecodeOffset, CodeBlock*); - JSValue throwOutOfMemoryError(ExecState*); + JSObject* createInterruptedExecutionException(JSGlobalData*); + JSObject* createTerminatedExecutionException(JSGlobalData*); + JSObject* createStackOverflowError(ExecState*); + JSObject* createStackOverflowError(JSGlobalObject*); + JSObject* createUndefinedVariableError(ExecState*, const Identifier&); + JSObject* createNotAnObjectError(ExecState*, JSValue); + JSObject* createInvalidParamError(ExecState*, const char* op, JSValue); + JSObject* createNotAConstructorError(ExecState*, JSValue); + JSObject* createNotAFunctionError(ExecState*, JSValue); + JSObject* createErrorForInvalidGlobalAssignment(ExecState*, const UString&); + + JSObject* throwOutOfMemoryError(ExecState*); + JSObject* throwStackOverflowError(ExecState*); } // namespace JSC diff --git a/JavaScriptCore/runtime/Executable.cpp b/JavaScriptCore/runtime/Executable.cpp index 79900dc..f229f96 100644 --- a/JavaScriptCore/runtime/Executable.cpp +++ b/JavaScriptCore/runtime/Executable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ #include "CodeBlock.h" #include "JIT.h" #include "Parser.h" -#include "StringBuilder.h" +#include "UStringBuilder.h" #include "Vector.h" namespace JSC { @@ -45,236 +45,269 @@ VPtrHackExecutable::~VPtrHackExecutable() { } +EvalExecutable::EvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext) + : ScriptExecutable(exec, source, inStrictContext) +{ +} + EvalExecutable::~EvalExecutable() { - delete m_evalCodeBlock; +} + +ProgramExecutable::ProgramExecutable(ExecState* exec, const SourceCode& source) + : ScriptExecutable(exec, source, false) +{ } ProgramExecutable::~ProgramExecutable() { - delete m_programCodeBlock; +} + +FunctionExecutable::FunctionExecutable(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext, int firstLine, int lastLine) + : ScriptExecutable(globalData, source, inStrictContext) + , m_numCapturedVariables(0) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_name(name) + , m_symbolTable(0) +{ + m_firstLine = firstLine; + m_lastLine = lastLine; +} + +FunctionExecutable::FunctionExecutable(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext, int firstLine, int lastLine) + : ScriptExecutable(exec, source, inStrictContext) + , m_numCapturedVariables(0) + , m_forceUsesArguments(forceUsesArguments) + , m_parameters(parameters) + , m_name(name) + , m_symbolTable(0) +{ + m_firstLine = firstLine; + m_lastLine = lastLine; } FunctionExecutable::~FunctionExecutable() { - delete m_codeBlock; } -JSObject* EvalExecutable::compile(ExecState* exec, ScopeChainNode* scopeChainNode) +JSObject* EvalExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode) { - int errLine; - UString errMsg; - RefPtr<EvalNode> evalNode = exec->globalData().parser->parse<EvalNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); - if (!evalNode) - return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); - recordParse(evalNode->features(), evalNode->lineNo(), evalNode->lastLine()); + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<EvalNode> evalNode = globalData->parser->parse<EvalNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!evalNode) { + ASSERT(exception); + return exception; + } + recordParse(evalNode->features(), evalNode->hasCapturedVariables(), evalNode->lineNo(), evalNode->lastLine()); ScopeChain scopeChain(scopeChainNode); JSGlobalObject* globalObject = scopeChain.globalObject(); ASSERT(!m_evalCodeBlock); - m_evalCodeBlock = new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth()); - OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(evalNode.get(), globalObject->debugger(), scopeChain, m_evalCodeBlock->symbolTable(), m_evalCodeBlock)); + m_evalCodeBlock = adoptPtr(new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth())); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(evalNode.get(), scopeChain, m_evalCodeBlock->symbolTable(), m_evalCodeBlock.get()))); generator->generate(); evalNode->destroyData(); + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_evalCodeBlock.get()); +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_evalCodeBlock->discardBytecode(); +#endif + } +#endif + return 0; } JSObject* ProgramExecutable::checkSyntax(ExecState* exec) { - int errLine; - UString errMsg; - RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); - if (!programNode) - return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); - return 0; + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> programNode = globalData->parser->parse<ProgramNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, JSParseNormal, &exception); + if (programNode) + return 0; + ASSERT(exception); + return exception; } -JSObject* ProgramExecutable::compile(ExecState* exec, ScopeChainNode* scopeChainNode) +JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode) { - int errLine; - UString errMsg; - RefPtr<ProgramNode> programNode = exec->globalData().parser->parse<ProgramNode>(&exec->globalData(), exec->lexicalGlobalObject()->debugger(), exec, m_source, &errLine, &errMsg); - if (!programNode) - return Error::create(exec, SyntaxError, errMsg, errLine, m_source.provider()->asID(), m_source.provider()->url()); - recordParse(programNode->features(), programNode->lineNo(), programNode->lastLine()); + ASSERT(!m_programCodeBlock); + + JSObject* exception = 0; + JSGlobalData* globalData = &exec->globalData(); + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> programNode = globalData->parser->parse<ProgramNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!programNode) { + ASSERT(exception); + return exception; + } + recordParse(programNode->features(), programNode->hasCapturedVariables(), programNode->lineNo(), programNode->lastLine()); ScopeChain scopeChain(scopeChainNode); JSGlobalObject* globalObject = scopeChain.globalObject(); - ASSERT(!m_programCodeBlock); - m_programCodeBlock = new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider()); - OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(programNode.get(), globalObject->debugger(), scopeChain, &globalObject->symbolTable(), m_programCodeBlock)); + m_programCodeBlock = adoptPtr(new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider())); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(programNode.get(), scopeChain, &globalObject->symbolTable(), m_programCodeBlock.get()))); generator->generate(); programNode->destroyData(); - return 0; + +#if ENABLE(JIT) + if (exec->globalData().canUseJIT()) { + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_programCodeBlock.get()); +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_programCodeBlock->discardBytecode(); +#endif + } +#endif + + return 0; } -void FunctionExecutable::compile(ExecState*, ScopeChainNode* scopeChainNode) +JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, ScopeChainNode* scopeChainNode) { + JSObject* exception = 0; JSGlobalData* globalData = scopeChainNode->globalData; - RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(globalData, 0, 0, m_source); + RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(exec->lexicalGlobalObject(), 0, 0, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!body) { + ASSERT(exception); + return exception; + } if (m_forceUsesArguments) body->setUsesArguments(); body->finishParsing(m_parameters, m_name); - recordParse(body->features(), body->lineNo(), body->lastLine()); + recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); ScopeChain scopeChain(scopeChainNode); JSGlobalObject* globalObject = scopeChain.globalObject(); - ASSERT(!m_codeBlock); - m_codeBlock = new FunctionCodeBlock(this, FunctionCode, source().provider(), source().startOffset()); - OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(body.get(), globalObject->debugger(), scopeChain, m_codeBlock->symbolTable(), m_codeBlock)); + ASSERT(!m_codeBlockForCall); + m_codeBlockForCall = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), false)); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChain, m_codeBlockForCall->symbolTable(), m_codeBlockForCall.get()))); generator->generate(); - m_numParameters = m_codeBlock->m_numParameters; - ASSERT(m_numParameters); - m_numVariables = m_codeBlock->m_numVars; + m_numParametersForCall = m_codeBlockForCall->m_numParameters; + ASSERT(m_numParametersForCall); + m_numCapturedVariables = m_codeBlockForCall->m_numCapturedVars; + m_symbolTable = m_codeBlockForCall->sharedSymbolTable(); body->destroyData(); -} #if ENABLE(JIT) - -void EvalExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) -{ - CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); - m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); - -#if !ENABLE(OPCODE_SAMPLING) - if (!BytecodeGenerator::dumpsGeneratedCode()) - codeBlock->discardBytecode(); -#endif -} - -void ProgramExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) -{ - CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); - m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); - + if (exec->globalData().canUseJIT()) { + m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_codeBlockForCall.get(), &m_jitCodeForCallWithArityCheck); #if !ENABLE(OPCODE_SAMPLING) - if (!BytecodeGenerator::dumpsGeneratedCode()) - codeBlock->discardBytecode(); + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_codeBlockForCall->discardBytecode(); #endif -} - -void FunctionExecutable::generateJITCode(ExecState* exec, ScopeChainNode* scopeChainNode) -{ - CodeBlock* codeBlock = &bytecode(exec, scopeChainNode); - m_jitCode = JIT::compile(scopeChainNode->globalData, codeBlock); - -#if !ENABLE(OPCODE_SAMPLING) - if (!BytecodeGenerator::dumpsGeneratedCode()) - codeBlock->discardBytecode(); -#endif -} - + } #endif -void FunctionExecutable::markAggregate(MarkStack& markStack) -{ - if (m_codeBlock) - m_codeBlock->markAggregate(markStack); + return 0; } -ExceptionInfo* FunctionExecutable::reparseExceptionInfo(JSGlobalData* globalData, ScopeChainNode* scopeChainNode, CodeBlock* codeBlock) +JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, ScopeChainNode* scopeChainNode) { - RefPtr<FunctionBodyNode> newFunctionBody = globalData->parser->parse<FunctionBodyNode>(globalData, 0, 0, m_source); + JSObject* exception = 0; + JSGlobalData* globalData = scopeChainNode->globalData; + RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(exec->lexicalGlobalObject(), 0, 0, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, &exception); + if (!body) { + ASSERT(exception); + return exception; + } if (m_forceUsesArguments) - newFunctionBody->setUsesArguments(); - newFunctionBody->finishParsing(m_parameters, m_name); + body->setUsesArguments(); + body->finishParsing(m_parameters, m_name); + recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); ScopeChain scopeChain(scopeChainNode); JSGlobalObject* globalObject = scopeChain.globalObject(); - OwnPtr<CodeBlock> newCodeBlock(new FunctionCodeBlock(this, FunctionCode, source().provider(), source().startOffset())); - globalData->functionCodeBlockBeingReparsed = newCodeBlock.get(); - - OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(newFunctionBody.get(), globalObject->debugger(), scopeChain, newCodeBlock->symbolTable(), newCodeBlock.get())); - generator->setRegeneratingForExceptionInfo(static_cast<FunctionCodeBlock*>(codeBlock)); + ASSERT(!m_codeBlockForConstruct); + m_codeBlockForConstruct = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), true)); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChain, m_codeBlockForConstruct->symbolTable(), m_codeBlockForConstruct.get()))); generator->generate(); + m_numParametersForConstruct = m_codeBlockForConstruct->m_numParameters; + ASSERT(m_numParametersForConstruct); + m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars; + m_symbolTable = m_codeBlockForConstruct->sharedSymbolTable(); - ASSERT(newCodeBlock->instructionCount() == codeBlock->instructionCount()); + body->destroyData(); #if ENABLE(JIT) - JITCode newJITCode = JIT::compile(globalData, newCodeBlock.get()); - ASSERT(newJITCode.size() == generatedJITCode().size()); + if (exec->globalData().canUseJIT()) { + m_jitCodeForConstruct = JIT::compile(scopeChainNode->globalData, m_codeBlockForConstruct.get(), &m_jitCodeForConstructWithArityCheck); +#if !ENABLE(OPCODE_SAMPLING) + if (!BytecodeGenerator::dumpsGeneratedCode()) + m_codeBlockForConstruct->discardBytecode(); +#endif + } #endif - globalData->functionCodeBlockBeingReparsed = 0; - - return newCodeBlock->extractExceptionInfo(); + return 0; } -ExceptionInfo* EvalExecutable::reparseExceptionInfo(JSGlobalData* globalData, ScopeChainNode* scopeChainNode, CodeBlock* codeBlock) +void FunctionExecutable::markAggregate(MarkStack& markStack) { - RefPtr<EvalNode> newEvalBody = globalData->parser->parse<EvalNode>(globalData, 0, 0, m_source); - - ScopeChain scopeChain(scopeChainNode); - JSGlobalObject* globalObject = scopeChain.globalObject(); - - OwnPtr<EvalCodeBlock> newCodeBlock(new EvalCodeBlock(this, globalObject, source().provider(), scopeChain.localDepth())); - - OwnPtr<BytecodeGenerator> generator(new BytecodeGenerator(newEvalBody.get(), globalObject->debugger(), scopeChain, newCodeBlock->symbolTable(), newCodeBlock.get())); - generator->setRegeneratingForExceptionInfo(static_cast<EvalCodeBlock*>(codeBlock)); - generator->generate(); - - ASSERT(newCodeBlock->instructionCount() == codeBlock->instructionCount()); - -#if ENABLE(JIT) - JITCode newJITCode = JIT::compile(globalData, newCodeBlock.get()); - ASSERT(newJITCode.size() == generatedJITCode().size()); -#endif - - return newCodeBlock->extractExceptionInfo(); + if (m_codeBlockForCall) + m_codeBlockForCall->markAggregate(markStack); + if (m_codeBlockForConstruct) + m_codeBlockForConstruct->markAggregate(markStack); } void FunctionExecutable::recompile(ExecState*) { - delete m_codeBlock; - m_codeBlock = 0; - m_numParameters = NUM_PARAMETERS_NOT_COMPILED; + m_codeBlockForCall.clear(); + m_codeBlockForConstruct.clear(); + m_numParametersForCall = NUM_PARAMETERS_NOT_COMPILED; + m_numParametersForConstruct = NUM_PARAMETERS_NOT_COMPILED; #if ENABLE(JIT) - m_jitCode = JITCode(); + m_jitCodeForCall = JITCode(); + m_jitCodeForConstruct = JITCode(); #endif } -PassRefPtr<FunctionExecutable> FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, int* errLine, UString* errMsg) +PassRefPtr<FunctionExecutable> FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, JSObject** exception) { - RefPtr<ProgramNode> program = exec->globalData().parser->parse<ProgramNode>(&exec->globalData(), debugger, exec, source, errLine, errMsg); - if (!program) + JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject(); + RefPtr<ProgramNode> program = exec->globalData().parser->parse<ProgramNode>(lexicalGlobalObject, debugger, exec, source, 0, JSParseNormal, exception); + if (!program) { + ASSERT(*exception); return 0; + } + // Uses of this function that would not result in a single function expression are invalid. StatementNode* exprStatement = program->singleStatement(); ASSERT(exprStatement); ASSERT(exprStatement->isExprStatement()); - if (!exprStatement || !exprStatement->isExprStatement()) - return 0; - ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr(); ASSERT(funcExpr); ASSERT(funcExpr->isFuncExprNode()); - if (!funcExpr || !funcExpr->isFuncExprNode()) - return 0; - FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body(); ASSERT(body); - return FunctionExecutable::create(&exec->globalData(), functionName, body->source(), body->usesArguments(), body->parameters(), body->lineNo(), body->lastLine()); + + return FunctionExecutable::create(&exec->globalData(), functionName, body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine()); } UString FunctionExecutable::paramString() const { FunctionParameters& parameters = *m_parameters; - StringBuilder builder; + UStringBuilder builder; for (size_t pos = 0; pos < parameters.size(); ++pos) { if (!builder.isEmpty()) builder.append(", "); builder.append(parameters[pos].ustring()); } - return builder.build(); + return builder.toUString(); } -}; - - +} diff --git a/JavaScriptCore/runtime/Executable.h b/JavaScriptCore/runtime/Executable.h index f74abe9..14ed927 100644 --- a/JavaScriptCore/runtime/Executable.h +++ b/JavaScriptCore/runtime/Executable.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,16 +26,19 @@ #ifndef Executable_h #define Executable_h +#include "CallData.h" #include "JSFunction.h" #include "Interpreter.h" #include "Nodes.h" #include "SamplingTool.h" +#include <wtf/PassOwnPtr.h> namespace JSC { class CodeBlock; class Debugger; class EvalCodeBlock; + class FunctionCodeBlock; class ProgramCodeBlock; class ScopeChainNode; @@ -50,45 +53,76 @@ namespace JSC { public: ExecutableBase(int numParameters) - : m_numParameters(numParameters) + : m_numParametersForCall(numParameters) + , m_numParametersForConstruct(numParameters) { } virtual ~ExecutableBase() {} - bool isHostFunction() const { return m_numParameters == NUM_PARAMETERS_IS_HOST; } + bool isHostFunction() const + { + ASSERT((m_numParametersForCall == NUM_PARAMETERS_IS_HOST) == (m_numParametersForConstruct == NUM_PARAMETERS_IS_HOST)); + return m_numParametersForCall == NUM_PARAMETERS_IS_HOST; + } protected: - int m_numParameters; + int m_numParametersForCall; + int m_numParametersForConstruct; #if ENABLE(JIT) public: - JITCode& generatedJITCode() + JITCode& generatedJITCodeForCall() { - ASSERT(m_jitCode); - return m_jitCode; + ASSERT(m_jitCodeForCall); + return m_jitCodeForCall; } - ExecutablePool* getExecutablePool() + JITCode& generatedJITCodeForConstruct() { - return m_jitCode.getExecutablePool(); + ASSERT(m_jitCodeForConstruct); + return m_jitCodeForConstruct; } protected: - JITCode m_jitCode; + JITCode m_jitCodeForCall; + JITCode m_jitCodeForConstruct; + MacroAssemblerCodePtr m_jitCodeForCallWithArityCheck; + MacroAssemblerCodePtr m_jitCodeForConstructWithArityCheck; #endif }; #if ENABLE(JIT) class NativeExecutable : public ExecutableBase { + friend class JIT; public: - NativeExecutable(ExecState* exec) - : ExecutableBase(NUM_PARAMETERS_IS_HOST) + static PassRefPtr<NativeExecutable> create(MacroAssemblerCodePtr callThunk, NativeFunction function, MacroAssemblerCodePtr constructThunk, NativeFunction constructor) { - m_jitCode = JITCode(JITCode::HostFunction(exec->globalData().jitStubs.ctiNativeCallThunk())); + if (!callThunk) + return adoptRef(new NativeExecutable(JITCode(), function, JITCode(), constructor)); + return adoptRef(new NativeExecutable(JITCode::HostFunction(callThunk), function, JITCode::HostFunction(constructThunk), constructor)); } ~NativeExecutable(); + + NativeFunction function() { return m_function; } + + private: + NativeExecutable(JITCode callThunk, NativeFunction function, JITCode constructThunk, NativeFunction constructor) + : ExecutableBase(NUM_PARAMETERS_IS_HOST) + , m_function(function) + , m_constructor(constructor) + { + m_jitCodeForCall = callThunk; + m_jitCodeForConstruct = constructThunk; + m_jitCodeForCallWithArityCheck = callThunk.addressForCall(); + m_jitCodeForConstructWithArityCheck = constructThunk.addressForCall(); + } + + NativeFunction m_function; + // Probably should be a NativeConstructor, but this will currently require rewriting the JIT + // trampoline. It may be easier to make NativeFunction be passed 'this' as a part of the ArgList. + NativeFunction m_constructor; }; #endif @@ -104,12 +138,13 @@ namespace JSC { class ScriptExecutable : public ExecutableBase { public: - ScriptExecutable(JSGlobalData* globalData, const SourceCode& source) + ScriptExecutable(JSGlobalData* globalData, const SourceCode& source, bool isInStrictContext) : ExecutableBase(NUM_PARAMETERS_NOT_COMPILED) , m_source(source) - , m_features(0) + , m_features(isInStrictContext ? StrictModeFeature : 0) { #if ENABLE(CODEBLOCK_SAMPLING) + relaxAdoptionRequirement(); if (SamplingTool* sampler = globalData->interpreter->sampler()) sampler->notifyOfScope(this); #else @@ -117,12 +152,13 @@ namespace JSC { #endif } - ScriptExecutable(ExecState* exec, const SourceCode& source) + ScriptExecutable(ExecState* exec, const SourceCode& source, bool isInStrictContext) : ExecutableBase(NUM_PARAMETERS_NOT_COMPILED) , m_source(source) - , m_features(0) + , m_features(isInStrictContext ? StrictModeFeature : 0) { #if ENABLE(CODEBLOCK_SAMPLING) + relaxAdoptionRequirement(); if (SamplingTool* sampler = exec->globalData().interpreter->sampler()) sampler->notifyOfScope(this); #else @@ -138,20 +174,21 @@ namespace JSC { bool usesEval() const { return m_features & EvalFeature; } bool usesArguments() const { return m_features & ArgumentsFeature; } - bool needsActivation() const { return m_features & (EvalFeature | ClosureFeature | WithFeature | CatchFeature); } - - virtual ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*) = 0; + bool needsActivation() const { return m_hasCapturedVariables || m_features & (EvalFeature | WithFeature | CatchFeature); } + bool isStrictMode() const { return m_features & StrictModeFeature; } protected: - void recordParse(CodeFeatures features, int firstLine, int lastLine) + void recordParse(CodeFeatures features, bool hasCapturedVariables, int firstLine, int lastLine) { m_features = features; + m_hasCapturedVariables = hasCapturedVariables; m_firstLine = firstLine; m_lastLine = lastLine; } SourceCode m_source; CodeFeatures m_features; + bool m_hasCapturedVariables; int m_firstLine; int m_lastLine; }; @@ -161,40 +198,36 @@ namespace JSC { ~EvalExecutable(); - EvalCodeBlock& bytecode(ExecState* exec, ScopeChainNode* scopeChainNode) + JSObject* compile(ExecState* exec, ScopeChainNode* scopeChainNode) { - if (!m_evalCodeBlock) { - JSObject* error = compile(exec, scopeChainNode); - ASSERT_UNUSED(!error, error); - } - return *m_evalCodeBlock; + JSObject* error = 0; + if (!m_evalCodeBlock) + error = compileInternal(exec, scopeChainNode); + ASSERT(!error == !!m_evalCodeBlock); + return error; } - JSObject* compile(ExecState*, ScopeChainNode*); - - ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*); - static PassRefPtr<EvalExecutable> create(ExecState* exec, const SourceCode& source) { return adoptRef(new EvalExecutable(exec, source)); } - - private: - EvalExecutable(ExecState* exec, const SourceCode& source) - : ScriptExecutable(exec, source) - , m_evalCodeBlock(0) + EvalCodeBlock& generatedBytecode() { + ASSERT(m_evalCodeBlock); + return *m_evalCodeBlock; } - EvalCodeBlock* m_evalCodeBlock; + + static PassRefPtr<EvalExecutable> create(ExecState* exec, const SourceCode& source, bool isInStrictContext) { return adoptRef(new EvalExecutable(exec, source, isInStrictContext)); } #if ENABLE(JIT) - public: - JITCode& jitCode(ExecState* exec, ScopeChainNode* scopeChainNode) + JITCode& generatedJITCode() { - if (!m_jitCode) - generateJITCode(exec, scopeChainNode); - return m_jitCode; + return generatedJITCodeForCall(); } +#endif private: - void generateJITCode(ExecState*, ScopeChainNode*); -#endif + EvalExecutable(ExecState*, const SourceCode&, bool); + + JSObject* compileInternal(ExecState*, ScopeChainNode*); + + OwnPtr<EvalCodeBlock> m_evalCodeBlock; }; class ProgramExecutable : public ScriptExecutable { @@ -206,54 +239,49 @@ namespace JSC { ~ProgramExecutable(); - ProgramCodeBlock& bytecode(ExecState* exec, ScopeChainNode* scopeChainNode) + JSObject* compile(ExecState* exec, ScopeChainNode* scopeChainNode) { - if (!m_programCodeBlock) { - JSObject* error = compile(exec, scopeChainNode); - ASSERT_UNUSED(!error, error); - } - return *m_programCodeBlock; + JSObject* error = 0; + if (!m_programCodeBlock) + error = compileInternal(exec, scopeChainNode); + ASSERT(!error == !!m_programCodeBlock); + return error; } - JSObject* checkSyntax(ExecState*); - JSObject* compile(ExecState*, ScopeChainNode*); - - // CodeBlocks for program code are transient and therefore do not gain from from throwing out there exception information. - ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*) { ASSERT_NOT_REACHED(); return 0; } - - private: - ProgramExecutable(ExecState* exec, const SourceCode& source) - : ScriptExecutable(exec, source) - , m_programCodeBlock(0) + ProgramCodeBlock& generatedBytecode() { + ASSERT(m_programCodeBlock); + return *m_programCodeBlock; } - ProgramCodeBlock* m_programCodeBlock; + + JSObject* checkSyntax(ExecState*); #if ENABLE(JIT) - public: - JITCode& jitCode(ExecState* exec, ScopeChainNode* scopeChainNode) + JITCode& generatedJITCode() { - if (!m_jitCode) - generateJITCode(exec, scopeChainNode); - return m_jitCode; + return generatedJITCodeForCall(); } +#endif private: - void generateJITCode(ExecState*, ScopeChainNode*); -#endif + ProgramExecutable(ExecState*, const SourceCode&); + + JSObject* compileInternal(ExecState*, ScopeChainNode*); + + OwnPtr<ProgramCodeBlock> m_programCodeBlock; }; class FunctionExecutable : public ScriptExecutable { friend class JIT; public: - static PassRefPtr<FunctionExecutable> create(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) + static PassRefPtr<FunctionExecutable> create(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool isInStrictContext, int firstLine, int lastLine) { - return adoptRef(new FunctionExecutable(exec, name, source, forceUsesArguments, parameters, firstLine, lastLine)); + return adoptRef(new FunctionExecutable(exec, name, source, forceUsesArguments, parameters, isInStrictContext, firstLine, lastLine)); } - static PassRefPtr<FunctionExecutable> create(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) + static PassRefPtr<FunctionExecutable> create(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool isInStrictContext, int firstLine, int lastLine) { - return adoptRef(new FunctionExecutable(globalData, name, source, forceUsesArguments, parameters, firstLine, lastLine)); + return adoptRef(new FunctionExecutable(globalData, name, source, forceUsesArguments, parameters, isInStrictContext, firstLine, lastLine)); } ~FunctionExecutable(); @@ -262,80 +290,99 @@ namespace JSC { { return new (exec) JSFunction(exec, this, scopeChain); } + + // Returns either call or construct bytecode. This can be appropriate + // for answering questions that that don't vary between call and construct -- + // for example, argumentsRegister(). + FunctionCodeBlock& generatedBytecode() + { + if (m_codeBlockForCall) + return *m_codeBlockForCall; + ASSERT(m_codeBlockForConstruct); + return *m_codeBlockForConstruct; + } + + JSObject* compileForCall(ExecState* exec, ScopeChainNode* scopeChainNode) + { + JSObject* error = 0; + if (!m_codeBlockForCall) + error = compileForCallInternal(exec, scopeChainNode); + ASSERT(!error == !!m_codeBlockForCall); + return error; + } + + bool isGeneratedForCall() const + { + return m_codeBlockForCall; + } + + FunctionCodeBlock& generatedBytecodeForCall() + { + ASSERT(m_codeBlockForCall); + return *m_codeBlockForCall; + } - CodeBlock& bytecode(ExecState* exec, ScopeChainNode* scopeChainNode) + JSObject* compileForConstruct(ExecState* exec, ScopeChainNode* scopeChainNode) { - ASSERT(scopeChainNode); - if (!m_codeBlock) - compile(exec, scopeChainNode); - return *m_codeBlock; + JSObject* error = 0; + if (!m_codeBlockForConstruct) + error = compileForConstructInternal(exec, scopeChainNode); + ASSERT(!error == !!m_codeBlockForConstruct); + return error; } - bool isGenerated() const + bool isGeneratedForConstruct() const { - return m_codeBlock; + return m_codeBlockForConstruct; } - CodeBlock& generatedBytecode() + FunctionCodeBlock& generatedBytecodeForConstruct() { - ASSERT(m_codeBlock); - return *m_codeBlock; + ASSERT(m_codeBlockForConstruct); + return *m_codeBlockForConstruct; } const Identifier& name() { return m_name; } size_t parameterCount() const { return m_parameters->size(); } - size_t variableCount() const { return m_numVariables; } + unsigned capturedVariableCount() const { return m_numCapturedVariables; } UString paramString() const; + SharedSymbolTable* symbolTable() const { return m_symbolTable; } void recompile(ExecState*); - ExceptionInfo* reparseExceptionInfo(JSGlobalData*, ScopeChainNode*, CodeBlock*); - void markAggregate(MarkStack& markStack); - static PassRefPtr<FunctionExecutable> fromGlobalCode(const Identifier&, ExecState*, Debugger*, const SourceCode&, int* errLine = 0, UString* errMsg = 0); + void markAggregate(MarkStack&); + static PassRefPtr<FunctionExecutable> fromGlobalCode(const Identifier&, ExecState*, Debugger*, const SourceCode&, JSObject** exception); private: - FunctionExecutable(JSGlobalData* globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) - : ScriptExecutable(globalData, source) - , m_forceUsesArguments(forceUsesArguments) - , m_parameters(parameters) - , m_codeBlock(0) - , m_name(name) - , m_numVariables(0) - { - m_firstLine = firstLine; - m_lastLine = lastLine; - } + FunctionExecutable(JSGlobalData*, const Identifier& name, const SourceCode&, bool forceUsesArguments, FunctionParameters*, bool, int firstLine, int lastLine); + FunctionExecutable(ExecState*, const Identifier& name, const SourceCode&, bool forceUsesArguments, FunctionParameters*, bool, int firstLine, int lastLine); - FunctionExecutable(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, int firstLine, int lastLine) - : ScriptExecutable(exec, source) - , m_forceUsesArguments(forceUsesArguments) - , m_parameters(parameters) - , m_codeBlock(0) - , m_name(name) - , m_numVariables(0) - { - m_firstLine = firstLine; - m_lastLine = lastLine; - } + JSObject* compileForCallInternal(ExecState*, ScopeChainNode*); + JSObject* compileForConstructInternal(ExecState*, ScopeChainNode*); - void compile(ExecState*, ScopeChainNode*); + unsigned m_numCapturedVariables : 31; + bool m_forceUsesArguments : 1; - bool m_forceUsesArguments; RefPtr<FunctionParameters> m_parameters; - CodeBlock* m_codeBlock; + OwnPtr<FunctionCodeBlock> m_codeBlockForCall; + OwnPtr<FunctionCodeBlock> m_codeBlockForConstruct; Identifier m_name; - size_t m_numVariables; + SharedSymbolTable* m_symbolTable; #if ENABLE(JIT) public: - JITCode& jitCode(ExecState* exec, ScopeChainNode* scopeChainNode) + MacroAssemblerCodePtr generatedJITCodeForCallWithArityCheck() { - if (!m_jitCode) - generateJITCode(exec, scopeChainNode); - return m_jitCode; + ASSERT(m_jitCodeForCall); + ASSERT(m_jitCodeForCallWithArityCheck); + return m_jitCodeForCallWithArityCheck; } - private: - void generateJITCode(ExecState*, ScopeChainNode*); + MacroAssemblerCodePtr generatedJITCodeForConstructWithArityCheck() + { + ASSERT(m_jitCodeForConstruct); + ASSERT(m_jitCodeForConstructWithArityCheck); + return m_jitCodeForConstructWithArityCheck; + } #endif }; @@ -351,6 +398,13 @@ namespace JSC { return m_executable->isHostFunction(); } +#if ENABLE(JIT) + inline NativeFunction JSFunction::nativeFunction() + { + ASSERT(isHostFunction()); + return static_cast<NativeExecutable*>(m_executable.get())->function(); + } +#endif } #endif diff --git a/JavaScriptCore/runtime/FunctionConstructor.cpp b/JavaScriptCore/runtime/FunctionConstructor.cpp index 9b0b1bb..45b4802 100644 --- a/JavaScriptCore/runtime/FunctionConstructor.cpp +++ b/JavaScriptCore/runtime/FunctionConstructor.cpp @@ -22,6 +22,7 @@ #include "FunctionConstructor.h" #include "Debugger.h" +#include "ExceptionHelpers.h" #include "FunctionPrototype.h" #include "JSFunction.h" #include "JSGlobalObject.h" @@ -29,24 +30,26 @@ #include "Lexer.h" #include "Nodes.h" #include "Parser.h" -#include "StringBuilder.h" +#include "UStringBuilder.h" +#include "UStringConcatenate.h" namespace JSC { ASSERT_CLASS_FITS_IN_CELL(FunctionConstructor); -FunctionConstructor::FunctionConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, FunctionPrototype* functionPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, functionPrototype->classInfo()->className)) +FunctionConstructor::FunctionConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, FunctionPrototype* functionPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, functionPrototype->classInfo()->className)) { putDirectWithoutTransition(exec->propertyNames().prototype, functionPrototype, DontEnum | DontDelete | ReadOnly); // Number of arguments for constructor - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontDelete | DontEnum); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontDelete | DontEnum); } -static JSObject* constructWithFunctionConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithFunctionConstructor(ExecState* exec) { - return constructFunction(exec, args); + ArgList args(exec); + return JSValue::encode(constructFunction(exec, args)); } ConstructType FunctionConstructor::getConstructData(ConstructData& constructData) @@ -55,9 +58,10 @@ ConstructType FunctionConstructor::getConstructData(ConstructData& constructData return ConstructTypeHost; } -static JSValue JSC_HOST_CALL callFunctionConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callFunctionConstructor(ExecState* exec) { - return constructFunction(exec, args); + ArgList args(exec); + return JSValue::encode(constructFunction(exec, args)); } // ECMA 15.3.1 The Function Constructor Called as a Function @@ -77,9 +81,9 @@ JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifi if (args.isEmpty()) program = "(function() { \n})"; else if (args.size() == 1) - program = makeString("(function() { ", args.at(0).toString(exec), "\n})"); + program = makeUString("(function() { ", args.at(0).toString(exec), "\n})"); else { - StringBuilder builder; + UStringBuilder builder; builder.append("(function("); builder.append(args.at(0).toString(exec)); for (size_t i = 1; i < args.size() - 1; i++) { @@ -89,18 +93,20 @@ JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifi builder.append(") { "); builder.append(args.at(args.size() - 1).toString(exec)); builder.append("\n})"); - program = builder.build(); + program = builder.toUString(); } - int errLine; - UString errMsg; + JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + JSGlobalData& globalData = globalObject->globalData(); SourceCode source = makeSource(program, sourceURL, lineNumber); - RefPtr<FunctionExecutable> function = FunctionExecutable::fromGlobalCode(functionName, exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); - if (!function) - return throwError(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), source.provider()->url()); + JSObject* exception = 0; + RefPtr<FunctionExecutable> function = FunctionExecutable::fromGlobalCode(functionName, exec, exec->dynamicGlobalObject()->debugger(), source, &exception); + if (!function) { + ASSERT(exception); + return throwError(exec, exception); + } - JSGlobalObject* globalObject = exec->lexicalGlobalObject(); - ScopeChain scopeChain(globalObject, globalObject->globalData(), globalObject, exec->globalThisValue()); + ScopeChain scopeChain(globalObject, &globalData, globalObject, exec->globalThisValue()); return new (exec) JSFunction(exec, function, scopeChain.node()); } diff --git a/JavaScriptCore/runtime/FunctionConstructor.h b/JavaScriptCore/runtime/FunctionConstructor.h index 197f320..6af4861 100644 --- a/JavaScriptCore/runtime/FunctionConstructor.h +++ b/JavaScriptCore/runtime/FunctionConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class FunctionConstructor : public InternalFunction { public: - FunctionConstructor(ExecState*, NonNullPassRefPtr<Structure>, FunctionPrototype*); + FunctionConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, FunctionPrototype*); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/FunctionPrototype.cpp b/JavaScriptCore/runtime/FunctionPrototype.cpp index 3475f08..cd7739d 100644 --- a/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -34,28 +34,28 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(FunctionPrototype); -static JSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionProtoFuncApply(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionProtoFuncCall(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncApply(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionProtoFuncCall(ExecState*); -FunctionPrototype::FunctionPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure) - : InternalFunction(&exec->globalData(), structure, exec->propertyNames().emptyIdentifier) +FunctionPrototype::FunctionPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : InternalFunction(&exec->globalData(), globalObject, structure, exec->propertyNames().nullIdentifier) { - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 0), DontDelete | ReadOnly | DontEnum); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); } -void FunctionPrototype::addFunctionProperties(ExecState* exec, Structure* prototypeFunctionStructure, NativeFunctionWrapper** callFunction, NativeFunctionWrapper** applyFunction) +void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* globalObject, Structure* prototypeFunctionStructure, NativeFunctionWrapper** callFunction, NativeFunctionWrapper** applyFunction) { - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, functionProtoFuncToString), DontEnum); - *applyFunction = new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().apply, functionProtoFuncApply); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, functionProtoFuncToString), DontEnum); + *applyFunction = new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().apply, functionProtoFuncApply); putDirectFunctionWithoutTransition(exec, *applyFunction, DontEnum); - *callFunction = new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().call, functionProtoFuncCall); + *callFunction = new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().call, functionProtoFuncCall); putDirectFunctionWithoutTransition(exec, *callFunction, DontEnum); } -static JSValue JSC_HOST_CALL callFunctionPrototype(ExecState*, JSObject*, JSValue, const ArgList&) +static EncodedJSValue JSC_HOST_CALL callFunctionPrototype(ExecState*) { - return jsUndefined(); + return JSValue::encode(jsUndefined()); } // ECMA 15.3.4 @@ -71,51 +71,53 @@ CallType FunctionPrototype::getCallData(CallData& callData) static inline void insertSemicolonIfNeeded(UString& functionBody) { ASSERT(functionBody[0] == '{'); - ASSERT(functionBody[functionBody.size() - 1] == '}'); + ASSERT(functionBody[functionBody.length() - 1] == '}'); - for (size_t i = functionBody.size() - 2; i > 0; --i) { + for (size_t i = functionBody.length() - 2; i > 0; --i) { UChar ch = functionBody[i]; if (!Lexer::isWhiteSpace(ch) && !Lexer::isLineTerminator(ch)) { if (ch != ';' && ch != '}') - functionBody = makeString(functionBody.substr(0, i + 1), ";", functionBody.substr(i + 1, functionBody.size() - (i + 1))); + functionBody = makeUString(functionBody.substringSharingImpl(0, i + 1), ";", functionBody.substringSharingImpl(i + 1, functionBody.length() - (i + 1))); return; } } } -JSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (thisValue.inherits(&JSFunction::info)) { JSFunction* function = asFunction(thisValue); - if (!function->isHostFunction()) { - FunctionExecutable* executable = function->jsExecutable(); - UString sourceString = executable->source().toString(); - insertSemicolonIfNeeded(sourceString); - return jsMakeNontrivialString(exec, "function ", function->name(exec), "(", executable->paramString(), ") ", sourceString); - } + if (function->isHostFunction()) + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); + FunctionExecutable* executable = function->jsExecutable(); + UString sourceString = executable->source().toString(); + insertSemicolonIfNeeded(sourceString); + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "(", executable->paramString(), ") ", sourceString)); } if (thisValue.inherits(&InternalFunction::info)) { InternalFunction* function = asInternalFunction(thisValue); - return jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}"); + return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(exec), "() {\n [native code]\n}")); } - return throwError(exec, TypeError); + return throwVMTypeError(exec); } -JSValue JSC_HOST_CALL functionProtoFuncApply(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionProtoFuncApply(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); CallData callData; - CallType callType = thisValue.getCallData(callData); + CallType callType = getCallData(thisValue, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - JSValue array = args.at(1); + JSValue array = exec->argument(1); MarkedArgumentBuffer applyArgs; if (!array.isUndefinedOrNull()) { if (!array.isObject()) - return throwError(exec, TypeError); + return throwVMTypeError(exec); if (asObject(array)->classInfo() == &Arguments::info) asArguments(array)->fillArgList(exec, applyArgs); else if (isJSArray(&exec->globalData(), array)) @@ -125,22 +127,24 @@ JSValue JSC_HOST_CALL functionProtoFuncApply(ExecState* exec, JSObject*, JSValue for (unsigned i = 0; i < length; ++i) applyArgs.append(asArray(array)->get(exec, i)); } else - return throwError(exec, TypeError); + return throwVMTypeError(exec); } - return call(exec, thisValue, callType, callData, args.at(0), applyArgs); + return JSValue::encode(call(exec, thisValue, callType, callData, exec->argument(0), applyArgs)); } -JSValue JSC_HOST_CALL functionProtoFuncCall(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionProtoFuncCall(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); CallData callData; - CallType callType = thisValue.getCallData(callData); + CallType callType = getCallData(thisValue, callData); if (callType == CallTypeNone) - return throwError(exec, TypeError); + return throwVMTypeError(exec); + ArgList args(exec); ArgList callArgs; args.getSlice(1, callArgs); - return call(exec, thisValue, callType, callData, args.at(0), callArgs); + return JSValue::encode(call(exec, thisValue, callType, callData, exec->argument(0), callArgs)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/FunctionPrototype.h b/JavaScriptCore/runtime/FunctionPrototype.h index af783f7..5661194 100644 --- a/JavaScriptCore/runtime/FunctionPrototype.h +++ b/JavaScriptCore/runtime/FunctionPrototype.h @@ -29,8 +29,8 @@ namespace JSC { class FunctionPrototype : public InternalFunction { public: - FunctionPrototype(ExecState*, NonNullPassRefPtr<Structure>); - void addFunctionProperties(ExecState*, Structure* prototypeFunctionStructure, NativeFunctionWrapper** callFunction, NativeFunctionWrapper** applyFunction); + FunctionPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>); + void addFunctionProperties(ExecState*, JSGlobalObject*, Structure* prototypeFunctionStructure, NativeFunctionWrapper** callFunction, NativeFunctionWrapper** applyFunction); static PassRefPtr<Structure> createStructure(JSValue proto) { diff --git a/JavaScriptCore/runtime/GCActivityCallback.cpp b/JavaScriptCore/runtime/GCActivityCallback.cpp new file mode 100644 index 0000000..2f2c079 --- /dev/null +++ b/JavaScriptCore/runtime/GCActivityCallback.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "GCActivityCallback.h" + +namespace JSC { + +struct DefaultGCActivityCallbackPlatformData { +}; + +DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap) +{ +} + +DefaultGCActivityCallback::~DefaultGCActivityCallback() +{ +} + +void DefaultGCActivityCallback::operator()() +{ +} + +} + diff --git a/JavaScriptCore/runtime/GCActivityCallback.h b/JavaScriptCore/runtime/GCActivityCallback.h new file mode 100644 index 0000000..66d56e8 --- /dev/null +++ b/JavaScriptCore/runtime/GCActivityCallback.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 GCActivityCallback_h +#define GCActivityCallback_h + +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +namespace JSC { + +class Heap; + +class GCActivityCallback { +public: + virtual ~GCActivityCallback() {} + virtual void operator()() {} + +protected: + GCActivityCallback() {} +}; + +struct DefaultGCActivityCallbackPlatformData; + +class DefaultGCActivityCallback : public GCActivityCallback { +public: + static PassOwnPtr<DefaultGCActivityCallback> create(Heap*); + + DefaultGCActivityCallback(Heap*); + ~DefaultGCActivityCallback(); + + void operator()(); + +private: + OwnPtr<DefaultGCActivityCallbackPlatformData*> d; +}; + +inline PassOwnPtr<DefaultGCActivityCallback> DefaultGCActivityCallback::create(Heap* heap) +{ + return adoptPtr(new DefaultGCActivityCallback(heap)); +} + +} + +#endif diff --git a/JavaScriptCore/runtime/GCActivityCallbackCF.cpp b/JavaScriptCore/runtime/GCActivityCallbackCF.cpp new file mode 100644 index 0000000..45329ca --- /dev/null +++ b/JavaScriptCore/runtime/GCActivityCallbackCF.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "GCActivityCallback.h" + +#include "APIShims.h" +#include "Collector.h" +#include "JSGlobalData.h" +#include "JSLock.h" +#include <wtf/RetainPtr.h> +#include <wtf/WTFThreadData.h> +#include <CoreFoundation/CoreFoundation.h> + +#if !PLATFORM(CF) +#error "This file should only be used on CF platforms." +#endif + +namespace JSC { + +struct DefaultGCActivityCallbackPlatformData { + static void trigger(CFRunLoopTimerRef, void *info); + + RetainPtr<CFRunLoopTimerRef> timer; + CFRunLoopTimerContext context; +}; + +const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10; + +void DefaultGCActivityCallbackPlatformData::trigger(CFRunLoopTimerRef, void *info) +{ + Heap* heap = static_cast<Heap*>(info); + APIEntryShim shim(heap->globalData()); + heap->collectAllGarbage(); +} + +DefaultGCActivityCallback::DefaultGCActivityCallback(Heap* heap) +{ + d = adoptPtr(new DefaultGCActivityCallbackPlatformData); + + memset(&d->context, '\0', sizeof(CFRunLoopTimerContext)); + d->context.info = heap; + d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::trigger, &d->context)); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), d->timer.get(), kCFRunLoopCommonModes); +} + +DefaultGCActivityCallback::~DefaultGCActivityCallback() +{ + CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), d->timer.get(), kCFRunLoopCommonModes); + CFRunLoopTimerInvalidate(d->timer.get()); + d->context.info = 0; + d->timer = 0; +} + +void DefaultGCActivityCallback::operator()() +{ + CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() + 2); +} + +} diff --git a/JavaScriptCore/runtime/GCHandle.cpp b/JavaScriptCore/runtime/GCHandle.cpp new file mode 100644 index 0000000..3331517 --- /dev/null +++ b/JavaScriptCore/runtime/GCHandle.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "GCHandle.h" + +namespace JSC { + +WeakGCHandlePool* WeakGCHandle::pool() +{ + uintptr_t pool = (reinterpret_cast<uintptr_t>(this) & WeakGCHandlePool::poolMask); + return reinterpret_cast<WeakGCHandlePool*>(pool); +} + +WeakGCHandlePool::WeakGCHandlePool() +{ + ASSERT(sizeof(WeakGCHandlePool) <= WeakGCHandlePool::poolSize); + m_entriesSize = 0; + m_initialAlloc = 1; + m_entries[0].setNextInFreeList(0); +} + +WeakGCHandle* WeakGCHandlePool::allocate(JSCell* cell) +{ + ASSERT(cell); + ASSERT(m_entries[0].isNext()); + unsigned freeList = m_entries[0].getNextInFreeList(); + ASSERT(freeList < WeakGCHandlePool::numPoolEntries); + ASSERT(m_entriesSize < WeakGCHandlePool::numPoolEntries); + + if (m_entriesSize == WeakGCHandlePool::numPoolEntries - 1) + return 0; + + if (freeList) { + unsigned i = freeList; + freeList = m_entries[i].getNextInFreeList(); + m_entries[i].set(cell); + m_entries[0].setNextInFreeList(freeList); + ++m_entriesSize; + return &m_entries[i]; + } + + ASSERT(m_initialAlloc < WeakGCHandlePool::numPoolEntries); + + unsigned i = m_initialAlloc; + ++m_initialAlloc; + m_entries[i].set(cell); + ++m_entriesSize; + return &m_entries[i]; + +} + +void WeakGCHandlePool::free(WeakGCHandle* handle) +{ + ASSERT(handle->pool() == this); + ASSERT(m_entries[0].isNext()); + unsigned freeList = m_entries[0].getNextInFreeList(); + ASSERT(freeList < WeakGCHandlePool::numPoolEntries); + handle->setNextInFreeList(freeList); + m_entries[0].setNextInFreeList(handle - m_entries); + --m_entriesSize; +} + +void* WeakGCHandlePool::operator new(size_t, AlignedMemory<WeakGCHandlePool::poolSize>& allocation) +{ + return allocation.base(); +} + +} diff --git a/JavaScriptCore/runtime/GCHandle.h b/JavaScriptCore/runtime/GCHandle.h new file mode 100644 index 0000000..38a7be9 --- /dev/null +++ b/JavaScriptCore/runtime/GCHandle.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 GCHandle_h +#define GCHandle_h + +#include "AlignedMemoryAllocator.h" + +namespace JSC { + +class Heap; +class JSCell; +class WeakGCHandle; +class WeakGCHandlePool; + +class WeakGCHandle { + friend class WeakGCHandlePool; + +public: + // Because JSCell objects are aligned, we can use the lower two bits as + // status flags. The least significant bit is set when the handle is not a + // pointer, i.e. when it's used as a offset for the free list in + // WeakGCHandlePool. The second least significant bit is set when the object + // the pointer corresponds to has been deleted by a garbage collection + + bool isValidPtr() { return !(m_ptr & 3); } + bool isPtr() { return !(m_ptr & 1); } + bool isNext() { return (m_ptr & 3) == 1; } + + void invalidate() + { + ASSERT(isValidPtr()); + m_ptr |= 2; + } + + JSCell* get() + { + ASSERT(isPtr()); + return reinterpret_cast<JSCell*>(m_ptr & ~3); + } + + void set(JSCell* p) + { + m_ptr = reinterpret_cast<uintptr_t>(p); + ASSERT(isPtr()); + } + + WeakGCHandlePool* pool(); + +private: + uintptr_t getNextInFreeList() + { + ASSERT(isNext()); + return m_ptr >> 2; + } + + void setNextInFreeList(uintptr_t n) + { + m_ptr = (n << 2) | 1; + ASSERT(isNext()); + } + + uintptr_t m_ptr; +}; + +class WeakGCHandlePool { +public: + static const size_t poolSize = 32 * 1024; // 32k + static const size_t poolMask = ~(poolSize - 1); + static const size_t numPoolEntries = (poolSize - sizeof(Heap*) - 3 * sizeof(unsigned)) / sizeof(WeakGCHandle); + + typedef AlignedMemoryAllocator<WeakGCHandlePool::poolSize> Allocator; + + WeakGCHandlePool(); + + WeakGCHandle* allocate(JSCell* cell); + void free(WeakGCHandle*); + + bool isFull() + { + ASSERT(m_entriesSize < WeakGCHandlePool::numPoolEntries); + return m_entriesSize == WeakGCHandlePool::numPoolEntries - 1; + } + + void update(); + + void* operator new(size_t, AlignedMemory<WeakGCHandlePool::poolSize>&); + +private: + Heap* m_heap; + unsigned m_entriesSize; + unsigned m_initialAlloc; + + WeakGCHandle m_entries[WeakGCHandlePool::numPoolEntries]; +}; + +} +#endif diff --git a/JavaScriptCore/runtime/GetterSetter.h b/JavaScriptCore/runtime/GetterSetter.h index 4e47361..e7b1938 100644 --- a/JavaScriptCore/runtime/GetterSetter.h +++ b/JavaScriptCore/runtime/GetterSetter.h @@ -34,6 +34,7 @@ namespace JSC { // This is an internal value object which stores getter and setter functions // for a property. class GetterSetter : public JSCell { + friend class JIT; public: GetterSetter(ExecState* exec) : JSCell(exec->globalData().getterSetterStructure.get()) @@ -63,8 +64,8 @@ namespace JSC { inline GetterSetter* asGetterSetter(JSValue value) { - ASSERT(asCell(value)->isGetterSetter()); - return static_cast<GetterSetter*>(asCell(value)); + ASSERT(value.asCell()->isGetterSetter()); + return static_cast<GetterSetter*>(value.asCell()); } diff --git a/JavaScriptCore/runtime/GlobalEvalFunction.cpp b/JavaScriptCore/runtime/GlobalEvalFunction.cpp index c26002b..3ad4644 100644 --- a/JavaScriptCore/runtime/GlobalEvalFunction.cpp +++ b/JavaScriptCore/runtime/GlobalEvalFunction.cpp @@ -32,8 +32,8 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(GlobalEvalFunction); -GlobalEvalFunction::GlobalEvalFunction(ExecState* exec, NonNullPassRefPtr<Structure> structure, int len, const Identifier& name, NativeFunction function, JSGlobalObject* cachedGlobalObject) - : PrototypeFunction(exec, structure, len, name, function) +GlobalEvalFunction::GlobalEvalFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, int len, const Identifier& name, NativeFunction function, JSGlobalObject* cachedGlobalObject) + : PrototypeFunction(exec, globalObject, structure, len, name, function) , m_cachedGlobalObject(cachedGlobalObject) { ASSERT_ARG(cachedGlobalObject, cachedGlobalObject); diff --git a/JavaScriptCore/runtime/GlobalEvalFunction.h b/JavaScriptCore/runtime/GlobalEvalFunction.h index a14ce4d..b889ca9 100644 --- a/JavaScriptCore/runtime/GlobalEvalFunction.h +++ b/JavaScriptCore/runtime/GlobalEvalFunction.h @@ -32,7 +32,7 @@ namespace JSC { class GlobalEvalFunction : public PrototypeFunction { public: - GlobalEvalFunction(ExecState*, NonNullPassRefPtr<Structure>, int len, const Identifier&, NativeFunction, JSGlobalObject* expectedThisObject); + GlobalEvalFunction(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, int len, const Identifier&, NativeFunction, JSGlobalObject* expectedThisObject); JSGlobalObject* cachedGlobalObject() const { return m_cachedGlobalObject; } static PassRefPtr<Structure> createStructure(JSValue prototype) diff --git a/JavaScriptCore/runtime/Identifier.cpp b/JavaScriptCore/runtime/Identifier.cpp index 97929e2..28cfd0a 100644 --- a/JavaScriptCore/runtime/Identifier.cpp +++ b/JavaScriptCore/runtime/Identifier.cpp @@ -22,50 +22,38 @@ #include "Identifier.h" #include "CallFrame.h" +#include "NumericStrings.h" #include <new> // for placement new #include <string.h> // for strlen #include <wtf/Assertions.h> #include <wtf/FastMalloc.h> #include <wtf/HashSet.h> +#include <wtf/WTFThreadData.h> +#include <wtf/text/StringHash.h> using WTF::ThreadSpecific; namespace JSC { -typedef HashMap<const char*, RefPtr<UString::Rep>, PtrHash<const char*> > LiteralIdentifierTable; - -class IdentifierTable : public FastAllocBase { -public: - ~IdentifierTable() - { - HashSet<UString::Rep*>::iterator end = m_table.end(); - for (HashSet<UString::Rep*>::iterator iter = m_table.begin(); iter != end; ++iter) - (*iter)->setIsIdentifier(false); - } - - std::pair<HashSet<UString::Rep*>::iterator, bool> add(UString::Rep* value) - { - std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add(value); - (*result.first)->setIsIdentifier(true); - return result; - } - - template<typename U, typename V> - std::pair<HashSet<UString::Rep*>::iterator, bool> add(U value) - { - std::pair<HashSet<UString::Rep*>::iterator, bool> result = m_table.add<U, V>(value); - (*result.first)->setIsIdentifier(true); - return result; - } - - void remove(UString::Rep* r) { m_table.remove(r); } - - LiteralIdentifierTable& literalTable() { return m_literalTable; } - -private: - HashSet<UString::Rep*> m_table; - LiteralIdentifierTable m_literalTable; -}; +IdentifierTable::~IdentifierTable() +{ + HashSet<StringImpl*>::iterator end = m_table.end(); + for (HashSet<StringImpl*>::iterator iter = m_table.begin(); iter != end; ++iter) + (*iter)->setIsIdentifier(false); +} +std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(StringImpl* value) +{ + std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add(value); + (*result.first)->setIsIdentifier(true); + return result; +} +template<typename U, typename V> +std::pair<HashSet<StringImpl*>::iterator, bool> IdentifierTable::add(U value) +{ + std::pair<HashSet<StringImpl*>::iterator, bool> result = m_table.add<U, V>(value); + (*result.first)->setIsIdentifier(true); + return result; +} IdentifierTable* createIdentifierTable() { @@ -77,43 +65,43 @@ void deleteIdentifierTable(IdentifierTable* table) delete table; } -bool Identifier::equal(const UString::Rep* r, const char* s) +bool Identifier::equal(const StringImpl* r, const char* s) { - int length = r->size(); - const UChar* d = r->data(); + int length = r->length(); + const UChar* d = r->characters(); for (int i = 0; i != length; ++i) if (d[i] != (unsigned char)s[i]) return false; return s[length] == 0; } -bool Identifier::equal(const UString::Rep* r, const UChar* s, int length) +bool Identifier::equal(const StringImpl* r, const UChar* s, unsigned length) { - if (r->size() != length) + if (r->length() != length) return false; - const UChar* d = r->data(); - for (int i = 0; i != length; ++i) + const UChar* d = r->characters(); + for (unsigned i = 0; i != length; ++i) if (d[i] != s[i]) return false; return true; } -struct CStringTranslator { +struct IdentifierCStringTranslator { static unsigned hash(const char* c) { - return UString::Rep::computeHash(c); + return WTF::StringHasher::createHash<char>(c); } - static bool equal(UString::Rep* r, const char* s) + static bool equal(StringImpl* r, const char* s) { return Identifier::equal(r, s); } - static void translate(UString::Rep*& location, const char* c, unsigned hash) + static void translate(StringImpl*& location, const char* c, unsigned hash) { size_t length = strlen(c); UChar* d; - UString::Rep* r = UString::Rep::createUninitialized(length, d).releaseRef(); + StringImpl* r = StringImpl::createUninitialized(length, d).leakRef(); for (size_t i = 0; i != length; i++) d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend r->setHash(hash); @@ -121,14 +109,12 @@ struct CStringTranslator { } }; -PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const char* c) +PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const char* c) { - ASSERT(c); - - if (!c[0]) { - UString::Rep::empty().hash(); - return &UString::Rep::empty(); - } + if (!c) + return 0; + if (!c[0]) + return StringImpl::empty(); if (!c[1]) return add(globalData, globalData->smallStrings.singleCharacterStringRep(static_cast<unsigned char>(c[0]))); @@ -139,18 +125,18 @@ PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const char* c if (iter != literalIdentifierTable.end()) return iter->second; - pair<HashSet<UString::Rep*>::iterator, bool> addResult = identifierTable.add<const char*, CStringTranslator>(c); + pair<HashSet<StringImpl*>::iterator, bool> addResult = identifierTable.add<const char*, IdentifierCStringTranslator>(c); // If the string is newly-translated, then we need to adopt it. // The boolean in the pair tells us if that is so. - RefPtr<UString::Rep> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first; + RefPtr<StringImpl> addedString = addResult.second ? adoptRef(*addResult.first) : *addResult.first; literalIdentifierTable.add(c, addedString.get()); return addedString.release(); } -PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const char* c) +PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const char* c) { return add(&exec->globalData(), c); } @@ -160,21 +146,21 @@ struct UCharBuffer { unsigned int length; }; -struct UCharBufferTranslator { +struct IdentifierUCharBufferTranslator { static unsigned hash(const UCharBuffer& buf) { - return UString::Rep::computeHash(buf.s, buf.length); + return WTF::StringHasher::createHash<UChar>(buf.s, buf.length); } - static bool equal(UString::Rep* str, const UCharBuffer& buf) + static bool equal(StringImpl* str, const UCharBuffer& buf) { return Identifier::equal(str, buf.s, buf.length); } - static void translate(UString::Rep*& location, const UCharBuffer& buf, unsigned hash) + static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash) { UChar* d; - UString::Rep* r = UString::Rep::createUninitialized(buf.length, d).releaseRef(); + StringImpl* r = StringImpl::createUninitialized(buf.length, d).leakRef(); for (unsigned i = 0; i != buf.length; i++) d[i] = buf.s[i]; r->setHash(hash); @@ -182,108 +168,144 @@ struct UCharBufferTranslator { } }; -PassRefPtr<UString::Rep> Identifier::add(JSGlobalData* globalData, const UChar* s, int length) +uint32_t Identifier::toUInt32(const UString& string, bool& ok) +{ + ok = false; + + unsigned length = string.length(); + const UChar* characters = string.characters(); + + // An empty string is not a number. + if (!length) + return 0; + + // Get the first character, turning it into a digit. + uint32_t value = characters[0] - '0'; + if (value > 9) + return 0; + + // Check for leading zeros. If the first characher is 0, then the + // length of the string must be one - e.g. "042" is not equal to "42". + if (!value && length > 1) + return 0; + + while (--length) { + // Multiply value by 10, checking for overflow out of 32 bits. + if (value > 0xFFFFFFFFU / 10) + return 0; + value *= 10; + + // Get the next character, turning it into a digit. + uint32_t newValue = *(++characters) - '0'; + if (newValue > 9) + return 0; + + // Add in the old value, checking for overflow out of 32 bits. + newValue += value; + if (newValue < value) + return 0; + value = newValue; + } + + ok = true; + return value; +} + +PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const UChar* s, int length) { if (length == 1) { UChar c = s[0]; if (c <= 0xFF) return add(globalData, globalData->smallStrings.singleCharacterStringRep(c)); } - if (!length) { - UString::Rep::empty().hash(); - return &UString::Rep::empty(); - } + if (!length) + return StringImpl::empty(); UCharBuffer buf = {s, length}; - pair<HashSet<UString::Rep*>::iterator, bool> addResult = globalData->identifierTable->add<UCharBuffer, UCharBufferTranslator>(buf); + pair<HashSet<StringImpl*>::iterator, bool> addResult = globalData->identifierTable->add<UCharBuffer, IdentifierUCharBufferTranslator>(buf); // If the string is newly-translated, then we need to adopt it. // The boolean in the pair tells us if that is so. return addResult.second ? adoptRef(*addResult.first) : *addResult.first; } -PassRefPtr<UString::Rep> Identifier::add(ExecState* exec, const UChar* s, int length) +PassRefPtr<StringImpl> Identifier::add(ExecState* exec, const UChar* s, int length) { return add(&exec->globalData(), s, length); } -PassRefPtr<UString::Rep> Identifier::addSlowCase(JSGlobalData* globalData, UString::Rep* r) +PassRefPtr<StringImpl> Identifier::addSlowCase(JSGlobalData* globalData, StringImpl* r) { ASSERT(!r->isIdentifier()); - if (r->size() == 1) { - UChar c = r->data()[0]; + // The empty & null strings are static singletons, and static strings are handled + // in ::add() in the header, so we should never get here with a zero length string. + ASSERT(r->length()); + + if (r->length() == 1) { + UChar c = r->characters()[0]; if (c <= 0xFF) r = globalData->smallStrings.singleCharacterStringRep(c); - if (r->isIdentifier()) { -#ifndef NDEBUG - checkSameIdentifierTable(globalData, r); -#endif + if (r->isIdentifier()) return r; - } - } - if (!r->size()) { - UString::Rep::empty().hash(); - return &UString::Rep::empty(); } + return *globalData->identifierTable->add(r).first; } -PassRefPtr<UString::Rep> Identifier::addSlowCase(ExecState* exec, UString::Rep* r) +PassRefPtr<StringImpl> Identifier::addSlowCase(ExecState* exec, StringImpl* r) { return addSlowCase(&exec->globalData(), r); } -void Identifier::remove(UString::Rep* r) +Identifier Identifier::from(ExecState* exec, unsigned value) { - currentIdentifierTable()->remove(r); + return Identifier(exec, exec->globalData().numericStrings.add(value)); } -#ifndef NDEBUG - -void Identifier::checkSameIdentifierTable(ExecState* exec, UString::Rep*) +Identifier Identifier::from(ExecState* exec, int value) { - ASSERT_UNUSED(exec, exec->globalData().identifierTable == currentIdentifierTable()); + return Identifier(exec, exec->globalData().numericStrings.add(value)); } -void Identifier::checkSameIdentifierTable(JSGlobalData* globalData, UString::Rep*) +Identifier Identifier::from(ExecState* exec, double value) { - ASSERT_UNUSED(globalData, globalData->identifierTable == currentIdentifierTable()); + return Identifier(exec, exec->globalData().numericStrings.add(value)); } -#else - -void Identifier::checkSameIdentifierTable(ExecState*, UString::Rep*) +Identifier Identifier::from(JSGlobalData* globalData, unsigned value) { + return Identifier(globalData, globalData->numericStrings.add(value)); } -void Identifier::checkSameIdentifierTable(JSGlobalData*, UString::Rep*) +Identifier Identifier::from(JSGlobalData* globalData, int value) { + return Identifier(globalData, globalData->numericStrings.add(value)); } -#endif - -ThreadSpecific<ThreadIdentifierTableData>* g_identifierTableSpecific = 0; +Identifier Identifier::from(JSGlobalData* globalData, double value) +{ + return Identifier(globalData, globalData->numericStrings.add(value)); +} -#if ENABLE(JSC_MULTIPLE_THREADS) +#ifndef NDEBUG -pthread_once_t createIdentifierTableSpecificOnce = PTHREAD_ONCE_INIT; -static void createIdentifierTableSpecificCallback() +void Identifier::checkCurrentIdentifierTable(JSGlobalData* globalData) { - ASSERT(!g_identifierTableSpecific); - g_identifierTableSpecific = new ThreadSpecific<ThreadIdentifierTableData>(); + // Check the identifier table accessible through the threadspecific matches the + // globalData's identifier table. + ASSERT_UNUSED(globalData, globalData->identifierTable == wtfThreadData().currentIdentifierTable()); } -void createIdentifierTableSpecific() + +void Identifier::checkCurrentIdentifierTable(ExecState* exec) { - pthread_once(&createIdentifierTableSpecificOnce, createIdentifierTableSpecificCallback); - ASSERT(g_identifierTableSpecific); + checkCurrentIdentifierTable(&exec->globalData()); } -#else +#else -void createIdentifierTableSpecific() -{ - ASSERT(!g_identifierTableSpecific); - g_identifierTableSpecific = new ThreadSpecific<ThreadIdentifierTableData>(); -} +// These only exists so that our exports are the same for debug and release builds. +// This would be an ASSERT_NOT_REACHED(), but we're in NDEBUG only code here! +void Identifier::checkCurrentIdentifierTable(JSGlobalData*) { CRASH(); } +void Identifier::checkCurrentIdentifierTable(ExecState*) { CRASH(); } #endif diff --git a/JavaScriptCore/runtime/Identifier.h b/JavaScriptCore/runtime/Identifier.h index 1d1bd18..3a8aed7 100644 --- a/JavaScriptCore/runtime/Identifier.h +++ b/JavaScriptCore/runtime/Identifier.h @@ -24,6 +24,7 @@ #include "JSGlobalData.h" #include "ThreadSpecific.h" #include "UString.h" +#include <wtf/text/CString.h> namespace JSC { @@ -34,38 +35,37 @@ namespace JSC { public: Identifier() { } - Identifier(ExecState* exec, const char* s) : _ustring(add(exec, s)) { } // Only to be used with string literals. - Identifier(ExecState* exec, const UChar* s, int length) : _ustring(add(exec, s, length)) { } - Identifier(ExecState* exec, UString::Rep* rep) : _ustring(add(exec, rep)) { } - Identifier(ExecState* exec, const UString& s) : _ustring(add(exec, s.rep())) { } + Identifier(ExecState* exec, const char* s) : m_string(add(exec, s)) { } // Only to be used with string literals. + Identifier(ExecState* exec, const UChar* s, int length) : m_string(add(exec, s, length)) { } + Identifier(ExecState* exec, StringImpl* rep) : m_string(add(exec, rep)) { } + Identifier(ExecState* exec, const UString& s) : m_string(add(exec, s.impl())) { } - Identifier(JSGlobalData* globalData, const char* s) : _ustring(add(globalData, s)) { } // Only to be used with string literals. - Identifier(JSGlobalData* globalData, const UChar* s, int length) : _ustring(add(globalData, s, length)) { } - Identifier(JSGlobalData* globalData, UString::Rep* rep) : _ustring(add(globalData, rep)) { } - Identifier(JSGlobalData* globalData, const UString& s) : _ustring(add(globalData, s.rep())) { } + Identifier(JSGlobalData* globalData, const char* s) : m_string(add(globalData, s)) { } // Only to be used with string literals. + Identifier(JSGlobalData* globalData, const UChar* s, int length) : m_string(add(globalData, s, length)) { } + Identifier(JSGlobalData* globalData, StringImpl* rep) : m_string(add(globalData, rep)) { } + Identifier(JSGlobalData* globalData, const UString& s) : m_string(add(globalData, s.impl())) { } - // Special constructor for cases where we overwrite an object in place. - Identifier(PlacementNewAdoptType) : _ustring(PlacementNewAdopt) { } + const UString& ustring() const { return m_string; } + StringImpl* impl() const { return m_string.impl(); } - const UString& ustring() const { return _ustring; } + const UChar* characters() const { return m_string.characters(); } + int length() const { return m_string.length(); } - const UChar* data() const { return _ustring.data(); } - int size() const { return _ustring.size(); } + CString ascii() const { return m_string.ascii(); } - const char* ascii() const { return _ustring.ascii(); } - - static Identifier from(ExecState* exec, unsigned y) { return Identifier(exec, UString::from(y)); } - static Identifier from(ExecState* exec, int y) { return Identifier(exec, UString::from(y)); } - static Identifier from(ExecState* exec, double y) { return Identifier(exec, UString::from(y)); } - - bool isNull() const { return _ustring.isNull(); } - bool isEmpty() const { return _ustring.isEmpty(); } - - uint32_t toUInt32(bool* ok) const { return _ustring.toUInt32(ok); } - uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const { return _ustring.toUInt32(ok, tolerateEmptyString); }; - uint32_t toStrictUInt32(bool* ok) const { return _ustring.toStrictUInt32(ok); } - unsigned toArrayIndex(bool* ok) const { return _ustring.toArrayIndex(ok); } - double toDouble() const { return _ustring.toDouble(); } + static Identifier from(ExecState* exec, unsigned y); + static Identifier from(ExecState* exec, int y); + static Identifier from(ExecState* exec, double y); + static Identifier from(JSGlobalData*, unsigned y); + static Identifier from(JSGlobalData*, int y); + static Identifier from(JSGlobalData*, double y); + + static uint32_t toUInt32(const UString&, bool& ok); + uint32_t toUInt32(bool& ok) const { return toUInt32(m_string, ok); } + unsigned toArrayIndex(bool& ok) const; + + bool isNull() const { return m_string.isNull(); } + bool isEmpty() const { return m_string.isEmpty(); } friend bool operator==(const Identifier&, const Identifier&); friend bool operator!=(const Identifier&, const Identifier&); @@ -73,50 +73,46 @@ namespace JSC { friend bool operator==(const Identifier&, const char*); friend bool operator!=(const Identifier&, const char*); - static void remove(UString::Rep*); - - static bool equal(const UString::Rep*, const char*); - static bool equal(const UString::Rep*, const UChar*, int length); - static bool equal(const UString::Rep* a, const UString::Rep* b) { return JSC::equal(a, b); } + static bool equal(const StringImpl*, const char*); + static bool equal(const StringImpl*, const UChar*, unsigned length); + static bool equal(const StringImpl* a, const StringImpl* b) { return ::equal(a, b); } - static PassRefPtr<UString::Rep> add(ExecState*, const char*); // Only to be used with string literals. - static PassRefPtr<UString::Rep> add(JSGlobalData*, const char*); // Only to be used with string literals. + static PassRefPtr<StringImpl> add(ExecState*, const char*); // Only to be used with string literals. + static PassRefPtr<StringImpl> add(JSGlobalData*, const char*); // Only to be used with string literals. private: - UString _ustring; + UString m_string; - static bool equal(const Identifier& a, const Identifier& b) { return a._ustring.rep() == b._ustring.rep(); } - static bool equal(const Identifier& a, const char* b) { return equal(a._ustring.rep(), b); } + static bool equal(const Identifier& a, const Identifier& b) { return a.m_string.impl() == b.m_string.impl(); } + static bool equal(const Identifier& a, const char* b) { return equal(a.m_string.impl(), b); } - static PassRefPtr<UString::Rep> add(ExecState*, const UChar*, int length); - static PassRefPtr<UString::Rep> add(JSGlobalData*, const UChar*, int length); + static PassRefPtr<StringImpl> add(ExecState*, const UChar*, int length); + static PassRefPtr<StringImpl> add(JSGlobalData*, const UChar*, int length); - static PassRefPtr<UString::Rep> add(ExecState* exec, UString::Rep* r) + static PassRefPtr<StringImpl> add(ExecState* exec, StringImpl* r) { - if (r->isIdentifier()) { #ifndef NDEBUG - checkSameIdentifierTable(exec, r); + checkCurrentIdentifierTable(exec); #endif + if (r->isIdentifier()) return r; - } return addSlowCase(exec, r); } - static PassRefPtr<UString::Rep> add(JSGlobalData* globalData, UString::Rep* r) + static PassRefPtr<StringImpl> add(JSGlobalData* globalData, StringImpl* r) { - if (r->isIdentifier()) { #ifndef NDEBUG - checkSameIdentifierTable(globalData, r); + checkCurrentIdentifierTable(globalData); #endif + if (r->isIdentifier()) return r; - } return addSlowCase(globalData, r); } - static PassRefPtr<UString::Rep> addSlowCase(ExecState*, UString::Rep* r); - static PassRefPtr<UString::Rep> addSlowCase(JSGlobalData*, UString::Rep* r); + static PassRefPtr<StringImpl> addSlowCase(ExecState*, StringImpl* r); + static PassRefPtr<StringImpl> addSlowCase(JSGlobalData*, StringImpl* r); - static void checkSameIdentifierTable(ExecState*, UString::Rep*); - static void checkSameIdentifierTable(JSGlobalData*, UString::Rep*); + static void checkCurrentIdentifierTable(ExecState*); + static void checkCurrentIdentifierTable(JSGlobalData*); }; inline bool operator==(const Identifier& a, const Identifier& b) @@ -142,67 +138,11 @@ namespace JSC { IdentifierTable* createIdentifierTable(); void deleteIdentifierTable(IdentifierTable*); - struct ThreadIdentifierTableData { - ThreadIdentifierTableData() - : defaultIdentifierTable(0) - , currentIdentifierTable(0) - { - } - - IdentifierTable* defaultIdentifierTable; - IdentifierTable* currentIdentifierTable; + struct IdentifierRepHash : PtrHash<RefPtr<StringImpl> > { + static unsigned hash(const RefPtr<StringImpl>& key) { return key->existingHash(); } + static unsigned hash(StringImpl* key) { return key->existingHash(); } }; - extern WTF::ThreadSpecific<ThreadIdentifierTableData>* g_identifierTableSpecific; - void createIdentifierTableSpecific(); - - inline IdentifierTable* defaultIdentifierTable() - { - if (!g_identifierTableSpecific) - createIdentifierTableSpecific(); - ThreadIdentifierTableData& data = **g_identifierTableSpecific; - - return data.defaultIdentifierTable; - } - - inline void setDefaultIdentifierTable(IdentifierTable* identifierTable) - { - if (!g_identifierTableSpecific) - createIdentifierTableSpecific(); - ThreadIdentifierTableData& data = **g_identifierTableSpecific; - - data.defaultIdentifierTable = identifierTable; - } - - inline IdentifierTable* currentIdentifierTable() - { - if (!g_identifierTableSpecific) - createIdentifierTableSpecific(); - ThreadIdentifierTableData& data = **g_identifierTableSpecific; - - return data.currentIdentifierTable; - } - - inline IdentifierTable* setCurrentIdentifierTable(IdentifierTable* identifierTable) - { - if (!g_identifierTableSpecific) - createIdentifierTableSpecific(); - ThreadIdentifierTableData& data = **g_identifierTableSpecific; - - IdentifierTable* oldIdentifierTable = data.currentIdentifierTable; - data.currentIdentifierTable = identifierTable; - return oldIdentifierTable; - } - - inline void resetCurrentIdentifierTable() - { - if (!g_identifierTableSpecific) - createIdentifierTableSpecific(); - ThreadIdentifierTableData& data = **g_identifierTableSpecific; - - data.currentIdentifierTable = data.defaultIdentifierTable; - } - } // namespace JSC #endif // Identifier_h diff --git a/JavaScriptCore/runtime/InitializeThreading.cpp b/JavaScriptCore/runtime/InitializeThreading.cpp index 2605a9a..08dddc1 100644 --- a/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/JavaScriptCore/runtime/InitializeThreading.cpp @@ -36,6 +36,7 @@ #include "UString.h" #include <wtf/DateMath.h> #include <wtf/Threading.h> +#include <wtf/WTFThreadData.h> using namespace WTF; @@ -47,12 +48,17 @@ static pthread_once_t initializeThreadingKeyOnce = PTHREAD_ONCE_INIT; static void initializeThreadingOnce() { + // StringImpl::empty() does not construct its static string in a threadsafe fashion, + // so ensure it has been initialized from here. + StringImpl::empty(); + WTF::initializeThreading(); - initializeUString(); + wtfThreadData(); JSGlobalData::storeVPtrs(); #if ENABLE(JSC_MULTIPLE_THREADS) s_dtoaP5Mutex = new Mutex; initializeDates(); + RegisterFile::initializeThreading(); #endif } diff --git a/JavaScriptCore/runtime/InternalFunction.cpp b/JavaScriptCore/runtime/InternalFunction.cpp index c48d628..0a8d9de 100644 --- a/JavaScriptCore/runtime/InternalFunction.cpp +++ b/JavaScriptCore/runtime/InternalFunction.cpp @@ -24,6 +24,7 @@ #include "InternalFunction.h" #include "FunctionPrototype.h" +#include "JSGlobalObject.h" #include "JSString.h" namespace JSC { @@ -37,15 +38,20 @@ const ClassInfo* InternalFunction::classInfo() const return &info; } -InternalFunction::InternalFunction(JSGlobalData* globalData, NonNullPassRefPtr<Structure> structure, const Identifier& name) - : JSObject(structure) +InternalFunction::InternalFunction(NonNullPassRefPtr<Structure> structure) + : JSObjectWithGlobalObject(structure) { - putDirect(globalData->propertyNames->name, jsString(globalData, name.ustring()), DontDelete | ReadOnly | DontEnum); +} + +InternalFunction::InternalFunction(JSGlobalData* globalData, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const Identifier& name) + : JSObjectWithGlobalObject(globalObject, structure) +{ + putDirect(globalData->propertyNames->name, jsString(globalData, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); } const UString& InternalFunction::name(ExecState* exec) { - return asString(getDirect(exec->globalData().propertyNames->name))->value(exec); + return asString(getDirect(exec->globalData().propertyNames->name))->tryGetValue(); } const UString InternalFunction::displayName(ExecState* exec) @@ -53,9 +59,9 @@ const UString InternalFunction::displayName(ExecState* exec) JSValue displayName = getDirect(exec->globalData().propertyNames->displayName); if (displayName && isJSString(&exec->globalData(), displayName)) - return asString(displayName)->value(exec); + return asString(displayName)->tryGetValue(); - return UString::null(); + return UString(); } const UString InternalFunction::calculatedDisplayName(ExecState* exec) diff --git a/JavaScriptCore/runtime/InternalFunction.h b/JavaScriptCore/runtime/InternalFunction.h index d19b82b..401f17b 100644 --- a/JavaScriptCore/runtime/InternalFunction.h +++ b/JavaScriptCore/runtime/InternalFunction.h @@ -24,14 +24,14 @@ #ifndef InternalFunction_h #define InternalFunction_h -#include "JSObject.h" +#include "JSObjectWithGlobalObject.h" #include "Identifier.h" namespace JSC { class FunctionPrototype; - class InternalFunction : public JSObject { + class InternalFunction : public JSObjectWithGlobalObject { public: virtual const ClassInfo* classInfo() const; static JS_EXPORTDATA const ClassInfo info; @@ -48,8 +48,10 @@ namespace JSC { protected: static const unsigned StructureFlags = ImplementsHasInstance | JSObject::StructureFlags; - InternalFunction(NonNullPassRefPtr<Structure> structure) : JSObject(structure) { } - InternalFunction(JSGlobalData*, NonNullPassRefPtr<Structure>, const Identifier&); + // Only used to allow us to determine the JSFunction vptr + InternalFunction(NonNullPassRefPtr<Structure> structure); + + InternalFunction(JSGlobalData*, JSGlobalObject*, NonNullPassRefPtr<Structure>, const Identifier&); private: virtual CallType getCallData(CallData&) = 0; diff --git a/JavaScriptCore/runtime/JSAPIValueWrapper.h b/JavaScriptCore/runtime/JSAPIValueWrapper.h index b5016c2..10ded4c 100644 --- a/JavaScriptCore/runtime/JSAPIValueWrapper.h +++ b/JavaScriptCore/runtime/JSAPIValueWrapper.h @@ -23,8 +23,6 @@ #ifndef JSAPIValueWrapper_h #define JSAPIValueWrapper_h -#include <wtf/Platform.h> - #include "JSCell.h" #include "CallFrame.h" diff --git a/JavaScriptCore/runtime/JSActivation.cpp b/JavaScriptCore/runtime/JSActivation.cpp index 22fdaaf..1147858 100644 --- a/JavaScriptCore/runtime/JSActivation.cpp +++ b/JavaScriptCore/runtime/JSActivation.cpp @@ -62,28 +62,80 @@ void JSActivation::markChildren(MarkStack& markStack) size_t count = numParametersMinusThis; markStack.appendValues(registerArray, count); - size_t numVars = d()->functionExecutable->variableCount(); + size_t numVars = d()->functionExecutable->capturedVariableCount(); // Skip the call frame, which sits between the parameters and vars. markStack.appendValues(registerArray + count + RegisterFile::CallFrameHeaderSize, numVars, MayContainNullValues); } -bool JSActivation::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +inline bool JSActivation::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) { - if (symbolTableGet(propertyName, slot)) + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (!entry.isNull()) { + ASSERT(entry.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); + slot.setRegisterSlot(®isterAt(entry.getIndex())); return true; + } + return false; +} - if (JSValue* location = getDirectLocation(propertyName)) { - slot.setValueSlot(location); +inline bool JSActivation::symbolTablePut(const Identifier& propertyName, JSValue value) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); + if (entry.isNull()) + return false; + if (entry.isReadOnly()) return true; + ASSERT(entry.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); + registerAt(entry.getIndex()) = value; + return true; +} + +void JSActivation::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + SymbolTable::const_iterator end = symbolTable().end(); + for (SymbolTable::const_iterator it = symbolTable().begin(); it != end; ++it) { + ASSERT(it->second.getIndex() < static_cast<int>(d()->functionExecutable->capturedVariableCount())); + if (!(it->second.getAttributes() & DontEnum) || (mode == IncludeDontEnumProperties)) + propertyNames.add(Identifier(exec, it->first.get())); } + // Skip the JSVariableObject implementation of getOwnPropertyNames + JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +inline bool JSActivation::symbolTablePutWithAttributes(const Identifier& propertyName, JSValue value, unsigned attributes) +{ + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + + SymbolTable::iterator iter = symbolTable().find(propertyName.impl()); + if (iter == symbolTable().end()) + return false; + SymbolTableEntry& entry = iter->second; + ASSERT(!entry.isNull()); + if (entry.getIndex() >= static_cast<int>(d()->functionExecutable->capturedVariableCount())) + return false; + entry.setAttributes(attributes); + registerAt(entry.getIndex()) = value; + return true; +} - // Only return the built-in arguments object if it wasn't overridden above. +bool JSActivation::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ if (propertyName == exec->propertyNames().arguments) { slot.setCustom(this, getArgumentsGetter()); return true; } + if (symbolTableGet(propertyName, slot)) + return true; + + if (JSValue* location = getDirectLocation(propertyName)) { + slot.setValueSlot(location); + return true; + } + // We don't call through to JSObject because there's no way to give an // activation object getter properties or a prototype. ASSERT(!hasGetterSetterProperties()); @@ -134,31 +186,30 @@ JSObject* JSActivation::toThisObject(ExecState* exec) const return exec->globalThisValue(); } -bool JSActivation::isDynamicScope() const +JSValue JSActivation::toStrictThisObject(ExecState*) const { - return d()->functionExecutable->usesEval(); + return jsNull(); } - -JSValue JSActivation::argumentsGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) + +bool JSActivation::isDynamicScope(bool& requiresDynamicChecks) const { - JSActivation* activation = asActivation(slot.slotBase()); - - if (activation->d()->functionExecutable->usesArguments()) { - PropertySlot slot; - activation->symbolTableGet(exec->propertyNames().arguments, slot); - return slot.getValue(exec, exec->propertyNames().arguments); - } + requiresDynamicChecks = d()->functionExecutable->usesEval(); + return false; +} +JSValue JSActivation::argumentsGetter(ExecState*, JSValue slotBase, const Identifier&) +{ + JSActivation* activation = asActivation(slotBase); CallFrame* callFrame = CallFrame::create(activation->d()->registers); - Arguments* arguments = callFrame->optionalCalleeArguments(); - if (!arguments) { - arguments = new (callFrame) Arguments(callFrame); - arguments->copyRegisters(); - callFrame->setCalleeArguments(arguments); + int argumentsRegister = activation->d()->functionExecutable->generatedBytecode().argumentsRegister(); + if (!callFrame->uncheckedR(argumentsRegister).jsValue()) { + JSValue arguments = JSValue(new (callFrame) Arguments(callFrame)); + callFrame->uncheckedR(argumentsRegister) = arguments; + callFrame->uncheckedR(unmodifiedArgumentsRegister(argumentsRegister)) = arguments; } - ASSERT(arguments->inherits(&Arguments::info)); - return arguments; + ASSERT(callFrame->uncheckedR(argumentsRegister).jsValue().inherits(&Arguments::info)); + return callFrame->uncheckedR(argumentsRegister).jsValue(); } // These two functions serve the purpose of isolating the common case from a diff --git a/JavaScriptCore/runtime/JSActivation.h b/JavaScriptCore/runtime/JSActivation.h index 761bee4..6dd6d70 100644 --- a/JavaScriptCore/runtime/JSActivation.h +++ b/JavaScriptCore/runtime/JSActivation.h @@ -31,7 +31,6 @@ #include "CodeBlock.h" #include "JSVariableObject.h" -#include "RegisterFile.h" #include "SymbolTable.h" #include "Nodes.h" @@ -48,11 +47,12 @@ namespace JSC { virtual void markChildren(MarkStack&); - virtual bool isDynamicScope() const; + virtual bool isDynamicScope(bool& requiresDynamicChecks) const; virtual bool isActivationObject() const { return true; } virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); + virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode); virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); @@ -60,8 +60,9 @@ namespace JSC { virtual bool deleteProperty(ExecState*, const Identifier& propertyName); virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; - void copyRegisters(Arguments* arguments); + void copyRegisters(); virtual const ClassInfo* classInfo() const { return &info; } static const ClassInfo info; @@ -74,12 +75,12 @@ namespace JSC { private: struct JSActivationData : public JSVariableObjectData { JSActivationData(NonNullPassRefPtr<FunctionExecutable> _functionExecutable, Register* registers) - : JSVariableObjectData(_functionExecutable->generatedBytecode().symbolTable(), registers) + : JSVariableObjectData(_functionExecutable->symbolTable(), registers) , functionExecutable(_functionExecutable) { // We have to manually ref and deref the symbol table as JSVariableObjectData // doesn't know about SharedSymbolTable - functionExecutable->generatedBytecode().sharedSymbolTable()->ref(); + functionExecutable->symbolTable()->ref(); } ~JSActivationData() { @@ -88,8 +89,14 @@ namespace JSC { RefPtr<FunctionExecutable> functionExecutable; }; - - static JSValue argumentsGetter(ExecState*, const Identifier&, const PropertySlot&); + + bool symbolTableGet(const Identifier&, PropertySlot&); + bool symbolTableGet(const Identifier&, PropertyDescriptor&); + bool symbolTableGet(const Identifier&, PropertySlot&, bool& slotIsWriteable); + bool symbolTablePut(const Identifier&, JSValue); + bool symbolTablePutWithAttributes(const Identifier&, JSValue, unsigned attributes); + + static JSValue argumentsGetter(ExecState*, JSValue, const Identifier&); NEVER_INLINE PropertySlot::GetValueFunc getArgumentsGetter(); JSActivationData* d() const { return static_cast<JSActivationData*>(JSVariableObject::d); } diff --git a/JavaScriptCore/runtime/JSArray.cpp b/JavaScriptCore/runtime/JSArray.cpp index 2be7371..b8b92f4 100644 --- a/JavaScriptCore/runtime/JSArray.cpp +++ b/JavaScriptCore/runtime/JSArray.cpp @@ -33,8 +33,6 @@ #include <wtf/OwnPtr.h> #include <Operations.h> -#define CHECK_ARRAY_CONSISTENCY 0 - using namespace std; using namespace WTF; @@ -80,6 +78,14 @@ ASSERT_CLASS_FITS_IN_CELL(JSArray); // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer. #define MAX_ARRAY_INDEX 0xFFFFFFFEU +// The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate +// for an array that was created with a sepcified length (e.g. a = new Array(123)) +#define BASE_VECTOR_LEN 4U + +// The upper bound to the size we'll grow a zero length array when the first element +// is added. +#define FIRST_VECTOR_GROW 4U + // Our policy for when to use a vector and when to use a sparse map. // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector. // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector @@ -88,6 +94,11 @@ static const unsigned minDensityMultiplier = 8; const ClassInfo JSArray::info = {"Array", 0, 0, 0}; +// We keep track of the size of the last array after it was grown. We use this +// as a simple heuristic for as the value to grow the next array from size 0. +// This value is capped by the constant FIRST_VECTOR_GROW defined above. +static unsigned lastArraySize = 0; + static inline size_t storageSize(unsigned vectorLength) { ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH); @@ -102,21 +113,6 @@ static inline size_t storageSize(unsigned vectorLength) return size; } -static inline unsigned increasedVectorLength(unsigned newLength) -{ - ASSERT(newLength <= MAX_STORAGE_VECTOR_LENGTH); - - // Mathematically equivalent to: - // increasedLength = (newLength * 3 + 1) / 2; - // or: - // increasedLength = (unsigned)ceil(newLength * 1.5)); - // This form is not prone to internal overflow. - unsigned increasedLength = newLength + (newLength >> 1) + (newLength & 1); - ASSERT(increasedLength >= newLength); - - return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); -} - static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) { return length / minDensityMultiplier <= numValues; @@ -130,60 +126,115 @@ inline void JSArray::checkConsistency(ConsistencyCheckType) #endif +JSArray::JSArray(VPtrStealingHackType) + : JSObject(createStructure(jsNull())) +{ + unsigned initialCapacity = 0; + + m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); + m_storage->m_allocBase = m_storage; + m_indexBias = 0; + m_vectorLength = initialCapacity; + + checkConsistency(); + + // It's not safe to call Heap::heap(this) in order to report extra memory + // cost here, because the VPtrStealingHackType JSArray is not allocated on + // the heap. For the same reason, it's OK not to report extra cost. +} + JSArray::JSArray(NonNullPassRefPtr<Structure> structure) : JSObject(structure) { unsigned initialCapacity = 0; m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity))); + m_storage->m_allocBase = m_storage; + m_indexBias = 0; m_vectorLength = initialCapacity; checkConsistency(); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(0)); } -JSArray::JSArray(NonNullPassRefPtr<Structure> structure, unsigned initialLength) +JSArray::JSArray(NonNullPassRefPtr<Structure> structure, unsigned initialLength, ArrayCreationMode creationMode) : JSObject(structure) { - unsigned initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX); - + unsigned initialCapacity; + if (creationMode == CreateCompact) + initialCapacity = initialLength; + else + initialCapacity = min(BASE_VECTOR_LEN, MIN_SPARSE_ARRAY_INDEX); + m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); + m_storage->m_allocBase = m_storage; m_storage->m_length = initialLength; + m_indexBias = 0; m_vectorLength = initialCapacity; - m_storage->m_numValuesInVector = 0; m_storage->m_sparseValueMap = 0; - m_storage->lazyCreationData = 0; + m_storage->subclassData = 0; m_storage->reportedMapCapacity = 0; - JSValue* vector = m_storage->m_vector; - for (size_t i = 0; i < initialCapacity; ++i) - vector[i] = JSValue(); + if (creationMode == CreateCompact) { +#if CHECK_ARRAY_CONSISTENCY + m_storage->m_inCompactInitialization = !!initialCapacity; +#endif + m_storage->m_length = 0; + m_storage->m_numValuesInVector = initialCapacity; + } else { +#if CHECK_ARRAY_CONSISTENCY + storage->m_inCompactInitialization = false; +#endif + m_storage->m_length = initialLength; + m_storage->m_numValuesInVector = 0; + JSValue* vector = m_storage->m_vector; + for (size_t i = 0; i < initialCapacity; ++i) + vector[i] = JSValue(); + } checkConsistency(); - - Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue)); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity)); } JSArray::JSArray(NonNullPassRefPtr<Structure> structure, const ArgList& list) : JSObject(structure) { unsigned initialCapacity = list.size(); - - m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity))); + unsigned initialStorage; + + // If the ArgList is empty, allocate space for 3 entries. This value empirically + // works well for benchmarks. + if (!initialCapacity) + initialStorage = 3; + else + initialStorage = initialCapacity; + + m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialStorage))); + m_storage->m_allocBase = m_storage; + m_indexBias = 0; m_storage->m_length = initialCapacity; - m_vectorLength = initialCapacity; + m_vectorLength = initialStorage; m_storage->m_numValuesInVector = initialCapacity; m_storage->m_sparseValueMap = 0; - m_storage->lazyCreationData = 0; + m_storage->subclassData = 0; m_storage->reportedMapCapacity = 0; +#if CHECK_ARRAY_CONSISTENCY + m_storage->m_inCompactInitialization = false; +#endif size_t i = 0; + JSValue* vector = m_storage->m_vector; ArgList::const_iterator end = list.end(); for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i) - m_storage->m_vector[i] = *it; + vector[i] = *it; + for (; i < initialStorage; i++) + vector[i] = JSValue(); checkConsistency(); - Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity)); + Heap::heap(this)->reportExtraMemoryCost(storageSize(initialStorage)); } JSArray::~JSArray() @@ -192,13 +243,13 @@ JSArray::~JSArray() checkConsistency(DestructorConsistencyCheck); delete m_storage->m_sparseValueMap; - fastFree(m_storage); + fastFree(m_storage->m_allocBase); } bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot) { ArrayStorage* storage = m_storage; - + if (i >= storage->m_length) { if (i > MAX_ARRAY_INDEX) return getOwnPropertySlot(exec, Identifier::from(exec, i), slot); @@ -227,12 +278,12 @@ bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { if (propertyName == exec->propertyNames().length) { - slot.setValue(jsNumber(exec, length())); + slot.setValue(jsNumber(length())); return true; } bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex) return JSArray::getOwnPropertySlot(exec, i, slot); @@ -242,22 +293,24 @@ bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { if (propertyName == exec->propertyNames().length) { - descriptor.setDescriptor(jsNumber(exec, length()), DontDelete | DontEnum); + descriptor.setDescriptor(jsNumber(length()), DontDelete | DontEnum); return true; } + + ArrayStorage* storage = m_storage; bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex) { - if (i >= m_storage->m_length) + if (i >= storage->m_length) return false; if (i < m_vectorLength) { - JSValue& value = m_storage->m_vector[i]; + JSValue& value = storage->m_vector[i]; if (value) { descriptor.setDescriptor(value, 0); return true; } - } else if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { + } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) { if (i >= MIN_SPARSE_ARRAY_INDEX) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->end()) { @@ -274,7 +327,7 @@ bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& proper void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex) { put(exec, i, value); return; @@ -283,7 +336,7 @@ void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value if (propertyName == exec->propertyNames().length) { unsigned newLength = value.toUInt32(exec); if (value.toNumber(exec) != static_cast<double>(newLength)) { - throwError(exec, RangeError, "Invalid array length."); + throwError(exec, createRangeError(exec, "Invalid array length.")); return; } setLength(newLength); @@ -297,21 +350,23 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue value) { checkConsistency(); - unsigned length = m_storage->m_length; + ArrayStorage* storage = m_storage; + + unsigned length = storage->m_length; if (i >= length && i <= MAX_ARRAY_INDEX) { length = i + 1; - m_storage->m_length = length; + storage->m_length = length; } if (i < m_vectorLength) { - JSValue& valueSlot = m_storage->m_vector[i]; + JSValue& valueSlot = storage->m_vector[i]; if (valueSlot) { valueSlot = value; checkConsistency(); return; } valueSlot = value; - ++m_storage->m_numValuesInVector; + ++storage->m_numValuesInVector; checkConsistency(); return; } @@ -322,6 +377,7 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue value) NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value) { ArrayStorage* storage = m_storage; + SparseArrayValueMap* map = storage->m_sparseValueMap; if (i >= MIN_SPARSE_ARRAY_INDEX) { @@ -369,16 +425,17 @@ NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue valu // Decide how many values it would be best to move from the map. unsigned newNumValuesInVector = storage->m_numValuesInVector + 1; - unsigned newVectorLength = increasedVectorLength(i + 1); + unsigned newVectorLength = getNewVectorLength(i + 1); for (unsigned j = max(m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) newNumValuesInVector += map->contains(j); if (i >= MIN_SPARSE_ARRAY_INDEX) newNumValuesInVector -= map->contains(i); if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) { + unsigned needLength = max(i + 1, storage->m_length); unsigned proposedNewNumValuesInVector = newNumValuesInVector; // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further. - while (newVectorLength < MAX_STORAGE_VECTOR_LENGTH) { - unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1); + while ((newVectorLength < needLength) && (newVectorLength < MAX_STORAGE_VECTOR_LENGTH)) { + unsigned proposedNewVectorLength = getNewVectorLength(newVectorLength + 1); for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j) proposedNewNumValuesInVector += map->contains(j); if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) @@ -388,31 +445,38 @@ NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue valu } } - if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) { + void* baseStorage = storage->m_allocBase; + + if (!tryFastRealloc(baseStorage, storageSize(newVectorLength + m_indexBias)).getValue(baseStorage)) { throwOutOfMemoryError(exec); return; } + m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + m_indexBias * sizeof(JSValue)); + m_storage->m_allocBase = baseStorage; + storage = m_storage; + unsigned vectorLength = m_vectorLength; + JSValue* vector = storage->m_vector; if (newNumValuesInVector == storage->m_numValuesInVector + 1) { for (unsigned j = vectorLength; j < newVectorLength; ++j) - storage->m_vector[j] = JSValue(); + vector[j] = JSValue(); if (i > MIN_SPARSE_ARRAY_INDEX) map->remove(i); } else { for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j) - storage->m_vector[j] = JSValue(); + vector[j] = JSValue(); for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j) - storage->m_vector[j] = map->take(j); + vector[j] = map->take(j); } - storage->m_vector[i] = value; + ASSERT(i < newVectorLength); m_vectorLength = newVectorLength; storage->m_numValuesInVector = newNumValuesInVector; - m_storage = storage; + storage->m_vector[i] = value; checkConsistency(); @@ -422,7 +486,7 @@ NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue valu bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName) { bool isArrayIndex; - unsigned i = propertyName.toArrayIndex(&isArrayIndex); + unsigned i = propertyName.toArrayIndex(isArrayIndex); if (isArrayIndex) return deleteProperty(exec, i); @@ -437,7 +501,7 @@ bool JSArray::deleteProperty(ExecState* exec, unsigned i) checkConsistency(); ArrayStorage* storage = m_storage; - + if (i < m_vectorLength) { JSValue& valueSlot = storage->m_vector[i]; if (!valueSlot) { @@ -476,7 +540,7 @@ void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNa // which almost certainly means a different structure for PropertyNameArray. ArrayStorage* storage = m_storage; - + unsigned usedVectorLength = min(storage->m_length, m_vectorLength); for (unsigned i = 0; i < usedVectorLength; ++i) { if (storage->m_vector[i]) @@ -495,6 +559,33 @@ void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNa JSObject::getOwnPropertyNames(exec, propertyNames, mode); } +ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength) +{ + ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH); + + unsigned increasedLength; + unsigned maxInitLength = min(m_storage->m_length, 100000U); + + if (desiredLength < maxInitLength) + increasedLength = maxInitLength; + else if (!m_vectorLength) + increasedLength = max(desiredLength, lastArraySize); + else { + // Mathematically equivalent to: + // increasedLength = (newLength * 3 + 1) / 2; + // or: + // increasedLength = (unsigned)ceil(newLength * 1.5)); + // This form is not prone to internal overflow. + increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1); + } + + ASSERT(increasedLength >= desiredLength); + + lastArraySize = min(increasedLength, FIRST_VECTOR_GROW); + + return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH); +} + bool JSArray::increaseVectorLength(unsigned newLength) { // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map @@ -505,30 +596,72 @@ bool JSArray::increaseVectorLength(unsigned newLength) unsigned vectorLength = m_vectorLength; ASSERT(newLength > vectorLength); ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX); - unsigned newVectorLength = increasedVectorLength(newLength); + unsigned newVectorLength = getNewVectorLength(newLength); + void* baseStorage = storage->m_allocBase; - if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) + if (!tryFastRealloc(baseStorage, storageSize(newVectorLength + m_indexBias)).getValue(baseStorage)) return false; - m_vectorLength = newVectorLength; + storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + m_indexBias * sizeof(JSValue)); + m_storage->m_allocBase = baseStorage; + JSValue* vector = storage->m_vector; for (unsigned i = vectorLength; i < newVectorLength; ++i) - storage->m_vector[i] = JSValue(); - - m_storage = storage; + vector[i] = JSValue(); + m_vectorLength = newVectorLength; + Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); return true; } -void JSArray::setLength(unsigned newLength) +bool JSArray::increaseVectorPrefixLength(unsigned newLength) { - checkConsistency(); + // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map + // to the vector. Callers have to account for that, because they can do it more efficiently. + + ArrayStorage* storage = m_storage; + + unsigned vectorLength = m_vectorLength; + ASSERT(newLength > vectorLength); + ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX); + unsigned newVectorLength = getNewVectorLength(newLength); + + void* newBaseStorage = fastMalloc(storageSize(newVectorLength + m_indexBias)); + if (!newBaseStorage) + return false; + + m_indexBias += newVectorLength - newLength; + + m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newBaseStorage) + m_indexBias * sizeof(JSValue)); + + memcpy(m_storage, storage, storageSize(0)); + memcpy(&m_storage->m_vector[newLength - m_vectorLength], &storage->m_vector[0], vectorLength * sizeof(JSValue)); + + m_storage->m_allocBase = newBaseStorage; + m_vectorLength = newLength; + + fastFree(storage->m_allocBase); + + Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength)); + + return true; +} + +void JSArray::setLength(unsigned newLength) +{ ArrayStorage* storage = m_storage; + +#if CHECK_ARRAY_CONSISTENCY + if (!storage->m_inCompactInitialization) + checkConsistency(); + else + storage->m_inCompactInitialization = false; +#endif - unsigned length = m_storage->m_length; + unsigned length = storage->m_length; if (newLength < length) { unsigned usedVectorLength = min(length, m_vectorLength); @@ -553,7 +686,7 @@ void JSArray::setLength(unsigned newLength) } } - m_storage->m_length = newLength; + storage->m_length = newLength; checkConsistency(); } @@ -562,7 +695,9 @@ JSValue JSArray::pop() { checkConsistency(); - unsigned length = m_storage->m_length; + ArrayStorage* storage = m_storage; + + unsigned length = storage->m_length; if (!length) return jsUndefined(); @@ -571,29 +706,29 @@ JSValue JSArray::pop() JSValue result; if (length < m_vectorLength) { - JSValue& valueSlot = m_storage->m_vector[length]; + JSValue& valueSlot = storage->m_vector[length]; if (valueSlot) { - --m_storage->m_numValuesInVector; + --storage->m_numValuesInVector; result = valueSlot; valueSlot = JSValue(); } else result = jsUndefined(); } else { result = jsUndefined(); - if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { SparseArrayValueMap::iterator it = map->find(length); if (it != map->end()) { result = it->second; map->remove(it); if (map->isEmpty()) { delete map; - m_storage->m_sparseValueMap = 0; + storage->m_sparseValueMap = 0; } } } } - m_storage->m_length = length; + storage->m_length = length; checkConsistency(); @@ -603,22 +738,25 @@ JSValue JSArray::pop() void JSArray::push(ExecState* exec, JSValue value) { checkConsistency(); + + ArrayStorage* storage = m_storage; - if (m_storage->m_length < m_vectorLength) { - m_storage->m_vector[m_storage->m_length] = value; - ++m_storage->m_numValuesInVector; - ++m_storage->m_length; + if (storage->m_length < m_vectorLength) { + storage->m_vector[storage->m_length] = value; + ++storage->m_numValuesInVector; + ++storage->m_length; checkConsistency(); return; } - if (m_storage->m_length < MIN_SPARSE_ARRAY_INDEX) { - SparseArrayValueMap* map = m_storage->m_sparseValueMap; + if (storage->m_length < MIN_SPARSE_ARRAY_INDEX) { + SparseArrayValueMap* map = storage->m_sparseValueMap; if (!map || map->isEmpty()) { - if (increaseVectorLength(m_storage->m_length + 1)) { - m_storage->m_vector[m_storage->m_length] = value; - ++m_storage->m_numValuesInVector; - ++m_storage->m_length; + if (increaseVectorLength(storage->m_length + 1)) { + storage = m_storage; + storage->m_vector[storage->m_length] = value; + ++storage->m_numValuesInVector; + ++storage->m_length; checkConsistency(); return; } @@ -628,7 +766,100 @@ void JSArray::push(ExecState* exec, JSValue value) } } - putSlowCase(exec, m_storage->m_length++, value); + putSlowCase(exec, storage->m_length++, value); +} + +void JSArray::shiftCount(ExecState* exec, int count) +{ + ASSERT(count > 0); + + ArrayStorage* storage = m_storage; + + unsigned oldLength = storage->m_length; + + if (!oldLength) + return; + + if (oldLength != storage->m_numValuesInVector) { + // If m_length and m_numValuesInVector aren't the same, we have a sparse vector + // which means we need to go through each entry looking for the the "empty" + // slots and then fill them with possible properties. See ECMA spec. + // 15.4.4.9 steps 11 through 13. + for (unsigned i = count; i < oldLength; ++i) { + if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { + PropertySlot slot(this); + JSValue p = prototype(); + if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) + put(exec, i, slot.getValue(exec, i)); + } + } + + storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. + + // Need to decrement numValuesInvector based on number of real entries + for (unsigned i = 0; i < (unsigned)count; ++i) + if ((i < m_vectorLength) && (storage->m_vector[i])) + --storage->m_numValuesInVector; + } else + storage->m_numValuesInVector -= count; + + storage->m_length -= count; + + if (m_vectorLength) { + count = min(m_vectorLength, (unsigned)count); + + m_vectorLength -= count; + + if (m_vectorLength) { + char* newBaseStorage = reinterpret_cast<char*>(storage) + count * sizeof(JSValue); + memmove(newBaseStorage, storage, storageSize(0)); + m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); + + m_indexBias += count; + } + } +} + +void JSArray::unshiftCount(ExecState* exec, int count) +{ + ArrayStorage* storage = m_storage; + + ASSERT(m_indexBias >= 0); + ASSERT(count >= 0); + + unsigned length = storage->m_length; + + if (length != storage->m_numValuesInVector) { + // If m_length and m_numValuesInVector aren't the same, we have a sparse vector + // which means we need to go through each entry looking for the the "empty" + // slots and then fill them with possible properties. See ECMA spec. + // 15.4.4.13 steps 8 through 10. + for (unsigned i = 0; i < length; ++i) { + if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) { + PropertySlot slot(this); + JSValue p = prototype(); + if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot))) + put(exec, i, slot.getValue(exec, i)); + } + } + } + + storage = m_storage; // The put() above could have grown the vector and realloc'ed storage. + + if (m_indexBias >= count) { + m_indexBias -= count; + char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(JSValue); + memmove(newBaseStorage, storage, storageSize(0)); + m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage); + m_vectorLength += count; + } else if (!increaseVectorPrefixLength(m_vectorLength + count)) { + throwOutOfMemoryError(exec); + return; + } + + JSValue* vector = m_storage->m_vector; + for (int i = 0; i < count; i++) + vector[i] = JSValue(); } void JSArray::markChildren(MarkStack& markStack) @@ -649,13 +880,15 @@ static int compareByStringPairForQSort(const void* a, const void* b) { const ValueStringPair* va = static_cast<const ValueStringPair*>(a); const ValueStringPair* vb = static_cast<const ValueStringPair*>(b); - return compare(va->second, vb->second); + return codePointCompare(va->second, vb->second); } void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) { + ArrayStorage* storage = m_storage; + unsigned lengthNotIncludingUndefined = compactForSorting(); - if (m_storage->m_sparseValueMap) { + if (storage->m_sparseValueMap) { throwOutOfMemoryError(exec); return; } @@ -664,9 +897,9 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal return; bool allValuesAreNumbers = true; - size_t size = m_storage->m_numValuesInVector; + size_t size = storage->m_numValuesInVector; for (size_t i = 0; i < size; ++i) { - if (!m_storage->m_vector[i].isNumber()) { + if (!storage->m_vector[i].isNumber()) { allValuesAreNumbers = false; break; } @@ -678,15 +911,17 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal // For numeric comparison, which is fast, qsort is faster than mergesort. We // also don't require mergesort's stability, since there's no user visible // side-effect from swapping the order of equal primitive values. - qsort(m_storage->m_vector, size, sizeof(JSValue), compareNumbersForQSort); + qsort(storage->m_vector, size, sizeof(JSValue), compareNumbersForQSort); checkConsistency(SortConsistencyCheck); } void JSArray::sort(ExecState* exec) { + ArrayStorage* storage = m_storage; + unsigned lengthNotIncludingUndefined = compactForSorting(); - if (m_storage->m_sparseValueMap) { + if (storage->m_sparseValueMap) { throwOutOfMemoryError(exec); return; } @@ -706,7 +941,7 @@ void JSArray::sort(ExecState* exec) } for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { - JSValue value = m_storage->m_vector[i]; + JSValue value = storage->m_vector[i]; ASSERT(!value.isUndefined()); values[i].first = value; } @@ -738,7 +973,7 @@ void JSArray::sort(ExecState* exec) // modifying the vector incorrectly. for (size_t i = 0; i < lengthNotIncludingUndefined; i++) - m_storage->m_vector[i] = values[i].first; + storage->m_vector[i] = values[i].first; checkConsistency(SortConsistencyCheck); } @@ -825,18 +1060,21 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, { checkConsistency(); + ArrayStorage* storage = m_storage; + // FIXME: This ignores exceptions raised in the compare function or in toNumber. // The maximum tree depth is compiled in - but the caller is clearly up to no good // if a larger array is passed. - ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); - if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) + ASSERT(storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); + if (storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) return; - if (!m_storage->m_length) - return; + unsigned usedVectorLength = min(storage->m_length, m_vectorLength); + unsigned nodeCount = usedVectorLength + (storage->m_sparseValueMap ? storage->m_sparseValueMap->size() : 0); - unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); + if (!nodeCount) + return; AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items tree.abstractor().m_exec = exec; @@ -844,10 +1082,10 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, tree.abstractor().m_compareCallType = callType; tree.abstractor().m_compareCallData = &callData; tree.abstractor().m_globalThisValue = exec->globalThisValue(); - tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0)); + tree.abstractor().m_nodes.grow(nodeCount); if (callType == CallTypeJS) - tree.abstractor().m_cachedCall.set(new CachedCall(exec, asFunction(compareFunction), 2, exec->exceptionSlot())); + tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, asFunction(compareFunction), 2)); if (!tree.abstractor().m_nodes.begin()) { throwOutOfMemoryError(exec); @@ -862,14 +1100,14 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. for (; numDefined < usedVectorLength; ++numDefined) { - JSValue v = m_storage->m_vector[numDefined]; + JSValue v = storage->m_vector[numDefined]; if (!v || v.isUndefined()) break; tree.abstractor().m_nodes[numDefined].value = v; tree.insert(numDefined); } for (unsigned i = numDefined; i < usedVectorLength; ++i) { - JSValue v = m_storage->m_vector[i]; + JSValue v = storage->m_vector[i]; if (v) { if (v.isUndefined()) ++numUndefined; @@ -883,7 +1121,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, unsigned newUsedVectorLength = numDefined + numUndefined; - if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { + if (SparseArrayValueMap* map = storage->m_sparseValueMap) { newUsedVectorLength += map->size(); if (newUsedVectorLength > m_vectorLength) { // Check that it is possible to allocate an array large enough to hold all the entries. @@ -892,6 +1130,8 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, return; } } + + storage = m_storage; SparseArrayValueMap::iterator end = map->end(); for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) { @@ -901,7 +1141,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, } delete map; - m_storage->m_sparseValueMap = 0; + storage->m_sparseValueMap = 0; } ASSERT(tree.abstractor().m_nodes.size() >= numDefined); @@ -913,27 +1153,29 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; iter.start_iter_least(tree); for (unsigned i = 0; i < numDefined; ++i) { - m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value; + storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value; ++iter; } // Put undefined values back in. for (unsigned i = numDefined; i < newUsedVectorLength; ++i) - m_storage->m_vector[i] = jsUndefined(); + storage->m_vector[i] = jsUndefined(); // Ensure that unused values in the vector are zeroed out. for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) - m_storage->m_vector[i] = JSValue(); + storage->m_vector[i] = JSValue(); - m_storage->m_numValuesInVector = newUsedVectorLength; + storage->m_numValuesInVector = newUsedVectorLength; checkConsistency(SortConsistencyCheck); } void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) { - JSValue* vector = m_storage->m_vector; - unsigned vectorEnd = min(m_storage->m_length, m_vectorLength); + ArrayStorage* storage = m_storage; + + JSValue* vector = storage->m_vector; + unsigned vectorEnd = min(storage->m_length, m_vectorLength); unsigned i = 0; for (; i < vectorEnd; ++i) { JSValue& v = vector[i]; @@ -942,16 +1184,16 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) args.append(v); } - for (; i < m_storage->m_length; ++i) + for (; i < storage->m_length; ++i) args.append(get(exec, i)); } void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize) { - ASSERT(m_storage->m_length == maxSize); + ASSERT(m_storage->m_length >= maxSize); UNUSED_PARAM(maxSize); JSValue* vector = m_storage->m_vector; - unsigned vectorEnd = min(m_storage->m_length, m_vectorLength); + unsigned vectorEnd = min(maxSize, m_vectorLength); unsigned i = 0; for (; i < vectorEnd; ++i) { JSValue& v = vector[i]; @@ -960,7 +1202,7 @@ void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSiz buffer[i] = v; } - for (; i < m_storage->m_length; ++i) + for (; i < maxSize; ++i) buffer[i] = get(exec, i); } @@ -970,7 +1212,7 @@ unsigned JSArray::compactForSorting() ArrayStorage* storage = m_storage; - unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength); + unsigned usedVectorLength = min(storage->m_length, m_vectorLength); unsigned numDefined = 0; unsigned numUndefined = 0; @@ -999,6 +1241,7 @@ unsigned JSArray::compactForSorting() // exception is thrown by caller. if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) return 0; + storage = m_storage; } @@ -1022,49 +1265,51 @@ unsigned JSArray::compactForSorting() return numDefined; } -void* JSArray::lazyCreationData() +void* JSArray::subclassData() const { - return m_storage->lazyCreationData; + return m_storage->subclassData; } -void JSArray::setLazyCreationData(void* d) +void JSArray::setSubclassData(void* d) { - m_storage->lazyCreationData = d; + m_storage->subclassData = d; } #if CHECK_ARRAY_CONSISTENCY void JSArray::checkConsistency(ConsistencyCheckType type) { - ASSERT(m_storage); + ArrayStorage* storage = m_storage; + + ASSERT(storage); if (type == SortConsistencyCheck) - ASSERT(!m_storage->m_sparseValueMap); + ASSERT(!storage->m_sparseValueMap); unsigned numValuesInVector = 0; for (unsigned i = 0; i < m_vectorLength; ++i) { - if (JSValue value = m_storage->m_vector[i]) { - ASSERT(i < m_storage->m_length); + if (JSValue value = storage->m_vector[i]) { + ASSERT(i < storage->m_length); if (type != DestructorConsistencyCheck) - value->type(); // Likely to crash if the object was deallocated. + value.isUndefined(); // Likely to crash if the object was deallocated. ++numValuesInVector; } else { if (type == SortConsistencyCheck) - ASSERT(i >= m_storage->m_numValuesInVector); + ASSERT(i >= storage->m_numValuesInVector); } } - ASSERT(numValuesInVector == m_storage->m_numValuesInVector); - ASSERT(numValuesInVector <= m_storage->m_length); + ASSERT(numValuesInVector == storage->m_numValuesInVector); + ASSERT(numValuesInVector <= storage->m_length); - if (m_storage->m_sparseValueMap) { - SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end(); - for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) { + if (storage->m_sparseValueMap) { + SparseArrayValueMap::iterator end = storage->m_sparseValueMap->end(); + for (SparseArrayValueMap::iterator it = storage->m_sparseValueMap->begin(); it != end; ++it) { unsigned index = it->first; - ASSERT(index < m_storage->m_length); - ASSERT(index >= m_vectorLength); + ASSERT(index < storage->m_length); + ASSERT(index >= storage->m_vectorLength); ASSERT(index <= MAX_ARRAY_INDEX); ASSERT(it->second); if (type != DestructorConsistencyCheck) - it->second->type(); // Likely to crash if the object was deallocated. + it->second.isUndefined(); // Likely to crash if the object was deallocated. } } } diff --git a/JavaScriptCore/runtime/JSArray.h b/JavaScriptCore/runtime/JSArray.h index ad6ee88..9e155d8 100644 --- a/JavaScriptCore/runtime/JSArray.h +++ b/JavaScriptCore/runtime/JSArray.h @@ -23,26 +23,50 @@ #include "JSObject.h" +#define CHECK_ARRAY_CONSISTENCY 0 + namespace JSC { typedef HashMap<unsigned, JSValue> SparseArrayValueMap; + // This struct holds the actual data values of an array. A JSArray object points to it's contained ArrayStorage + // struct by pointing to m_vector. To access the contained ArrayStorage struct, use the getStorage() and + // setStorage() methods. It is important to note that there may be space before the ArrayStorage that + // is used to quick unshift / shift operation. The actual allocated pointer is available by using: + // getStorage() - m_indexBias * sizeof(JSValue) struct ArrayStorage { - unsigned m_length; + unsigned m_length; // The "length" property on the array unsigned m_numValuesInVector; SparseArrayValueMap* m_sparseValueMap; - void* lazyCreationData; // A JSArray subclass can use this to fill the vector lazily. + void* subclassData; // A JSArray subclass can use this to fill the vector lazily. + void* m_allocBase; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector. size_t reportedMapCapacity; +#if CHECK_ARRAY_CONSISTENCY + bool m_inCompactInitialization; +#endif JSValue m_vector[1]; }; + // The CreateCompact creation mode is used for fast construction of arrays + // whose size and contents are known at time of creation. + // + // There are two obligations when using this mode: + // + // - uncheckedSetIndex() must be used when initializing the array. + // - setLength() must be called after initialization. + + enum ArrayCreationMode { CreateCompact, CreateInitialized }; + class JSArray : public JSObject { friend class JIT; friend class Walker; public: + enum VPtrStealingHackType { VPtrStealingHack }; + JSArray(VPtrStealingHackType); + explicit JSArray(NonNullPassRefPtr<Structure>); - JSArray(NonNullPassRefPtr<Structure>, unsigned initialLength); + JSArray(NonNullPassRefPtr<Structure>, unsigned initialLength, ArrayCreationMode); JSArray(NonNullPassRefPtr<Structure>, const ArgList& initialValues); virtual ~JSArray(); @@ -52,7 +76,7 @@ namespace JSC { virtual void put(ExecState*, unsigned propertyName, JSValue); // FIXME: Make protected and add setItem. static JS_EXPORTDATA const ClassInfo info; - + unsigned length() const { return m_storage->m_length; } void setLength(unsigned); // OK to use on new arrays, but not if it might be a RegExpMatchArray. @@ -63,6 +87,9 @@ namespace JSC { void push(ExecState*, JSValue); JSValue pop(); + void shiftCount(ExecState*, int count); + void unshiftCount(ExecState*, int count); + bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; } JSValue getIndex(unsigned i) { @@ -74,14 +101,26 @@ namespace JSC { void setIndex(unsigned i, JSValue v) { ASSERT(canSetIndex(i)); + JSValue& x = m_storage->m_vector[i]; if (!x) { - ++m_storage->m_numValuesInVector; - if (i >= m_storage->m_length) - m_storage->m_length = i + 1; + ArrayStorage *storage = m_storage; + ++storage->m_numValuesInVector; + if (i >= storage->m_length) + storage->m_length = i + 1; } x = v; } + + void uncheckedSetIndex(unsigned i, JSValue v) + { + ASSERT(canSetIndex(i)); + ArrayStorage *storage = m_storage; +#if CHECK_ARRAY_CONSISTENCY + ASSERT(storage->m_inCompactInitialization); +#endif + storage->m_vector[i] = v; + } void fillArgList(ExecState*, MarkedArgumentBuffer&); void copyToRegisters(ExecState*, Register*, uint32_t); @@ -101,24 +140,27 @@ namespace JSC { virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); virtual void markChildren(MarkStack&); - void* lazyCreationData(); - void setLazyCreationData(void*); - + void* subclassData() const; + void setSubclassData(void*); + private: virtual const ClassInfo* classInfo() const { return &info; } bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&); void putSlowCase(ExecState*, unsigned propertyName, JSValue); + unsigned getNewVectorLength(unsigned desiredLength); bool increaseVectorLength(unsigned newLength); + bool increaseVectorPrefixLength(unsigned newLength); unsigned compactForSorting(); enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck }; void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck); - unsigned m_vectorLength; - ArrayStorage* m_storage; + unsigned m_vectorLength; // The valid length of m_vector + int m_indexBias; // The number of JSValue sized blocks before ArrayStorage. + ArrayStorage *m_storage; }; JSArray* asArray(JSValue); @@ -194,7 +236,7 @@ namespace JSC { current.m_values++; JSCell* cell; - if (!value || !value.isCell() || Heap::isCellMarked(cell = value.asCell())) { + if (!value || !value.isCell() || Heap::checkMarkCell(cell = value.asCell())) { if (current.m_values == end) { m_markSets.removeLast(); continue; @@ -202,7 +244,6 @@ namespace JSC { goto findNextUnmarkedNullValue; } - Heap::markCell(cell); if (cell->structure()->typeInfo().type() < CompoundType) { if (current.m_values == end) { m_markSets.removeLast(); @@ -220,7 +261,17 @@ namespace JSC { markChildren(m_values.removeLast()); } } - + + // Rule from ECMA 15.2 about what an array index is. + // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1. + inline unsigned Identifier::toArrayIndex(bool& ok) const + { + unsigned i = toUInt32(ok); + if (ok && i >= 0xFFFFFFFFU) + ok = false; + return i; + } + } // namespace JSC #endif // JSArray_h diff --git a/JavaScriptCore/runtime/JSByteArray.cpp b/JavaScriptCore/runtime/JSByteArray.cpp index 803a08c..6af9d75 100644 --- a/JavaScriptCore/runtime/JSByteArray.cpp +++ b/JavaScriptCore/runtime/JSByteArray.cpp @@ -40,7 +40,7 @@ JSByteArray::JSByteArray(ExecState* exec, NonNullPassRefPtr<Structure> structure , m_storage(storage) , m_classInfo(classInfo) { - putDirect(exec->globalData().propertyNames->length, jsNumber(exec, m_storage->length()), ReadOnly | DontDelete); + putDirect(exec->globalData().propertyNames->length, jsNumber(m_storage->length()), ReadOnly | DontDelete); } #if !ASSERT_DISABLED @@ -60,7 +60,7 @@ PassRefPtr<Structure> JSByteArray::createStructure(JSValue prototype) bool JSByteArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { bool ok; - unsigned index = propertyName.toUInt32(&ok, false); + unsigned index = propertyName.toUInt32(ok); if (ok && canAccessIndex(index)) { slot.setValue(getIndex(exec, index)); return true; @@ -71,7 +71,7 @@ bool JSByteArray::getOwnPropertySlot(ExecState* exec, const Identifier& property bool JSByteArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { bool ok; - unsigned index = propertyName.toUInt32(&ok, false); + unsigned index = propertyName.toUInt32(ok); if (ok && canAccessIndex(index)) { descriptor.setDescriptor(getIndex(exec, index), DontDelete); return true; @@ -91,7 +91,7 @@ bool JSByteArray::getOwnPropertySlot(ExecState* exec, unsigned propertyName, Pro void JSByteArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { bool ok; - unsigned index = propertyName.toUInt32(&ok, false); + unsigned index = propertyName.toUInt32(ok); if (ok) { setIndex(exec, index, value); return; diff --git a/JavaScriptCore/runtime/JSByteArray.h b/JavaScriptCore/runtime/JSByteArray.h index 5b7adcf..44bae2d 100644 --- a/JavaScriptCore/runtime/JSByteArray.h +++ b/JavaScriptCore/runtime/JSByteArray.h @@ -36,34 +36,27 @@ namespace JSC { friend class JSGlobalData; public: bool canAccessIndex(unsigned i) { return i < m_storage->length(); } - JSValue getIndex(ExecState* exec, unsigned i) + JSValue getIndex(ExecState*, unsigned i) { ASSERT(canAccessIndex(i)); - return jsNumber(exec, m_storage->data()[i]); + return jsNumber(m_storage->data()[i]); } void setIndex(unsigned i, int value) { ASSERT(canAccessIndex(i)); - if (value & ~0xFF) { - if (value < 0) - value = 0; - else - value = 255; - } m_storage->data()[i] = static_cast<unsigned char>(value); } - + void setIndex(unsigned i, double value) { ASSERT(canAccessIndex(i)); - if (!(value > 0)) // Clamp NaN to 0 - value = 0; - else if (value > 255) - value = 255; - m_storage->data()[i] = static_cast<unsigned char>(value + 0.5); + // The largest integer value that a double can represent without loss of precision + // is 2^53. long long is the smallest integral type that gives correct results + // when casting numbers larger than 2^31 from a value of type double. + m_storage->data()[i] = static_cast<unsigned char>(static_cast<long long>(value)); } - + void setIndex(ExecState* exec, unsigned i, JSValue value) { double byteValue = value.toNumber(exec); @@ -75,7 +68,7 @@ namespace JSC { JSByteArray(ExecState* exec, NonNullPassRefPtr<Structure>, WTF::ByteArray* storage, const JSC::ClassInfo* = &s_defaultInfo); static PassRefPtr<Structure> createStructure(JSValue prototype); - + virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&); virtual bool getOwnPropertySlot(JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); @@ -86,7 +79,7 @@ namespace JSC { virtual const ClassInfo* classInfo() const { return m_classInfo; } static const ClassInfo s_defaultInfo; - + size_t length() const { return m_storage->length(); } WTF::ByteArray* storage() const { return m_storage.get(); } @@ -100,7 +93,7 @@ namespace JSC { private: enum VPtrStealingHackType { VPtrStealingHack }; - JSByteArray(VPtrStealingHackType) + JSByteArray(VPtrStealingHackType) : JSObject(createStructure(jsNull())) , m_classInfo(0) { @@ -109,11 +102,11 @@ namespace JSC { RefPtr<WTF::ByteArray> m_storage; const ClassInfo* m_classInfo; }; - + JSByteArray* asByteArray(JSValue value); inline JSByteArray* asByteArray(JSValue value) { - return static_cast<JSByteArray*>(asCell(value)); + return static_cast<JSByteArray*>(value.asCell()); } inline bool isJSByteArray(JSGlobalData* globalData, JSValue v) { return v.isCell() && v.asCell()->vptr() == globalData->jsByteArrayVPtr; } diff --git a/JavaScriptCore/runtime/JSCell.cpp b/JavaScriptCore/runtime/JSCell.cpp index 869fbfc..0cc1ab1 100644 --- a/JavaScriptCore/runtime/JSCell.cpp +++ b/JavaScriptCore/runtime/JSCell.cpp @@ -163,16 +163,6 @@ JSObject* JSCell::toThisObject(ExecState* exec) const return toObject(exec); } -UString JSCell::toThisString(ExecState* exec) const -{ - return toThisObject(exec)->toString(exec); -} - -JSString* JSCell::toThisJSString(ExecState* exec) -{ - return jsString(exec, toThisString(exec)); -} - const ClassInfo* JSCell::classInfo() const { return 0; diff --git a/JavaScriptCore/runtime/JSCell.h b/JavaScriptCore/runtime/JSCell.h index 3c8c829..7d4929d 100644 --- a/JavaScriptCore/runtime/JSCell.h +++ b/JavaScriptCore/runtime/JSCell.h @@ -23,6 +23,8 @@ #ifndef JSCell_h #define JSCell_h +#include "CallData.h" +#include "ConstructData.h" #include "Collector.h" #include "JSImmediate.h" #include "JSValue.h" @@ -32,7 +34,24 @@ namespace JSC { - class JSCell : public NoncopyableCustomAllocated { +#if COMPILER(MSVC) + // If WTF_MAKE_NONCOPYABLE is applied to JSCell we end up with a bunch of + // undefined references to the JSCell copy constructor and assignment operator + // when linking JavaScriptCore. + class MSVCBugWorkaround { + WTF_MAKE_NONCOPYABLE(MSVCBugWorkaround); + + protected: + MSVCBugWorkaround() { } + ~MSVCBugWorkaround() { } + }; + + class JSCell : MSVCBugWorkaround { +#else + class JSCell { + WTF_MAKE_NONCOPYABLE(JSCell); +#endif + friend class GetterSetter; friend class Heap; friend class JIT; @@ -56,9 +75,6 @@ namespace JSC { } // Querying the type. -#if USE(JSVALUE32) - bool isNumber() const; -#endif bool isString() const; bool isObject() const; virtual bool isGetterSetter() const; @@ -107,18 +123,21 @@ namespace JSC { virtual bool deleteProperty(ExecState*, unsigned propertyName); virtual JSObject* toThisObject(ExecState*) const; - virtual UString toThisString(ExecState*) const; - virtual JSString* toThisJSString(ExecState*); virtual JSValue getJSNumber(); void* vptr() { return *reinterpret_cast<void**>(this); } void setVPtr(void* vptr) { *reinterpret_cast<void**>(this) = vptr; } + // FIXME: Rename getOwnPropertySlot to virtualGetOwnPropertySlot, and + // fastGetOwnPropertySlot to getOwnPropertySlot. Callers should always + // call this function, not its slower virtual counterpart. (For integer + // property names, we want a similar interface with appropriate optimizations.) + bool fastGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + protected: static const unsigned AnonymousSlotCount = 0; private: // Base implementation; for non-object classes implements getPropertySlot. - bool fastGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); @@ -134,13 +153,6 @@ namespace JSC { { } -#if USE(JSVALUE32) - inline bool JSCell::isNumber() const - { - return m_structure->typeInfo().type() == NumberType; - } -#endif - inline bool JSCell::isObject() const { return m_structure->typeInfo().type() == ObjectType; @@ -202,14 +214,18 @@ namespace JSC { return isCell() ? asCell()->getObject() : 0; } - inline CallType JSValue::getCallData(CallData& callData) + inline CallType getCallData(JSValue value, CallData& callData) { - return isCell() ? asCell()->getCallData(callData) : CallTypeNone; + CallType result = value.isCell() ? value.asCell()->getCallData(callData) : CallTypeNone; + ASSERT(result == CallTypeNone || value.isValidCallee()); + return result; } - inline ConstructType JSValue::getConstructData(ConstructData& constructData) + inline ConstructType getConstructData(JSValue value, ConstructData& constructData) { - return isCell() ? asCell()->getConstructData(constructData) : ConstructTypeNone; + ConstructType result = value.isCell() ? value.asCell()->getConstructData(constructData) : ConstructTypeNone; + ASSERT(result == ConstructTypeNone || value.isValidCallee()); + return result; } ALWAYS_INLINE bool JSValue::getUInt32(uint32_t& v) const @@ -227,13 +243,13 @@ namespace JSC { return false; } -#if !USE(JSVALUE32_64) +#if USE(JSVALUE64) ALWAYS_INLINE JSCell* JSValue::asCell() const { ASSERT(isCell()); return m_ptr; } -#endif // !USE(JSVALUE32_64) +#endif // USE(JSVALUE64) inline JSValue JSValue::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const { @@ -301,11 +317,6 @@ namespace JSC { return asCell()->structure()->typeInfo().needsThisConversion(); } - inline UString JSValue::toThisString(ExecState* exec) const - { - return isCell() ? asCell()->toThisString(exec) : toString(exec); - } - inline JSValue JSValue::getJSNumber() { if (isInt32() || isDouble()) @@ -329,9 +340,8 @@ namespace JSC { { ASSERT(!m_isCheckingForDefaultMarkViolation); ASSERT(cell); - if (Heap::isCellMarked(cell)) + if (Heap::checkMarkCell(cell)) return; - Heap::markCell(cell); if (cell->structure()->typeInfo().type() >= CompoundType) m_values.append(cell); } diff --git a/JavaScriptCore/runtime/JSFunction.cpp b/JavaScriptCore/runtime/JSFunction.cpp index d213b4a..99f8e6f 100644 --- a/JavaScriptCore/runtime/JSFunction.cpp +++ b/JavaScriptCore/runtime/JSFunction.cpp @@ -28,8 +28,10 @@ #include "CodeBlock.h" #include "CommonIdentifiers.h" #include "CallFrame.h" +#include "ExceptionHelpers.h" #include "FunctionPrototype.h" #include "JSGlobalObject.h" +#include "JSNotAnObject.h" #include "Interpreter.h" #include "ObjectPrototype.h" #include "Parser.h" @@ -40,10 +42,16 @@ using namespace WTF; using namespace Unicode; namespace JSC { +#if ENABLE(JIT) +EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) +{ + return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); +} +#endif ASSERT_CLASS_FITS_IN_CELL(JSFunction); -const ClassInfo JSFunction::info = { "Function", &InternalFunction::info, 0, 0 }; +const ClassInfo JSFunction::info = { "Function", 0, 0, 0 }; bool JSFunction::isHostFunctionNonInline() const { @@ -53,18 +61,31 @@ bool JSFunction::isHostFunctionNonInline() const JSFunction::JSFunction(NonNullPassRefPtr<Structure> structure) : Base(structure) , m_executable(adoptRef(new VPtrHackExecutable())) + , m_scopeChain(NoScopeChain()) +{ +} + +#if ENABLE(JIT) +JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, PassRefPtr<NativeExecutable> thunk) + : Base(globalObject, structure) + , m_executable(thunk) + , m_scopeChain(globalObject->globalScopeChain()) { + putDirect(exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); } +#endif -JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, NativeFunction func) - : Base(&exec->globalData(), structure, name) +JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, int length, const Identifier& name, NativeFunction func) + : Base(globalObject, structure) #if ENABLE(JIT) - , m_executable(adoptRef(new NativeExecutable(exec))) + , m_executable(exec->globalData().getHostFunction(func)) #endif + , m_scopeChain(globalObject->globalScopeChain()) { + putDirect(exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); #if ENABLE(JIT) - setNativeFunction(func); - putDirect(exec->propertyNames().length, jsNumber(exec, length), DontDelete | ReadOnly | DontEnum); + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); #else UNUSED_PARAM(length); UNUSED_PARAM(func); @@ -73,10 +94,12 @@ JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<Structure> structure, } JSFunction::JSFunction(ExecState* exec, NonNullPassRefPtr<FunctionExecutable> executable, ScopeChainNode* scopeChainNode) - : Base(&exec->globalData(), exec->lexicalGlobalObject()->functionStructure(), executable->name()) + : Base(scopeChainNode->globalObject, scopeChainNode->globalObject->functionStructure()) , m_executable(executable) + , m_scopeChain(scopeChainNode) { - setScopeChain(scopeChainNode); + const Identifier& name = static_cast<FunctionExecutable*>(m_executable.get())->name(); + putDirect(exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); } JSFunction::~JSFunction() @@ -89,58 +112,89 @@ JSFunction::~JSFunction() if (!isHostFunction()) { #if ENABLE(JIT_OPTIMIZE_CALL) ASSERT(m_executable); - if (jsExecutable()->isGenerated()) - jsExecutable()->generatedBytecode().unlinkCallers(); + if (jsExecutable()->isGeneratedForCall()) + jsExecutable()->generatedBytecodeForCall().unlinkCallers(); + if (jsExecutable()->isGeneratedForConstruct()) + jsExecutable()->generatedBytecodeForConstruct().unlinkCallers(); #endif - scopeChain().~ScopeChain(); // FIXME: Don't we need to do this in the interpreter too? } } +static const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function"; +static const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function"; + +static void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message) +{ + JSValue thrower = createTypeErrorFunction(exec, message); + descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter); +} + +const UString& JSFunction::name(ExecState* exec) +{ + return asString(getDirect(exec->globalData().propertyNames->name))->tryGetValue(); +} + +const UString JSFunction::displayName(ExecState* exec) +{ + JSValue displayName = getDirect(exec->globalData().propertyNames->displayName); + + if (displayName && isJSString(&exec->globalData(), displayName)) + return asString(displayName)->tryGetValue(); + + return UString(); +} + +const UString JSFunction::calculatedDisplayName(ExecState* exec) +{ + const UString explicitName = displayName(exec); + + if (!explicitName.isEmpty()) + return explicitName; + + return name(exec); +} + void JSFunction::markChildren(MarkStack& markStack) { Base::markChildren(markStack); if (!isHostFunction()) { jsExecutable()->markAggregate(markStack); - scopeChain().markAggregate(markStack); + scope().markAggregate(markStack); } } CallType JSFunction::getCallData(CallData& callData) { +#if ENABLE(JIT) if (isHostFunction()) { callData.native.function = nativeFunction(); return CallTypeHost; } +#endif callData.js.functionExecutable = jsExecutable(); - callData.js.scopeChain = scopeChain().node(); + callData.js.scopeChain = scope().node(); return CallTypeJS; } -JSValue JSFunction::call(ExecState* exec, JSValue thisValue, const ArgList& args) +JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&) { - ASSERT(!isHostFunction()); - return exec->interpreter()->execute(jsExecutable(), exec, this, thisValue.toThisObject(exec), args, scopeChain().node(), exec->exceptionSlot()); -} - -JSValue JSFunction::argumentsGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) -{ - JSFunction* thisObj = asFunction(slot.slotBase()); + JSFunction* thisObj = asFunction(slotBase); ASSERT(!thisObj->isHostFunction()); return exec->interpreter()->retrieveArguments(exec, thisObj); } -JSValue JSFunction::callerGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identifier&) { - JSFunction* thisObj = asFunction(slot.slotBase()); + JSFunction* thisObj = asFunction(slotBase); ASSERT(!thisObj->isHostFunction()); return exec->interpreter()->retrieveCaller(exec, thisObj); } -JSValue JSFunction::lengthGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&) { - JSFunction* thisObj = asFunction(slot.slotBase()); + JSFunction* thisObj = asFunction(slotBase); ASSERT(!thisObj->isHostFunction()); - return jsNumber(exec, thisObj->jsExecutable()->parameterCount()); + return jsNumber(thisObj->jsExecutable()->parameterCount()); } bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -152,9 +206,9 @@ bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyN JSValue* location = getDirectLocation(propertyName); if (!location) { - JSObject* prototype = new (exec) JSObject(scopeChain().globalObject()->emptyObjectStructure()); + JSObject* prototype = new (exec) JSObject(scope().globalObject()->emptyObjectStructure()); prototype->putDirect(exec->propertyNames().constructor, this, DontEnum); - putDirect(exec->propertyNames().prototype, prototype, DontDelete); + putDirect(exec->propertyNames().prototype, prototype, DontDelete | DontEnum); location = getDirectLocation(propertyName); } @@ -162,55 +216,76 @@ bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyN } if (propertyName == exec->propertyNames().arguments) { - slot.setCustom(this, argumentsGetter); + if (jsExecutable()->isStrictMode()) { + throwTypeError(exec, "Can't access arguments object of a strict mode function"); + slot.setValue(jsNull()); + return true; + } + + slot.setCacheableCustom(this, argumentsGetter); return true; } if (propertyName == exec->propertyNames().length) { - slot.setCustom(this, lengthGetter); + slot.setCacheableCustom(this, lengthGetter); return true; } if (propertyName == exec->propertyNames().caller) { - slot.setCustom(this, callerGetter); + if (jsExecutable()->isStrictMode()) { + throwTypeError(exec, StrictModeCallerAccessError); + slot.setValue(jsNull()); + return true; + } + slot.setCacheableCustom(this, callerGetter); return true; } return Base::getOwnPropertySlot(exec, propertyName, slot); } - bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) - { - if (isHostFunction()) - return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); - - if (propertyName == exec->propertyNames().prototype) { - PropertySlot slot; - getOwnPropertySlot(exec, propertyName, slot); - return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); - } - - if (propertyName == exec->propertyNames().arguments) { +bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + if (isHostFunction()) + return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); + + if (propertyName == exec->propertyNames().prototype) { + PropertySlot slot; + getOwnPropertySlot(exec, propertyName, slot); + return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); + } + + if (propertyName == exec->propertyNames().arguments) { + if (jsExecutable()->isStrictMode()) + createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError); + else descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete); - return true; - } - - if (propertyName == exec->propertyNames().length) { - descriptor.setDescriptor(jsNumber(exec, jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete); - return true; - } - - if (propertyName == exec->propertyNames().caller) { + return true; + } + + if (propertyName == exec->propertyNames().length) { + descriptor.setDescriptor(jsNumber(jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete); + return true; + } + + if (propertyName == exec->propertyNames().caller) { + if (jsExecutable()->isStrictMode()) + createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError); + else descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete); - return true; - } - - return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); + return true; } + return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + void JSFunction::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { if (!isHostFunction() && (mode == IncludeDontEnumProperties)) { + // Make sure prototype has been reified. + PropertySlot slot; + getOwnPropertySlot(exec, exec->propertyNames().prototype, slot); + propertyNames.add(exec->propertyNames().arguments); propertyNames.add(exec->propertyNames().callee); propertyNames.add(exec->propertyNames().caller); @@ -225,6 +300,22 @@ void JSFunction::put(ExecState* exec, const Identifier& propertyName, JSValue va Base::put(exec, propertyName, value, slot); return; } + if (propertyName == exec->propertyNames().prototype) { + // Make sure prototype has been reified, such that it can only be overwritten + // following the rules set out in ECMA-262 8.12.9. + PropertySlot slot; + getOwnPropertySlot(exec, propertyName, slot); + } + if (jsExecutable()->isStrictMode()) { + if (propertyName == exec->propertyNames().arguments) { + throwTypeError(exec, StrictModeArgumentsAccessError); + return; + } + if (propertyName == exec->propertyNames().caller) { + throwTypeError(exec, StrictModeCallerAccessError); + return; + } + } if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) return; Base::put(exec, propertyName, value, slot); @@ -245,25 +336,8 @@ ConstructType JSFunction::getConstructData(ConstructData& constructData) if (isHostFunction()) return ConstructTypeNone; constructData.js.functionExecutable = jsExecutable(); - constructData.js.scopeChain = scopeChain().node(); + constructData.js.scopeChain = scope().node(); return ConstructTypeJS; } -JSObject* JSFunction::construct(ExecState* exec, const ArgList& args) -{ - ASSERT(!isHostFunction()); - Structure* structure; - JSValue prototype = get(exec, exec->propertyNames().prototype); - if (prototype.isObject()) - structure = asObject(prototype)->inheritorID(); - else - structure = exec->lexicalGlobalObject()->emptyObjectStructure(); - JSObject* thisObj = new (exec) JSObject(structure); - - JSValue result = exec->interpreter()->execute(jsExecutable(), exec, this, thisObj, args, scopeChain().node(), exec->exceptionSlot()); - if (exec->hadException() || !result.isObject()) - return thisObj; - return asObject(result); -} - } // namespace JSC diff --git a/JavaScriptCore/runtime/JSFunction.h b/JavaScriptCore/runtime/JSFunction.h index 8cd4b51..3a2fe30 100644 --- a/JavaScriptCore/runtime/JSFunction.h +++ b/JavaScriptCore/runtime/JSFunction.h @@ -24,7 +24,7 @@ #ifndef JSFunction_h #define JSFunction_h -#include "InternalFunction.h" +#include "JSObjectWithGlobalObject.h" namespace JSC { @@ -33,23 +33,38 @@ namespace JSC { class FunctionPrototype; class JSActivation; class JSGlobalObject; + class NativeExecutable; - class JSFunction : public InternalFunction { + EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState*); + + class JSFunction : public JSObjectWithGlobalObject { friend class JIT; friend class JSGlobalData; - typedef InternalFunction Base; + typedef JSObjectWithGlobalObject Base; public: - JSFunction(ExecState*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); + JSFunction(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); +#if ENABLE(JIT) + JSFunction(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, int length, const Identifier&, PassRefPtr<NativeExecutable>); +#endif JSFunction(ExecState*, NonNullPassRefPtr<FunctionExecutable>, ScopeChainNode*); virtual ~JSFunction(); - JSObject* construct(ExecState*, const ArgList&); - JSValue call(ExecState*, JSValue thisValue, const ArgList&); + const UString& name(ExecState*); + const UString displayName(ExecState*); + const UString calculatedDisplayName(ExecState*); - void setScope(const ScopeChain& scopeChain) { setScopeChain(scopeChain); } - ScopeChain& scope() { return scopeChain(); } + ScopeChain& scope() + { + ASSERT(!isHostFunctionNonInline()); + return m_scopeChain; + } + void setScope(const ScopeChain& scopeChain) + { + ASSERT(!isHostFunctionNonInline()); + m_scopeChain = scopeChain; + } ExecutableBase* executable() const { return m_executable.get(); } @@ -64,16 +79,13 @@ namespace JSC { return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); } - NativeFunction nativeFunction() - { - return *WTF::bitwise_cast<NativeFunction*>(m_data); - } + NativeFunction nativeFunction(); virtual ConstructType getConstructData(ConstructData&); virtual CallType getCallData(CallData&); protected: - const static unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesMarkChildren | OverridesGetPropertyNames | InternalFunction::StructureFlags; + const static unsigned StructureFlags = OverridesGetOwnPropertySlot | ImplementsHasInstance | OverridesMarkChildren | OverridesGetPropertyNames | JSObject::StructureFlags; private: JSFunction(NonNullPassRefPtr<Structure>); @@ -90,36 +102,12 @@ namespace JSC { virtual const ClassInfo* classInfo() const { return &info; } - static JSValue argumentsGetter(ExecState*, const Identifier&, const PropertySlot&); - static JSValue callerGetter(ExecState*, const Identifier&, const PropertySlot&); - static JSValue lengthGetter(ExecState*, const Identifier&, const PropertySlot&); + static JSValue argumentsGetter(ExecState*, JSValue, const Identifier&); + static JSValue callerGetter(ExecState*, JSValue, const Identifier&); + static JSValue lengthGetter(ExecState*, JSValue, const Identifier&); RefPtr<ExecutableBase> m_executable; - ScopeChain& scopeChain() - { - ASSERT(!isHostFunctionNonInline()); - return *WTF::bitwise_cast<ScopeChain*>(m_data); - } - void clearScopeChain() - { - ASSERT(!isHostFunctionNonInline()); - new (m_data) ScopeChain(NoScopeChain()); - } - void setScopeChain(ScopeChainNode* sc) - { - ASSERT(!isHostFunctionNonInline()); - new (m_data) ScopeChain(sc); - } - void setScopeChain(const ScopeChain& sc) - { - ASSERT(!isHostFunctionNonInline()); - *WTF::bitwise_cast<ScopeChain*>(m_data) = sc; - } - void setNativeFunction(NativeFunction func) - { - *WTF::bitwise_cast<NativeFunction*>(m_data) = func; - } - unsigned char m_data[sizeof(void*)]; + ScopeChain m_scopeChain; }; JSFunction* asFunction(JSValue); diff --git a/JavaScriptCore/runtime/JSGlobalData.cpp b/JavaScriptCore/runtime/JSGlobalData.cpp index 45abc86..9948877 100644 --- a/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/JavaScriptCore/runtime/JSGlobalData.cpp @@ -49,6 +49,13 @@ #include "Lookup.h" #include "Nodes.h" #include "Parser.h" +#include "RegExpCache.h" +#include "StrictEvalActivation.h" +#include <wtf/WTFThreadData.h> +#if ENABLE(REGEXP_TRACING) +#include "RegExp.h" +#endif + #if ENABLE(JSC_MULTIPLE_THREADS) #include <wtf/Threading.h> @@ -56,6 +63,7 @@ #if PLATFORM(MAC) #include "ProfilerServer.h" +#include <CoreFoundation/CoreFoundation.h> #endif using namespace WTF; @@ -82,7 +90,7 @@ void JSGlobalData::storeVPtrs() void* storage = &cell; COMPILE_ASSERT(sizeof(JSArray) <= sizeof(CollectorCell), sizeof_JSArray_must_be_less_than_CollectorCell); - JSCell* jsArray = new (storage) JSArray(JSArray::createStructure(jsNull())); + JSCell* jsArray = new (storage) JSArray(JSArray::VPtrStealingHack); JSGlobalData::jsArrayVPtr = jsArray->vptr(); jsArray->~JSCell(); @@ -102,8 +110,8 @@ void JSGlobalData::storeVPtrs() jsFunction->~JSCell(); } -JSGlobalData::JSGlobalData(bool isShared) - : isSharedInstance(isShared) +JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType threadStackType) + : globalDataType(globalDataType) , clientData(0) , arrayTable(fastNew<HashTable>(JSC::arrayTable)) , dateTable(fastNew<HashTable>(JSC::dateTable)) @@ -115,42 +123,62 @@ JSGlobalData::JSGlobalData(bool isShared) , stringTable(fastNew<HashTable>(JSC::stringTable)) , activationStructure(JSActivation::createStructure(jsNull())) , interruptedExecutionErrorStructure(JSObject::createStructure(jsNull())) + , terminatedExecutionErrorStructure(JSObject::createStructure(jsNull())) , staticScopeStructure(JSStaticScopeObject::createStructure(jsNull())) + , strictEvalActivationStructure(StrictEvalActivation::createStructure(jsNull())) , stringStructure(JSString::createStructure(jsNull())) - , notAnObjectErrorStubStructure(JSNotAnObjectErrorStub::createStructure(jsNull())) , notAnObjectStructure(JSNotAnObject::createStructure(jsNull())) , propertyNameIteratorStructure(JSPropertyNameIterator::createStructure(jsNull())) , getterSetterStructure(GetterSetter::createStructure(jsNull())) , apiWrapperStructure(JSAPIValueWrapper::createStructure(jsNull())) , dummyMarkableCellStructure(JSCell::createDummyStructure()) -#if USE(JSVALUE32) - , numberStructure(JSNumberCell::createStructure(jsNull())) -#endif - , identifierTable(createIdentifierTable()) + , identifierTable(globalDataType == Default ? wtfThreadData().currentIdentifierTable() : createIdentifierTable()) , propertyNames(new CommonIdentifiers(this)) , emptyList(new MarkedArgumentBuffer) , lexer(new Lexer(this)) , parser(new Parser) , interpreter(new Interpreter) -#if ENABLE(JIT) - , jitStubs(this) -#endif , heap(this) - , initializingLazyNumericCompareFunction(false) , head(0) , dynamicGlobalObject(0) - , functionCodeBlockBeingReparsed(0) , firstStringifierToMark(0) , markStack(jsArrayVPtr) , cachedUTCOffset(NaN) - , weakRandom(static_cast<int>(currentTime())) + , maxReentryDepth(threadStackType == ThreadStackTypeSmall ? MaxSmallThreadReentryDepth : MaxLargeThreadReentryDepth) + , m_regExpCache(new RegExpCache(this)) +#if ENABLE(REGEXP_TRACING) + , m_rtTraceList(new RTTraceList()) +#endif #ifndef NDEBUG - , mainThreadOnly(false) + , exclusiveThread(0) #endif { #if PLATFORM(MAC) startProfilerServerIfNeeded(); #endif +#if ENABLE(JIT) && ENABLE(INTERPRETER) +#if PLATFORM(CF) + CFStringRef canUseJITKey = CFStringCreateWithCString(0 , "JavaScriptCoreUseJIT", kCFStringEncodingMacRoman); + CFBooleanRef canUseJIT = (CFBooleanRef)CFPreferencesCopyAppValue(canUseJITKey, kCFPreferencesCurrentApplication); + if (canUseJIT) { + m_canUseJIT = kCFBooleanTrue == canUseJIT; + CFRelease(canUseJIT); + } else + m_canUseJIT = !getenv("JavaScriptCoreUseJIT"); + CFRelease(canUseJITKey); +#elif OS(UNIX) + m_canUseJIT = !getenv("JavaScriptCoreUseJIT"); +#else + m_canUseJIT = true; +#endif +#endif +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (m_canUseJIT) + m_canUseJIT = executableAllocator.isValid(); +#endif + jitStubs = new JITThunks(this); +#endif } JSGlobalData::~JSGlobalData() @@ -189,28 +217,30 @@ JSGlobalData::~JSGlobalData() delete emptyList; delete propertyNames; - deleteIdentifierTable(identifierTable); + if (globalDataType != Default) + deleteIdentifierTable(identifierTable); delete clientData; + delete m_regExpCache; +#if ENABLE(REGEXP_TRACING) + delete m_rtTraceList; +#endif } -PassRefPtr<JSGlobalData> JSGlobalData::createNonDefault() +PassRefPtr<JSGlobalData> JSGlobalData::createContextGroup(ThreadStackType type) { - return adoptRef(new JSGlobalData(false)); + return adoptRef(new JSGlobalData(APIContextGroup, type)); } -PassRefPtr<JSGlobalData> JSGlobalData::create() +PassRefPtr<JSGlobalData> JSGlobalData::create(ThreadStackType type) { - JSGlobalData* globalData = new JSGlobalData(false); - setDefaultIdentifierTable(globalData->identifierTable); - setCurrentIdentifierTable(globalData->identifierTable); - return adoptRef(globalData); + return adoptRef(new JSGlobalData(Default, type)); } -PassRefPtr<JSGlobalData> JSGlobalData::createLeaked() +PassRefPtr<JSGlobalData> JSGlobalData::createLeaked(ThreadStackType type) { Structure::startIgnoringLeaks(); - RefPtr<JSGlobalData> data = create(); + RefPtr<JSGlobalData> data = create(type); Structure::stopIgnoringLeaks(); return data.release(); } @@ -224,7 +254,7 @@ JSGlobalData& JSGlobalData::sharedInstance() { JSGlobalData*& instance = sharedInstanceInternal(); if (!instance) { - instance = new JSGlobalData(true); + instance = adoptRef(new JSGlobalData(APIShared, ThreadStackTypeSmall)).leakRef(); #if ENABLE(JSC_MULTIPLE_THREADS) instance->makeUsableFromMultipleThreads(); #endif @@ -239,18 +269,16 @@ JSGlobalData*& JSGlobalData::sharedInstanceInternal() return sharedInstance; } -// FIXME: We can also detect forms like v1 < v2 ? -1 : 0, reverse comparison, etc. -const Vector<Instruction>& JSGlobalData::numericCompareFunction(ExecState* exec) +#if ENABLE(JIT) +PassRefPtr<NativeExecutable> JSGlobalData::getHostFunction(NativeFunction function) { - if (!lazyNumericCompareFunction.size() && !initializingLazyNumericCompareFunction) { - initializingLazyNumericCompareFunction = true; - RefPtr<FunctionExecutable> function = FunctionExecutable::fromGlobalCode(Identifier(exec, "numericCompare"), exec, 0, makeSource(UString("(function (v1, v2) { return v1 - v2; })")), 0, 0); - lazyNumericCompareFunction = function->bytecode(exec, exec->scopeChain()).instructions(); - initializingLazyNumericCompareFunction = false; - } - - return lazyNumericCompareFunction; + return jitStubs->hostFunctionStub(this, function); } +PassRefPtr<NativeExecutable> JSGlobalData::getHostFunction(NativeFunction function, ThunkGenerator generator) +{ + return jitStubs->hostFunctionStub(this, function, generator); +} +#endif JSGlobalData::ClientData::~ClientData() { @@ -261,6 +289,7 @@ void JSGlobalData::resetDateCache() cachedUTCOffset = NaN; dstOffsetCache.reset(); cachedDateString = UString(); + cachedDateStringValue = NaN; dateInstanceCache.reset(); } @@ -279,4 +308,38 @@ void JSGlobalData::dumpSampleData(ExecState* exec) interpreter->dumpSampleData(exec); } + +#if ENABLE(REGEXP_TRACING) +void JSGlobalData::addRegExpToTrace(PassRefPtr<RegExp> regExp) +{ + m_rtTraceList->add(regExp); +} + +void JSGlobalData::dumpRegExpTrace() +{ + // The first RegExp object is ignored. It is create by the RegExpPrototype ctor and not used. + RTTraceList::iterator iter = ++m_rtTraceList->begin(); + + if (iter != m_rtTraceList->end()) { + printf("\nRegExp Tracing\n"); + printf(" match() matches\n"); + printf("Regular Expression JIT Address calls found\n"); + printf("----------------------------------------+----------------+----------+----------\n"); + + unsigned reCount = 0; + + for (; iter != m_rtTraceList->end(); ++iter, ++reCount) + (*iter)->printTraceData(); + + printf("%d Regular Expressions\n", reCount); + } + + m_rtTraceList->clear(); +} +#else +void JSGlobalData::dumpRegExpTrace() +{ +} +#endif + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSGlobalData.h b/JavaScriptCore/runtime/JSGlobalData.h index 0f1f3c6..1819a0c 100644 --- a/JavaScriptCore/runtime/JSGlobalData.h +++ b/JavaScriptCore/runtime/JSGlobalData.h @@ -29,6 +29,7 @@ #ifndef JSGlobalData_h #define JSGlobalData_h +#include "CachedTranscendentalFunction.h" #include "Collector.h" #include "DateInstanceCache.h" #include "ExecutableAllocator.h" @@ -37,11 +38,17 @@ #include "MarkStack.h" #include "NumericStrings.h" #include "SmallStrings.h" +#include "Terminator.h" #include "TimeoutChecker.h" #include "WeakRandom.h" +#include <wtf/BumpPointerAllocator.h> #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/RefCounted.h> +#include <wtf/ThreadSpecific.h> +#if ENABLE(REGEXP_TRACING) +#include <wtf/ListHashSet.h> +#endif struct OpaqueJSClass; struct OpaqueJSClassContextData; @@ -56,9 +63,13 @@ namespace JSC { class JSObject; class Lexer; class Parser; + class RegExpCache; class Stringifier; class Structure; class UString; +#if ENABLE(REGEXP_TRACING) + class RegExp; +#endif struct HashTable; struct Instruction; @@ -83,18 +94,35 @@ namespace JSC { double increment; }; + enum ThreadStackType { + ThreadStackTypeLarge, + ThreadStackTypeSmall + }; + class JSGlobalData : public RefCounted<JSGlobalData> { public: + // WebCore has a one-to-one mapping of threads to JSGlobalDatas; + // either create() or createLeaked() should only be called once + // on a thread, this is the 'default' JSGlobalData (it uses the + // thread's default string uniquing table from wtfThreadData). + // API contexts created using the new context group aware interface + // create APIContextGroup objects which require less locking of JSC + // than the old singleton APIShared JSGlobalData created for use by + // the original API. + enum GlobalDataType { Default, APIContextGroup, APIShared }; + struct ClientData { virtual ~ClientData() = 0; }; + bool isSharedInstance() { return globalDataType == APIShared; } + bool usingAPI() { return globalDataType != Default; } static bool sharedInstanceExists(); static JSGlobalData& sharedInstance(); - static PassRefPtr<JSGlobalData> create(); - static PassRefPtr<JSGlobalData> createLeaked(); - static PassRefPtr<JSGlobalData> createNonDefault(); + static PassRefPtr<JSGlobalData> create(ThreadStackType); + static PassRefPtr<JSGlobalData> createLeaked(ThreadStackType); + static PassRefPtr<JSGlobalData> createContextGroup(ThreadStackType); ~JSGlobalData(); #if ENABLE(JSC_MULTIPLE_THREADS) @@ -102,7 +130,7 @@ namespace JSC { void makeUsableFromMultipleThreads() { heap.makeUsableFromMultipleThreads(); } #endif - bool isSharedInstance; + GlobalDataType globalDataType; ClientData* clientData; const HashTable* arrayTable; @@ -116,19 +144,16 @@ namespace JSC { RefPtr<Structure> activationStructure; RefPtr<Structure> interruptedExecutionErrorStructure; + RefPtr<Structure> terminatedExecutionErrorStructure; RefPtr<Structure> staticScopeStructure; + RefPtr<Structure> strictEvalActivationStructure; RefPtr<Structure> stringStructure; - RefPtr<Structure> notAnObjectErrorStubStructure; RefPtr<Structure> notAnObjectStructure; RefPtr<Structure> propertyNameIteratorStructure; RefPtr<Structure> getterSetterStructure; RefPtr<Structure> apiWrapperStructure; RefPtr<Structure> dummyMarkableCellStructure; -#if USE(JSVALUE32) - RefPtr<Structure> numberStructure; -#endif - static void storeVPtrs(); static JS_EXPORTDATA void* jsArrayVPtr; static JS_EXPORTDATA void* jsByteArrayVPtr; @@ -146,13 +171,27 @@ namespace JSC { ExecutableAllocator executableAllocator; #endif +#if !ENABLE(JIT) + bool canUseJIT() { return false; } // interpreter only +#elif !ENABLE(INTERPRETER) + bool canUseJIT() { return true; } // jit only +#else + bool canUseJIT() { return m_canUseJIT; } +#endif Lexer* lexer; Parser* parser; Interpreter* interpreter; #if ENABLE(JIT) - JITThunks jitStubs; + OwnPtr<JITThunks> jitStubs; + MacroAssemblerCodePtr getCTIStub(ThunkGenerator generator) + { + return jitStubs->ctiStub(this, generator); + } + PassRefPtr<NativeExecutable> getHostFunction(NativeFunction function); + PassRefPtr<NativeExecutable> getHostFunction(NativeFunction function, ThunkGenerator generator); #endif TimeoutChecker timeoutChecker; + Terminator terminator; Heap heap; JSValue exception; @@ -160,10 +199,6 @@ namespace JSC { ReturnAddressPtr exceptionLocation; #endif - const Vector<Instruction>& numericCompareFunction(ExecState*); - Vector<Instruction> lazyNumericCompareFunction; - bool initializingLazyNumericCompareFunction; - HashMap<OpaqueJSClass*, OpaqueJSClassContextData*> opaqueJSClassData; JSGlobalObject* head; @@ -171,7 +206,6 @@ namespace JSC { HashSet<JSObject*> arrayVisitedElements; - CodeBlock* functionCodeBlockBeingReparsed; Stringifier* firstStringifierToMark; MarkStack markStack; @@ -181,24 +215,43 @@ namespace JSC { UString cachedDateString; double cachedDateStringValue; - - WeakRandom weakRandom; + + int maxReentryDepth; + + RegExpCache* m_regExpCache; + + BumpPointerAllocator m_regexAllocator; + +#if ENABLE(REGEXP_TRACING) + typedef ListHashSet<RefPtr<RegExp> > RTTraceList; + RTTraceList* m_rtTraceList; +#endif #ifndef NDEBUG - bool mainThreadOnly; + ThreadIdentifier exclusiveThread; #endif + CachedTranscendentalFunction<sin> cachedSin; + void resetDateCache(); void startSampling(); void stopSampling(); void dumpSampleData(ExecState* exec); + RegExpCache* regExpCache() { return m_regExpCache; } +#if ENABLE(REGEXP_TRACING) + void addRegExpToTrace(PassRefPtr<RegExp> regExp); +#endif + void dumpRegExpTrace(); private: - JSGlobalData(bool isShared); + JSGlobalData(GlobalDataType, ThreadStackType); static JSGlobalData*& sharedInstanceInternal(); void createNativeThunk(); +#if ENABLE(JIT) && ENABLE(INTERPRETER) + bool m_canUseJIT; +#endif }; - + } // namespace JSC #endif // JSGlobalData_h diff --git a/JavaScriptCore/runtime/JSGlobalObject.cpp b/JavaScriptCore/runtime/JSGlobalObject.cpp index 4bf0a69..a8fb7bf 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -116,11 +116,9 @@ JSGlobalObject::~JSGlobalObject() for (HashSet<GlobalCodeBlock*>::const_iterator it = codeBlocks().begin(); it != end; ++it) (*it)->clearGlobalObject(); - RegisterFile& registerFile = globalData()->interpreter->registerFile(); - if (registerFile.globalObject() == this) { - registerFile.setGlobalObject(0); + RegisterFile& registerFile = globalData().interpreter->registerFile(); + if (registerFile.clearGlobalObject(this)) registerFile.setNumGlobals(0); - } d()->destructor(d()); } @@ -133,7 +131,7 @@ void JSGlobalObject::init(JSObject* thisValue) d()->globalData = Heap::heap(this)->globalData(); d()->globalScopeChain = ScopeChain(this, d()->globalData.get(), this, thisValue); - JSGlobalObject::globalExec()->init(0, 0, d()->globalScopeChain.node(), CallFrame::noCaller(), 0, 0, 0); + JSGlobalObject::globalExec()->init(0, 0, d()->globalScopeChain.node(), CallFrame::noCaller(), 0, 0); if (JSGlobalObject*& headObject = head()) { d()->prev = headObject; @@ -143,7 +141,6 @@ void JSGlobalObject::init(JSObject* thisValue) } else headObject = d()->next = d()->prev = this; - d()->recursion = 0; d()->debugger = 0; d()->profileGroup = 0; @@ -205,14 +202,15 @@ void JSGlobalObject::reset(JSValue prototype) // Prototypes - d()->functionPrototype = new (exec) FunctionPrototype(exec, FunctionPrototype::createStructure(jsNull())); // The real prototype will be set once ObjectPrototype is created. + d()->functionPrototype = new (exec) FunctionPrototype(exec, this, FunctionPrototype::createStructure(jsNull())); // The real prototype will be set once ObjectPrototype is created. d()->prototypeFunctionStructure = PrototypeFunction::createStructure(d()->functionPrototype); + d()->internalFunctionStructure = InternalFunction::createStructure(d()->functionPrototype); NativeFunctionWrapper* callFunction = 0; NativeFunctionWrapper* applyFunction = 0; - d()->functionPrototype->addFunctionProperties(exec, d()->prototypeFunctionStructure.get(), &callFunction, &applyFunction); + d()->functionPrototype->addFunctionProperties(exec, this, d()->prototypeFunctionStructure.get(), &callFunction, &applyFunction); d()->callFunction = callFunction; d()->applyFunction = applyFunction; - d()->objectPrototype = new (exec) ObjectPrototype(exec, ObjectPrototype::createStructure(jsNull()), d()->prototypeFunctionStructure.get()); + d()->objectPrototype = new (exec) ObjectPrototype(exec, this, ObjectPrototype::createStructure(jsNull()), d()->prototypeFunctionStructure.get()); d()->functionPrototype->structure()->setPrototypeWithoutTransition(d()->objectPrototype); d()->emptyObjectStructure = d()->objectPrototype->inheritorID(); @@ -221,63 +219,54 @@ void JSGlobalObject::reset(JSValue prototype) d()->callbackFunctionStructure = JSCallbackFunction::createStructure(d()->functionPrototype); d()->argumentsStructure = Arguments::createStructure(d()->objectPrototype); d()->callbackConstructorStructure = JSCallbackConstructor::createStructure(d()->objectPrototype); - d()->callbackObjectStructure = JSCallbackObject<JSObject>::createStructure(d()->objectPrototype); + d()->callbackObjectStructure = JSCallbackObject<JSObjectWithGlobalObject>::createStructure(d()->objectPrototype); - d()->arrayPrototype = new (exec) ArrayPrototype(ArrayPrototype::createStructure(d()->objectPrototype)); + d()->arrayPrototype = new (exec) ArrayPrototype(this, ArrayPrototype::createStructure(d()->objectPrototype)); d()->arrayStructure = JSArray::createStructure(d()->arrayPrototype); d()->regExpMatchesArrayStructure = RegExpMatchesArray::createStructure(d()->arrayPrototype); - d()->stringPrototype = new (exec) StringPrototype(exec, StringPrototype::createStructure(d()->objectPrototype)); + d()->stringPrototype = new (exec) StringPrototype(exec, this, StringPrototype::createStructure(d()->objectPrototype)); d()->stringObjectStructure = StringObject::createStructure(d()->stringPrototype); - d()->booleanPrototype = new (exec) BooleanPrototype(exec, BooleanPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + d()->booleanPrototype = new (exec) BooleanPrototype(exec, this, BooleanPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); d()->booleanObjectStructure = BooleanObject::createStructure(d()->booleanPrototype); - d()->numberPrototype = new (exec) NumberPrototype(exec, NumberPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + d()->numberPrototype = new (exec) NumberPrototype(exec, this, NumberPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); d()->numberObjectStructure = NumberObject::createStructure(d()->numberPrototype); - d()->datePrototype = new (exec) DatePrototype(exec, DatePrototype::createStructure(d()->objectPrototype)); + d()->datePrototype = new (exec) DatePrototype(exec, this, DatePrototype::createStructure(d()->objectPrototype)); d()->dateStructure = DateInstance::createStructure(d()->datePrototype); - d()->regExpPrototype = new (exec) RegExpPrototype(exec, RegExpPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + d()->regExpPrototype = new (exec) RegExpPrototype(exec, this, RegExpPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); d()->regExpStructure = RegExpObject::createStructure(d()->regExpPrototype); d()->methodCallDummy = constructEmptyObject(exec); - ErrorPrototype* errorPrototype = new (exec) ErrorPrototype(exec, ErrorPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); + ErrorPrototype* errorPrototype = new (exec) ErrorPrototype(exec, this, ErrorPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get()); d()->errorStructure = ErrorInstance::createStructure(errorPrototype); - RefPtr<Structure> nativeErrorPrototypeStructure = NativeErrorPrototype::createStructure(errorPrototype); - - NativeErrorPrototype* evalErrorPrototype = new (exec) NativeErrorPrototype(exec, nativeErrorPrototypeStructure, "EvalError", "EvalError"); - NativeErrorPrototype* rangeErrorPrototype = new (exec) NativeErrorPrototype(exec, nativeErrorPrototypeStructure, "RangeError", "RangeError"); - NativeErrorPrototype* referenceErrorPrototype = new (exec) NativeErrorPrototype(exec, nativeErrorPrototypeStructure, "ReferenceError", "ReferenceError"); - NativeErrorPrototype* syntaxErrorPrototype = new (exec) NativeErrorPrototype(exec, nativeErrorPrototypeStructure, "SyntaxError", "SyntaxError"); - NativeErrorPrototype* typeErrorPrototype = new (exec) NativeErrorPrototype(exec, nativeErrorPrototypeStructure, "TypeError", "TypeError"); - NativeErrorPrototype* URIErrorPrototype = new (exec) NativeErrorPrototype(exec, nativeErrorPrototypeStructure, "URIError", "URIError"); - // Constructors - JSCell* objectConstructor = new (exec) ObjectConstructor(exec, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype, d()->prototypeFunctionStructure.get()); - JSCell* functionConstructor = new (exec) FunctionConstructor(exec, FunctionConstructor::createStructure(d()->functionPrototype), d()->functionPrototype); - JSCell* arrayConstructor = new (exec) ArrayConstructor(exec, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype, d()->prototypeFunctionStructure.get()); - JSCell* stringConstructor = new (exec) StringConstructor(exec, StringConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->stringPrototype); - JSCell* booleanConstructor = new (exec) BooleanConstructor(exec, BooleanConstructor::createStructure(d()->functionPrototype), d()->booleanPrototype); - JSCell* numberConstructor = new (exec) NumberConstructor(exec, NumberConstructor::createStructure(d()->functionPrototype), d()->numberPrototype); - JSCell* dateConstructor = new (exec) DateConstructor(exec, DateConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->datePrototype); + JSCell* objectConstructor = new (exec) ObjectConstructor(exec, this, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype, d()->prototypeFunctionStructure.get()); + JSCell* functionConstructor = new (exec) FunctionConstructor(exec, this, FunctionConstructor::createStructure(d()->functionPrototype), d()->functionPrototype); + JSCell* arrayConstructor = new (exec) ArrayConstructor(exec, this, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype, d()->prototypeFunctionStructure.get()); + JSCell* stringConstructor = new (exec) StringConstructor(exec, this, StringConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->stringPrototype); + JSCell* booleanConstructor = new (exec) BooleanConstructor(exec, this, BooleanConstructor::createStructure(d()->functionPrototype), d()->booleanPrototype); + JSCell* numberConstructor = new (exec) NumberConstructor(exec, this, NumberConstructor::createStructure(d()->functionPrototype), d()->numberPrototype); + JSCell* dateConstructor = new (exec) DateConstructor(exec, this, DateConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->datePrototype); - d()->regExpConstructor = new (exec) RegExpConstructor(exec, RegExpConstructor::createStructure(d()->functionPrototype), d()->regExpPrototype); + d()->regExpConstructor = new (exec) RegExpConstructor(exec, this, RegExpConstructor::createStructure(d()->functionPrototype), d()->regExpPrototype); - d()->errorConstructor = new (exec) ErrorConstructor(exec, ErrorConstructor::createStructure(d()->functionPrototype), errorPrototype); + d()->errorConstructor = new (exec) ErrorConstructor(exec, this, ErrorConstructor::createStructure(d()->functionPrototype), errorPrototype); + RefPtr<Structure> nativeErrorPrototypeStructure = NativeErrorPrototype::createStructure(errorPrototype); RefPtr<Structure> nativeErrorStructure = NativeErrorConstructor::createStructure(d()->functionPrototype); - - d()->evalErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, evalErrorPrototype); - d()->rangeErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, rangeErrorPrototype); - d()->referenceErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, referenceErrorPrototype); - d()->syntaxErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, syntaxErrorPrototype); - d()->typeErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, typeErrorPrototype); - d()->URIErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, URIErrorPrototype); + d()->evalErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "EvalError"); + d()->rangeErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "RangeError"); + d()->referenceErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "ReferenceError"); + d()->syntaxErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "SyntaxError"); + d()->typeErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "TypeError"); + d()->URIErrorConstructor = new (exec) NativeErrorConstructor(exec, this, nativeErrorStructure, nativeErrorPrototypeStructure, "URIError"); d()->objectPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, objectConstructor, DontEnum); d()->functionPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, functionConstructor, DontEnum); @@ -289,13 +278,6 @@ void JSGlobalObject::reset(JSValue prototype) d()->regExpPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, d()->regExpConstructor, DontEnum); errorPrototype->putDirectFunctionWithoutTransition(exec->propertyNames().constructor, d()->errorConstructor, DontEnum); - evalErrorPrototype->putDirect(exec->propertyNames().constructor, d()->evalErrorConstructor, DontEnum); - rangeErrorPrototype->putDirect(exec->propertyNames().constructor, d()->rangeErrorConstructor, DontEnum); - referenceErrorPrototype->putDirect(exec->propertyNames().constructor, d()->referenceErrorConstructor, DontEnum); - syntaxErrorPrototype->putDirect(exec->propertyNames().constructor, d()->syntaxErrorConstructor, DontEnum); - typeErrorPrototype->putDirect(exec->propertyNames().constructor, d()->typeErrorConstructor, DontEnum); - URIErrorPrototype->putDirect(exec->propertyNames().constructor, d()->URIErrorConstructor, DontEnum); - // Set global constructors // FIXME: These properties could be handled by a static hash table. @@ -309,40 +291,40 @@ void JSGlobalObject::reset(JSValue prototype) putDirectFunctionWithoutTransition(Identifier(exec, "Date"), dateConstructor, DontEnum); putDirectFunctionWithoutTransition(Identifier(exec, "RegExp"), d()->regExpConstructor, DontEnum); putDirectFunctionWithoutTransition(Identifier(exec, "Error"), d()->errorConstructor, DontEnum); - putDirectFunctionWithoutTransition(Identifier(exec, "EvalError"), d()->evalErrorConstructor); - putDirectFunctionWithoutTransition(Identifier(exec, "RangeError"), d()->rangeErrorConstructor); - putDirectFunctionWithoutTransition(Identifier(exec, "ReferenceError"), d()->referenceErrorConstructor); - putDirectFunctionWithoutTransition(Identifier(exec, "SyntaxError"), d()->syntaxErrorConstructor); - putDirectFunctionWithoutTransition(Identifier(exec, "TypeError"), d()->typeErrorConstructor); - putDirectFunctionWithoutTransition(Identifier(exec, "URIError"), d()->URIErrorConstructor); + putDirectFunctionWithoutTransition(Identifier(exec, "EvalError"), d()->evalErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "RangeError"), d()->rangeErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "ReferenceError"), d()->referenceErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "SyntaxError"), d()->syntaxErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "TypeError"), d()->typeErrorConstructor, DontEnum); + putDirectFunctionWithoutTransition(Identifier(exec, "URIError"), d()->URIErrorConstructor, DontEnum); // Set global values. GlobalPropertyInfo staticGlobals[] = { - GlobalPropertyInfo(Identifier(exec, "Math"), new (exec) MathObject(exec, MathObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete), - GlobalPropertyInfo(Identifier(exec, "NaN"), jsNaN(exec), DontEnum | DontDelete), - GlobalPropertyInfo(Identifier(exec, "Infinity"), jsNumber(exec, Inf), DontEnum | DontDelete), - GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete), - GlobalPropertyInfo(Identifier(exec, "JSON"), new (exec) JSONObject(JSONObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete) + GlobalPropertyInfo(Identifier(exec, "Math"), new (exec) MathObject(exec, this, MathObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete), + GlobalPropertyInfo(Identifier(exec, "NaN"), jsNaN(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "Infinity"), jsNumber(Inf), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(exec, "JSON"), new (exec) JSONObject(this, JSONObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete) }; - addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo)); + addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals)); // Set global functions. - d()->evalFunction = new (exec) GlobalEvalFunction(exec, GlobalEvalFunction::createStructure(d()->functionPrototype), 1, exec->propertyNames().eval, globalFuncEval, this); + d()->evalFunction = new (exec) GlobalEvalFunction(exec, this, GlobalEvalFunction::createStructure(d()->functionPrototype), 1, exec->propertyNames().eval, globalFuncEval, this); putDirectFunctionWithoutTransition(exec, d()->evalFunction, DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 2, Identifier(exec, "parseInt"), globalFuncParseInt), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "parseFloat"), globalFuncParseFloat), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "isNaN"), globalFuncIsNaN), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "isFinite"), globalFuncIsFinite), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "escape"), globalFuncEscape), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "unescape"), globalFuncUnescape), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "decodeURI"), globalFuncDecodeURI), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "decodeURIComponent"), globalFuncDecodeURIComponent), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURI"), globalFuncEncodeURI), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURIComponent"), globalFuncEncodeURIComponent), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 2, Identifier(exec, "parseInt"), globalFuncParseInt), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "parseFloat"), globalFuncParseFloat), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "isNaN"), globalFuncIsNaN), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "isFinite"), globalFuncIsFinite), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "escape"), globalFuncEscape), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "unescape"), globalFuncUnescape), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "decodeURI"), globalFuncDecodeURI), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "decodeURIComponent"), globalFuncDecodeURIComponent), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURI"), globalFuncEncodeURI), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "encodeURIComponent"), globalFuncEncodeURIComponent), DontEnum); #ifndef NDEBUG - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "jscprint"), globalFuncJSCPrint), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, this, d()->prototypeFunctionStructure.get(), 1, Identifier(exec, "jscprint"), globalFuncJSCPrint), DontEnum); #endif resetPrototype(prototype); @@ -367,9 +349,9 @@ void JSGlobalObject::markChildren(MarkStack& markStack) for (HashSet<GlobalCodeBlock*>::const_iterator it = codeBlocks().begin(); it != end; ++it) (*it)->markAggregate(markStack); - RegisterFile& registerFile = globalData()->interpreter->registerFile(); + RegisterFile& registerFile = globalData().interpreter->registerFile(); if (registerFile.globalObject() == this) - registerFile.markGlobals(markStack, &globalData()->heap); + registerFile.markGlobals(markStack, &globalData().heap); markIfNeeded(markStack, d()->regExpConstructor); markIfNeeded(markStack, d()->errorConstructor); @@ -428,7 +410,7 @@ ExecState* JSGlobalObject::globalExec() return CallFrame::create(d()->globalCallFrame + RegisterFile::CallFrameHeaderSize); } -bool JSGlobalObject::isDynamicScope() const +bool JSGlobalObject::isDynamicScope(bool&) const { return true; } diff --git a/JavaScriptCore/runtime/JSGlobalObject.h b/JavaScriptCore/runtime/JSGlobalObject.h index bbb6d5e..714999f 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.h +++ b/JavaScriptCore/runtime/JSGlobalObject.h @@ -25,11 +25,13 @@ #include "JSArray.h" #include "JSGlobalData.h" #include "JSVariableObject.h" +#include "JSWeakObjectMapRefInternal.h" #include "NativeFunctionWrapper.h" #include "NumberPrototype.h" #include "StringPrototype.h" #include <wtf/HashSet.h> #include <wtf/OwnPtr.h> +#include <wtf/RandomNumber.h> namespace JSC { @@ -56,6 +58,7 @@ namespace JSC { class JSGlobalObject : public JSVariableObject { protected: using JSVariableObject::JSVariableObjectData; + typedef HashSet<RefPtr<OpaqueJSWeakObjectMap> > WeakMapSet; struct JSGlobalObjectData : public JSVariableObjectData { // We use an explicit destructor function pointer instead of a @@ -90,6 +93,7 @@ namespace JSC { , datePrototype(0) , regExpPrototype(0) , methodCallDummy(0) + , weakRandom(static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0))) { } @@ -105,8 +109,6 @@ namespace JSC { ScopeChain globalScopeChain; Register globalCallFrame[RegisterFile::CallFrameHeaderSize]; - int recursion; - RegExpConstructor* regExpConstructor; ErrorConstructor* errorConstructor; NativeErrorConstructor* evalErrorConstructor; @@ -146,6 +148,7 @@ namespace JSC { RefPtr<Structure> regExpMatchesArrayStructure; RefPtr<Structure> regExpStructure; RefPtr<Structure> stringObjectStructure; + RefPtr<Structure> internalFunctionStructure; SymbolTable symbolTable; unsigned profileGroup; @@ -153,14 +156,26 @@ namespace JSC { RefPtr<JSGlobalData> globalData; HashSet<GlobalCodeBlock*> codeBlocks; + WeakMapSet weakMaps; + WeakRandom weakRandom; }; public: void* operator new(size_t, JSGlobalData*); - + explicit JSGlobalObject() : JSVariableObject(JSGlobalObject::createStructure(jsNull()), new JSGlobalObjectData(destroyJSGlobalObjectData)) { + COMPILE_ASSERT(JSGlobalObject::AnonymousSlotCount == 1, JSGlobalObject_has_only_a_single_slot); + putAnonymousValue(0, this); + init(this); + } + + explicit JSGlobalObject(NonNullPassRefPtr<Structure> structure) + : JSVariableObject(structure, new JSGlobalObjectData(destroyJSGlobalObjectData)) + { + COMPILE_ASSERT(JSGlobalObject::AnonymousSlotCount == 1, JSGlobalObject_has_only_a_single_slot); + putAnonymousValue(0, this); init(this); } @@ -168,6 +183,8 @@ namespace JSC { JSGlobalObject(NonNullPassRefPtr<Structure> structure, JSGlobalObjectData* data, JSObject* thisValue) : JSVariableObject(structure, data) { + COMPILE_ASSERT(JSGlobalObject::AnonymousSlotCount == 1, JSGlobalObject_has_only_a_single_slot); + putAnonymousValue(0, this); init(thisValue); } @@ -227,6 +244,7 @@ namespace JSC { Structure* functionStructure() const { return d()->functionStructure.get(); } Structure* numberObjectStructure() const { return d()->numberObjectStructure.get(); } Structure* prototypeFunctionStructure() const { return d()->prototypeFunctionStructure.get(); } + Structure* internalFunctionStructure() const { return d()->internalFunctionStructure.get(); } Structure* regExpMatchesArrayStructure() const { return d()->regExpMatchesArrayStructure.get(); } Structure* regExpStructure() const { return d()->regExpStructure.get(); } Structure* stringObjectStructure() const { return d()->stringObjectStructure.get(); } @@ -236,13 +254,10 @@ namespace JSC { Debugger* debugger() const { return d()->debugger; } void setDebugger(Debugger* debugger) { d()->debugger = debugger; } - + virtual bool supportsProfiling() const { return false; } - - int recursion() { return d()->recursion; } - void incRecursion() { ++d()->recursion; } - void decRecursion() { --d()->recursion; } - + virtual bool supportsRichSourceInfo() const { return true; } + ScopeChain& globalScopeChain() { return d()->globalScopeChain; } virtual bool isGlobalObject() const { return true; } @@ -253,7 +268,7 @@ namespace JSC { virtual bool allowsAccessFrom(const JSGlobalObject*) const { return true; } - virtual bool isDynamicScope() const; + virtual bool isDynamicScope(bool& requiresDynamicChecks) const; HashSet<GlobalCodeBlock*>& codeBlocks() { return d()->codeBlocks; } @@ -262,7 +277,7 @@ namespace JSC { void resetPrototype(JSValue prototype); - JSGlobalData* globalData() { return d()->globalData.get(); } + JSGlobalData& globalData() const { return *d()->globalData.get(); } JSGlobalObjectData* d() const { return static_cast<JSGlobalObjectData*>(JSVariableObject::d); } static PassRefPtr<Structure> createStructure(JSValue prototype) @@ -270,8 +285,20 @@ namespace JSC { return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); } + void registerWeakMap(OpaqueJSWeakObjectMap* map) + { + d()->weakMaps.add(map); + } + + void deregisterWeakMap(OpaqueJSWeakObjectMap* map) + { + d()->weakMaps.remove(map); + } + + double weakRandomNumber() { return d()->weakRandom.get(); } protected: + static const unsigned AnonymousSlotCount = JSVariableObject::AnonymousSlotCount + 1; static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesMarkChildren | OverridesGetPropertyNames | JSVariableObject::StructureFlags; struct GlobalPropertyInfo { @@ -327,7 +354,7 @@ namespace JSC { GlobalPropertyInfo& global = globals[i]; ASSERT(global.attributes & DontDelete); SymbolTableEntry newEntry(index, global.attributes); - symbolTable().add(global.identifier.ustring().rep(), newEntry); + symbolTable().add(global.identifier.impl(), newEntry); registerAt(index) = global.value; } } @@ -360,16 +387,8 @@ namespace JSC { if (typeInfo().type() == ObjectType) return m_prototype; -#if USE(JSVALUE32) - if (typeInfo().type() == StringType) - return exec->lexicalGlobalObject()->stringPrototype(); - - ASSERT(typeInfo().type() == NumberType); - return exec->lexicalGlobalObject()->numberPrototype(); -#else ASSERT(typeInfo().type() == StringType); return exec->lexicalGlobalObject()->stringPrototype(); -#endif } inline StructureChain* Structure::prototypeChain(ExecState* exec) const @@ -431,7 +450,7 @@ namespace JSC { inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength) { - return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength); + return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength, CreateInitialized); } inline JSArray* constructArray(ExecState* exec, JSValue singleItemValue) diff --git a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp index 3ddac7c..284806e 100644 --- a/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp +++ b/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp @@ -35,14 +35,14 @@ #include "LiteralParser.h" #include "Nodes.h" #include "Parser.h" -#include "StringExtras.h" +#include "UStringBuilder.h" #include "dtoa.h" #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <wtf/ASCIICType.h> #include <wtf/Assertions.h> #include <wtf/MathExtras.h> +#include <wtf/StringExtras.h> #include <wtf/unicode/UTF8.h> using namespace WTF; @@ -50,35 +50,35 @@ using namespace Unicode; namespace JSC { -static JSValue encode(ExecState* exec, const ArgList& args, const char* doNotEscape) +static JSValue encode(ExecState* exec, const char* doNotEscape) { - UString str = args.at(0).toString(exec); - CString cstr = str.UTF8String(true); - if (!cstr.c_str()) - return throwError(exec, URIError, "String contained an illegal UTF-16 sequence."); + UString str = exec->argument(0).toString(exec); + CString cstr = str.utf8(true); + if (!cstr.data()) + return throwError(exec, createURIError(exec, "String contained an illegal UTF-16 sequence.")); JSStringBuilder builder; - const char* p = cstr.c_str(); - for (size_t k = 0; k < cstr.size(); k++, p++) { + const char* p = cstr.data(); + for (size_t k = 0; k < cstr.length(); k++, p++) { char c = *p; if (c && strchr(doNotEscape, c)) builder.append(c); else { char tmp[4]; - snprintf(tmp, 4, "%%%02X", static_cast<unsigned char>(c)); + snprintf(tmp, sizeof(tmp), "%%%02X", static_cast<unsigned char>(c)); builder.append(tmp); } } return builder.build(exec); } -static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUnescape, bool strict) +static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict) { JSStringBuilder builder; - UString str = args.at(0).toString(exec); + UString str = exec->argument(0).toString(exec); int k = 0; - int len = str.size(); - const UChar* d = str.data(); + int len = str.length(); + const UChar* d = str.characters(); UChar u = 0; while (k < len) { const UChar* p = d + k; @@ -117,7 +117,7 @@ static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUne } if (charLen == 0) { if (strict) - return throwError(exec, URIError); + return throwError(exec, createURIError(exec, "URI error")); // The only case where we don't use "strict" mode is the "unescape" function. // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE. if (k <= len - 6 && p[1] == 'u' @@ -141,6 +141,7 @@ static JSValue decode(ExecState* exec, const ArgList& args, const char* doNotUne bool isStrWhiteSpace(UChar c) { switch (c) { + // ECMA-262-5th 7.2 & 7.3 case 0x0009: case 0x000A: case 0x000B: @@ -150,6 +151,7 @@ bool isStrWhiteSpace(UChar c) case 0x00A0: case 0x2028: case 0x2029: + case 0xFEFF: return true; default: return c > 0xff && isSeparatorSpace(c); @@ -194,10 +196,32 @@ double parseIntOverflow(const char* s, int length, int radix) return number; } +double parseIntOverflow(const UChar* s, int length, int radix) +{ + double number = 0.0; + double radixMultiplier = 1.0; + + for (const UChar* p = s + length - 1; p >= s; p--) { + if (radixMultiplier == Inf) { + if (*p != '0') { + number = Inf; + break; + } + } else { + int digit = parseDigit(*p, radix); + number += digit * radixMultiplier; + } + + radixMultiplier *= radix; + } + + return number; +} + static double parseInt(const UString& s, int radix) { - int length = s.size(); - const UChar* data = s.data(); + int length = s.length(); + const UChar* data = s.characters(); int p = 0; while (p < length && isStrWhiteSpace(data[p])) @@ -241,9 +265,9 @@ static double parseInt(const UString& s, int radix) if (number >= mantissaOverflowLowerBound) { if (radix == 10) - number = WTF::strtod(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), 0); + number = WTF::strtod(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), 0); else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) - number = parseIntOverflow(s.substr(firstDigitPosition, p - firstDigitPosition).ascii(), p - firstDigitPosition, radix); + number = parseIntOverflow(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), p - firstDigitPosition, radix); } if (!sawDigit) @@ -252,103 +276,241 @@ static double parseInt(const UString& s, int radix) return sign * number; } +static const int SizeOfInfinity = 8; + +static bool isInfinity(const UChar* data, const UChar* end) +{ + return (end - data) >= SizeOfInfinity + && data[0] == 'I' + && data[1] == 'n' + && data[2] == 'f' + && data[3] == 'i' + && data[4] == 'n' + && data[5] == 'i' + && data[6] == 't' + && data[7] == 'y'; +} + +// See ecma-262 9.3.1 +static double jsHexIntegerLiteral(const UChar*& data, const UChar* end) +{ + // Hex number. + data += 2; + const UChar* firstDigitPosition = data; + double number = 0; + while (true) { + number = number * 16 + toASCIIHexValue(*data); + ++data; + if (data == end) + break; + if (!isASCIIHexDigit(*data)) + break; + } + if (number >= mantissaOverflowLowerBound) + number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16); + + return number; +} + +// See ecma-262 9.3.1 +static double jsStrDecimalLiteral(const UChar*& data, const UChar* end) +{ + ASSERT(data < end); + + // Copy the sting into a null-terminated byte buffer, and call strtod. + Vector<char, 32> byteBuffer; + for (const UChar* characters = data; characters < end; ++characters) { + UChar character = *characters; + byteBuffer.append(isASCII(character) ? character : 0); + } + byteBuffer.append(0); + char* endOfNumber; + double number = WTF::strtod(byteBuffer.data(), &endOfNumber); + + // Check if strtod found a number; if so return it. + ptrdiff_t consumed = endOfNumber - byteBuffer.data(); + if (consumed) { + data += consumed; + return number; + } + + // Check for [+-]?Infinity + switch (*data) { + case 'I': + if (isInfinity(data, end)) { + data += SizeOfInfinity; + return Inf; + } + break; + + case '+': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return Inf; + } + break; + + case '-': + if (isInfinity(data + 1, end)) { + data += SizeOfInfinity + 1; + return -Inf; + } + break; + } + + // Not a number. + return NaN; +} + +// See ecma-262 9.3.1 +double jsToNumber(const UString& s) +{ + unsigned size = s.length(); + + if (size == 1) { + UChar c = s.characters()[0]; + if (isASCIIDigit(c)) + return c - '0'; + if (isStrWhiteSpace(c)) + return 0; + return NaN; + } + + const UChar* data = s.characters(); + const UChar* end = data + size; + + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return 0.0; + + double number; + if (data[0] == '0' && data + 2 < end && (data[1] | 0x20) == 'x' && isASCIIHexDigit(data[2])) + number = jsHexIntegerLiteral(data, end); + else + number = jsStrDecimalLiteral(data, end); + + // Allow trailing white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + if (data != end) + return NaN; + + return number; +} + static double parseFloat(const UString& s) { - // Check for 0x prefix here, because toDouble allows it, but we must treat it as 0. - // Need to skip any whitespace and then one + or - sign. - int length = s.size(); - const UChar* data = s.data(); - int p = 0; - while (p < length && isStrWhiteSpace(data[p])) - ++p; + unsigned size = s.length(); - if (p < length && (data[p] == '+' || data[p] == '-')) - ++p; + if (size == 1) { + UChar c = s.characters()[0]; + if (isASCIIDigit(c)) + return c - '0'; + return NaN; + } - if (length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) - return 0; + const UChar* data = s.characters(); + const UChar* end = data + size; - return s.toDouble(true /*tolerant*/, false /* NaN for empty string */); + // Skip leading white space. + for (; data < end; ++data) { + if (!isStrWhiteSpace(*data)) + break; + } + + // Empty string. + if (data == end) + return NaN; + + return jsStrDecimalLiteral(data, end); } -JSValue JSC_HOST_CALL globalFuncEval(ExecState* exec, JSObject* function, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) { - JSObject* thisObject = thisValue.toThisObject(exec); + JSObject* thisObject = exec->hostThisValue().toThisObject(exec); JSObject* unwrappedObject = thisObject->unwrappedObject(); - if (!unwrappedObject->isGlobalObject() || static_cast<JSGlobalObject*>(unwrappedObject)->evalFunction() != function) - return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated"); + if (!unwrappedObject->isGlobalObject() || static_cast<JSGlobalObject*>(unwrappedObject)->evalFunction() != exec->callee()) + return throwVMError(exec, createEvalError(exec, "The \"this\" value passed to eval must be the global object from which eval originated")); - JSValue x = args.at(0); + JSValue x = exec->argument(0); if (!x.isString()) - return x; + return JSValue::encode(x); UString s = x.toString(exec); LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) - return parsedObject; + return JSValue::encode(parsedObject); - RefPtr<EvalExecutable> eval = EvalExecutable::create(exec, makeSource(s)); + RefPtr<EvalExecutable> eval = EvalExecutable::create(exec, makeSource(s), false); JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node()); if (error) - return throwError(exec, error); + return throwVMError(exec, error); - return exec->interpreter()->execute(eval.get(), exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node(), exec->exceptionSlot()); + return JSValue::encode(exec->interpreter()->execute(eval.get(), exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain().node())); } -JSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) { - JSValue value = args.at(0); - int32_t radix = args.at(1).toInt32(exec); + JSValue value = exec->argument(0); + int32_t radix = exec->argument(1).toInt32(exec); if (radix != 0 && radix != 10) - return jsNumber(exec, parseInt(value.toString(exec), radix)); + return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); if (value.isInt32()) - return value; + return JSValue::encode(value); if (value.isDouble()) { double d = value.asDouble(); if (isfinite(d)) - return jsNumber(exec, (d > 0) ? floor(d) : ceil(d)); + return JSValue::encode(jsNumber((d > 0) ? floor(d) : ceil(d))); if (isnan(d) || isinf(d)) - return jsNaN(exec); - return jsNumber(exec, 0); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(0)); } - return jsNumber(exec, parseInt(value.toString(exec), radix)); + return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); } -JSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec) { - return jsNumber(exec, parseFloat(args.at(0).toString(exec))); + return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)))); } -JSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec) { - return jsBoolean(isnan(args.at(0).toNumber(exec))); + return JSValue::encode(jsBoolean(isnan(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec) { - double n = args.at(0).toNumber(exec); - return jsBoolean(!isnan(n) && !isinf(n)); + double n = exec->argument(0).toNumber(exec); + return JSValue::encode(jsBoolean(!isnan(n) && !isinf(n))); } -JSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec) { static const char do_not_unescape_when_decoding_URI[] = "#$&+,/:;=?@"; - return decode(exec, args, do_not_unescape_when_decoding_URI, true); + return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true)); } -JSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec) { - return decode(exec, args, "", true); + return JSValue::encode(decode(exec, "", true)); } -JSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec) { static const char do_not_escape_when_encoding_URI[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -356,10 +518,10 @@ JSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec, JSObject*, JSValue, c "0123456789" "!#$&'()*+,-./:;=?@_~"; - return encode(exec, args, do_not_escape_when_encoding_URI); + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI)); } -JSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec) { static const char do_not_escape_when_encoding_URI_component[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -367,10 +529,10 @@ JSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec, JSObject*, J "0123456789" "!'()*-._~"; - return encode(exec, args, do_not_escape_when_encoding_URI_component); + return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component)); } -JSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) { static const char do_not_escape[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -379,34 +541,34 @@ JSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec, JSObject*, JSValue, cons "*+-./@_"; JSStringBuilder builder; - UString str = args.at(0).toString(exec); - const UChar* c = str.data(); - for (int k = 0; k < str.size(); k++, c++) { + UString str = exec->argument(0).toString(exec); + const UChar* c = str.characters(); + for (unsigned k = 0; k < str.length(); k++, c++) { int u = c[0]; if (u > 255) { char tmp[7]; - sprintf(tmp, "%%u%04X", u); + snprintf(tmp, sizeof(tmp), "%%u%04X", u); builder.append(tmp); } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u))) builder.append(c, 1); else { char tmp[4]; - sprintf(tmp, "%%%02X", u); + snprintf(tmp, sizeof(tmp), "%%%02X", u); builder.append(tmp); } } - return builder.build(exec); + return JSValue::encode(builder.build(exec)); } -JSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec) { - StringBuilder builder; - UString str = args.at(0).toString(exec); + UStringBuilder builder; + UString str = exec->argument(0).toString(exec); int k = 0; - int len = str.size(); + int len = str.length(); while (k < len) { - const UChar* c = str.data() + k; + const UChar* c = str.characters() + k; UChar u; if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { @@ -423,16 +585,15 @@ JSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec, JSObject*, JSValue, co builder.append(*c); } - return jsString(exec, builder.build()); + return JSValue::encode(jsString(exec, builder.toUString())); } #ifndef NDEBUG -JSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState* exec) { - CStringBuffer string; - args.at(0).toString(exec).getCString(string); + CString string = exec->argument(0).toString(exec).utf8(); puts(string.data()); - return jsUndefined(); + return JSValue::encode(jsUndefined()); } #endif diff --git a/JavaScriptCore/runtime/JSGlobalObjectFunctions.h b/JavaScriptCore/runtime/JSGlobalObjectFunctions.h index b1046f2..6dc7343 100644 --- a/JavaScriptCore/runtime/JSGlobalObjectFunctions.h +++ b/JavaScriptCore/runtime/JSGlobalObjectFunctions.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * @@ -24,6 +24,7 @@ #ifndef JSGlobalObjectFunctions_h #define JSGlobalObjectFunctions_h +#include "JSValue.h" #include <wtf/unicode/Unicode.h> namespace JSC { @@ -31,29 +32,30 @@ namespace JSC { class ArgList; class ExecState; class JSObject; - class JSValue; // FIXME: These functions should really be in JSGlobalObject.cpp, but putting them there // is a 0.5% reduction. - JSValue JSC_HOST_CALL globalFuncEval(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncParseInt(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncParseFloat(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncIsNaN(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncIsFinite(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncEscape(ExecState*, JSObject*, JSValue, const ArgList&); - JSValue JSC_HOST_CALL globalFuncUnescape(ExecState*, JSObject*, JSValue, const ArgList&); + EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState*); + EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState*); #ifndef NDEBUG - JSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState*, JSObject*, JSValue, const ArgList&); + EncodedJSValue JSC_HOST_CALL globalFuncJSCPrint(ExecState*); #endif static const double mantissaOverflowLowerBound = 9007199254740992.0; double parseIntOverflow(const char*, int length, int radix); + double parseIntOverflow(const UChar*, int length, int radix); bool isStrWhiteSpace(UChar); + double jsToNumber(const UString& s); } // namespace JSC diff --git a/JavaScriptCore/runtime/JSImmediate.h b/JavaScriptCore/runtime/JSImmediate.h index 4ed35fc..68ba75c 100644 --- a/JavaScriptCore/runtime/JSImmediate.h +++ b/JavaScriptCore/runtime/JSImmediate.h @@ -22,9 +22,7 @@ #ifndef JSImmediate_h #define JSImmediate_h -#include <wtf/Platform.h> - -#if !USE(JSVALUE32_64) +#if USE(JSVALUE64) #include <wtf/Assertions.h> #include <wtf/AlwaysInline.h> @@ -41,12 +39,10 @@ namespace JSC { class ExecState; class JSCell; - class JSFastMath; class JSGlobalData; class JSObject; class UString; -#if USE(JSVALUE64) inline intptr_t reinterpretDoubleToIntptr(double value) { return WTF::bitwise_cast<intptr_t>(value); @@ -56,7 +52,6 @@ namespace JSC { { return WTF::bitwise_cast<double>(value); } -#endif /* * A JSValue* is either a pointer to a cell (a heap-allocated object) or an immediate (a type-tagged @@ -137,7 +132,8 @@ namespace JSC { private: friend class JIT; friend class JSValue; - friend class JSFastMath; + friend class JSInterfaceJIT; + friend class SpecializedThunkJIT; friend JSValue jsNumber(ExecState* exec, double d); friend JSValue jsNumber(ExecState*, char i); friend JSValue jsNumber(ExecState*, unsigned char i); @@ -159,16 +155,12 @@ namespace JSC { friend JSValue jsNumber(JSGlobalData* globalData, long long i); friend JSValue jsNumber(JSGlobalData* globalData, unsigned long long i); -#if USE(JSVALUE64) // If all bits in the mask are set, this indicates an integer number, // if any but not all are set this value is a double precision number. static const intptr_t TagTypeNumber = 0xffff000000000000ll; // This value is 2^48, used to encode doubles such that the encoded value will begin // with a 16-bit pattern within the range 0x0001..0xFFFE. static const intptr_t DoubleEncodeOffset = 0x1000000000000ll; -#else - static const intptr_t TagTypeNumber = 0x1; // bottom bit set indicates integer, this dominates the following bit -#endif static const intptr_t TagBitTypeOther = 0x2; // second bit set indicates immediate other than an integer static const intptr_t TagMask = TagTypeNumber | TagBitTypeOther; @@ -181,11 +173,7 @@ namespace JSC { static const intptr_t FullTagTypeUndefined = TagBitTypeOther | ExtendedTagBitUndefined; static const intptr_t FullTagTypeNull = TagBitTypeOther; -#if USE(JSVALUE64) static const int32_t IntegerPayloadShift = 0; -#else - static const int32_t IntegerPayloadShift = 1; -#endif static const int32_t ExtendedPayloadShift = 4; static const intptr_t ExtendedPayloadBitBoolValue = 1 << ExtendedPayloadShift; @@ -204,19 +192,13 @@ namespace JSC { static ALWAYS_INLINE bool isIntegerNumber(JSValue v) { -#if USE(JSVALUE64) return (rawValue(v) & TagTypeNumber) == TagTypeNumber; -#else - return isNumber(v); -#endif } -#if USE(JSVALUE64) static ALWAYS_INLINE bool isDouble(JSValue v) { return isNumber(v) && !isIntegerNumber(v); } -#endif static ALWAYS_INLINE bool isPositiveIntegerNumber(JSValue v) { @@ -260,11 +242,7 @@ namespace JSC { static ALWAYS_INLINE bool areBothImmediateIntegerNumbers(JSValue v1, JSValue v2) { -#if USE(JSVALUE64) return (rawValue(v1) & rawValue(v2) & TagTypeNumber) == TagTypeNumber; -#else - return rawValue(v1) & rawValue(v2) & TagTypeNumber; -#endif } static double toDouble(JSValue); @@ -285,13 +263,8 @@ namespace JSC { static JSValue oneImmediate(); private: -#if USE(JSVALUE64) static const int minImmediateInt = ((-INT_MAX) - 1); static const int maxImmediateInt = INT_MAX; -#else - static const int minImmediateInt = ((-INT_MAX) - 1) >> IntegerPayloadShift; - static const int maxImmediateInt = INT_MAX >> IntegerPayloadShift; -#endif static const unsigned maxImmediateUInt = maxImmediateInt; static ALWAYS_INLINE JSValue makeValue(intptr_t integer) @@ -302,21 +275,15 @@ namespace JSC { // With USE(JSVALUE64) we want the argument to be zero extended, so the // integer doesn't interfere with the tag bits in the upper word. In the default encoding, // if intptr_t id larger then int32_t we sign extend the value through the upper word. -#if USE(JSVALUE64) static ALWAYS_INLINE JSValue makeInt(uint32_t value) -#else - static ALWAYS_INLINE JSValue makeInt(int32_t value) -#endif { return makeValue((static_cast<intptr_t>(value) << IntegerPayloadShift) | TagTypeNumber); } -#if USE(JSVALUE64) static ALWAYS_INLINE JSValue makeDouble(double value) { return makeValue(reinterpretDoubleToIntptr(value) + DoubleEncodeOffset); } -#endif static ALWAYS_INLINE JSValue makeBool(bool b) { @@ -336,12 +303,10 @@ namespace JSC { template<typename T> static JSValue fromNumberOutsideIntegerRange(T); -#if USE(JSVALUE64) static ALWAYS_INLINE double doubleValue(JSValue v) { return reinterpretIntptrToDouble(rawValue(v) - DoubleEncodeOffset); } -#endif static ALWAYS_INLINE int32_t intValue(JSValue v) { @@ -371,7 +336,6 @@ namespace JSC { ALWAYS_INLINE JSValue JSImmediate::zeroImmediate() { return makeInt(0); } ALWAYS_INLINE JSValue JSImmediate::oneImmediate() { return makeInt(1); } -#if USE(JSVALUE64) inline bool doubleToBoolean(double value) { return value < 0.0 || value > 0.0; @@ -383,13 +347,6 @@ namespace JSC { return isNumber(v) ? isIntegerNumber(v) ? v != zeroImmediate() : doubleToBoolean(doubleValue(v)) : v == trueImmediate(); } -#else - ALWAYS_INLINE bool JSImmediate::toBoolean(JSValue v) - { - ASSERT(isImmediate(v)); - return isIntegerNumber(v) ? v != zeroImmediate() : v == trueImmediate(); - } -#endif ALWAYS_INLINE uint32_t JSImmediate::getTruncatedUInt32(JSValue v) { @@ -398,19 +355,11 @@ namespace JSC { return intValue(v); } -#if USE(JSVALUE64) template<typename T> inline JSValue JSImmediate::fromNumberOutsideIntegerRange(T value) { return makeDouble(static_cast<double>(value)); } -#else - template<typename T> - inline JSValue JSImmediate::fromNumberOutsideIntegerRange(T) - { - return JSValue(); - } -#endif ALWAYS_INLINE JSValue JSImmediate::from(char i) { @@ -439,10 +388,6 @@ namespace JSC { ALWAYS_INLINE JSValue JSImmediate::from(int i) { -#if !USE(JSVALUE64) - if ((i < minImmediateInt) | (i > maxImmediateInt)) - return fromNumberOutsideIntegerRange(i); -#endif return makeInt(i); } @@ -505,14 +450,10 @@ namespace JSC { if (isIntegerNumber(v)) return intValue(v); -#if USE(JSVALUE64) if (isNumber(v)) { ASSERT(isDouble(v)); return doubleValue(v); } -#else - ASSERT(!isNumber(v)); -#endif if (rawValue(v) == FullTagTypeUndefined) return nonInlineNaN(); @@ -620,104 +561,8 @@ namespace JSC { return JSImmediate::getTruncatedUInt32(asValue()); } - class JSFastMath { - public: - static ALWAYS_INLINE bool canDoFastBitwiseOperations(JSValue v1, JSValue v2) - { - return JSImmediate::areBothImmediateIntegerNumbers(v1, v2); - } - - static ALWAYS_INLINE JSValue equal(JSValue v1, JSValue v2) - { - ASSERT(canDoFastBitwiseOperations(v1, v2)); - return jsBoolean(v1 == v2); - } - - static ALWAYS_INLINE JSValue notEqual(JSValue v1, JSValue v2) - { - ASSERT(canDoFastBitwiseOperations(v1, v2)); - return jsBoolean(v1 != v2); - } - - static ALWAYS_INLINE JSValue andImmediateNumbers(JSValue v1, JSValue v2) - { - ASSERT(canDoFastBitwiseOperations(v1, v2)); - return JSImmediate::makeValue(JSImmediate::rawValue(v1) & JSImmediate::rawValue(v2)); - } - - static ALWAYS_INLINE JSValue xorImmediateNumbers(JSValue v1, JSValue v2) - { - ASSERT(canDoFastBitwiseOperations(v1, v2)); - return JSImmediate::makeValue((JSImmediate::rawValue(v1) ^ JSImmediate::rawValue(v2)) | JSImmediate::TagTypeNumber); - } - - static ALWAYS_INLINE JSValue orImmediateNumbers(JSValue v1, JSValue v2) - { - ASSERT(canDoFastBitwiseOperations(v1, v2)); - return JSImmediate::makeValue(JSImmediate::rawValue(v1) | JSImmediate::rawValue(v2)); - } - - static ALWAYS_INLINE bool canDoFastRshift(JSValue v1, JSValue v2) - { - return JSImmediate::areBothImmediateIntegerNumbers(v1, v2); - } - - static ALWAYS_INLINE bool canDoFastUrshift(JSValue v1, JSValue v2) - { - return JSImmediate::areBothImmediateIntegerNumbers(v1, v2) && !(JSImmediate::rawValue(v1) & JSImmediate::signBit); - } - - static ALWAYS_INLINE JSValue rightShiftImmediateNumbers(JSValue val, JSValue shift) - { - ASSERT(canDoFastRshift(val, shift) || canDoFastUrshift(val, shift)); -#if USE(JSVALUE64) - return JSImmediate::makeValue(static_cast<intptr_t>(static_cast<uint32_t>(static_cast<int32_t>(JSImmediate::rawValue(val)) >> ((JSImmediate::rawValue(shift) >> JSImmediate::IntegerPayloadShift) & 0x1f))) | JSImmediate::TagTypeNumber); -#else - return JSImmediate::makeValue((JSImmediate::rawValue(val) >> ((JSImmediate::rawValue(shift) >> JSImmediate::IntegerPayloadShift) & 0x1f)) | JSImmediate::TagTypeNumber); -#endif - } - - static ALWAYS_INLINE bool canDoFastAdditiveOperations(JSValue v) - { - // Number is non-negative and an operation involving two of these can't overflow. - // Checking for allowed negative numbers takes more time than it's worth on SunSpider. - return (JSImmediate::rawValue(v) & (JSImmediate::TagTypeNumber + (JSImmediate::signBit | (JSImmediate::signBit >> 1)))) == JSImmediate::TagTypeNumber; - } - - static ALWAYS_INLINE bool canDoFastAdditiveOperations(JSValue v1, JSValue v2) - { - // Number is non-negative and an operation involving two of these can't overflow. - // Checking for allowed negative numbers takes more time than it's worth on SunSpider. - return canDoFastAdditiveOperations(v1) && canDoFastAdditiveOperations(v2); - } - - static ALWAYS_INLINE JSValue addImmediateNumbers(JSValue v1, JSValue v2) - { - ASSERT(canDoFastAdditiveOperations(v1, v2)); - return JSImmediate::makeValue(JSImmediate::rawValue(v1) + JSImmediate::rawValue(v2) - JSImmediate::TagTypeNumber); - } - - static ALWAYS_INLINE JSValue subImmediateNumbers(JSValue v1, JSValue v2) - { - ASSERT(canDoFastAdditiveOperations(v1, v2)); - return JSImmediate::makeValue(JSImmediate::rawValue(v1) - JSImmediate::rawValue(v2) + JSImmediate::TagTypeNumber); - } - - static ALWAYS_INLINE JSValue incImmediateNumber(JSValue v) - { - ASSERT(canDoFastAdditiveOperations(v)); - return JSImmediate::makeValue(JSImmediate::rawValue(v) + (1 << JSImmediate::IntegerPayloadShift)); - } - - static ALWAYS_INLINE JSValue decImmediateNumber(JSValue v) - { - ASSERT(canDoFastAdditiveOperations(v)); - return JSImmediate::makeValue(JSImmediate::rawValue(v) - (1 << JSImmediate::IntegerPayloadShift)); - } - }; - } // namespace JSC -#endif // !USE(JSVALUE32_64) +#endif // USE(JSVALUE64) #endif // JSImmediate_h diff --git a/JavaScriptCore/runtime/JSLock.cpp b/JavaScriptCore/runtime/JSLock.cpp index 8f056c8..10f4f3f 100644 --- a/JavaScriptCore/runtime/JSLock.cpp +++ b/JavaScriptCore/runtime/JSLock.cpp @@ -60,7 +60,13 @@ static void setLockCount(intptr_t count) } JSLock::JSLock(ExecState* exec) - : m_lockBehavior(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly) + : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +{ + lock(m_lockBehavior); +} + +JSLock::JSLock(JSGlobalData* globalData) + : m_lockBehavior(globalData->isSharedInstance() ? LockForReal : SilenceAssertionsOnly) { lock(m_lockBehavior); } @@ -105,12 +111,12 @@ void JSLock::unlock(JSLockBehavior lockBehavior) void JSLock::lock(ExecState* exec) { - lock(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly); + lock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); } void JSLock::unlock(ExecState* exec) { - unlock(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly); + unlock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); } bool JSLock::currentThreadIsHoldingLock() @@ -162,7 +168,7 @@ bool JSLock::currentThreadIsHoldingLock() static unsigned lockDropDepth = 0; JSLock::DropAllLocks::DropAllLocks(ExecState* exec) - : m_lockBehavior(exec->globalData().isSharedInstance ? LockForReal : SilenceAssertionsOnly) + : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly) { pthread_once(&createJSLockCountOnce, createJSLockCount); diff --git a/JavaScriptCore/runtime/JSLock.h b/JavaScriptCore/runtime/JSLock.h index 8b015c4..05b388c 100644 --- a/JavaScriptCore/runtime/JSLock.h +++ b/JavaScriptCore/runtime/JSLock.h @@ -49,12 +49,14 @@ namespace JSC { // assertions working, so that clients that use the shared context don't break. class ExecState; + class JSGlobalData; enum JSLockBehavior { SilenceAssertionsOnly, LockForReal }; class JSLock : public Noncopyable { public: JSLock(ExecState*); + JSLock(JSGlobalData*); JSLock(JSLockBehavior lockBehavior) : m_lockBehavior(lockBehavior) diff --git a/JavaScriptCore/runtime/JSNotAnObject.cpp b/JavaScriptCore/runtime/JSNotAnObject.cpp index f4764e2..e01b401 100644 --- a/JavaScriptCore/runtime/JSNotAnObject.cpp +++ b/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -39,91 +39,84 @@ ASSERT_CLASS_FITS_IN_CELL(JSNotAnObject); // JSValue methods JSValue JSNotAnObject::toPrimitive(ExecState* exec, PreferredPrimitiveType) const { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); - return m_exception; + ASSERT_UNUSED(exec, exec->hadException()); + return jsNumber(0); } bool JSNotAnObject::getPrimitiveNumber(ExecState* exec, double&, JSValue&) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return false; } bool JSNotAnObject::toBoolean(ExecState* exec) const { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return false; } double JSNotAnObject::toNumber(ExecState* exec) const { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return NaN; } UString JSNotAnObject::toString(ExecState* exec) const { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return ""; } JSObject* JSNotAnObject::toObject(ExecState* exec) const { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); - return m_exception; -} - -// Marking -void JSNotAnObject::markChildren(MarkStack& markStack) -{ - JSObject::markChildren(markStack); - markStack.append(m_exception); + ASSERT_UNUSED(exec, exec->hadException()); + return const_cast<JSNotAnObject*>(this); } // JSObject methods bool JSNotAnObject::getOwnPropertySlot(ExecState* exec, const Identifier&, PropertySlot&) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return false; } bool JSNotAnObject::getOwnPropertySlot(ExecState* exec, unsigned, PropertySlot&) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return false; } bool JSNotAnObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier&, PropertyDescriptor&) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return false; } void JSNotAnObject::put(ExecState* exec, const Identifier& , JSValue, PutPropertySlot&) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); } void JSNotAnObject::put(ExecState* exec, unsigned, JSValue) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); } bool JSNotAnObject::deleteProperty(ExecState* exec, const Identifier&) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return false; } bool JSNotAnObject::deleteProperty(ExecState* exec, unsigned) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); return false; } void JSNotAnObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray&, EnumerationMode) { - ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception); + ASSERT_UNUSED(exec, exec->hadException()); } } // namespace JSC diff --git a/JavaScriptCore/runtime/JSNotAnObject.h b/JavaScriptCore/runtime/JSNotAnObject.h index 339d41f..9f527cf 100644 --- a/JavaScriptCore/runtime/JSNotAnObject.h +++ b/JavaScriptCore/runtime/JSNotAnObject.h @@ -33,30 +33,13 @@ namespace JSC { - class JSNotAnObjectErrorStub : public JSObject { - public: - JSNotAnObjectErrorStub(ExecState* exec, bool isNull) - : JSObject(exec->globalData().notAnObjectErrorStubStructure) - , m_isNull(isNull) - { - } - - bool isNull() const { return m_isNull; } - - private: - virtual bool isNotAnObjectErrorStub() const { return true; } - - bool m_isNull; - }; - // This unholy class is used to allow us to avoid multiple exception checks // in certain SquirrelFish bytecodes -- effectively it just silently consumes // any operations performed on the result of a failed toObject call. class JSNotAnObject : public JSObject { public: - JSNotAnObject(ExecState* exec, JSNotAnObjectErrorStub* exception) + JSNotAnObject(ExecState* exec) : JSObject(exec->globalData().notAnObjectStructure) - , m_exception(exception) { } @@ -67,7 +50,7 @@ namespace JSC { private: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesMarkChildren | OverridesGetPropertyNames | JSObject::StructureFlags; + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags; // JSValue methods virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; @@ -77,9 +60,6 @@ namespace JSC { virtual UString toString(ExecState*) const; virtual JSObject* toObject(ExecState*) const; - // Marking - virtual void markChildren(MarkStack&); - // JSObject methods virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); @@ -92,8 +72,6 @@ namespace JSC { virtual bool deleteProperty(ExecState*, unsigned propertyName); virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); - - JSNotAnObjectErrorStub* m_exception; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/JSNumberCell.cpp b/JavaScriptCore/runtime/JSNumberCell.cpp index f1009b9..6fa6b2a 100644 --- a/JavaScriptCore/runtime/JSNumberCell.cpp +++ b/JavaScriptCore/runtime/JSNumberCell.cpp @@ -23,80 +23,6 @@ #include "config.h" #include "JSNumberCell.h" -#if USE(JSVALUE32) - -#include "NumberObject.h" -#include "UString.h" - -namespace JSC { - -JSValue JSNumberCell::toPrimitive(ExecState*, PreferredPrimitiveType) const -{ - return const_cast<JSNumberCell*>(this); -} - -bool JSNumberCell::getPrimitiveNumber(ExecState*, double& number, JSValue& value) -{ - number = m_value; - value = this; - return true; -} - -bool JSNumberCell::toBoolean(ExecState*) const -{ - return m_value < 0.0 || m_value > 0.0; // false for NaN -} - -double JSNumberCell::toNumber(ExecState*) const -{ - return m_value; -} - -UString JSNumberCell::toString(ExecState*) const -{ - return UString::from(m_value); -} - -UString JSNumberCell::toThisString(ExecState*) const -{ - return UString::from(m_value); -} - -JSObject* JSNumberCell::toObject(ExecState* exec) const -{ - return constructNumber(exec, const_cast<JSNumberCell*>(this)); -} - -JSObject* JSNumberCell::toThisObject(ExecState* exec) const -{ - return constructNumber(exec, const_cast<JSNumberCell*>(this)); -} - -bool JSNumberCell::getUInt32(uint32_t& uint32) const -{ - uint32 = static_cast<uint32_t>(m_value); - return uint32 == m_value; -} - -JSValue JSNumberCell::getJSNumber() -{ - return this; -} - -JSValue jsNumberCell(ExecState* exec, double d) -{ - return new (exec) JSNumberCell(exec, d); -} - -JSValue jsNumberCell(JSGlobalData* globalData, double d) -{ - return new (globalData) JSNumberCell(globalData, d); -} - -} // namespace JSC - -#else // USE(JSVALUE32) - // Keep our exported symbols lists happy. namespace JSC { @@ -110,4 +36,3 @@ JSValue jsNumberCell(ExecState*, double) } // namespace JSC -#endif // USE(JSVALUE32) diff --git a/JavaScriptCore/runtime/JSNumberCell.h b/JavaScriptCore/runtime/JSNumberCell.h index bcb506b..0040067 100644 --- a/JavaScriptCore/runtime/JSNumberCell.h +++ b/JavaScriptCore/runtime/JSNumberCell.h @@ -35,244 +35,61 @@ namespace JSC { extern const double NaN; extern const double Inf; -#if USE(JSVALUE32) - JSValue jsNumberCell(ExecState*, double); - - class Identifier; - class JSCell; - class JSObject; - class JSString; - class PropertySlot; - - struct ClassInfo; - struct Instruction; - - class JSNumberCell : public JSCell { - friend class JIT; - friend JSValue jsNumberCell(JSGlobalData*, double); - friend JSValue jsNumberCell(ExecState*, double); - - public: - double value() const { return m_value; } - - virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; - virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); - virtual bool toBoolean(ExecState*) const; - virtual double toNumber(ExecState*) const; - virtual UString toString(ExecState*) const; - virtual JSObject* toObject(ExecState*) const; - - virtual UString toThisString(ExecState*) const; - virtual JSObject* toThisObject(ExecState*) const; - virtual JSValue getJSNumber(); - - void* operator new(size_t size, ExecState* exec) - { - return exec->heap()->allocateNumber(size); - } - - void* operator new(size_t size, JSGlobalData* globalData) - { - return globalData->heap.allocateNumber(size); - } - - static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(NumberType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); } - - private: - JSNumberCell(JSGlobalData* globalData, double value) - : JSCell(globalData->numberStructure.get()) - , m_value(value) - { - } - - JSNumberCell(ExecState* exec, double value) - : JSCell(exec->globalData().numberStructure.get()) - , m_value(value) - { - } - - virtual bool getUInt32(uint32_t&) const; - - double m_value; - }; - - JSValue jsNumberCell(JSGlobalData*, double); - - inline bool isNumberCell(JSValue v) - { - return v.isCell() && v.asCell()->isNumber(); - } - - inline JSNumberCell* asNumberCell(JSValue v) - { - ASSERT(isNumberCell(v)); - 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); - *this = v ? v : jsNumberCell(exec, d); - } - - inline JSValue::JSValue(ExecState* exec, int i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(exec, i); - } - - inline JSValue::JSValue(ExecState* exec, unsigned i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(exec, i); - } - - inline JSValue::JSValue(ExecState* exec, long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(exec, i); - } - - inline JSValue::JSValue(ExecState* exec, unsigned long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(exec, i); - } - - inline JSValue::JSValue(ExecState* exec, long long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(exec, static_cast<double>(i)); - } - - inline JSValue::JSValue(ExecState* exec, unsigned long long i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(exec, static_cast<double>(i)); - } - - inline JSValue::JSValue(JSGlobalData* globalData, double d) - { - JSValue v = JSImmediate::from(d); - *this = v ? v : jsNumberCell(globalData, d); - } - - inline JSValue::JSValue(JSGlobalData* globalData, int i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(globalData, i); - } - - inline JSValue::JSValue(JSGlobalData* globalData, unsigned i) - { - JSValue v = JSImmediate::from(i); - *this = v ? v : jsNumberCell(globalData, i); - } - - inline bool JSValue::isDouble() const - { - return isNumberCell(asValue()); - } - - inline double JSValue::asDouble() const - { - return asNumberCell(asValue())->value(); - } - - inline bool JSValue::isNumber() const - { - return JSImmediate::isNumber(asValue()) || isDouble(); - } - - inline double JSValue::uncheckedGetNumber() const - { - ASSERT(isNumber()); - return JSImmediate::isImmediate(asValue()) ? JSImmediate::toDouble(asValue()) : asDouble(); - } - -#endif // USE(JSVALUE32) - #if USE(JSVALUE64) - ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState*, double d) + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) { *this = JSImmediate::fromNumberOutsideIntegerRange(d); } - inline JSValue::JSValue(ExecState*, double d) + inline JSValue::JSValue(double d) { JSValue v = JSImmediate::from(d); ASSERT(v); *this = v; } - inline JSValue::JSValue(ExecState*, int i) + inline JSValue::JSValue(int i) { JSValue v = JSImmediate::from(i); ASSERT(v); *this = v; } - inline JSValue::JSValue(ExecState*, unsigned i) + inline JSValue::JSValue(unsigned i) { JSValue v = JSImmediate::from(i); ASSERT(v); *this = v; } - inline JSValue::JSValue(ExecState*, long i) + inline JSValue::JSValue(long i) { JSValue v = JSImmediate::from(i); ASSERT(v); *this = v; } - inline JSValue::JSValue(ExecState*, unsigned long i) + inline JSValue::JSValue(unsigned long i) { JSValue v = JSImmediate::from(i); ASSERT(v); *this = v; } - inline JSValue::JSValue(ExecState*, long long i) + inline JSValue::JSValue(long long i) { JSValue v = JSImmediate::from(static_cast<double>(i)); ASSERT(v); *this = v; } - inline JSValue::JSValue(ExecState*, unsigned long long i) + inline JSValue::JSValue(unsigned long long i) { JSValue v = JSImmediate::from(static_cast<double>(i)); ASSERT(v); *this = v; } - inline JSValue::JSValue(JSGlobalData*, double d) - { - JSValue v = JSImmediate::from(d); - ASSERT(v); - *this = v; - } - - inline JSValue::JSValue(JSGlobalData*, int i) - { - JSValue v = JSImmediate::from(i); - ASSERT(v); - *this = v; - } - - inline JSValue::JSValue(JSGlobalData*, unsigned i) - { - JSValue v = JSImmediate::from(i); - ASSERT(v); - *this = v; - } - inline bool JSValue::isDouble() const { return JSImmediate::isDouble(asValue()); @@ -296,47 +113,42 @@ namespace JSC { #endif // USE(JSVALUE64) -#if USE(JSVALUE32) || USE(JSVALUE64) +#if USE(JSVALUE64) - inline JSValue::JSValue(ExecState*, char i) + inline JSValue::JSValue(char i) { ASSERT(JSImmediate::from(i)); *this = JSImmediate::from(i); } - inline JSValue::JSValue(ExecState*, unsigned char i) + inline JSValue::JSValue(unsigned char i) { ASSERT(JSImmediate::from(i)); *this = JSImmediate::from(i); } - inline JSValue::JSValue(ExecState*, short i) + inline JSValue::JSValue(short i) { ASSERT(JSImmediate::from(i)); *this = JSImmediate::from(i); } - inline JSValue::JSValue(ExecState*, unsigned short i) + inline JSValue::JSValue(unsigned short i) { ASSERT(JSImmediate::from(i)); *this = JSImmediate::from(i); } - inline JSValue jsNaN(ExecState* exec) - { - return jsNumber(exec, NaN); - } - - inline JSValue jsNaN(JSGlobalData* globalData) + inline JSValue jsNaN() { - return jsNumber(globalData, NaN); + return jsNumber(NaN); } // --- JSValue inlines ---------------------------- ALWAYS_INLINE JSValue JSValue::toJSNumber(ExecState* exec) const { - return isNumber() ? asValue() : jsNumber(exec, this->toNumber(exec)); + return isNumber() ? asValue() : jsNumber(this->toNumber(exec)); } inline bool JSValue::getNumber(double &result) const @@ -352,7 +164,7 @@ namespace JSC { return true; } -#endif // USE(JSVALUE32) || USE(JSVALUE64) +#endif // USE(JSVALUE64) } // namespace JSC diff --git a/JavaScriptCore/runtime/JSONObject.cpp b/JavaScriptCore/runtime/JSONObject.cpp index acd9280..b5477a1 100644 --- a/JavaScriptCore/runtime/JSONObject.cpp +++ b/JavaScriptCore/runtime/JSONObject.cpp @@ -30,17 +30,20 @@ #include "Error.h" #include "ExceptionHelpers.h" #include "JSArray.h" +#include "JSGlobalObject.h" #include "LiteralParser.h" +#include "Lookup.h" #include "PropertyNameArray.h" -#include "StringBuilder.h" +#include "UStringBuilder.h" +#include "UStringConcatenate.h" #include <wtf/MathExtras.h> namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSONObject); -static JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*); +static EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*); } @@ -48,6 +51,11 @@ static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSVal namespace JSC { +JSONObject::JSONObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : JSObjectWithGlobalObject(globalObject, structure) +{ +} + // PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked. class PropertyNameForFunctionCall { public: @@ -77,7 +85,7 @@ private: JSObject* object() const { return m_object; } - bool appendNextProperty(Stringifier&, StringBuilder&); + bool appendNextProperty(Stringifier&, UStringBuilder&); private: JSObject* const m_object; @@ -90,17 +98,17 @@ private: friend class Holder; - static void appendQuotedString(StringBuilder&, const UString&); + static void appendQuotedString(UStringBuilder&, const UString&); JSValue toJSON(JSValue, const PropertyNameForFunctionCall&); enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedValue }; - StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&); + StringifyResult appendStringifiedValue(UStringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&); bool willIndent() const; void indent(); void unindent(); - void startNewLine(StringBuilder&) const; + void startNewLine(UStringBuilder&) const; Stringifier* const m_nextStringifierToMark; ExecState* const m_exec; @@ -125,7 +133,7 @@ static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value) return value; JSObject* object = asObject(value); if (object->inherits(&NumberObject::info)) - return jsNumber(exec, object->toNumber(exec)); + return jsNumber(object->toNumber(exec)); if (object->inherits(&StringObject::info)) return jsString(exec, object->toString(exec)); if (object->inherits(&BooleanObject::info)) @@ -135,7 +143,7 @@ static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value) static inline UString gap(ExecState* exec, JSValue space) { - const int maxGapLength = 10; + const unsigned maxGapLength = 10; space = unwrapBoxedPrimitive(exec, space); // If the space value is a number, create a gap string with that number of spaces. @@ -156,8 +164,8 @@ 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(exec); - if (spaces.size() > maxGapLength) { - spaces = spaces.substr(0, maxGapLength); + if (spaces.length() > maxGapLength) { + spaces = spaces.substringSharingImpl(0, maxGapLength); } return spaces; } @@ -181,7 +189,7 @@ JSValue PropertyNameForFunctionCall::value(ExecState* exec) const if (m_identifier) m_value = jsString(exec, m_identifier->ustring()); else - m_value = jsNumber(exec, m_number); + m_value = jsNumber(m_number); } return m_value; } @@ -262,25 +270,25 @@ JSValue Stringifier::stringify(JSValue value) PropertyNameForFunctionCall emptyPropertyName(m_exec->globalData().propertyNames->emptyIdentifier); object->putDirect(m_exec->globalData().propertyNames->emptyIdentifier, value); - StringBuilder result; + UStringBuilder result; if (appendStringifiedValue(result, value, object, emptyPropertyName) != StringifySucceeded) return jsUndefined(); if (m_exec->hadException()) return jsNull(); - return jsString(m_exec, result.build()); + return jsString(m_exec, result.toUString()); } -void Stringifier::appendQuotedString(StringBuilder& builder, const UString& value) +void Stringifier::appendQuotedString(UStringBuilder& builder, const UString& value) { - int length = value.size(); + int length = value.length(); // String length plus 2 for quote marks plus 8 so we can accomodate a few escaped characters. - builder.reserveCapacity(builder.size() + length + 2 + 8); + builder.reserveCapacity(builder.length() + length + 2 + 8); builder.append('"'); - const UChar* data = value.data(); + const UChar* data = value.characters(); for (int i = 0; i < length; ++i) { int start = i; while (i < length && (data[i] > 0x1F && data[i] != '"' && data[i] != '\\')) @@ -321,7 +329,7 @@ void Stringifier::appendQuotedString(StringBuilder& builder, const UString& valu static const char hexDigits[] = "0123456789abcdef"; UChar ch = data[i]; UChar hex[] = { '\\', 'u', hexDigits[(ch >> 12) & 0xF], hexDigits[(ch >> 8) & 0xF], hexDigits[(ch >> 4) & 0xF], hexDigits[ch & 0xF] }; - builder.append(hex, sizeof(hex) / sizeof(UChar)); + builder.append(hex, WTF_ARRAY_LENGTH(hex)); break; } } @@ -349,11 +357,11 @@ inline JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionC return value; JSValue list[] = { propertyName.value(m_exec) }; - ArgList args(list, sizeof(list) / sizeof(JSValue)); + ArgList args(list, WTF_ARRAY_LENGTH(list)); return call(m_exec, object, callType, callData, value, args); } -Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName) +Stringifier::StringifyResult Stringifier::appendStringifiedValue(UStringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName) { // Call the toJSON function. value = toJSON(value, propertyName); @@ -363,7 +371,7 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& // Call the replacer function. if (m_replacerCallType != CallTypeNone) { JSValue list[] = { propertyName.value(m_exec), value }; - ArgList args(list, sizeof(list) / sizeof(JSValue)); + ArgList args(list, WTF_ARRAY_LENGTH(list)); value = call(m_exec, m_replacer, m_replacerCallType, m_replacerCallData, holder, args); if (m_exec->hadException()) return StringifyFailed; @@ -398,7 +406,7 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& if (!isfinite(numericValue)) builder.append("null"); else - builder.append(UString::from(numericValue)); + builder.append(UString::number(numericValue)); return StringifySucceeded; } @@ -418,7 +426,7 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& // Handle cycle detection, and put the holder on the stack. if (!m_holderCycleDetector.add(object).second) { - throwError(m_exec, TypeError, "JSON.stringify cannot serialize cyclic structures."); + throwError(m_exec, createTypeError(m_exec, "JSON.stringify cannot serialize cyclic structures.")); return StringifyFailed; } bool holderStackWasEmpty = m_holderStack.isEmpty(); @@ -436,7 +444,7 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& return StringifyFailed; if (!--tickCount) { if (localTimeoutChecker.didTimeOut(m_exec)) { - m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); + throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); return StringifyFailed; } tickCount = localTimeoutChecker.ticksUntilNextCheck(); @@ -456,20 +464,20 @@ inline bool Stringifier::willIndent() const inline void Stringifier::indent() { // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent. - int newSize = m_indent.size() + m_gap.size(); - if (newSize > m_repeatedGap.size()) - m_repeatedGap = makeString(m_repeatedGap, m_gap); - ASSERT(newSize <= m_repeatedGap.size()); - m_indent = m_repeatedGap.substr(0, newSize); + unsigned newSize = m_indent.length() + m_gap.length(); + if (newSize > m_repeatedGap.length()) + m_repeatedGap = makeUString(m_repeatedGap, m_gap); + ASSERT(newSize <= m_repeatedGap.length()); + m_indent = m_repeatedGap.substringSharingImpl(0, newSize); } inline void Stringifier::unindent() { - ASSERT(m_indent.size() >= m_gap.size()); - m_indent = m_repeatedGap.substr(0, m_indent.size() - m_gap.size()); + ASSERT(m_indent.length() >= m_gap.length()); + m_indent = m_repeatedGap.substringSharingImpl(0, m_indent.length() - m_gap.length()); } -inline void Stringifier::startNewLine(StringBuilder& builder) const +inline void Stringifier::startNewLine(UStringBuilder& builder) const { if (m_gap.isEmpty()) return; @@ -484,7 +492,7 @@ inline Stringifier::Holder::Holder(JSObject* object) { } -bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder) +bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, UStringBuilder& builder) { ASSERT(m_index <= m_size); @@ -513,7 +521,7 @@ bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBui // Last time through, finish up and return false. if (m_index == m_size) { stringifier.unindent(); - if (m_size && builder[builder.size() - 1] != '{') + if (m_size && builder[builder.length() - 1] != '{') stringifier.startNewLine(builder); builder.append(m_isArray ? ']' : '}'); return false; @@ -554,7 +562,7 @@ bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBui if (exec->hadException()) return false; - rollBackPoint = builder.size(); + rollBackPoint = builder.length(); // Append the separator string. if (builder[rollBackPoint - 1] != '{') @@ -672,10 +680,8 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) case ArrayStartState: { ASSERT(inValue.isObject()); ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue)) || asObject(inValue)->inherits(&JSArray::info)); - if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) { - m_exec->setException(createStackOverflowError(m_exec)); - return jsUndefined(); - } + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwError(m_exec, createStackOverflowError(m_exec)); JSArray* array = asArray(inValue); arrayStack.append(array); @@ -685,10 +691,8 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) arrayStartVisitMember: case ArrayStartVisitMember: { if (!--tickCount) { - if (localTimeoutChecker.didTimeOut(m_exec)) { - m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); - return jsUndefined(); - } + if (localTimeoutChecker.didTimeOut(m_exec)) + return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); tickCount = localTimeoutChecker.ticksUntilNextCheck(); } @@ -719,7 +723,7 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) } case ArrayEndVisitMember: { JSArray* array = arrayStack.last(); - JSValue filteredValue = callReviver(array, jsString(m_exec, UString::from(indexStack.last())), outValue); + JSValue filteredValue = callReviver(array, jsString(m_exec, UString::number(indexStack.last())), outValue); if (filteredValue.isUndefined()) array->deleteProperty(m_exec, indexStack.last()); else { @@ -737,10 +741,8 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) case ObjectStartState: { ASSERT(inValue.isObject()); ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue)) && !asObject(inValue)->inherits(&JSArray::info)); - if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) { - m_exec->setException(createStackOverflowError(m_exec)); - return jsUndefined(); - } + if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) + return throwError(m_exec, createStackOverflowError(m_exec)); JSObject* object = asObject(inValue); objectStack.append(object); @@ -752,10 +754,8 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) objectStartVisitMember: case ObjectStartVisitMember: { if (!--tickCount) { - if (localTimeoutChecker.didTimeOut(m_exec)) { - m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); - return jsUndefined(); - } + if (localTimeoutChecker.didTimeOut(m_exec)) + return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); tickCount = localTimeoutChecker.ticksUntilNextCheck(); } @@ -818,10 +818,8 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) stateStack.removeLast(); if (!--tickCount) { - if (localTimeoutChecker.didTimeOut(m_exec)) { - m_exec->setException(createInterruptedExecutionException(&m_exec->globalData())); - return jsUndefined(); - } + if (localTimeoutChecker.didTimeOut(m_exec)) + return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData())); tickCount = localTimeoutChecker.ticksUntilNextCheck(); } } @@ -832,40 +830,48 @@ NEVER_INLINE JSValue Walker::walk(JSValue unfiltered) } // ECMA-262 v5 15.12.2 -JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec) { - if (args.isEmpty()) - return throwError(exec, GeneralError, "JSON.parse requires at least one parameter"); - JSValue value = args.at(0); + if (!exec->argumentCount()) + return throwVMError(exec, createError(exec, "JSON.parse requires at least one parameter")); + JSValue value = exec->argument(0); UString source = value.toString(exec); if (exec->hadException()) - return jsNull(); + return JSValue::encode(jsNull()); LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON); JSValue unfiltered = jsonParser.tryLiteralParse(); if (!unfiltered) - return throwError(exec, SyntaxError, "Unable to parse JSON string"); + return throwVMError(exec, createSyntaxError(exec, "Unable to parse JSON string")); - if (args.size() < 2) - return unfiltered; + if (exec->argumentCount() < 2) + return JSValue::encode(unfiltered); - JSValue function = args.at(1); + JSValue function = exec->argument(1); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return unfiltered; - return Walker(exec, asObject(function), callType, callData).walk(unfiltered); + return JSValue::encode(unfiltered); + return JSValue::encode(Walker(exec, asObject(function), callType, callData).walk(unfiltered)); } // ECMA-262 v5 15.12.3 -JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec) +{ + if (!exec->argumentCount()) + return throwVMError(exec, createError(exec, "No input to stringify")); + JSValue value = exec->argument(0); + JSValue replacer = exec->argument(1); + JSValue space = exec->argument(2); + return JSValue::encode(Stringifier(exec, replacer, space).stringify(value)); +} + +UString JSONStringify(ExecState* exec, JSValue value, unsigned indent) { - if (args.isEmpty()) - return throwError(exec, GeneralError, "No input to stringify"); - JSValue value = args.at(0); - JSValue replacer = args.at(1); - JSValue space = args.at(2); - return Stringifier(exec, replacer, space).stringify(value); + JSValue result = Stringifier(exec, jsNull(), jsNumber(indent)).stringify(value); + if (result.isUndefinedOrNull()) + return UString(); + return result.getString(exec); } } // namespace JSC diff --git a/JavaScriptCore/runtime/JSONObject.h b/JavaScriptCore/runtime/JSONObject.h index 905e4bc..f64be12 100644 --- a/JavaScriptCore/runtime/JSONObject.h +++ b/JavaScriptCore/runtime/JSONObject.h @@ -26,18 +26,15 @@ #ifndef JSONObject_h #define JSONObject_h -#include "JSObject.h" +#include "JSObjectWithGlobalObject.h" namespace JSC { class Stringifier; - class JSONObject : public JSObject { + class JSONObject : public JSObjectWithGlobalObject { public: - JSONObject(NonNullPassRefPtr<Structure> structure) - : JSObject(structure) - { - } + JSONObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure); static PassRefPtr<Structure> createStructure(JSValue prototype) { @@ -57,6 +54,8 @@ namespace JSC { static const ClassInfo info; }; + UString JSONStringify(ExecState* exec, JSValue value, unsigned indent); + } // namespace JSC #endif // JSONObject_h diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp index d9500aa..30e40e4 100644 --- a/JavaScriptCore/runtime/JSObject.cpp +++ b/JavaScriptCore/runtime/JSObject.cpp @@ -27,6 +27,7 @@ #include "DatePrototype.h" #include "ErrorConstructor.h" #include "GetterSetter.h" +#include "JSFunction.h" #include "JSGlobalObject.h" #include "NativeErrorConstructor.h" #include "ObjectPrototype.h" @@ -42,6 +43,8 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(JSObject); +const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; + static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode) { // Add properties from the static hashtables of properties @@ -90,7 +93,7 @@ bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, Proper static void throwSetterError(ExecState* exec) { - throwError(exec, TypeError, "setting a property that has only a getter"); + throwError(exec, createTypeError(exec, "setting a property that has only a getter")); } // ECMA 8.6.2.2 @@ -103,18 +106,8 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue valu // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. if (!value.isObject() && !value.isNull()) return; - - JSValue nextPrototypeValue = value; - while (nextPrototypeValue && nextPrototypeValue.isObject()) { - JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); - if (nextPrototype == this) { - throwError(exec, GeneralError, "cyclic __proto__ value"); - return; - } - nextPrototypeValue = nextPrototype->prototype(); - } - - setPrototype(value); + if (!setPrototypeWithCycleCheck(value)) + throwError(exec, createError(exec, "cyclic __proto__ value")); return; } @@ -123,15 +116,19 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue valu for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype.isNull()) { - putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); + if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } } unsigned attributes; JSCell* specificValue; - if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) + if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) { + if (slot.isStrictMode()) + throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); return; + } for (JSObject* obj = this; ; obj = asObject(prototype)) { if (JSValue gs = obj->getDirect(propertyName)) { @@ -160,7 +157,8 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue valu break; } - putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); + if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } @@ -170,6 +168,21 @@ void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value) put(exec, Identifier::from(exec, propertyName), value, slot); } +void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + putDirectInternal(*globalData, propertyName, value, attributes); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes) +{ + putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes); +} + void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot); @@ -233,7 +246,7 @@ static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSO { JSValue function = object->get(exec, propertyName); CallData callData; - CallType callType = function.getCallData(callData); + CallType callType = getCallData(function, callData); if (callType == CallTypeNone) return exec->exception(); @@ -280,7 +293,7 @@ JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) con ASSERT(!exec->hadException()); - return throwError(exec, TypeError, "No default value"); + return throwError(exec, createTypeError(exec, "No default value")); } const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const @@ -392,7 +405,7 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) return false; if (!proto.isObject()) { - throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property."); + throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property.")); return false; } @@ -483,6 +496,11 @@ JSObject* JSObject::toThisObject(ExecState*) const return const_cast<JSObject*>(this); } +JSValue JSObject::toStrictThisObject(ExecState*) const +{ + return const_cast<JSObject*>(this); +} + JSObject* JSObject::unwrappedObject() { return this; @@ -509,16 +527,29 @@ void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, un putDirectFunction(Identifier(exec, function->name(exec)), function, attr); } +void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr) +{ + putDirectFunction(Identifier(exec, function->name(exec)), function, attr); +} + void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) { putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr); } +void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr) +{ + putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr); +} + NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) { - if (JSObject* getterFunction = asGetterSetter(*location)->getter()) - slot.setGetterSlot(getterFunction); - else + if (JSObject* getterFunction = asGetterSetter(*location)->getter()) { + if (!structure()->isDictionary()) + slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); + else + slot.setGetterSlot(getterFunction); + } else slot.setUndefined(); } @@ -590,12 +621,12 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName if (!current.configurable()) { if (descriptor.configurable()) { if (throwException) - throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property."); + throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); return false; } if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { if (throwException) - throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property."); + throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); return false; } } @@ -613,7 +644,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { if (!current.configurable()) { if (throwException) - throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property."); + throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property.")); return false; } deleteProperty(exec, propertyName); @@ -625,13 +656,13 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName if (!current.configurable()) { if (!current.writable() && descriptor.writable()) { if (throwException) - throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property."); + throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); return false; } if (!current.writable()) { if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) { if (throwException) - throwError(exec, TypeError, "Attempting to change value of a readonly property."); + throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property.")); return false; } } @@ -653,12 +684,12 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName if (!current.configurable()) { 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."); + throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property.")); return false; } 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."); + throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property.")); return false; } } @@ -683,4 +714,9 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName return true; } +JSObject* throwTypeError(ExecState* exec, const UString& message) +{ + return throwError(exec, createTypeError(exec, message)); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSObject.h b/JavaScriptCore/runtime/JSObject.h index 2b31a65..803abfd 100644 --- a/JavaScriptCore/runtime/JSObject.h +++ b/JavaScriptCore/runtime/JSObject.h @@ -26,6 +26,7 @@ #include "ArgList.h" #include "ClassInfo.h" #include "CommonIdentifiers.h" +#include "Completion.h" #include "CallFrame.h" #include "JSCell.h" #include "JSNumberCell.h" @@ -35,6 +36,7 @@ #include "ScopeChain.h" #include "Structure.h" #include "JSGlobalData.h" +#include "JSString.h" #include <wtf/StdLibExtras.h> namespace JSC { @@ -53,6 +55,9 @@ namespace JSC { class Structure; struct HashTable; + JSObject* throwTypeError(ExecState*, const UString&); + extern const char* StrictModeReadonlyPropertyWriteError; + // ECMA 262-3 8.6.1 // Property attributes enum Attribute { @@ -72,6 +77,7 @@ namespace JSC { friend class BatchedTransitionOptimizer; friend class JIT; friend class JSCell; + friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot); public: explicit JSObject(NonNullPassRefPtr<Structure>); @@ -85,6 +91,7 @@ namespace JSC { JSValue prototype() const; void setPrototype(JSValue prototype); + bool setPrototypeWithCycleCheck(JSValue prototype); void setStructure(NonNullPassRefPtr<Structure>); Structure* inheritorID(); @@ -105,6 +112,9 @@ namespace JSC { virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&); virtual void put(ExecState*, unsigned propertyName, JSValue value); + virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); + virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes); + virtual void putWithAttributes(JSGlobalData*, unsigned propertyName, JSValue value, unsigned attributes); virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes); @@ -133,6 +143,7 @@ namespace JSC { virtual JSObject* toObject(ExecState*) const; virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; virtual JSObject* unwrappedObject(); bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; @@ -168,16 +179,19 @@ namespace JSC { bool hasCustomProperties() { return !m_structure->isEmpty(); } bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); } - void putDirect(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); + bool putDirect(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); void putDirect(const Identifier& propertyName, JSValue value, unsigned attr = 0); + bool putDirect(const Identifier& propertyName, JSValue value, PutPropertySlot&); void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr = 0); void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0); void putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attr = 0); void putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attr = 0); void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr = 0); // Fast access to known property offsets. JSValue getDirectOffset(size_t offset) const { return JSValue::decode(propertyStorage()[offset]); } @@ -194,8 +208,10 @@ namespace JSC { virtual bool isGlobalObject() const { return false; } virtual bool isVariableObject() const { return false; } virtual bool isActivationObject() const { return false; } - virtual bool isWatchdogException() const { return false; } - virtual bool isNotAnObjectErrorStub() const { return false; } + virtual bool isStrictModeFunction() const { return false; } + virtual bool isErrorInstance() const { return false; } + + virtual ComplType exceptionType() const { return Throw; } void allocatePropertyStorage(size_t oldSize, size_t newSize); void allocatePropertyStorageInline(size_t oldSize, size_t newSize); @@ -214,9 +230,6 @@ namespace JSC { m_structure->flattenDictionaryStructure(this); } - protected: - static const unsigned StructureFlags = 0; - void putAnonymousValue(unsigned index, JSValue value) { ASSERT(index < m_structure->anonymousSlotCount()); @@ -227,7 +240,10 @@ namespace JSC { ASSERT(index < m_structure->anonymousSlotCount()); return *locationForOffset(index); } - + + protected: + static const unsigned StructureFlags = 0; + private: // Nobody should ever ask any of these questions on something already known to be a JSObject. using JSCell::isAPIValueWrapper; @@ -237,9 +253,6 @@ namespace JSC { void getString(ExecState* exec); void isObject(); void isString(); -#if USE(JSVALUE32) - void isNumber(); -#endif ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } @@ -254,8 +267,8 @@ namespace JSC { return reinterpret_cast<JSValue*>(&propertyStorage()[offset]); } - void putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot, JSCell*); - void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); + bool putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot, JSCell*); + bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0); bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); @@ -288,9 +301,7 @@ inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure) ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); ASSERT(m_structure->isEmpty()); ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); -#if USE(JSVALUE64) || USE(JSVALUE32_64) ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0); -#endif } inline JSObject::~JSObject() @@ -306,6 +317,19 @@ inline JSValue JSObject::prototype() const return m_structure->storedPrototype(); } +inline bool JSObject::setPrototypeWithCycleCheck(JSValue prototype) +{ + JSValue nextPrototypeValue = prototype; + while (nextPrototypeValue && nextPrototypeValue.isObject()) { + JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); + if (nextPrototype == this) + return false; + nextPrototypeValue = nextPrototype->prototype(); + } + setPrototype(prototype); + return true; +} + inline void JSObject::setPrototype(JSValue prototype) { ASSERT(prototype); @@ -316,7 +340,7 @@ inline void JSObject::setPrototype(JSValue prototype) inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure) { m_structure->deref(); - m_structure = structure.releaseRef(); // ~JSObject balances this ref() + m_structure = structure.leakRef(); // ~JSObject balances this ref() } inline Structure* JSObject::inheritorID() @@ -426,7 +450,7 @@ inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const return jsUndefined(); } -inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) +inline bool JSObject::putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); @@ -436,14 +460,23 @@ inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue JSCell* currentSpecificFunction; size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); if (offset != WTF::notFound) { + // If there is currently a specific function, and there now either isn't, + // or the new value is different, then despecify. if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) m_structure->despecifyDictionaryFunction(propertyName); if (checkReadOnly && currentAttributes & ReadOnly) - return; + return false; + putDirectOffset(offset, value); - if (!specificFunction && !currentSpecificFunction) + // At this point, the objects structure only has a specific value set if previously there + // had been one set, and if the new value being specified is the same (otherwise we would + // have despecified, above). So, if currentSpecificFunction is not set, or if the new + // value is different (or there is no new value), then the slot now has no value - and + // as such it is cachable. + // If there was previously a value, and the new value is the same, then we cannot cache. + if (!currentSpecificFunction || (specificFunction != currentSpecificFunction)) slot.setExistingProperty(this, offset); - return; + return true; } size_t currentCapacity = m_structure->propertyStorageCapacity(); @@ -456,7 +489,7 @@ inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue // See comment on setNewProperty call below. if (!specificFunction) slot.setNewProperty(this, offset); - return; + return true; } size_t offset; @@ -468,10 +501,11 @@ inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue ASSERT(offset < structure->propertyStorageCapacity()); setStructure(structure.release()); putDirectOffset(offset, value); - // See comment on setNewProperty call below. + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. if (!specificFunction) slot.setNewProperty(this, offset); - return; + return true; } unsigned currentAttributes; @@ -479,17 +513,31 @@ inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); if (offset != WTF::notFound) { if (checkReadOnly && currentAttributes & ReadOnly) - return; + return false; - if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) { + // There are three possibilities here: + // (1) There is an existing specific value set, and we're overwriting with *the same value*. + // * Do nothing - no need to despecify, but that means we can't cache (a cached + // put could write a different value). Leave the slot in an uncachable state. + // (2) There is a specific value currently set, but we're writing a different value. + // * First, we have to despecify. Having done so, this is now a regular slot + // with no specific value, so go ahead & cache like normal. + // (3) Normal case, there is no specific value set. + // * Go ahead & cache like normal. + if (currentSpecificFunction) { + // case (1) Do the put, then return leaving the slot uncachable. + if (specificFunction == currentSpecificFunction) { + putDirectOffset(offset, value); + return true; + } + // case (2) Despecify, fall through to (3). setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName)); - putDirectOffset(offset, value); - // Function transitions are not currently cachable, so leave the slot in an uncachable state. - return; } - putDirectOffset(offset, value); + + // case (3) set the slot, do the put, return. slot.setExistingProperty(this, offset); - return; + putDirectOffset(offset, value); + return true; } // If we have a specific function, we may have got to this point if there is @@ -510,17 +558,19 @@ inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue ASSERT(offset < structure->propertyStorageCapacity()); setStructure(structure.release()); putDirectOffset(offset, value); - // Function transitions are not currently cachable, so leave the slot in an uncachable state. + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. if (!specificFunction) slot.setNewProperty(this, offset); + return true; } -inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); + return putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); } inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) @@ -529,12 +579,12 @@ inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifi putDirectInternal(propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); } -inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline bool JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0); + return putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0); } inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes) @@ -543,6 +593,11 @@ inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, u putDirectInternal(propertyName, value, attributes, false, slot, 0); } +inline bool JSObject::putDirect(const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + return putDirectInternal(propertyName, value, 0, false, slot, 0); +} + inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, value); @@ -645,6 +700,13 @@ inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValu asCell()->put(exec, propertyName, value, slot); } +inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(isCell() && isObject()); + if (!asObject(asCell())->putDirect(propertyName, value, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); +} + inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) { if (UNLIKELY(!isCell())) { @@ -685,6 +747,25 @@ ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack) markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize); } +// --- JSValue inlines ---------------------------- + +ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const +{ + return isString() ? static_cast<JSString*>(asCell())->value(exec) : toThisObject(exec)->toString(exec); +} + +inline JSString* JSValue::toThisJSString(ExecState* exec) const +{ + return isString() ? static_cast<JSString*>(asCell()) : jsString(exec, toThisObject(exec)->toString(exec)); +} + +inline JSValue JSValue::toStrictThisObject(ExecState* exec) const +{ + if (!isObject()) + return *this; + return asObject(asCell())->toStrictThisObject(exec); +} + } // namespace JSC #endif // JSObject_h diff --git a/JavaScriptCore/runtime/JSObjectWithGlobalObject.cpp b/JavaScriptCore/runtime/JSObjectWithGlobalObject.cpp new file mode 100644 index 0000000..e9d6c96 --- /dev/null +++ b/JavaScriptCore/runtime/JSObjectWithGlobalObject.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "JSObjectWithGlobalObject.h" + +#include "JSGlobalObject.h" + +namespace JSC { + +JSObjectWithGlobalObject::JSObjectWithGlobalObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : JSObject(structure) +{ + COMPILE_ASSERT(AnonymousSlotCount == 1, AnonymousSlotCount_must_be_one); + ASSERT(!globalObject || globalObject->isGlobalObject()); + putAnonymousValue(GlobalObjectSlot, globalObject); +} + +JSGlobalObject* JSObjectWithGlobalObject::globalObject() const +{ + return asGlobalObject((getAnonymousValue(GlobalObjectSlot).asCell())); +} + +} // namespace JSC diff --git a/JavaScriptCore/runtime/JSObjectWithGlobalObject.h b/JavaScriptCore/runtime/JSObjectWithGlobalObject.h new file mode 100644 index 0000000..9416a62 --- /dev/null +++ b/JavaScriptCore/runtime/JSObjectWithGlobalObject.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 JSObjectWithGlobalObject_h +#define JSObjectWithGlobalObject_h + +#include "JSObject.h" + +namespace JSC { + +class JSGlobalObject; + +class JSObjectWithGlobalObject : public JSObject { +public: + static PassRefPtr<Structure> createStructure(JSValue proto) + { + return Structure::create(proto, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + } + + JSGlobalObject* globalObject() const; + +protected: + JSObjectWithGlobalObject(JSGlobalObject*, NonNullPassRefPtr<Structure>); + + JSObjectWithGlobalObject(NonNullPassRefPtr<Structure> structure) + : JSObject(structure) + { + // Should only be used by JSFunction when we aquire the JSFunction vptr. + } + static const unsigned AnonymousSlotCount = JSObject::AnonymousSlotCount + 1; + static const unsigned GlobalObjectSlot = 0; +}; + +} // namespace JSC + +#endif // JSObjectWithGlobalObject_h diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.h b/JavaScriptCore/runtime/JSPropertyNameIterator.h index 3f533a0..01700ac 100644 --- a/JavaScriptCore/runtime/JSPropertyNameIterator.h +++ b/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -67,8 +67,13 @@ namespace JSC { JSValue get(ExecState*, JSObject*, size_t i); size_t size() { return m_jsStringsSize; } - void setCachedStructure(Structure* structure) { m_cachedStructure = structure; } - Structure* cachedStructure() { return m_cachedStructure; } + void setCachedStructure(Structure* structure) + { + ASSERT(!m_cachedStructure); + ASSERT(structure); + m_cachedStructure = structure; + } + Structure* cachedStructure() { return m_cachedStructure.get(); } void setCachedPrototypeChain(NonNullPassRefPtr<StructureChain> cachedPrototypeChain) { m_cachedPrototypeChain = cachedPrototypeChain; } StructureChain* cachedPrototypeChain() { return m_cachedPrototypeChain.get(); } @@ -76,7 +81,7 @@ namespace JSC { private: JSPropertyNameIterator(ExecState*, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlot); - Structure* m_cachedStructure; + RefPtr<Structure> m_cachedStructure; RefPtr<StructureChain> m_cachedPrototypeChain; uint32_t m_numCacheableSlots; uint32_t m_jsStringsSize; diff --git a/JavaScriptCore/runtime/JSStaticScopeObject.cpp b/JavaScriptCore/runtime/JSStaticScopeObject.cpp index a877ec6..7ab1d1c 100644 --- a/JavaScriptCore/runtime/JSStaticScopeObject.cpp +++ b/JavaScriptCore/runtime/JSStaticScopeObject.cpp @@ -42,6 +42,11 @@ JSObject* JSStaticScopeObject::toThisObject(ExecState* exec) const return exec->globalThisValue(); } +JSValue JSStaticScopeObject::toStrictThisObject(ExecState*) const +{ + return jsNull(); +} + void JSStaticScopeObject::put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&) { if (symbolTablePut(propertyName, value)) @@ -58,7 +63,7 @@ void JSStaticScopeObject::putWithAttributes(ExecState*, const Identifier& proper ASSERT_NOT_REACHED(); } -bool JSStaticScopeObject::isDynamicScope() const +bool JSStaticScopeObject::isDynamicScope(bool&) const { return false; } diff --git a/JavaScriptCore/runtime/JSStaticScopeObject.h b/JavaScriptCore/runtime/JSStaticScopeObject.h index 4d156d4..e69356a 100644 --- a/JavaScriptCore/runtime/JSStaticScopeObject.h +++ b/JavaScriptCore/runtime/JSStaticScopeObject.h @@ -47,12 +47,13 @@ namespace JSC{ : JSVariableObject(exec->globalData().staticScopeStructure, new JSStaticScopeObjectData()) { d()->registerStore = value; - symbolTable().add(ident.ustring().rep(), SymbolTableEntry(-1, attributes)); + symbolTable().add(ident.impl(), SymbolTableEntry(-1, attributes)); } virtual ~JSStaticScopeObject(); virtual void markChildren(MarkStack&); - bool isDynamicScope() const; + bool isDynamicScope(bool& requiresDynamicChecks) const; virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); void putWithAttributes(ExecState*, const Identifier&, JSValue, unsigned attributes); diff --git a/JavaScriptCore/runtime/JSString.cpp b/JavaScriptCore/runtime/JSString.cpp index 1e23a15..340a898 100644 --- a/JavaScriptCore/runtime/JSString.cpp +++ b/JavaScriptCore/runtime/JSString.cpp @@ -24,6 +24,7 @@ #include "JSString.h" #include "JSGlobalObject.h" +#include "JSGlobalObjectFunctions.h" #include "JSObject.h" #include "Operations.h" #include "StringObject.h" @@ -31,48 +32,13 @@ 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(); -} - // 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 +// we would likely have to place all of the constituent StringImpls 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.) @@ -82,51 +48,52 @@ void JSString::resolveRope(ExecState* exec) const // Allocate the buffer to hold the final string, position initially points to the end. UChar* buffer; - if (PassRefPtr<UStringImpl> newImpl = UStringImpl::tryCreateUninitialized(m_stringLength, buffer)) + if (PassRefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) m_value = newImpl; else { - for (unsigned i = 0; i < m_ropeLength; ++i) { - m_fibers[i].deref(); - m_fibers[i] = static_cast<void*>(0); + for (unsigned i = 0; i < m_fiberCount; ++i) { + RopeImpl::deref(m_other.m_fibers[i]); + m_other.m_fibers[i] = 0; } - m_ropeLength = 0; + m_fiberCount = 0; ASSERT(!isRope()); ASSERT(m_value == UString()); - throwOutOfMemoryError(exec); + if (exec) + 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]; + UChar* position = buffer + m_length; + + // Start with the current RopeImpl. + Vector<RopeImpl::Fiber, 32> workQueue; + RopeImpl::Fiber currentFiber; + for (unsigned i = 0; i < (m_fiberCount - 1); ++i) + workQueue.append(m_other.m_fibers[i]); + currentFiber = m_other.m_fibers[m_fiberCount - 1]; while (true) { - if (currentFiber.isRope()) { - Rope* rope = currentFiber.rope(); + if (RopeImpl::isRope(currentFiber)) { + RopeImpl* rope = static_cast<RopeImpl*>(currentFiber); // 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); + unsigned fiberCountMinusOne = rope->fiberCount() - 1; + for (unsigned i = 0; i < fiberCountMinusOne; ++i) + workQueue.append(rope->fibers()[i]); + currentFiber = rope->fibers()[fiberCountMinusOne]; } else { - UString::Rep* string = currentFiber.string(); - unsigned length = string->size(); + StringImpl* string = static_cast<StringImpl*>(currentFiber); + unsigned length = string->length(); position -= length; - UStringImpl::copyChars(position, string->data(), length); + StringImpl::copyChars(position, string->characters(), 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); - for (unsigned i = 0; i < m_ropeLength; ++i) { - m_fibers[i].deref(); - m_fibers[i] = static_cast<void*>(0); + for (unsigned i = 0; i < m_fiberCount; ++i) { + RopeImpl::deref(m_other.m_fibers[i]); + m_other.m_fibers[i] = 0; } - m_ropeLength = 0; + m_fiberCount = 0; ASSERT(!isRope()); return; @@ -139,6 +106,70 @@ void JSString::resolveRope(ExecState* exec) const } } +JSValue JSString::replaceCharacter(ExecState* exec, UChar character, const UString& replacement) +{ + if (!isRope()) { + size_t matchPosition = m_value.find(character); + if (matchPosition == notFound) + return JSValue(this); + return jsString(exec, m_value.substringSharingImpl(0, matchPosition), replacement, m_value.substringSharingImpl(matchPosition + 1)); + } + + RopeIterator end; + + // Count total fibers and find matching string. + size_t fiberCount = 0; + StringImpl* matchString = 0; + size_t matchPosition = notFound; + for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) { + ++fiberCount; + if (matchString) + continue; + + StringImpl* string = *it; + matchPosition = string->find(character); + if (matchPosition == notFound) + continue; + matchString = string; + } + + if (!matchString) + return this; + + RopeBuilder builder(replacement.length() ? fiberCount + 2 : fiberCount + 1); + if (UNLIKELY(builder.isOutOfMemory())) + return throwOutOfMemoryError(exec); + + for (RopeIterator it(m_other.m_fibers.data(), m_fiberCount); it != end; ++it) { + StringImpl* string = *it; + if (string != matchString) { + builder.append(UString(string)); + continue; + } + + builder.append(UString(string).substringSharingImpl(0, matchPosition)); + if (replacement.length()) + builder.append(replacement); + builder.append(UString(string).substringSharingImpl(matchPosition + 1)); + matchString = 0; + } + + JSGlobalData* globalData = &exec->globalData(); + return JSValue(new (globalData) JSString(globalData, builder.release())); +} + +JSString* JSString::getIndexSlowCase(ExecState* exec, unsigned i) +{ + ASSERT(isRope()); + resolveRope(exec); + // Return a safe no-value result, this should never be used, since the excetion will be thrown. + if (exec->exception()) + return jsString(exec, ""); + ASSERT(!isRope()); + ASSERT(i < m_value.length()); + return jsSingleCharacterSubstring(exec, m_value, i); +} + JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const { return const_cast<JSString*>(this); @@ -147,18 +178,18 @@ JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) { result = this; - number = value(exec).toDouble(); + number = jsToNumber(value(exec)); return false; } bool JSString::toBoolean(ExecState*) const { - return m_stringLength; + return m_length; } double JSString::toNumber(ExecState* exec) const { - return value(exec).toDouble(); + return jsToNumber(value(exec)); } UString JSString::toString(ExecState* exec) const @@ -166,16 +197,6 @@ UString JSString::toString(ExecState* exec) const return value(exec); } -UString JSString::toThisString(ExecState* exec) const -{ - return value(exec); -} - -JSString* JSString::toThisJSString(ExecState*) -{ - return this; -} - inline StringObject* StringObject::create(ExecState* exec, JSString* string) { return new (exec) StringObject(exec->lexicalGlobalObject()->stringObjectStructure(), string); @@ -215,14 +236,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_stringLength), DontEnum | DontDelete | ReadOnly); + descriptor.setDescriptor(jsNumber(m_length), DontEnum | DontDelete | ReadOnly); return true; } bool isStrictUInt32; - unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < m_stringLength) { - descriptor.setDescriptor(jsSingleCharacterSubstring(exec, value(exec), i), DontDelete | ReadOnly); + unsigned i = propertyName.toUInt32(isStrictUInt32); + if (isStrictUInt32 && i < m_length) { + descriptor.setDescriptor(getIndex(exec, i), DontDelete | ReadOnly); return true; } diff --git a/JavaScriptCore/runtime/JSString.h b/JavaScriptCore/runtime/JSString.h index cff8e3a..51b9f2d 100644 --- a/JavaScriptCore/runtime/JSString.h +++ b/JavaScriptCore/runtime/JSString.h @@ -29,6 +29,7 @@ #include "JSNumberCell.h" #include "PropertyDescriptor.h" #include "PropertySlot.h" +#include "RopeImpl.h" namespace JSC { @@ -41,7 +42,6 @@ namespace JSC { JSString* jsSingleCharacterString(JSGlobalData*, UChar); JSString* jsSingleCharacterString(ExecState*, UChar); - JSString* jsSingleCharacterSubstring(JSGlobalData*, const UString&, unsigned offset); JSString* jsSingleCharacterSubstring(ExecState*, const UString&, unsigned offset); JSString* jsSubstring(JSGlobalData*, const UString&, unsigned offset, unsigned length); JSString* jsSubstring(ExecState*, const UString&, unsigned offset, unsigned length); @@ -66,183 +66,205 @@ namespace JSC { public: friend class JIT; friend class JSGlobalData; + friend class SpecializedThunkJIT; + friend struct ThunkHelpers; - // A Rope is a string composed of a set of substrings. - class Rope : public RefCounted<Rope> { + class RopeBuilder { 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() : m_value(0) {} - Fiber(UString::Rep* string) : m_value(reinterpret_cast<intptr_t>(string)) {} - Fiber(Rope* rope) : m_value(reinterpret_cast<intptr_t>(rope) | 1) {} + RopeBuilder(unsigned fiberCount) + : m_index(0) + , m_rope(RopeImpl::tryCreateUninitialized(fiberCount)) + { + } - Fiber(void* nonFiber) : m_value(reinterpret_cast<intptr_t>(nonFiber)) {} + bool isOutOfMemory() { return !m_rope; } - void deref() + void append(RopeImpl::Fiber& fiber) + { + ASSERT(m_rope); + m_rope->initializeFiber(m_index, fiber); + } + void append(const UString& string) + { + ASSERT(m_rope); + m_rope->initializeFiber(m_index, string.impl()); + } + void append(JSString* jsString) + { + if (jsString->isRope()) { + for (unsigned i = 0; i < jsString->m_fiberCount; ++i) + append(jsString->m_other.m_fibers[i]); + } else + append(jsString->string()); + } + + PassRefPtr<RopeImpl> release() + { + ASSERT(m_index == m_rope->fiberCount()); + return m_rope.release(); + } + + unsigned length() { return m_rope->length(); } + + private: + unsigned m_index; + RefPtr<RopeImpl> m_rope; + }; + + class RopeIterator { + public: + RopeIterator() { } + + RopeIterator(RopeImpl::Fiber* fibers, size_t fiberCount) { - if (isRope()) - rope()->deref(); - else - string()->deref(); + ASSERT(fiberCount); + m_workQueue.append(WorkItem(fibers, fiberCount)); + skipRopes(); } - Fiber& ref() + RopeIterator& operator++() { - if (isString()) - string()->ref(); - else - rope()->ref(); + WorkItem& item = m_workQueue.last(); + ASSERT(!RopeImpl::isRope(item.fibers[item.i])); + if (++item.i == item.fiberCount) + m_workQueue.removeLast(); + skipRopes(); return *this; } - unsigned refAndGetLength() + StringImpl* operator*() { - if (isString()) { - UString::Rep* rep = string(); - return rep->ref()->size(); - } else { - Rope* r = rope(); - r->ref(); - return r->stringLength(); - } + WorkItem& item = m_workQueue.last(); + RopeImpl::Fiber fiber = item.fibers[item.i]; + ASSERT(!RopeImpl::isRope(fiber)); + return static_cast<StringImpl*>(fiber); } - 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); } + bool operator!=(const RopeIterator& other) const + { + return m_workQueue != other.m_workQueue; + } - void* nonFiber() { return reinterpret_cast<void*>(m_value); } private: - intptr_t m_value; - }; + struct WorkItem { + WorkItem(RopeImpl::Fiber* fibers, size_t fiberCount) + : fibers(fibers) + , fiberCount(fiberCount) + , i(0) + { + } - // 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; - } + bool operator!=(const WorkItem& other) const + { + return fibers != other.fibers || fiberCount != other.fiberCount || i != other.i; + } - ~Rope(); - void destructNonRecursive(); + RopeImpl::Fiber* fibers; + size_t fiberCount; + size_t i; + }; - 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()->size(); - } - 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]; } + void skipRopes() + { + if (m_workQueue.isEmpty()) + return; + + while (1) { + WorkItem& item = m_workQueue.last(); + RopeImpl::Fiber fiber = item.fibers[item.i]; + if (!RopeImpl::isRope(fiber)) + break; + RopeImpl* rope = static_cast<RopeImpl*>(fiber); + if (++item.i == item.fiberCount) + m_workQueue.removeLast(); + m_workQueue.append(WorkItem(rope->fibers(), rope->fiberCount())); + } + } - 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]; + Vector<WorkItem, 16> m_workQueue; }; ALWAYS_INLINE JSString(JSGlobalData* globalData, const UString& value) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value.size()) + , m_length(value.length()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { - Heap::heap(this)->reportExtraMemoryCost(value.cost()); + ASSERT(!m_value.isNull()); + Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost()); } enum HasOtherOwnerType { HasOtherOwner }; JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value.size()) + , m_length(value.length()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { + ASSERT(!m_value.isNull()); } - JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType) + JSString(JSGlobalData* globalData, PassRefPtr<StringImpl> value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value->size()) + , m_length(value->length()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { + ASSERT(!m_value.isNull()); } - JSString(JSGlobalData* globalData, PassRefPtr<JSString::Rope> rope) + JSString(JSGlobalData* globalData, PassRefPtr<RopeImpl> rope) : JSCell(globalData->stringStructure.get()) - , m_stringLength(rope->stringLength()) - , m_ropeLength(1) + , m_length(rope->length()) + , m_fiberCount(1) { - m_fibers[0] = rope.releaseRef(); + m_other.m_fibers[0] = rope.leakRef(); } // 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) + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, JSString* s2) : JSCell(globalData->stringStructure.get()) - , m_stringLength(s1->length() + s2->length()) - , m_ropeLength(ropeLength) + , m_length(s1->length() + s2->length()) + , m_fiberCount(fiberCount) { - ASSERT(ropeLength <= s_maxInternalRopeLength); + ASSERT(fiberCount <= s_maxInternalRopeLength); unsigned index = 0; appendStringInConstruct(index, s1); appendStringInConstruct(index, s2); - ASSERT(ropeLength == index); + ASSERT(fiberCount == 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) + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, JSString* s1, const UString& u2) : JSCell(globalData->stringStructure.get()) - , m_stringLength(s1->length() + u2.size()) - , m_ropeLength(ropeLength) + , m_length(s1->length() + u2.length()) + , m_fiberCount(fiberCount) { - ASSERT(ropeLength <= s_maxInternalRopeLength); + ASSERT(fiberCount <= s_maxInternalRopeLength); unsigned index = 0; appendStringInConstruct(index, s1); appendStringInConstruct(index, u2); - ASSERT(ropeLength == index); + ASSERT(fiberCount == 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) + // This should only be called with fiberCount <= 3. + JSString(JSGlobalData* globalData, unsigned fiberCount, const UString& u1, JSString* s2) : JSCell(globalData->stringStructure.get()) - , m_stringLength(u1.size() + s2->length()) - , m_ropeLength(ropeLength) + , m_length(u1.length() + s2->length()) + , m_fiberCount(fiberCount) { - ASSERT(ropeLength <= s_maxInternalRopeLength); + ASSERT(fiberCount <= s_maxInternalRopeLength); unsigned index = 0; appendStringInConstruct(index, u1); appendStringInConstruct(index, s2); - ASSERT(ropeLength == index); + ASSERT(fiberCount == 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 + // This should only be called with fiberCount <= 3 ... which since every + // value must require a fiberCount 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) + , m_length(0) + , m_fiberCount(s_maxInternalRopeLength) { unsigned index = 0; appendValueInConstructAndIncrementLength(exec, index, v1); @@ -251,28 +273,52 @@ namespace JSC { ASSERT(index == s_maxInternalRopeLength); } + // This constructor constructs a new string by concatenating u1 & u2. + JSString(JSGlobalData* globalData, const UString& u1, const UString& u2) + : JSCell(globalData->stringStructure.get()) + , m_length(u1.length() + u2.length()) + , m_fiberCount(2) + { + unsigned index = 0; + appendStringInConstruct(index, u1); + appendStringInConstruct(index, u2); + ASSERT(index <= s_maxInternalRopeLength); + } + + // This constructor constructs a new string by concatenating u1, u2 & u3. + JSString(JSGlobalData* globalData, const UString& u1, const UString& u2, const UString& u3) + : JSCell(globalData->stringStructure.get()) + , m_length(u1.length() + u2.length() + u3.length()) + , m_fiberCount(s_maxInternalRopeLength) + { + unsigned index = 0; + appendStringInConstruct(index, u1); + appendStringInConstruct(index, u2); + appendStringInConstruct(index, u3); + ASSERT(index <= s_maxInternalRopeLength); + } + JSString(JSGlobalData* globalData, const UString& value, JSStringFinalizerCallback finalizer, void* context) : JSCell(globalData->stringStructure.get()) - , m_stringLength(value.size()) + , m_length(value.length()) , m_value(value) - , m_ropeLength(0) + , m_fiberCount(0) { + ASSERT(!m_value.isNull()); // nasty hack because we can't union non-POD types - m_fibers[0] = reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(finalizer)); - m_fibers[1] = context; - Heap::heap(this)->reportExtraMemoryCost(value.cost()); + m_other.m_finalizerCallback = finalizer; + m_other.m_finalizerContext = context; + Heap::heap(this)->reportExtraMemoryCost(value.impl()->cost()); } ~JSString() { ASSERT(vptr() == JSGlobalData::jsStringVPtr); - for (unsigned i = 0; i < m_ropeLength; ++i) - m_fibers[i].deref(); + for (unsigned i = 0; i < m_fiberCount; ++i) + RopeImpl::deref(m_other.m_fibers[i]); - if (!m_ropeLength && m_fibers[0].nonFiber()) { - JSStringFinalizerCallback finalizer = reinterpret_cast<JSStringFinalizerCallback>(m_fibers[0].nonFiber()); - finalizer(this, m_fibers[1].nonFiber()); - } + if (!m_fiberCount && m_other.m_finalizerCallback) + m_other.m_finalizerCallback(this, m_other.m_finalizerContext); } const UString& value(ExecState* exec) const @@ -281,21 +327,23 @@ namespace JSC { resolveRope(exec); return m_value; } - const UString tryGetValue() const + const UString& tryGetValue() const { - // If this is a rope, m_value should be null - - // if this is not a rope, m_value should be non-null. - ASSERT(isRope() == m_value.isNull()); + if (isRope()) + resolveRope(0); return m_value; } - unsigned length() { return m_stringLength; } + unsigned length() { return m_length; } 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 < m_stringLength; } + bool canGetIndex(unsigned i) { return i < m_length; } JSString* getIndex(ExecState*, unsigned); + JSString* getIndexSlowCase(ExecState*, unsigned); + + JSValue replaceCharacter(ExecState*, UChar, const UString& replacement); static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion), AnonymousSlotCount); } @@ -303,7 +351,7 @@ namespace JSC { enum VPtrStealingHackType { VPtrStealingHack }; JSString(VPtrStealingHackType) : JSCell(0) - , m_ropeLength(0) + , m_fiberCount(0) { } @@ -311,14 +359,19 @@ namespace JSC { void appendStringInConstruct(unsigned& index, const UString& string) { - m_fibers[index++] = Rope::Fiber(string.rep()->ref()); + StringImpl* impl = string.impl(); + impl->ref(); + m_other.m_fibers[index++] = impl; } 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(); + for (unsigned i = 0; i < jsString->m_fiberCount; ++i) { + RopeImpl::Fiber fiber = jsString->m_other.m_fibers[i]; + fiber->ref(); + m_other.m_fibers[index++] = fiber; + } } else appendStringInConstruct(index, jsString->string()); } @@ -326,15 +379,17 @@ namespace JSC { 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); + ASSERT(v.asCell()->isString()); + JSString* s = static_cast<JSString*>(v.asCell()); + ASSERT(s->fiberCount() == 1); appendStringInConstruct(index, s); - m_stringLength += s->length(); + m_length += s->length(); } else { UString u(v.toString(exec)); - m_fibers[index++] = Rope::Fiber(u.rep()->ref()); - m_stringLength += u.size(); + StringImpl* impl = u.impl(); + impl->ref(); + m_other.m_fibers[index++] = impl; + m_length += u.length(); } } @@ -346,8 +401,6 @@ namespace JSC { virtual UString toString(ExecState*) const; virtual JSObject* toThisObject(ExecState*) const; - virtual UString toThisString(ExecState*) const; - virtual JSString* toThisJSString(ExecState*); // Actually getPropertySlot, not getOwnPropertySlot (see JSCell). virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); @@ -356,21 +409,31 @@ namespace JSC { static const unsigned s_maxInternalRopeLength = 3; - // A string is represented either by a UString or a Rope. - unsigned m_stringLength; + // A string is represented either by a UString or a RopeImpl. + unsigned m_length; mutable UString m_value; - mutable unsigned m_ropeLength; - mutable Rope::Fiber m_fibers[s_maxInternalRopeLength]; + mutable unsigned m_fiberCount; + // This structure exists to support a temporary workaround for a GC issue. + struct JSStringFinalizerStruct { + JSStringFinalizerStruct() : m_finalizerCallback(0) {} + union { + mutable FixedArray<RopeImpl::Fiber, s_maxInternalRopeLength> m_fibers; + struct { + JSStringFinalizerCallback m_finalizerCallback; + void* m_finalizerContext; + }; + }; + } m_other; - bool isRope() const { return m_ropeLength; } + bool isRope() const { return m_fiberCount; } UString& string() { ASSERT(!isRope()); return m_value; } - unsigned ropeLength() { return m_ropeLength ? m_ropeLength : 1; } + unsigned fiberCount() { return m_fiberCount ? m_fiberCount : 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); - friend JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args); + friend JSValue jsString(ExecState* exec, JSValue thisValue); friend JSString* jsStringWithFinalizer(ExecState*, const UString&, JSStringFinalizerCallback callback, void* context); }; @@ -388,8 +451,8 @@ namespace JSC { inline JSString* asString(JSValue value) { - ASSERT(asCell(value)->isString()); - return static_cast<JSString*>(asCell(value)); + ASSERT(value.asCell()->isString()); + return static_cast<JSString*>(value.asCell()); } inline JSString* jsEmptyString(JSGlobalData* globalData) @@ -404,13 +467,14 @@ namespace JSC { return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(&c, 1))); } - inline JSString* jsSingleCharacterSubstring(JSGlobalData* globalData, const UString& s, unsigned offset) + inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { - ASSERT(offset < static_cast<unsigned>(s.size())); - UChar c = s.data()[offset]; + JSGlobalData* globalData = &exec->globalData(); + ASSERT(offset < static_cast<unsigned>(s.length())); + UChar c = s.characters()[offset]; if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); - return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1)))); + return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, 1)))); } inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s) @@ -423,23 +487,26 @@ namespace JSC { inline JSString* jsNontrivialString(JSGlobalData* globalData, const UString& s) { - ASSERT(s.size() > 1); + ASSERT(s.length() > 1); return fixupVPtr(globalData, new (globalData) JSString(globalData, s)); } inline JSString* JSString::getIndex(ExecState* exec, unsigned i) { ASSERT(canGetIndex(i)); - return jsSingleCharacterSubstring(&exec->globalData(), value(exec), i); + if (isRope()) + return getIndexSlowCase(exec, i); + ASSERT(i < m_value.length()); + return jsSingleCharacterSubstring(exec, m_value, i); } inline JSString* jsString(JSGlobalData* globalData, const UString& s) { - int size = s.size(); + int size = s.length(); if (!size) return globalData->smallStrings.emptyString(globalData); if (size == 1) { - UChar c = s.data()[0]; + UChar c = s.characters()[0]; if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); } @@ -448,33 +515,33 @@ namespace JSC { inline JSString* jsStringWithFinalizer(ExecState* exec, const UString& s, JSStringFinalizerCallback callback, void* context) { - ASSERT(s.size() && (s.size() > 1 || s.data()[0] > 0xFF)); + ASSERT(s.length() && (s.length() > 1 || s.characters()[0] > 0xFF)); JSGlobalData* globalData = &exec->globalData(); return fixupVPtr(globalData, new (globalData) JSString(globalData, s, callback, context)); } inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length) { - ASSERT(offset <= static_cast<unsigned>(s.size())); - ASSERT(length <= static_cast<unsigned>(s.size())); - ASSERT(offset + length <= static_cast<unsigned>(s.size())); + ASSERT(offset <= static_cast<unsigned>(s.length())); + ASSERT(length <= static_cast<unsigned>(s.length())); + ASSERT(offset + length <= static_cast<unsigned>(s.length())); if (!length) return globalData->smallStrings.emptyString(globalData); if (length == 1) { - UChar c = s.data()[offset]; + UChar c = s.characters()[offset]; if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); } - return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length)), JSString::HasOtherOwner)); + return fixupVPtr(globalData, new (globalData) JSString(globalData, UString(StringImpl::create(s.impl(), offset, length)), JSString::HasOtherOwner)); } inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s) { - int size = s.size(); + int size = s.length(); if (!size) return globalData->smallStrings.emptyString(globalData); if (size == 1) { - UChar c = s.data()[0]; + UChar c = s.characters()[0]; if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); } @@ -484,7 +551,6 @@ namespace JSC { inline JSString* jsEmptyString(ExecState* exec) { return jsEmptyString(&exec->globalData()); } inline JSString* jsString(ExecState* exec, const UString& s) { return jsString(&exec->globalData(), s); } inline JSString* jsSingleCharacterString(ExecState* exec, UChar c) { return jsSingleCharacterString(&exec->globalData(), c); } - inline JSString* jsSingleCharacterSubstring(ExecState* exec, const UString& s, unsigned offset) { return jsSingleCharacterSubstring(&exec->globalData(), s, offset); } inline JSString* jsSubstring(ExecState* exec, const UString& s, unsigned offset, unsigned length) { return jsSubstring(&exec->globalData(), s, offset, length); } inline JSString* jsNontrivialString(ExecState* exec, const UString& s) { return jsNontrivialString(&exec->globalData(), s); } inline JSString* jsNontrivialString(ExecState* exec, const char* s) { return jsNontrivialString(&exec->globalData(), s); } @@ -493,14 +559,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_stringLength)); + slot.setValue(jsNumber(m_length)); return true; } bool isStrictUInt32; - unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < m_stringLength) { - slot.setValue(jsSingleCharacterSubstring(exec, value(exec), i)); + unsigned i = propertyName.toUInt32(isStrictUInt32); + if (isStrictUInt32 && i < m_length) { + slot.setValue(getIndex(exec, i)); return true; } @@ -509,8 +575,8 @@ namespace JSC { ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { - if (propertyName < m_stringLength) { - slot.setValue(jsSingleCharacterSubstring(exec, value(exec), propertyName)); + if (propertyName < m_length) { + slot.setValue(getIndex(exec, propertyName)); return true; } @@ -521,11 +587,6 @@ namespace JSC { // --- JSValue inlines ---------------------------- - inline JSString* JSValue::toThisJSString(ExecState* exec) - { - return isCell() ? asCell()->toThisJSString(exec) : jsString(exec, toString(exec)); - } - inline UString JSValue::toString(ExecState* exec) const { if (isString()) diff --git a/JavaScriptCore/runtime/JSStringBuilder.h b/JavaScriptCore/runtime/JSStringBuilder.h index 2b11736..49d4a63 100644 --- a/JavaScriptCore/runtime/JSStringBuilder.h +++ b/JavaScriptCore/runtime/JSStringBuilder.h @@ -28,37 +28,66 @@ #include "ExceptionHelpers.h" #include "JSString.h" -#include "StringBuilder.h" +#include "UStringConcatenate.h" +#include "Vector.h" namespace JSC { -class JSStringBuilder : public StringBuilder { +class JSStringBuilder { public: + JSStringBuilder() + : m_okay(true) + { + } + + void append(const UChar u) + { + m_okay &= buffer.tryAppend(&u, 1); + } + + void append(const char* str) + { + append(str, strlen(str)); + } + + void append(const char* str, size_t len) + { + m_okay &= buffer.tryReserveCapacity(buffer.size() + len); + for (size_t i = 0; i < len; i++) { + UChar u = static_cast<unsigned char>(str[i]); + m_okay &= buffer.tryAppend(&u, 1); + } + } + + void append(const UChar* str, size_t len) + { + m_okay &= buffer.tryAppend(str, len); + } + + void append(const UString& str) + { + m_okay &= buffer.tryAppend(str.characters(), str.length()); + } + JSValue build(ExecState* exec) { + if (!m_okay) + return throwOutOfMemoryError(exec); buffer.shrinkToFit(); if (!buffer.data()) return throwOutOfMemoryError(exec); return jsString(exec, UString::adopt(buffer)); } -private: - // Make attempts to call this compile error - if you only wanted a UString, - // Why didn't you just use a StringBuilder?! (This may change, maybe at some - // point in the future we'll need to start building a string not knowing whether - // we'll want a UString or a JSValue - but until we have this requirement, - // block this). - UString build() - { - ASSERT_NOT_REACHED(); - return StringBuilder::build(); - } +protected: + Vector<UChar, 64> buffer; + bool m_okay; }; template<typename StringType1, typename StringType2> inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2) { - PassRefPtr<UStringImpl> result = tryMakeString(string1, string2); + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2); if (!result) return throwOutOfMemoryError(exec); return jsNontrivialString(exec, result); @@ -67,7 +96,7 @@ inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, Stri template<typename StringType1, typename StringType2, typename StringType3> inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3) { - PassRefPtr<UStringImpl> result = tryMakeString(string1, string2, string3); + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3); if (!result) return throwOutOfMemoryError(exec); return jsNontrivialString(exec, result); @@ -76,7 +105,7 @@ inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, Stri template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) { - PassRefPtr<UStringImpl> result = tryMakeString(string1, string2, string3, string4); + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3, string4); if (!result) return throwOutOfMemoryError(exec); return jsNontrivialString(exec, result); @@ -85,7 +114,7 @@ inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, Stri template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) { - PassRefPtr<UStringImpl> result = tryMakeString(string1, string2, string3, string4, string5); + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3, string4, string5); if (!result) return throwOutOfMemoryError(exec); return jsNontrivialString(exec, result); @@ -94,7 +123,7 @@ inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, Stri template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> inline JSValue jsMakeNontrivialString(ExecState* exec, StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) { - PassRefPtr<UStringImpl> result = tryMakeString(string1, string2, string3, string4, string5, string6); + PassRefPtr<StringImpl> result = WTF::tryMakeString(string1, string2, string3, string4, string5, string6); if (!result) return throwOutOfMemoryError(exec); return jsNontrivialString(exec, result); diff --git a/JavaScriptCore/runtime/JSTypeInfo.h b/JavaScriptCore/runtime/JSTypeInfo.h index 7c89600..e225bc7 100644 --- a/JavaScriptCore/runtime/JSTypeInfo.h +++ b/JavaScriptCore/runtime/JSTypeInfo.h @@ -50,6 +50,8 @@ namespace JSC { TypeInfo(JSType type, unsigned flags = 0) : m_type(type) { + ASSERT(flags <= 0xFF); + ASSERT(type <= 0xFF); // ImplementsDefaultHasInstance means (ImplementsHasInstance & !OverridesHasInstance) if ((flags & (ImplementsHasInstance | OverridesHasInstance)) == ImplementsHasInstance) m_flags = flags | ImplementsDefaultHasInstance; @@ -57,7 +59,7 @@ namespace JSC { m_flags = flags; } - JSType type() const { return m_type; } + JSType type() const { return (JSType)m_type; } bool masqueradesAsUndefined() const { return m_flags & MasqueradesAsUndefined; } bool implementsHasInstance() const { return m_flags & ImplementsHasInstance; } @@ -69,8 +71,8 @@ namespace JSC { unsigned flags() const { return m_flags; } private: - JSType m_type; - unsigned m_flags; + unsigned char m_type; + unsigned char m_flags; }; } diff --git a/JavaScriptCore/runtime/JSValue.cpp b/JavaScriptCore/runtime/JSValue.cpp index 502312c..f4662db 100644 --- a/JavaScriptCore/runtime/JSValue.cpp +++ b/JavaScriptCore/runtime/JSValue.cpp @@ -25,6 +25,7 @@ #include "BooleanConstructor.h" #include "BooleanPrototype.h" +#include "Error.h" #include "ExceptionHelpers.h" #include "JSGlobalObject.h" #include "JSFunction.h" @@ -61,10 +62,10 @@ JSObject* JSValue::toObjectSlowCase(ExecState* exec) const return constructNumber(exec, asValue()); if (isTrue() || isFalse()) return constructBooleanFromImmediateBoolean(exec, asValue()); + ASSERT(isUndefinedOrNull()); - JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, isNull()); - exec->setException(exception); - return new (exec) JSNotAnObject(exec, exception); + throwError(exec, createNotAnObjectError(exec, *this)); + return new (exec) JSNotAnObject(exec); } JSObject* JSValue::toThisObjectSlowCase(ExecState* exec) const @@ -86,10 +87,10 @@ JSObject* JSValue::synthesizeObject(ExecState* exec) const return constructNumber(exec, asValue()); if (isBoolean()) return constructBooleanFromImmediateBoolean(exec, asValue()); - - JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, isNull()); - exec->setException(exception); - return new (exec) JSNotAnObject(exec, exception); + + ASSERT(isUndefinedOrNull()); + throwError(exec, createNotAnObjectError(exec, *this)); + return new (exec) JSNotAnObject(exec); } JSObject* JSValue::synthesizePrototype(ExecState* exec) const @@ -100,9 +101,9 @@ JSObject* JSValue::synthesizePrototype(ExecState* exec) const if (isBoolean()) return exec->lexicalGlobalObject()->booleanPrototype(); - JSNotAnObjectErrorStub* exception = createNotAnObjectErrorStub(exec, isNull()); - exec->setException(exception); - return new (exec) JSNotAnObject(exec, exception); + ASSERT(isUndefinedOrNull()); + throwError(exec, createNotAnObjectError(exec, *this)); + return new (exec) JSNotAnObject(exec); } #ifndef NDEBUG @@ -125,51 +126,58 @@ char* JSValue::description() snprintf(description, size, "False"); else if (isNull()) snprintf(description, size, "Null"); - else { - ASSERT(isUndefined()); + else if (isUndefined()) snprintf(description, size, "Undefined"); - } + else + snprintf(description, size, "INVALID"); return description; } #endif -int32_t toInt32SlowCase(double d, bool& ok) +// This in the ToInt32 operation is defined in section 9.5 of the ECMA-262 spec. +// Note that this operation is identical to ToUInt32 other than to interpretation +// of the resulting bit-pattern (as such this metod is also called to implement +// ToUInt32). +// +// The operation can be descibed as round towards zero, then select the 32 least +// bits of the resulting value in 2s-complement representation. +int32_t toInt32(double number) { - ok = true; - - if (d >= -D32 / 2 && d < D32 / 2) - return static_cast<int32_t>(d); - - if (isnan(d) || isinf(d)) { - ok = false; + int64_t bits = WTF::bitwise_cast<int64_t>(number); + int32_t exp = (static_cast<int32_t>(bits >> 52) & 0x7ff) - 0x3ff; + + // If exponent < 0 there will be no bits to the left of the decimal point + // after rounding; if the exponent is > 83 then no bits of precision can be + // left in the low 32-bit range of the result (IEEE-754 doubles have 52 bits + // of fractional precision). + // Note this case handles 0, -0, and all infinte, NaN, & denormal value. + if (exp < 0 || exp > 83) return 0; - } - double d32 = fmod(trunc(d), D32); - if (d32 >= D32 / 2) - d32 -= D32; - else if (d32 < -D32 / 2) - d32 += D32; - return static_cast<int32_t>(d32); -} - -uint32_t toUInt32SlowCase(double d, bool& ok) -{ - ok = true; - - if (d >= 0.0 && d < D32) - return static_cast<uint32_t>(d); - - if (isnan(d) || isinf(d)) { - ok = false; - return 0; + // Select the appropriate 32-bits from the floating point mantissa. If the + // exponent is 52 then the bits we need to select are already aligned to the + // lowest bits of the 64-bit integer representation of tghe number, no need + // to shift. If the exponent is greater than 52 we need to shift the value + // left by (exp - 52), if the value is less than 52 we need to shift right + // accordingly. + int32_t result = (exp > 52) + ? static_cast<int32_t>(bits << (exp - 52)) + : static_cast<int32_t>(bits >> (52 - exp)); + + // IEEE-754 double precision values are stored omitting an implicit 1 before + // the decimal point; we need to reinsert this now. We may also the shifted + // invalid bits into the result that are not a part of the mantissa (the sign + // and exponent bits from the floatingpoint representation); mask these out. + if (exp < 32) { + int32_t missingOne = 1 << exp; + result &= missingOne - 1; + result += missingOne; } - double d32 = fmod(trunc(d), D32); - if (d32 < 0) - d32 += D32; - return static_cast<uint32_t>(d32); + // If the input value was negative (we could test either 'number' or 'bits', + // but testing 'bits' is likely faster) invert the result appropriately. + return bits < 0 ? -result : result; } NEVER_INLINE double nonInlineNaN() @@ -181,4 +189,9 @@ NEVER_INLINE double nonInlineNaN() #endif } +bool JSValue::isValidCallee() +{ + return asObject(asObject(asCell())->getAnonymousValue(0))->isGlobalObject(); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/JSValue.h b/JavaScriptCore/runtime/JSValue.h index 6da921f..cad9662 100644 --- a/JavaScriptCore/runtime/JSValue.h +++ b/JavaScriptCore/runtime/JSValue.h @@ -23,8 +23,6 @@ #ifndef JSValue_h #define JSValue_h -#include "CallData.h" -#include "ConstructData.h" #include <math.h> #include <stddef.h> // for size_t #include <stdint.h> @@ -35,6 +33,7 @@ namespace JSC { + class ExecState; class Identifier; class JSCell; class JSGlobalData; @@ -57,8 +56,17 @@ namespace JSC { #endif double nonInlineNaN(); - int32_t toInt32SlowCase(double, bool& ok); - uint32_t toUInt32SlowCase(double, bool& ok); + + // This implements ToInt32, defined in ECMA-262 9.5. + int32_t toInt32(double); + + // This implements ToUInt32, defined in ECMA-262 9.6. + inline uint32_t toUInt32(double number) + { + // As commented in the spec, the operation of ToInt32 and ToUint32 only differ + // in how the result is interpreted; see NOTEs in sections 9.5 and 9.6. + return toInt32(number); + } class JSValue { friend class JSImmediate; @@ -66,11 +74,13 @@ namespace JSC { friend class JIT; friend class JITStubs; friend class JITStubCall; + friend class JSInterfaceJIT; + friend class SpecializedThunkJIT; public: static EncodedJSValue encode(JSValue value); static JSValue decode(EncodedJSValue ptr); -#if !USE(JSVALUE32_64) +#if USE(JSVALUE64) private: static JSValue makeImmediate(intptr_t value); intptr_t immediateValue(); @@ -91,21 +101,18 @@ namespace JSC { JSValue(const JSCell* ptr); // Numbers - JSValue(EncodeAsDoubleTag, ExecState*, double); - JSValue(ExecState*, double); - JSValue(ExecState*, char); - JSValue(ExecState*, unsigned char); - JSValue(ExecState*, short); - JSValue(ExecState*, unsigned short); - JSValue(ExecState*, int); - JSValue(ExecState*, unsigned); - JSValue(ExecState*, long); - JSValue(ExecState*, unsigned long); - JSValue(ExecState*, long long); - JSValue(ExecState*, unsigned long long); - JSValue(JSGlobalData*, double); - JSValue(JSGlobalData*, int); - JSValue(JSGlobalData*, unsigned); + JSValue(EncodeAsDoubleTag, double); + explicit JSValue(double); + explicit JSValue(char); + explicit JSValue(unsigned char); + explicit JSValue(short); + explicit JSValue(unsigned short); + explicit JSValue(int); + explicit JSValue(unsigned); + explicit JSValue(long); + explicit JSValue(unsigned long); + explicit JSValue(long long); + explicit JSValue(unsigned long long); operator bool() const; bool operator==(const JSValue& other) const; @@ -141,9 +148,6 @@ namespace JSC { UString getString(ExecState* exec) const; // null string if not a string JSObject* getObject() const; // 0 if not an object - CallType getCallData(CallData&); - ConstructType getConstructData(ConstructData&); - // Extracting integer values. bool getUInt32(uint32_t&) const; @@ -165,9 +169,7 @@ namespace JSC { double toInteger(ExecState*) const; double toIntegerPreserveNaN(ExecState*) const; int32_t toInt32(ExecState*) const; - int32_t toInt32(ExecState*, bool& ok) const; uint32_t toUInt32(ExecState*) const; - uint32_t toUInt32(ExecState*, bool& ok) const; #if ENABLE(JSC_ZOMBIES) bool isZombie() const; @@ -183,12 +185,14 @@ namespace JSC { JSValue get(ExecState*, unsigned propertyName) const; JSValue get(ExecState*, unsigned propertyName, PropertySlot&) const; void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + void putDirect(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); void put(ExecState*, unsigned propertyName, JSValue); bool needsThisConversion() const; JSObject* toThisObject(ExecState*) const; + JSValue toStrictThisObject(ExecState*) const; UString toThisString(ExecState*) const; - JSString* toThisJSString(ExecState*); + JSString* toThisJSString(ExecState*) const; static bool equal(ExecState* exec, JSValue v1, JSValue v2); static bool equalSlowCase(ExecState* exec, JSValue v1, JSValue v2); @@ -201,6 +205,7 @@ namespace JSC { bool isCell() const; JSCell* asCell() const; + bool isValidCallee(); #ifndef NDEBUG char* description(); @@ -214,24 +219,24 @@ namespace JSC { JSObject* toObjectSlowCase(ExecState*) const; JSObject* toThisObjectSlowCase(ExecState*) const; - enum { Int32Tag = 0xffffffff }; - enum { CellTag = 0xfffffffe }; - enum { TrueTag = 0xfffffffd }; - enum { FalseTag = 0xfffffffc }; - enum { NullTag = 0xfffffffb }; - enum { UndefinedTag = 0xfffffffa }; + JSObject* synthesizePrototype(ExecState*) const; + JSObject* synthesizeObject(ExecState*) const; + +#if USE(JSVALUE32_64) + enum { NullTag = 0xffffffff }; + enum { UndefinedTag = 0xfffffffe }; + enum { Int32Tag = 0xfffffffd }; + enum { CellTag = 0xfffffffc }; + enum { TrueTag = 0xfffffffb }; + enum { FalseTag = 0xfffffffa }; enum { EmptyValueTag = 0xfffffff9 }; enum { DeletedValueTag = 0xfffffff8 }; - + enum { LowestTag = DeletedValueTag }; - + uint32_t tag() const; int32_t payload() const; - JSObject* synthesizePrototype(ExecState*) const; - JSObject* synthesizeObject(ExecState*) const; - -#if USE(JSVALUE32_64) union { EncodedJSValue asEncodedJSValue; double asDouble; @@ -286,79 +291,64 @@ namespace JSC { return b ? JSValue(JSValue::JSTrue) : JSValue(JSValue::JSFalse); } - ALWAYS_INLINE JSValue jsDoubleNumber(ExecState* exec, double d) + ALWAYS_INLINE JSValue jsDoubleNumber(double d) { - return JSValue(JSValue::EncodeAsDouble, exec, d); + return JSValue(JSValue::EncodeAsDouble, d); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, double d) + ALWAYS_INLINE JSValue jsNumber(double d) { - return JSValue(exec, d); + return JSValue(d); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, char i) + ALWAYS_INLINE JSValue jsNumber(char i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, unsigned char i) + ALWAYS_INLINE JSValue jsNumber(unsigned char i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, short i) + ALWAYS_INLINE JSValue jsNumber(short i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, unsigned short i) + ALWAYS_INLINE JSValue jsNumber(unsigned short i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, int i) + ALWAYS_INLINE JSValue jsNumber(int i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, unsigned i) + ALWAYS_INLINE JSValue jsNumber(unsigned i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, long i) + ALWAYS_INLINE JSValue jsNumber(long i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, unsigned long i) + ALWAYS_INLINE JSValue jsNumber(unsigned long i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, long long i) + ALWAYS_INLINE JSValue jsNumber(long long i) { - return JSValue(exec, i); + return JSValue(i); } - ALWAYS_INLINE JSValue jsNumber(ExecState* exec, unsigned long long i) + ALWAYS_INLINE JSValue jsNumber(unsigned long long i) { - return JSValue(exec, i); - } - - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, double d) - { - return JSValue(globalData, d); - } - - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, int i) - { - return JSValue(globalData, i); - } - - ALWAYS_INLINE JSValue jsNumber(JSGlobalData* globalData, unsigned i) - { - return JSValue(globalData, i); + return JSValue(i); } inline bool operator==(const JSValue a, const JSCell* b) { return a == JSValue(b); } @@ -367,70 +357,23 @@ namespace JSC { inline bool operator!=(const JSValue a, const JSCell* b) { return a != JSValue(b); } inline bool operator!=(const JSCell* a, const JSValue b) { return JSValue(a) != b; } - inline int32_t toInt32(double val) - { - if (!(val >= -2147483648.0 && val < 2147483648.0)) { - bool ignored; - return toInt32SlowCase(val, ignored); - } - return static_cast<int32_t>(val); - } - - inline uint32_t toUInt32(double val) - { - if (!(val >= 0.0 && val < 4294967296.0)) { - bool ignored; - return toUInt32SlowCase(val, ignored); - } - return static_cast<uint32_t>(val); - } - - // FIXME: We should deprecate this and just use JSValue::asCell() instead. - JSCell* asCell(JSValue); - - inline JSCell* asCell(JSValue value) - { - return value.asCell(); - } - ALWAYS_INLINE int32_t JSValue::toInt32(ExecState* exec) const { if (isInt32()) return asInt32(); - bool ignored; - return toInt32SlowCase(toNumber(exec), ignored); + return JSC::toInt32(toNumber(exec)); } inline uint32_t JSValue::toUInt32(ExecState* exec) const { - if (isUInt32()) - return asInt32(); - bool ignored; - return toUInt32SlowCase(toNumber(exec), ignored); - } - - inline int32_t JSValue::toInt32(ExecState* exec, bool& ok) const - { - if (isInt32()) { - ok = true; - return asInt32(); - } - return toInt32SlowCase(toNumber(exec), ok); - } - - inline uint32_t JSValue::toUInt32(ExecState* exec, bool& ok) const - { - if (isUInt32()) { - ok = true; - return asInt32(); - } - return toUInt32SlowCase(toNumber(exec), ok); + // See comment on JSC::toUInt32, above. + return toInt32(exec); } #if USE(JSVALUE32_64) - inline JSValue jsNaN(ExecState* exec) + inline JSValue jsNaN() { - return JSValue(exec, nonInlineNaN()); + return JSValue(nonInlineNaN()); } // JSValue member functions. @@ -604,115 +547,90 @@ namespace JSC { return reinterpret_cast<JSCell*>(u.asBits.payload); } - ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState*, double d) + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d) { u.asDouble = d; } - inline JSValue::JSValue(ExecState* exec, double d) + inline JSValue::JSValue(double d) { const int32_t asInt32 = static_cast<int32_t>(d); if (asInt32 != d || (!asInt32 && signbit(d))) { // true for -0.0 u.asDouble = d; return; } - *this = JSValue(exec, static_cast<int32_t>(d)); + *this = JSValue(static_cast<int32_t>(d)); } - inline JSValue::JSValue(ExecState* exec, char i) + inline JSValue::JSValue(char i) { - *this = JSValue(exec, static_cast<int32_t>(i)); + *this = JSValue(static_cast<int32_t>(i)); } - inline JSValue::JSValue(ExecState* exec, unsigned char i) + inline JSValue::JSValue(unsigned char i) { - *this = JSValue(exec, static_cast<int32_t>(i)); + *this = JSValue(static_cast<int32_t>(i)); } - inline JSValue::JSValue(ExecState* exec, short i) + inline JSValue::JSValue(short i) { - *this = JSValue(exec, static_cast<int32_t>(i)); + *this = JSValue(static_cast<int32_t>(i)); } - inline JSValue::JSValue(ExecState* exec, unsigned short i) + inline JSValue::JSValue(unsigned short i) { - *this = JSValue(exec, static_cast<int32_t>(i)); + *this = JSValue(static_cast<int32_t>(i)); } - inline JSValue::JSValue(ExecState*, int i) + inline JSValue::JSValue(int i) { u.asBits.tag = Int32Tag; u.asBits.payload = i; } - inline JSValue::JSValue(ExecState* exec, unsigned i) + inline JSValue::JSValue(unsigned i) { if (static_cast<int32_t>(i) < 0) { - *this = JSValue(exec, static_cast<double>(i)); + *this = JSValue(static_cast<double>(i)); return; } - *this = JSValue(exec, static_cast<int32_t>(i)); + *this = JSValue(static_cast<int32_t>(i)); } - inline JSValue::JSValue(ExecState* exec, long i) + inline JSValue::JSValue(long i) { if (static_cast<int32_t>(i) != i) { - *this = JSValue(exec, static_cast<double>(i)); + *this = JSValue(static_cast<double>(i)); return; } - *this = JSValue(exec, static_cast<int32_t>(i)); + *this = JSValue(static_cast<int32_t>(i)); } - inline JSValue::JSValue(ExecState* exec, unsigned long i) + inline JSValue::JSValue(unsigned long i) { if (static_cast<uint32_t>(i) != i) { - *this = JSValue(exec, static_cast<double>(i)); + *this = JSValue(static_cast<double>(i)); return; } - *this = JSValue(exec, static_cast<uint32_t>(i)); + *this = JSValue(static_cast<uint32_t>(i)); } - inline JSValue::JSValue(ExecState* exec, long long i) + inline JSValue::JSValue(long long i) { if (static_cast<int32_t>(i) != i) { - *this = JSValue(exec, static_cast<double>(i)); + *this = JSValue(static_cast<double>(i)); return; } - *this = JSValue(exec, static_cast<int32_t>(i)); + *this = JSValue(static_cast<int32_t>(i)); } - inline JSValue::JSValue(ExecState* exec, unsigned long long i) + inline JSValue::JSValue(unsigned long long i) { if (static_cast<uint32_t>(i) != i) { - *this = JSValue(exec, static_cast<double>(i)); - return; - } - *this = JSValue(exec, static_cast<uint32_t>(i)); - } - - inline JSValue::JSValue(JSGlobalData* globalData, double d) - { - const int32_t asInt32 = static_cast<int32_t>(d); - if (asInt32 != d || (!asInt32 && signbit(d))) { // true for -0.0 - u.asDouble = d; - return; - } - *this = JSValue(globalData, static_cast<int32_t>(d)); - } - - inline JSValue::JSValue(JSGlobalData*, int i) - { - u.asBits.tag = Int32Tag; - u.asBits.payload = i; - } - - inline JSValue::JSValue(JSGlobalData* globalData, unsigned i) - { - if (static_cast<int32_t>(i) < 0) { - *this = JSValue(globalData, static_cast<double>(i)); + *this = JSValue(static_cast<double>(i)); return; } - *this = JSValue(globalData, static_cast<int32_t>(i)); + *this = JSValue(static_cast<uint32_t>(i)); } inline bool JSValue::isNumber() const @@ -753,7 +671,7 @@ namespace JSC { ALWAYS_INLINE JSValue JSValue::toJSNumber(ExecState* exec) const { - return isNumber() ? asValue() : jsNumber(exec, this->toNumber(exec)); + return isNumber() ? asValue() : jsNumber(this->toNumber(exec)); } inline bool JSValue::getNumber(double& result) const diff --git a/JavaScriptCore/runtime/JSVariableObject.cpp b/JavaScriptCore/runtime/JSVariableObject.cpp index 7365001..81d05ba 100644 --- a/JavaScriptCore/runtime/JSVariableObject.cpp +++ b/JavaScriptCore/runtime/JSVariableObject.cpp @@ -36,7 +36,7 @@ namespace JSC { bool JSVariableObject::deleteProperty(ExecState* exec, const Identifier& propertyName) { - if (symbolTable().contains(propertyName.ustring().rep())) + if (symbolTable().contains(propertyName.impl())) return false; return JSObject::deleteProperty(exec, propertyName); @@ -60,7 +60,7 @@ bool JSVariableObject::isVariableObject() const bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertyDescriptor& descriptor) { - SymbolTableEntry entry = symbolTable().inlineGet(propertyName.ustring().rep()); + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (!entry.isNull()) { descriptor.setDescriptor(registerAt(entry.getIndex()).jsValue(), entry.getAttributes() | DontDelete); return true; diff --git a/JavaScriptCore/runtime/JSVariableObject.h b/JavaScriptCore/runtime/JSVariableObject.h index 6c679ce..3f2e218 100644 --- a/JavaScriptCore/runtime/JSVariableObject.h +++ b/JavaScriptCore/runtime/JSVariableObject.h @@ -52,7 +52,7 @@ namespace JSC { virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); virtual bool isVariableObject() const; - virtual bool isDynamicScope() const = 0; + virtual bool isDynamicScope(bool& requiresDynamicChecks) const = 0; Register& registerAt(int index) const { return d->registers[index]; } @@ -103,7 +103,7 @@ namespace JSC { inline bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertySlot& slot) { - SymbolTableEntry entry = symbolTable().inlineGet(propertyName.ustring().rep()); + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (!entry.isNull()) { slot.setRegisterSlot(®isterAt(entry.getIndex())); return true; @@ -113,7 +113,7 @@ namespace JSC { inline bool JSVariableObject::symbolTableGet(const Identifier& propertyName, PropertySlot& slot, bool& slotIsWriteable) { - SymbolTableEntry entry = symbolTable().inlineGet(propertyName.ustring().rep()); + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (!entry.isNull()) { slot.setRegisterSlot(®isterAt(entry.getIndex())); slotIsWriteable = !entry.isReadOnly(); @@ -126,7 +126,7 @@ namespace JSC { { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - SymbolTableEntry entry = symbolTable().inlineGet(propertyName.ustring().rep()); + SymbolTableEntry entry = symbolTable().inlineGet(propertyName.impl()); if (entry.isNull()) return false; if (entry.isReadOnly()) @@ -139,7 +139,7 @@ namespace JSC { { ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - SymbolTable::iterator iter = symbolTable().find(propertyName.ustring().rep()); + SymbolTable::iterator iter = symbolTable().find(propertyName.impl()); if (iter == symbolTable().end()) return false; SymbolTableEntry& entry = iter->second; diff --git a/JavaScriptCore/runtime/JSZombie.cpp b/JavaScriptCore/runtime/JSZombie.cpp index 072d29b..8a36bda 100644 --- a/JavaScriptCore/runtime/JSZombie.cpp +++ b/JavaScriptCore/runtime/JSZombie.cpp @@ -37,7 +37,7 @@ Structure* JSZombie::leakedZombieStructure() { static Structure* structure = 0; if (!structure) { Structure::startIgnoringLeaks(); - structure = Structure::create(jsNull(), TypeInfo(UnspecifiedType)).releaseRef(); + structure = Structure::create(jsNull(), TypeInfo(UnspecifiedType), 0).leakRef(); Structure::stopIgnoringLeaks(); } return structure; diff --git a/JavaScriptCore/runtime/JSZombie.h b/JavaScriptCore/runtime/JSZombie.h index 8b33ea6..da45699 100644 --- a/JavaScriptCore/runtime/JSZombie.h +++ b/JavaScriptCore/runtime/JSZombie.h @@ -60,8 +60,7 @@ public: 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 toStrictThisObject(ExecState*) const { ASSERT_NOT_REACHED(); return JSValue(); } 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; } diff --git a/JavaScriptCore/runtime/LiteralParser.cpp b/JavaScriptCore/runtime/LiteralParser.cpp index cc33bae..ed69f4d 100644 --- a/JavaScriptCore/runtime/LiteralParser.cpp +++ b/JavaScriptCore/runtime/LiteralParser.cpp @@ -29,7 +29,7 @@ #include "JSArray.h" #include "JSString.h" #include "Lexer.h" -#include "StringBuilder.h" +#include "UStringBuilder.h" #include <wtf/ASCIICType.h> #include <wtf/dtoa.h> @@ -135,7 +135,7 @@ template <LiteralParser::ParserMode mode> inline LiteralParser::TokenType Litera { ++m_ptr; const UChar* runStart; - StringBuilder builder; + UStringBuilder builder; do { runStart = m_ptr; while (m_ptr < m_end && isSafeStringCharacter<mode>(*m_ptr)) @@ -200,7 +200,7 @@ template <LiteralParser::ParserMode mode> inline LiteralParser::TokenType Litera if (m_ptr >= m_end || *m_ptr != '"') return TokError; - token.stringToken = builder.build(); + token.stringToken = builder.toUString(); token.type = TokString; token.end = ++m_ptr; return TokString; @@ -393,7 +393,7 @@ JSValue LiteralParser::parse(ParserState initialState) case TokNumber: { Lexer::LiteralParserToken numberToken = m_lexer.currentToken(); m_lexer.next(); - lastValue = jsNumber(m_exec, numberToken.numberToken); + lastValue = jsNumber(numberToken.numberToken); break; } case TokNull: diff --git a/JavaScriptCore/runtime/LiteralParser.h b/JavaScriptCore/runtime/LiteralParser.h index 0f8072b..6df5d06 100644 --- a/JavaScriptCore/runtime/LiteralParser.h +++ b/JavaScriptCore/runtime/LiteralParser.h @@ -72,8 +72,8 @@ namespace JSC { Lexer(const UString& s, ParserMode mode) : m_string(s) , m_mode(mode) - , m_ptr(s.data()) - , m_end(s.data() + s.size()) + , m_ptr(s.characters()) + , m_end(s.characters() + s.length()) { } diff --git a/JavaScriptCore/runtime/Lookup.cpp b/JavaScriptCore/runtime/Lookup.cpp index 4e9e086..dac1c94 100644 --- a/JavaScriptCore/runtime/Lookup.cpp +++ b/JavaScriptCore/runtime/Lookup.cpp @@ -20,6 +20,7 @@ #include "config.h" #include "Lookup.h" +#include "Executable.h" #include "JSFunction.h" #include "PrototypeFunction.h" @@ -33,7 +34,7 @@ void HashTable::createTable(JSGlobalData* globalData) const for (int i = 0; i < compactSize; ++i) entries[i].setKey(0); for (int i = 0; values[i].key; ++i) { - UString::Rep* identifier = Identifier::add(globalData, values[i].key).releaseRef(); + StringImpl* identifier = Identifier::add(globalData, values[i].key).leakRef(); int hashIndex = identifier->existingHash() & compactHashSizeMask; HashEntry* entry = &entries[hashIndex]; @@ -46,7 +47,11 @@ void HashTable::createTable(JSGlobalData* globalData) const entry = entry->next(); } - entry->initialize(identifier, values[i].attributes, values[i].value1, values[i].value2); + entry->initialize(identifier, values[i].attributes, values[i].value1, values[i].value2 +#if ENABLE(JIT) + , values[i].generator +#endif + ); } table = entries; } @@ -56,7 +61,7 @@ void HashTable::deleteTable() const if (table) { int max = compactSize; for (int i = 0; i != max; ++i) { - if (UString::Rep* key = table[i].key()) + if (StringImpl* key = table[i].key()) key->deref(); } delete [] table; @@ -66,11 +71,20 @@ void HashTable::deleteTable() const void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) { + ASSERT(thisObj->structure()->anonymousSlotCount() > 0); + ASSERT(thisObj->getAnonymousValue(0).isCell() && asObject(thisObj->getAnonymousValue(0).asCell())->isGlobalObject()); ASSERT(entry->attributes() & Function); JSValue* location = thisObj->getDirectLocation(propertyName); if (!location) { - InternalFunction* function = new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), entry->functionLength(), propertyName, entry->function()); + NativeFunctionWrapper* function; + JSGlobalObject* globalObject = asGlobalObject(thisObj->getAnonymousValue(0).asCell()); +#if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL) + if (entry->generator()) + function = new (exec) NativeFunctionWrapper(exec, globalObject, globalObject->prototypeFunctionStructure(), entry->functionLength(), propertyName, exec->globalData().getHostFunction(entry->function(), entry->generator())); + else +#endif + function = new (exec) NativeFunctionWrapper(exec, globalObject, globalObject->prototypeFunctionStructure(), entry->functionLength(), propertyName, entry->function()); thisObj->putDirectFunction(propertyName, function, entry->attributes()); location = thisObj->getDirectLocation(propertyName); diff --git a/JavaScriptCore/runtime/Lookup.h b/JavaScriptCore/runtime/Lookup.h index e673c09..9bc81d4 100644 --- a/JavaScriptCore/runtime/Lookup.h +++ b/JavaScriptCore/runtime/Lookup.h @@ -37,13 +37,15 @@ #endif namespace JSC { - // Hash table generated by the create_hash_table script. struct HashTableValue { const char* key; // property name unsigned char attributes; // JSObject attributes intptr_t value1; intptr_t value2; +#if ENABLE(JIT) + ThunkGenerator generator; +#endif }; // FIXME: There is no reason this get function can't be simpler. @@ -53,20 +55,30 @@ namespace JSC { class HashEntry : public FastAllocBase { public: - void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2) + void initialize(StringImpl* key, unsigned char attributes, intptr_t v1, intptr_t v2 +#if ENABLE(JIT) + , ThunkGenerator generator = 0 +#endif + ) { m_key = key; m_attributes = attributes; m_u.store.value1 = v1; m_u.store.value2 = v2; +#if ENABLE(JIT) + m_u.function.generator = generator; +#endif m_next = 0; } - void setKey(UString::Rep* key) { m_key = key; } - UString::Rep* key() const { return m_key; } + void setKey(StringImpl* key) { m_key = key; } + StringImpl* key() const { return m_key; } unsigned char attributes() const { return m_attributes; } +#if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL) + ThunkGenerator generator() const { ASSERT(m_attributes & Function); return m_u.function.generator; } +#endif NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; } unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); } @@ -79,7 +91,7 @@ namespace JSC { HashEntry* next() const { return m_next; } private: - UString::Rep* m_key; + StringImpl* m_key; unsigned char m_attributes; // JSObject attributes union { @@ -90,6 +102,9 @@ namespace JSC { struct { NativeFunction functionValue; intptr_t length; // number of arguments for function +#if ENABLE(JIT) + ThunkGenerator generator; +#endif } function; struct { GetFunction get; @@ -144,13 +159,13 @@ namespace JSC { { ASSERT(table); - const HashEntry* entry = &table[identifier.ustring().rep()->existingHash() & compactHashSizeMask]; + const HashEntry* entry = &table[identifier.impl()->existingHash() & compactHashSizeMask]; if (!entry->key()) return 0; do { - if (entry->key() == identifier.ustring().rep()) + if (entry->key() == identifier.impl()) return entry; entry = entry->next(); } while (entry); @@ -181,7 +196,7 @@ namespace JSC { if (entry->attributes() & Function) setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); else - slot.setCustom(thisObj, entry->propertyGetter()); + slot.setCacheableCustom(thisObj, entry->propertyGetter()); return true; } @@ -258,7 +273,7 @@ namespace JSC { ASSERT(!(entry->attributes() & Function)); - slot.setCustom(thisObj, entry->propertyGetter()); + slot.setCacheableCustom(thisObj, entry->propertyGetter()); return true; } diff --git a/JavaScriptCore/runtime/MathObject.cpp b/JavaScriptCore/runtime/MathObject.cpp index 8f22fba..080d7d2 100644 --- a/JavaScriptCore/runtime/MathObject.cpp +++ b/JavaScriptCore/runtime/MathObject.cpp @@ -21,6 +21,7 @@ #include "config.h" #include "MathObject.h" +#include "Lookup.h" #include "ObjectPrototype.h" #include "Operations.h" #include <time.h> @@ -33,24 +34,24 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(MathObject); -static JSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncACos(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncASin(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncATan(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncCos(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncExp(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncLog(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncMax(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncMin(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncPow(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncRound(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncSin(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL mathProtoFuncTan(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncACos(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncASin(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncATan(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncCos(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncExp(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncLog(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncMax(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncMin(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncPow(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncRound(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncSin(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState*); +static EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState*); } @@ -85,17 +86,17 @@ const ClassInfo MathObject::info = { "Math", 0, 0, ExecState::mathTable }; @end */ -MathObject::MathObject(ExecState* exec, NonNullPassRefPtr<Structure> structure) - : JSObject(structure) +MathObject::MathObject(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) + : JSObjectWithGlobalObject(globalObject, structure) { - putDirectWithoutTransition(Identifier(exec, "E"), jsNumber(exec, exp(1.0)), DontDelete | DontEnum | ReadOnly); - putDirectWithoutTransition(Identifier(exec, "LN2"), jsNumber(exec, log(2.0)), DontDelete | DontEnum | ReadOnly); - putDirectWithoutTransition(Identifier(exec, "LN10"), jsNumber(exec, log(10.0)), DontDelete | DontEnum | ReadOnly); - putDirectWithoutTransition(Identifier(exec, "LOG2E"), jsNumber(exec, 1.0 / log(2.0)), DontDelete | DontEnum | ReadOnly); - putDirectWithoutTransition(Identifier(exec, "LOG10E"), jsNumber(exec, 1.0 / log(10.0)), DontDelete | DontEnum | ReadOnly); - 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); + putDirectWithoutTransition(Identifier(exec, "E"), jsNumber(exp(1.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LN2"), jsNumber(log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LN10"), jsNumber(log(10.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LOG2E"), jsNumber(1.0 / log(2.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "LOG10E"), jsNumber(1.0 / log(10.0)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "PI"), jsNumber(piDouble), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "SQRT1_2"), jsNumber(sqrt(0.5)), DontDelete | DontEnum | ReadOnly); + putDirectWithoutTransition(Identifier(exec, "SQRT2"), jsNumber(sqrt(2.0)), DontDelete | DontEnum | ReadOnly); } // ECMA 15.8 @@ -112,62 +113,64 @@ bool MathObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& pro // ------------------------------ Functions -------------------------------- -JSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState* exec) { - return jsNumber(exec, fabs(args.at(0).toNumber(exec))); + return JSValue::encode(jsNumber(fabs(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncACos(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncACos(ExecState* exec) { - return jsDoubleNumber(exec, acos(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(acos(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncASin(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncASin(ExecState* exec) { - return jsDoubleNumber(exec, asin(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(asin(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncATan(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan(ExecState* exec) { - return jsDoubleNumber(exec, atan(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(atan(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState* exec) { - return jsDoubleNumber(exec, atan2(args.at(0).toNumber(exec), args.at(1).toNumber(exec))); + double arg0 = exec->argument(0).toNumber(exec); + double arg1 = exec->argument(1).toNumber(exec); + return JSValue::encode(jsDoubleNumber(atan2(arg0, arg1))); } -JSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec) { - return jsNumber(exec, ceil(args.at(0).toNumber(exec))); + return JSValue::encode(jsNumber(ceil(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncCos(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncCos(ExecState* exec) { - return jsDoubleNumber(exec, cos(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(cos(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncExp(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncExp(ExecState* exec) { - return jsDoubleNumber(exec, exp(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(exp(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec) { - return jsNumber(exec, floor(args.at(0).toNumber(exec))); + return JSValue::encode(jsNumber(floor(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncLog(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncLog(ExecState* exec) { - return jsDoubleNumber(exec, log(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(log(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec) { - unsigned argsCount = args.size(); + unsigned argsCount = exec->argumentCount(); double result = -Inf; for (unsigned k = 0; k < argsCount; ++k) { - double val = args.at(k).toNumber(exec); + double val = exec->argument(k).toNumber(exec); if (isnan(val)) { result = NaN; break; @@ -175,15 +178,15 @@ JSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec, JSObject*, JSValue, cons if (val > result || (val == 0 && result == 0 && !signbit(val))) result = val; } - return jsNumber(exec, result); + return JSValue::encode(jsNumber(result)); } -JSValue JSC_HOST_CALL mathProtoFuncMin(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncMin(ExecState* exec) { - unsigned argsCount = args.size(); + unsigned argsCount = exec->argumentCount(); double result = +Inf; for (unsigned k = 0; k < argsCount; ++k) { - double val = args.at(k).toNumber(exec); + double val = exec->argument(k).toNumber(exec); if (isnan(val)) { result = NaN; break; @@ -191,50 +194,48 @@ JSValue JSC_HOST_CALL mathProtoFuncMin(ExecState* exec, JSObject*, JSValue, cons if (val < result || (val == 0 && result == 0 && signbit(val))) result = val; } - return jsNumber(exec, result); + return JSValue::encode(jsNumber(result)); } -JSValue JSC_HOST_CALL mathProtoFuncPow(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncPow(ExecState* exec) { // ECMA 15.8.2.1.13 - double arg = args.at(0).toNumber(exec); - double arg2 = args.at(1).toNumber(exec); + double arg = exec->argument(0).toNumber(exec); + double arg2 = exec->argument(1).toNumber(exec); if (isnan(arg2)) - return jsNaN(exec); + return JSValue::encode(jsNaN()); if (isinf(arg2) && fabs(arg) == 1) - return jsNaN(exec); - return jsNumber(exec, pow(arg, arg2)); + return JSValue::encode(jsNaN()); + return JSValue::encode(jsNumber(pow(arg, arg2))); } -JSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState* exec, JSObject*, JSValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState* exec) { - return jsDoubleNumber(exec, exec->globalData().weakRandom.get()); + return JSValue::encode(jsDoubleNumber(exec->lexicalGlobalObject()->weakRandomNumber())); } -JSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec) { - double arg = args.at(0).toNumber(exec); - if (signbit(arg) && arg >= -0.5) - return jsNumber(exec, -0.0); + double arg = exec->argument(0).toNumber(exec); double integer = ceil(arg); - return jsNumber(exec, integer - (integer - arg > 0.5)); + return JSValue::encode(jsNumber(integer - (integer - arg > 0.5))); } -JSValue JSC_HOST_CALL mathProtoFuncSin(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncSin(ExecState* exec) { - return jsDoubleNumber(exec, sin(args.at(0).toNumber(exec))); + return JSValue::encode(exec->globalData().cachedSin(exec->argument(0).toNumber(exec))); } -JSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState* exec) { - return jsDoubleNumber(exec, sqrt(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(sqrt(exec->argument(0).toNumber(exec)))); } -JSValue JSC_HOST_CALL mathProtoFuncTan(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState* exec) { - return jsDoubleNumber(exec, tan(args.at(0).toNumber(exec))); + return JSValue::encode(jsDoubleNumber(tan(exec->argument(0).toNumber(exec)))); } } // namespace JSC diff --git a/JavaScriptCore/runtime/MathObject.h b/JavaScriptCore/runtime/MathObject.h index a9f7031..31fa2fe 100644 --- a/JavaScriptCore/runtime/MathObject.h +++ b/JavaScriptCore/runtime/MathObject.h @@ -21,13 +21,13 @@ #ifndef MathObject_h #define MathObject_h -#include "JSObject.h" +#include "JSObjectWithGlobalObject.h" namespace JSC { - class MathObject : public JSObject { + class MathObject : public JSObjectWithGlobalObject { public: - MathObject(ExecState*, NonNullPassRefPtr<Structure>); + MathObject(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); diff --git a/JavaScriptCore/runtime/MemoryStatistics.cpp b/JavaScriptCore/runtime/MemoryStatistics.cpp new file mode 100644 index 0000000..7fafa9c --- /dev/null +++ b/JavaScriptCore/runtime/MemoryStatistics.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "MemoryStatistics.h" + +#include "ExecutableAllocator.h" +#include "JSGlobalData.h" +#include "RegisterFile.h" + +namespace JSC { + +Heap::Statistics heapStatistics(JSGlobalData* commonGlobalData) +{ + return commonGlobalData->heap.statistics(); +} + +GlobalMemoryStatistics globalMemoryStatistics() +{ + GlobalMemoryStatistics stats; + + stats.stackBytes = RegisterFile::committedByteCount(); +#if ENABLE(EXECUTABLE_ALLOCATOR_FIXED) + stats.JITBytes = ExecutableAllocator::committedByteCount(); +#else + stats.JITBytes = 0; +#endif + return stats; +} + +} + + diff --git a/JavaScriptCore/runtime/MemoryStatistics.h b/JavaScriptCore/runtime/MemoryStatistics.h new file mode 100644 index 0000000..1b92eb9 --- /dev/null +++ b/JavaScriptCore/runtime/MemoryStatistics.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 MemoryStatistics_h +#define MemoryStatistics_h + +#include "Collector.h" + +class JSGlobalData; + +namespace JSC { + +struct GlobalMemoryStatistics { + size_t stackBytes; + size_t JITBytes; +}; + +Heap::Statistics heapStatistics(JSGlobalData* commonGlobalData); +GlobalMemoryStatistics globalMemoryStatistics(); + +} + +#endif // MemoryStatistics_h + diff --git a/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/JavaScriptCore/runtime/NativeErrorConstructor.cpp index 403fc7e..eb508eb 100644 --- a/JavaScriptCore/runtime/NativeErrorConstructor.cpp +++ b/JavaScriptCore/runtime/NativeErrorConstructor.cpp @@ -32,25 +32,21 @@ 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(exec))) - , m_errorStructure(ErrorInstance::createStructure(nativeErrorPrototype)) +NativeErrorConstructor::NativeErrorConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<Structure> prototypeStructure, const UString& nameAndMessage) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, nameAndMessage)) { - putDirect(exec->propertyNames().length, jsNumber(exec, 1), DontDelete | ReadOnly | DontEnum); // ECMA 15.11.7.5 - putDirect(exec->propertyNames().prototype, nativeErrorPrototype, DontDelete | ReadOnly | DontEnum); -} + NativeErrorPrototype* prototype = new (exec) NativeErrorPrototype(exec, globalObject, prototypeStructure, nameAndMessage, this); -ErrorInstance* NativeErrorConstructor::construct(ExecState* exec, const ArgList& args) -{ - ErrorInstance* object = new (exec) ErrorInstance(m_errorStructure); - if (!args.at(0).isUndefined()) - object->putDirect(exec->propertyNames().message, jsString(exec, args.at(0).toString(exec))); - return object; + putDirect(exec->propertyNames().length, jsNumber(1), DontDelete | ReadOnly | DontEnum); // ECMA 15.11.7.5 + putDirect(exec->propertyNames().prototype, prototype, DontDelete | ReadOnly | DontEnum); + m_errorStructure = ErrorInstance::createStructure(prototype); } -static JSObject* constructWithNativeErrorConstructor(ExecState* exec, JSObject* constructor, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithNativeErrorConstructor(ExecState* exec) { - return static_cast<NativeErrorConstructor*>(constructor)->construct(exec, args); + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = static_cast<NativeErrorConstructor*>(exec->callee())->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); } ConstructType NativeErrorConstructor::getConstructData(ConstructData& constructData) @@ -59,9 +55,11 @@ ConstructType NativeErrorConstructor::getConstructData(ConstructData& constructD return ConstructTypeHost; } -static JSValue JSC_HOST_CALL callNativeErrorConstructor(ExecState* exec, JSObject* constructor, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callNativeErrorConstructor(ExecState* exec) { - return static_cast<NativeErrorConstructor*>(constructor)->construct(exec, args); + JSValue message = exec->argumentCount() ? exec->argument(0) : jsUndefined(); + Structure* errorStructure = static_cast<NativeErrorConstructor*>(exec->callee())->errorStructure(); + return JSValue::encode(ErrorInstance::create(exec, errorStructure, message)); } CallType NativeErrorConstructor::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/NativeErrorConstructor.h b/JavaScriptCore/runtime/NativeErrorConstructor.h index 152dbac..1ff8207 100644 --- a/JavaScriptCore/runtime/NativeErrorConstructor.h +++ b/JavaScriptCore/runtime/NativeErrorConstructor.h @@ -31,11 +31,11 @@ namespace JSC { class NativeErrorConstructor : public InternalFunction { public: - NativeErrorConstructor(ExecState*, NonNullPassRefPtr<Structure>, NativeErrorPrototype*); + NativeErrorConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<Structure> prototypeStructure, const UString&); static const ClassInfo info; - ErrorInstance* construct(ExecState*, const ArgList&); + Structure* errorStructure() { return m_errorStructure.get(); } private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/NativeErrorPrototype.cpp b/JavaScriptCore/runtime/NativeErrorPrototype.cpp index aa46a6a..540220a 100644 --- a/JavaScriptCore/runtime/NativeErrorPrototype.cpp +++ b/JavaScriptCore/runtime/NativeErrorPrototype.cpp @@ -22,18 +22,21 @@ #include "NativeErrorPrototype.h" #include "ErrorPrototype.h" +#include "JSGlobalObject.h" #include "JSString.h" +#include "NativeErrorConstructor.h" #include "UString.h" namespace JSC { ASSERT_CLASS_FITS_IN_CELL(NativeErrorPrototype); -NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, const UString& name, const UString& message) - : JSObject(structure) +NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, const UString& nameAndMessage, NativeErrorConstructor* constructor) + : JSObjectWithGlobalObject(globalObject, structure) { - putDirect(exec->propertyNames().name, jsString(exec, name), 0); - putDirect(exec->propertyNames().message, jsString(exec, message), 0); + putDirect(exec->propertyNames().name, jsString(exec, nameAndMessage), 0); + putDirect(exec->propertyNames().message, jsString(exec, nameAndMessage), 0); + putDirect(exec->propertyNames().constructor, constructor, DontEnum); } } // namespace JSC diff --git a/JavaScriptCore/runtime/NativeErrorPrototype.h b/JavaScriptCore/runtime/NativeErrorPrototype.h index 0c65a9c..30690d5 100644 --- a/JavaScriptCore/runtime/NativeErrorPrototype.h +++ b/JavaScriptCore/runtime/NativeErrorPrototype.h @@ -21,13 +21,14 @@ #ifndef NativeErrorPrototype_h #define NativeErrorPrototype_h -#include "JSObject.h" +#include "JSObjectWithGlobalObject.h" namespace JSC { + class NativeErrorConstructor; - class NativeErrorPrototype : public JSObject { + class NativeErrorPrototype : public JSObjectWithGlobalObject { public: - NativeErrorPrototype(ExecState*, NonNullPassRefPtr<Structure>, const UString& name, const UString& message); + NativeErrorPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, const UString&, NativeErrorConstructor*); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/NumberConstructor.cpp b/JavaScriptCore/runtime/NumberConstructor.cpp index cc6c51d..5369ca0 100644 --- a/JavaScriptCore/runtime/NumberConstructor.cpp +++ b/JavaScriptCore/runtime/NumberConstructor.cpp @@ -22,6 +22,7 @@ #include "config.h" #include "NumberConstructor.h" +#include "Lookup.h" #include "NumberObject.h" #include "NumberPrototype.h" @@ -29,11 +30,11 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(NumberConstructor); -static JSValue numberConstructorNaNValue(ExecState*, const Identifier&, const PropertySlot&); -static JSValue numberConstructorNegInfinity(ExecState*, const Identifier&, const PropertySlot&); -static JSValue numberConstructorPosInfinity(ExecState*, const Identifier&, const PropertySlot&); -static JSValue numberConstructorMaxValue(ExecState*, const Identifier&, const PropertySlot&); -static JSValue numberConstructorMinValue(ExecState*, const Identifier&, const PropertySlot&); +static JSValue numberConstructorNaNValue(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorNegInfinity(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorPosInfinity(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorMaxValue(ExecState*, JSValue, const Identifier&); +static JSValue numberConstructorMinValue(ExecState*, JSValue, const Identifier&); } // namespace JSC @@ -53,14 +54,14 @@ const ClassInfo NumberConstructor::info = { "Function", &InternalFunction::info, @end */ -NumberConstructor::NumberConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, NumberPrototype* numberPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, numberPrototype->info.className)) +NumberConstructor::NumberConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, NumberPrototype* numberPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, numberPrototype->info.className)) { // Number.Prototype putDirectWithoutTransition(exec->propertyNames().prototype, numberPrototype, DontEnum | DontDelete | ReadOnly); // no. of arguments for constructor - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); } bool NumberConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) @@ -73,38 +74,38 @@ bool NumberConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifi return getStaticValueDescriptor<NumberConstructor, InternalFunction>(exec, ExecState::numberTable(exec), this, propertyName, descriptor); } -static JSValue numberConstructorNaNValue(ExecState* exec, const Identifier&, const PropertySlot&) +static JSValue numberConstructorNaNValue(ExecState*, JSValue, const Identifier&) { - return jsNaN(exec); + return jsNaN(); } -static JSValue numberConstructorNegInfinity(ExecState* exec, const Identifier&, const PropertySlot&) +static JSValue numberConstructorNegInfinity(ExecState*, JSValue, const Identifier&) { - return jsNumber(exec, -Inf); + return jsNumber(-Inf); } -static JSValue numberConstructorPosInfinity(ExecState* exec, const Identifier&, const PropertySlot&) +static JSValue numberConstructorPosInfinity(ExecState*, JSValue, const Identifier&) { - return jsNumber(exec, Inf); + return jsNumber(Inf); } -static JSValue numberConstructorMaxValue(ExecState* exec, const Identifier&, const PropertySlot&) +static JSValue numberConstructorMaxValue(ExecState*, JSValue, const Identifier&) { - return jsNumber(exec, 1.7976931348623157E+308); + return jsNumber(1.7976931348623157E+308); } -static JSValue numberConstructorMinValue(ExecState* exec, const Identifier&, const PropertySlot&) +static JSValue numberConstructorMinValue(ExecState*, JSValue, const Identifier&) { - return jsNumber(exec, 5E-324); + return jsNumber(5E-324); } // ECMA 15.7.1 -static JSObject* constructWithNumberConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithNumberConstructor(ExecState* exec) { NumberObject* object = new (exec) NumberObject(exec->lexicalGlobalObject()->numberObjectStructure()); - double n = args.isEmpty() ? 0 : args.at(0).toNumber(exec); - object->setInternalValue(jsNumber(exec, n)); - return object; + double n = exec->argumentCount() ? exec->argument(0).toNumber(exec) : 0; + object->setInternalValue(jsNumber(n)); + return JSValue::encode(object); } ConstructType NumberConstructor::getConstructData(ConstructData& constructData) @@ -114,9 +115,9 @@ ConstructType NumberConstructor::getConstructData(ConstructData& constructData) } // ECMA 15.7.2 -static JSValue JSC_HOST_CALL callNumberConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callNumberConstructor(ExecState* exec) { - return jsNumber(exec, args.isEmpty() ? 0 : args.at(0).toNumber(exec)); + return JSValue::encode(jsNumber(!exec->argumentCount() ? 0 : exec->argument(0).toNumber(exec))); } CallType NumberConstructor::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/NumberConstructor.h b/JavaScriptCore/runtime/NumberConstructor.h index 723c4b2..d8a2593 100644 --- a/JavaScriptCore/runtime/NumberConstructor.h +++ b/JavaScriptCore/runtime/NumberConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class NumberConstructor : public InternalFunction { public: - NumberConstructor(ExecState*, NonNullPassRefPtr<Structure>, NumberPrototype*); + NumberConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, NumberPrototype*); virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); diff --git a/JavaScriptCore/runtime/NumberObject.h b/JavaScriptCore/runtime/NumberObject.h index 6c18cdd..e82b593 100644 --- a/JavaScriptCore/runtime/NumberObject.h +++ b/JavaScriptCore/runtime/NumberObject.h @@ -37,11 +37,7 @@ namespace JSC { } protected: -#if USE(JSVALUE32) - static const unsigned StructureFlags = OverridesMarkChildren | JSWrapperObject::StructureFlags; -#else static const unsigned StructureFlags = JSWrapperObject::StructureFlags; -#endif private: virtual const ClassInfo* classInfo() const { return &info; } diff --git a/JavaScriptCore/runtime/NumberPrototype.cpp b/JavaScriptCore/runtime/NumberPrototype.cpp index fa32b86..0b86c00 100644 --- a/JavaScriptCore/runtime/NumberPrototype.cpp +++ b/JavaScriptCore/runtime/NumberPrototype.cpp @@ -25,12 +25,11 @@ #include "Error.h" #include "JSFunction.h" #include "JSString.h" -#include "JSStringBuilder.h" #include "Operations.h" #include "PrototypeFunction.h" -#include "StringBuilder.h" #include "dtoa.h" #include <wtf/Assertions.h> +#include <wtf/DecimalNumber.h> #include <wtf/MathExtras.h> #include <wtf/Vector.h> @@ -38,120 +37,205 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(NumberPrototype); -static JSValue JSC_HOST_CALL numberProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState*); +static EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*); // ECMA 15.7.4 -NumberPrototype::NumberPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) +NumberPrototype::NumberPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) : NumberObject(structure) { - setInternalValue(jsNumber(exec, 0)); + setInternalValue(jsNumber(0)); // The constructor will be added later, after NumberConstructor has been constructed - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().toString, numberProtoFuncToString), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, numberProtoFuncToLocaleString), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, numberProtoFuncValueOf), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().toFixed, numberProtoFuncToFixed), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().toExponential, numberProtoFuncToExponential), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().toPrecision, numberProtoFuncToPrecision), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toString, numberProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, numberProtoFuncToLocaleString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, numberProtoFuncValueOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toFixed, numberProtoFuncToFixed), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toExponential, numberProtoFuncToExponential), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().toPrecision, numberProtoFuncToPrecision), DontEnum); } // ------------------------------ Functions --------------------------- // ECMA 15.7.4.2 - 15.7.4.7 -static UString integerPartNoExp(double d) +static ALWAYS_INLINE bool toThisNumber(JSValue thisValue, double &x) { - int decimalPoint; - int sign; - char result[80]; - WTF::dtoa(result, d, 0, &decimalPoint, &sign, NULL); - bool resultIsInfOrNan = (decimalPoint == 9999); - size_t length = strlen(result); - - StringBuilder builder; - builder.append(sign ? "-" : ""); - if (resultIsInfOrNan) - builder.append((const char*)result); - else if (decimalPoint <= 0) - builder.append("0"); - else { - Vector<char, 1024> buf(decimalPoint + 1); - - if (static_cast<int>(length) <= decimalPoint) { - ASSERT(decimalPoint < 1024); - memcpy(buf.data(), result, length); - memset(buf.data() + length, '0', decimalPoint - length); - } else - strncpy(buf.data(), result, decimalPoint); - buf[decimalPoint] = '\0'; - - builder.append((const char*)(buf.data())); + JSValue v = thisValue.getJSNumber(); + if (UNLIKELY(!v)) + return false; + x = v.uncheckedGetNumber(); + return true; +} + +static ALWAYS_INLINE bool getIntegerArgumentInRange(ExecState* exec, int low, int high, int& result, bool& isUndefined) +{ + result = 0; + isUndefined = false; + + JSValue argument0 = exec->argument(0); + if (argument0.isUndefined()) { + isUndefined = true; + return true; } - return builder.build(); + double asDouble = argument0.toInteger(exec); + if (asDouble < low || asDouble > high) + return false; + + result = static_cast<int>(asDouble); + return true; } -static UString charSequence(char c, int count) +// toExponential converts a number to a string, always formatting as an expoential. +// This method takes an optional argument specifying a number of *decimal places* +// to round the significand to (or, put another way, this method optionally rounds +// to argument-plus-one significant figures). +EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState* exec) { - Vector<char, 2048> buf(count + 1, c); - buf[count] = '\0'; + // Get x (the double value of this, which should be a Number). + double x; + if (!toThisNumber(exec->hostThisValue(), x)) + return throwVMTypeError(exec); + + // Get the argument. + int decimalPlacesInExponent; + bool isUndefined; + if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlacesInExponent, isUndefined)) + return throwVMError(exec, createRangeError(exec, "toExponential() argument must be between 0 and 20")); + + // Handle NaN and Infinity. + if (isnan(x) || isinf(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + // Round if the argument is not undefined, always format as exponential. + NumberToStringBuffer buffer; + unsigned length = isUndefined + ? DecimalNumber(x).toStringExponential(buffer, WTF::NumberToStringBufferLength) + : DecimalNumber(x, RoundingSignificantFigures, decimalPlacesInExponent + 1).toStringExponential(buffer, WTF::NumberToStringBufferLength); - return UString(buf.data()); + return JSValue::encode(jsString(exec, UString(buffer, length))); } -static double intPow10(int e) +// toFixed converts a number to a string, always formatting as an a decimal fraction. +// This method takes an argument specifying a number of decimal places to round the +// significand to. However when converting large values (1e+21 and above) this +// method will instead fallback to calling ToString. +EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState* exec) { - // This function uses the "exponentiation by squaring" algorithm and - // long double to quickly and precisely calculate integer powers of 10.0. - - // This is a handy workaround for <rdar://problem/4494756> - - if (e == 0) - return 1.0; - - bool negative = e < 0; - unsigned exp = negative ? -e : e; - - long double result = 10.0; - bool foundOne = false; - for (int bit = 31; bit >= 0; bit--) { - if (!foundOne) { - if ((exp >> bit) & 1) - foundOne = true; - } else { - result = result * result; - if ((exp >> bit) & 1) - result = result * 10.0; - } - } + // Get x (the double value of this, which should be a Number). + JSValue thisValue = exec->hostThisValue(); + JSValue v = thisValue.getJSNumber(); + if (!v) + return throwVMTypeError(exec); + double x = v.uncheckedGetNumber(); - if (negative) - return static_cast<double>(1.0 / result); - return static_cast<double>(result); + // Get the argument. + int decimalPlaces; + bool isUndefined; // This is ignored; undefined treated as 0. + if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlaces, isUndefined)) + return throwVMError(exec, createRangeError(exec, "toFixed() argument must be between 0 and 20")); + + // 15.7.4.5.7 states "If x >= 10^21, then let m = ToString(x)" + // This also covers Ininity, and structure the check so that NaN + // values are also handled by numberToString + if (!(fabs(x) < 1e+21)) + return JSValue::encode(jsString(exec, UString::number(x))); + + // The check above will return false for NaN or Infinity, these will be + // handled by numberToString. + ASSERT(!isnan(x) && !isinf(x)); + + // Convert to decimal with rounding, and format as decimal. + NumberToStringBuffer buffer; + unsigned length = DecimalNumber(x, RoundingDecimalPlaces, decimalPlaces).toStringDecimal(buffer, WTF::NumberToStringBufferLength); + return JSValue::encode(jsString(exec, UString(buffer, length))); } -JSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +// toPrecision converts a number to a string, takeing an argument specifying a +// number of significant figures to round the significand to. For positive +// exponent, all values that can be represented using a decimal fraction will +// be, e.g. when rounding to 3 s.f. any value up to 999 will be formated as a +// decimal, whilst 1000 is converted to the exponential representation 1.00e+3. +// For negative exponents values >= 1e-6 are formated as decimal fractions, +// with smaller values converted to exponential representation. +EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec) { + // Get x (the double value of this, which should be a Number). + JSValue thisValue = exec->hostThisValue(); JSValue v = thisValue.getJSNumber(); if (!v) - return throwError(exec, TypeError); + return throwVMTypeError(exec); + double x = v.uncheckedGetNumber(); + + // Get the argument. + int significantFigures; + bool isUndefined; + if (!getIntegerArgumentInRange(exec, 1, 21, significantFigures, isUndefined)) + return throwVMError(exec, createRangeError(exec, "toPrecision() argument must be between 1 and 21")); + + // To precision called with no argument is treated as ToString. + if (isUndefined) + return JSValue::encode(jsString(exec, UString::number(x))); + + // Handle NaN and Infinity. + if (isnan(x) || isinf(x)) + return JSValue::encode(jsString(exec, UString::number(x))); + + // Convert to decimal with rounding. + DecimalNumber number(x, RoundingSignificantFigures, significantFigures); + // If number is in the range 1e-6 <= x < pow(10, significantFigures) then format + // as decimal. Otherwise, format the number as an exponential. Decimal format + // demands a minimum of (exponent + 1) digits to represent a number, for example + // 1234 (1.234e+3) requires 4 digits. (See ECMA-262 15.7.4.7.10.c) + NumberToStringBuffer buffer; + unsigned length = number.exponent() >= -6 && number.exponent() < significantFigures + ? number.toStringDecimal(buffer, WTF::NumberToStringBufferLength) + : number.toStringExponential(buffer, WTF::NumberToStringBufferLength); + return JSValue::encode(jsString(exec, UString(buffer, length))); +} - double radixAsDouble = args.at(0).toInteger(exec); // nan -> 0 - if (radixAsDouble == 10 || args.at(0).isUndefined()) - return jsString(exec, v.toString(exec)); +EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec) +{ + JSValue thisValue = exec->hostThisValue(); + JSValue v = thisValue.getJSNumber(); + if (!v) + return throwVMTypeError(exec); + + JSValue radixValue = exec->argument(0); + int radix; + if (radixValue.isInt32()) + radix = radixValue.asInt32(); + else if (radixValue.isUndefined()) + radix = 10; + else + radix = static_cast<int>(radixValue.toInteger(exec)); // nan -> 0 + + if (radix == 10) + return JSValue::encode(jsString(exec, v.toString(exec))); + + static const char* const digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + + // Fast path for number to character conversion. + if (radix == 36) { + if (v.isInt32()) { + int x = v.asInt32(); + if (static_cast<unsigned>(x) < 36) { // Exclude negatives + JSGlobalData* globalData = &exec->globalData(); + return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, digits[x])); + } + } + } - if (radixAsDouble < 2 || radixAsDouble > 36) - return throwError(exec, RangeError, "toString() radix argument must be between 2 and 36"); + if (radix < 2 || radix > 36) + return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36")); - int radix = static_cast<int>(radixAsDouble); - const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; // INT_MAX results in 1024 characters left of the dot with radix 2 // give the same space on the right side. safety checks are in place // unless someone finds a precise rule. @@ -159,7 +243,7 @@ JSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec, JSObject*, JSValu const char* lastCharInString = s + sizeof(s) - 1; double x = v.uncheckedGetNumber(); if (isnan(x) || isinf(x)) - return jsString(exec, UString::from(x)); + return JSValue::encode(jsString(exec, UString::number(x))); bool isNegative = x < 0.0; if (isNegative) @@ -198,257 +282,29 @@ JSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec, JSObject*, JSValu *p = '\0'; ASSERT(p < s + sizeof(s)); - return jsString(exec, startOfResultString); + return JSValue::encode(jsString(exec, startOfResultString)); } -JSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); // FIXME: Not implemented yet. JSValue v = thisValue.getJSNumber(); if (!v) - return throwError(exec, TypeError); - - return jsString(exec, v.toString(exec)); -} - -JSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) -{ - JSValue v = thisValue.getJSNumber(); - if (!v) - return throwError(exec, TypeError); - - return v; -} - -JSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) -{ - JSValue v = thisValue.getJSNumber(); - if (!v) - return throwError(exec, TypeError); - - JSValue fractionDigits = args.at(0); - double df = fractionDigits.toInteger(exec); - if (!(df >= 0 && df <= 20)) - return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20"); - int f = static_cast<int>(df); - - double x = v.uncheckedGetNumber(); - if (isnan(x)) - return jsNontrivialString(exec, "NaN"); - - UString s; - if (x < 0) { - s = "-"; - x = -x; - } else { - s = ""; - if (x == -0.0) - x = 0; - } - - if (x >= pow(10.0, 21.0)) - return jsString(exec, makeString(s, UString::from(x))); - - const double tenToTheF = pow(10.0, f); - double n = floor(x * tenToTheF); - if (fabs(n / tenToTheF - x) >= fabs((n + 1) / tenToTheF - x)) - n++; - - UString m = integerPartNoExp(n); - - int k = m.size(); - if (k <= f) { - StringBuilder z; - for (int i = 0; i < f + 1 - k; i++) - z.append('0'); - z.append(m); - m = z.build(); - k = f + 1; - ASSERT(k == m.size()); - } - int kMinusf = k - f; - - if (kMinusf < m.size()) - return jsString(exec, makeString(s, m.substr(0, kMinusf), ".", m.substr(kMinusf))); - return jsString(exec, makeString(s, m.substr(0, kMinusf))); -} - -static void fractionalPartToString(char* buf, int& i, const char* result, int resultLength, int fractionalDigits) -{ - if (fractionalDigits <= 0) - return; - - int fDigitsInResult = static_cast<int>(resultLength) - 1; - buf[i++] = '.'; - if (fDigitsInResult > 0) { - if (fractionalDigits < fDigitsInResult) { - strncpy(buf + i, result + 1, fractionalDigits); - i += fractionalDigits; - } else { - ASSERT(i + resultLength - 1 < 80); - memcpy(buf + i, result + 1, resultLength - 1); - i += static_cast<int>(resultLength) - 1; - } - } - - for (int j = 0; j < fractionalDigits - fDigitsInResult; j++) - buf[i++] = '0'; -} - -static void exponentialPartToString(char* buf, int& i, int decimalPoint) -{ - buf[i++] = 'e'; - // decimalPoint can't be more than 3 digits decimal given the - // nature of float representation - int exponential = decimalPoint - 1; - buf[i++] = (exponential >= 0) ? '+' : '-'; - if (exponential < 0) - exponential *= -1; - 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); -} - -JSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) -{ - JSValue v = thisValue.getJSNumber(); - if (!v) - return throwError(exec, TypeError); - - double x = v.uncheckedGetNumber(); - - if (isnan(x) || isinf(x)) - return jsString(exec, UString::from(x)); - - JSValue fractionalDigitsValue = args.at(0); - double df = fractionalDigitsValue.toInteger(exec); - if (!(df >= 0 && df <= 20)) - return throwError(exec, RangeError, "toExponential() argument must between 0 and 20"); - int fractionalDigits = static_cast<int>(df); - bool includeAllDigits = fractionalDigitsValue.isUndefined(); - - int decimalAdjust = 0; - if (x && !includeAllDigits) { - double logx = floor(log10(fabs(x))); - x /= pow(10.0, logx); - const double tenToTheF = pow(10.0, fractionalDigits); - double fx = floor(x * tenToTheF) / tenToTheF; - double cx = ceil(x * tenToTheF) / tenToTheF; - - if (fabs(fx - x) < fabs(cx - x)) - x = fx; - else - x = cx; - - decimalAdjust = static_cast<int>(logx); - } + return throwVMTypeError(exec); - if (isnan(x)) - return jsNontrivialString(exec, "NaN"); - - if (x == -0.0) // (-0.0).toExponential() should print as 0 instead of -0 - x = 0; - - int decimalPoint; - int sign; - char result[80]; - WTF::dtoa(result, x, 0, &decimalPoint, &sign, NULL); - size_t resultLength = strlen(result); - decimalPoint += decimalAdjust; - - int i = 0; - char buf[80]; // digit + '.' + fractionDigits (max 20) + 'e' + sign + exponent (max?) - if (sign) - buf[i++] = '-'; - - // ? 9999 is the magical "result is Inf or NaN" value. what's 999?? - if (decimalPoint == 999) { - ASSERT(i + resultLength < 80); - memcpy(buf + i, result, resultLength); - buf[i + resultLength] = '\0'; - } else { - buf[i++] = result[0]; - - if (includeAllDigits) - fractionalDigits = static_cast<int>(resultLength) - 1; - - fractionalPartToString(buf, i, result, resultLength, fractionalDigits); - exponentialPartToString(buf, i, decimalPoint); - buf[i++] = '\0'; - } - ASSERT(i <= 80); - - return jsString(exec, buf); + return JSValue::encode(jsString(exec, v.toString(exec))); } -JSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); JSValue v = thisValue.getJSNumber(); if (!v) - return throwError(exec, TypeError); + return throwVMTypeError(exec); - double doublePrecision = args.at(0).toIntegerPreserveNaN(exec); - double x = v.uncheckedGetNumber(); - if (args.at(0).isUndefined() || isnan(x) || isinf(x)) - return jsString(exec, v.toString(exec)); - - UString s; - if (x < 0) { - s = "-"; - x = -x; - } else - s = ""; - - if (!(doublePrecision >= 1 && doublePrecision <= 21)) // true for NaN - return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21"); - int precision = static_cast<int>(doublePrecision); - - int e = 0; - UString m; - if (x) { - e = static_cast<int>(log10(x)); - double tens = intPow10(e - precision + 1); - double n = floor(x / tens); - if (n < intPow10(precision - 1)) { - e = e - 1; - tens = intPow10(e - precision + 1); - n = floor(x / tens); - } - - if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x)) - ++n; - // maintain n < 10^(precision) - if (n >= intPow10(precision)) { - n /= 10.0; - e += 1; - } - ASSERT(intPow10(precision - 1) <= n); - ASSERT(n < intPow10(precision)); - - m = integerPartNoExp(n); - if (e < -6 || e >= precision) { - if (m.size() > 1) - m = makeString(m.substr(0, 1), ".", m.substr(1)); - if (e >= 0) - return jsMakeNontrivialString(exec, s, m, "e+", UString::from(e)); - return jsMakeNontrivialString(exec, s, m, "e-", UString::from(-e)); - } - } else { - m = charSequence('0', precision); - e = 0; - } - - if (e == precision - 1) - return jsString(exec, makeString(s, m)); - if (e >= 0) { - if (e + 1 < m.size()) - return jsString(exec, makeString(s, m.substr(0, e + 1), ".", m.substr(e + 1))); - return jsString(exec, makeString(s, m)); - } - return jsMakeNontrivialString(exec, s, "0.", charSequence('0', -(e + 1)), m); + return JSValue::encode(v); } } // namespace JSC diff --git a/JavaScriptCore/runtime/NumberPrototype.h b/JavaScriptCore/runtime/NumberPrototype.h index 1fb2077..78b690e 100644 --- a/JavaScriptCore/runtime/NumberPrototype.h +++ b/JavaScriptCore/runtime/NumberPrototype.h @@ -27,7 +27,7 @@ namespace JSC { class NumberPrototype : public NumberObject { public: - NumberPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + NumberPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/NumericStrings.h b/JavaScriptCore/runtime/NumericStrings.h index c0696a4..d65f142 100644 --- a/JavaScriptCore/runtime/NumericStrings.h +++ b/JavaScriptCore/runtime/NumericStrings.h @@ -27,6 +27,7 @@ #define NumericStrings_h #include "UString.h" +#include <wtf/FixedArray.h> #include <wtf/HashFunctions.h> namespace JSC { @@ -39,20 +40,33 @@ namespace JSC { if (d == entry.key && !entry.value.isNull()) return entry.value; entry.key = d; - entry.value = UString::from(d); + entry.value = UString::number(d); return entry.value; } UString add(int i) { + if (static_cast<unsigned>(i) < cacheSize) + return lookupSmallString(static_cast<unsigned>(i)); CacheEntry<int>& entry = lookup(i); if (i == entry.key && !entry.value.isNull()) return entry.value; entry.key = i; - entry.value = UString::from(i); + entry.value = UString::number(i); return entry.value; } + UString add(unsigned i) + { + if (i < cacheSize) + return lookupSmallString(static_cast<unsigned>(i)); + CacheEntry<unsigned>& entry = lookup(i); + if (i == entry.key && !entry.value.isNull()) + return entry.value; + entry.key = i; + entry.value = UString::number(i); + return entry.value; + } private: static const size_t cacheSize = 64; @@ -64,9 +78,19 @@ namespace JSC { CacheEntry<double>& lookup(double d) { return doubleCache[WTF::FloatHash<double>::hash(d) & (cacheSize - 1)]; } CacheEntry<int>& lookup(int i) { return intCache[WTF::IntHash<int>::hash(i) & (cacheSize - 1)]; } + CacheEntry<unsigned>& lookup(unsigned i) { return unsignedCache[WTF::IntHash<unsigned>::hash(i) & (cacheSize - 1)]; } + const UString& lookupSmallString(unsigned i) + { + ASSERT(i < cacheSize); + if (smallIntCache[i].isNull()) + smallIntCache[i] = UString::number(i); + return smallIntCache[i]; + } - CacheEntry<double> doubleCache[cacheSize]; - CacheEntry<int> intCache[cacheSize]; + FixedArray<CacheEntry<double>, cacheSize> doubleCache; + FixedArray<CacheEntry<int>, cacheSize> intCache; + FixedArray<CacheEntry<unsigned>, cacheSize> unsignedCache; + FixedArray<UString, cacheSize> smallIntCache; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/ObjectConstructor.cpp b/JavaScriptCore/runtime/ObjectConstructor.cpp index 0838eb4..ca3dcd7 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -22,6 +22,7 @@ #include "ObjectConstructor.h" #include "Error.h" +#include "ExceptionHelpers.h" #include "JSFunction.h" #include "JSArray.h" #include "JSGlobalObject.h" @@ -34,30 +35,30 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); -static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectConstructorKeys(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectConstructorCreate(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*); -ObjectConstructor::ObjectConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) -: InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object")) +ObjectConstructor::ObjectConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) +: InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "Object")) { // ECMA 15.2.3.1 putDirectWithoutTransition(exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); // no. of arguments for constructor - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getOwnPropertyNames, objectConstructorGetOwnPropertyNames), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().getOwnPropertyNames, objectConstructorGetOwnPropertyNames), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), DontEnum); } // ECMA 15.2.2 @@ -69,9 +70,10 @@ static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, const ArgList& a return arg.toObject(exec); } -static JSObject* constructWithObjectConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec) { - return constructObject(exec, args); + ArgList args(exec); + return JSValue::encode(constructObject(exec, args)); } ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) @@ -80,9 +82,10 @@ ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) return ConstructTypeHost; } -static JSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec) { - return constructObject(exec, args); + ArgList args(exec); + return JSValue::encode(constructObject(exec, args)); } CallType ObjectConstructor::getCallData(CallData& callData) @@ -91,26 +94,26 @@ CallType ObjectConstructor::getCallData(CallData& callData) return CallTypeHost; } -JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec) { - if (!args.at(0).isObject()) - return throwError(exec, TypeError, "Requested prototype of a value that is not an object."); - return asObject(args.at(0))->prototype(); + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested prototype of a value that is not an object.")); + return JSValue::encode(asObject(exec->argument(0))->prototype()); } -JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec) { - if (!args.at(0).isObject()) - return throwError(exec, TypeError, "Requested property descriptor of a value that is not an object."); - UString propertyName = args.at(1).toString(exec); + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested property descriptor of a value that is not an object.")); + UString propertyName = exec->argument(1).toString(exec); if (exec->hadException()) - return jsNull(); - JSObject* object = asObject(args.at(0)); + return JSValue::encode(jsNull()); + JSObject* object = asObject(exec->argument(0)); PropertyDescriptor descriptor; if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) - return jsUndefined(); + return JSValue::encode(jsUndefined()); if (exec->hadException()) - return jsUndefined(); + return JSValue::encode(jsUndefined()); JSObject* description = constructEmptyObject(exec); if (!descriptor.isAccessorDescriptor()) { @@ -124,42 +127,42 @@ JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec, description->putDirect(exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); description->putDirect(exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); - return description; + return JSValue::encode(description); } // FIXME: Use the enumeration cache. -JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec) { - if (!args.at(0).isObject()) - return throwError(exec, TypeError, "Requested property names of a value that is not an object."); + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested property names of a value that is not an object.")); PropertyNameArray properties(exec); - asObject(args.at(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties); + asObject(exec->argument(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties); JSArray* names = constructEmptyArray(exec); size_t numProperties = properties.size(); for (size_t i = 0; i < numProperties; i++) names->push(exec, jsOwnedString(exec, properties[i].ustring())); - return names; + return JSValue::encode(names); } // FIXME: Use the enumeration cache. -JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) { - if (!args.at(0).isObject()) - return throwError(exec, TypeError, "Requested keys of a value that is not an object."); + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Requested keys of a value that is not an object.")); PropertyNameArray properties(exec); - asObject(args.at(0))->getOwnPropertyNames(exec, properties); + asObject(exec->argument(0))->getOwnPropertyNames(exec, properties); JSArray* keys = constructEmptyArray(exec); size_t numProperties = properties.size(); for (size_t i = 0; i < numProperties; i++) keys->push(exec, jsOwnedString(exec, properties[i].ustring())); - return keys; + return JSValue::encode(keys); } // ES5 8.10.5 ToPropertyDescriptor static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) { if (!in.isObject()) { - throwError(exec, TypeError, "Property description must be an object."); + throwError(exec, createTypeError(exec, "Property description must be an object.")); return false; } JSObject* description = asObject(in); @@ -200,8 +203,8 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor return false; if (!get.isUndefined()) { CallData callData; - if (get.getCallData(callData) == CallTypeNone) { - throwError(exec, TypeError, "Getter must be a function."); + if (getCallData(get, callData) == CallTypeNone) { + throwError(exec, createTypeError(exec, "Getter must be a function.")); return false; } } else @@ -216,8 +219,8 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor return false; if (!set.isUndefined()) { CallData callData; - if (set.getCallData(callData) == CallTypeNone) { - throwError(exec, TypeError, "Setter must be a function."); + if (getCallData(set, callData) == CallTypeNone) { + throwError(exec, createTypeError(exec, "Setter must be a function.")); return false; } } else @@ -230,32 +233,32 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor return true; if (desc.value()) { - throwError(exec, TypeError, "Invalid property. 'value' present on property with getter or setter."); + throwError(exec, createTypeError(exec, "Invalid property. 'value' present on property with getter or setter.")); return false; } if (desc.writablePresent()) { - throwError(exec, TypeError, "Invalid property. 'writable' present on property with getter or setter."); + throwError(exec, createTypeError(exec, "Invalid property. 'writable' present on property with getter or setter.")); return false; } return true; } -JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec) { - if (!args.at(0).isObject()) - return throwError(exec, TypeError, "Properties can only be defined on Objects."); - JSObject* O = asObject(args.at(0)); - UString propertyName = args.at(1).toString(exec); + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects.")); + JSObject* O = asObject(exec->argument(0)); + UString propertyName = exec->argument(1).toString(exec); if (exec->hadException()) - return jsNull(); + return JSValue::encode(jsNull()); PropertyDescriptor descriptor; - if (!toPropertyDescriptor(exec, args.at(2), descriptor)) - return jsNull(); + if (!toPropertyDescriptor(exec, exec->argument(2), descriptor)) + return JSValue::encode(jsNull()); ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor())); ASSERT(!exec->hadException()); O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true); - return O; + return JSValue::encode(O); } static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) @@ -292,26 +295,26 @@ static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* pro return object; } -JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec) { - if (!args.at(0).isObject()) - return throwError(exec, TypeError, "Properties can only be defined on Objects."); - if (!args.at(1).isObject()) - return throwError(exec, TypeError, "Property descriptor list must be an Object."); - return defineProperties(exec, asObject(args.at(0)), asObject(args.at(1))); + if (!exec->argument(0).isObject()) + return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects.")); + if (!exec->argument(1).isObject()) + return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object.")); + return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), asObject(exec->argument(1)))); } -JSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec) { - if (!args.at(0).isObject() && !args.at(0).isNull()) - return throwError(exec, TypeError, "Object prototype may only be an Object or null."); + if (!exec->argument(0).isObject() && !exec->argument(0).isNull()) + return throwVMError(exec, createTypeError(exec, "Object prototype may only be an Object or null.")); JSObject* newObject = constructEmptyObject(exec); - newObject->setPrototype(args.at(0)); - if (args.at(1).isUndefined()) - return newObject; - if (!args.at(1).isObject()) - return throwError(exec, TypeError, "Property descriptor list must be an Object."); - return defineProperties(exec, newObject, asObject(args.at(1))); + newObject->setPrototype(exec->argument(0)); + if (exec->argument(1).isUndefined()) + return JSValue::encode(newObject); + if (!exec->argument(1).isObject()) + return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object.")); + return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1)))); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ObjectConstructor.h b/JavaScriptCore/runtime/ObjectConstructor.h index 1d2cdde..04a3c1a 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.h +++ b/JavaScriptCore/runtime/ObjectConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class ObjectConstructor : public InternalFunction { public: - ObjectConstructor(ExecState*, NonNullPassRefPtr<Structure>, ObjectPrototype*, Structure* prototypeFunctionStructure); + ObjectConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, ObjectPrototype*, Structure* prototypeFunctionStructure); private: virtual ConstructType getConstructData(ConstructData&); diff --git a/JavaScriptCore/runtime/ObjectPrototype.cpp b/JavaScriptCore/runtime/ObjectPrototype.cpp index c32a007..57a8a31 100644 --- a/JavaScriptCore/runtime/ObjectPrototype.cpp +++ b/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -31,32 +31,32 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(ObjectPrototype); -static JSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*, JSObject*, JSValue, const ArgList&); - -ObjectPrototype::ObjectPrototype(ExecState* exec, NonNullPassRefPtr<Structure> stucture, Structure* prototypeFunctionStructure) +static EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState*); +static EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState*); + +ObjectPrototype::ObjectPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> stucture, Structure* prototypeFunctionStructure) : JSObject(stucture) , m_hasNoPropertiesWithUInt32Names(true) { - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, objectProtoFuncToString), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, objectProtoFuncToLocaleString), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, objectProtoFuncValueOf), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().hasOwnProperty, objectProtoFuncHasOwnProperty), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().isPrototypeOf, objectProtoFuncIsPrototypeOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, objectProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toLocaleString, objectProtoFuncToLocaleString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, objectProtoFuncValueOf), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().hasOwnProperty, objectProtoFuncHasOwnProperty), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().propertyIsEnumerable, objectProtoFuncPropertyIsEnumerable), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().isPrototypeOf, objectProtoFuncIsPrototypeOf), DontEnum); // Mozilla extensions - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().__defineGetter__, objectProtoFuncDefineGetter), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().__defineSetter__, objectProtoFuncDefineSetter), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().__lookupGetter__, objectProtoFuncLookupGetter), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().__lookupSetter__, objectProtoFuncLookupSetter), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().__defineGetter__, objectProtoFuncDefineGetter), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().__defineSetter__, objectProtoFuncDefineSetter), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().__lookupGetter__, objectProtoFuncLookupGetter), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().__lookupSetter__, objectProtoFuncLookupSetter), DontEnum); } void ObjectPrototype::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) @@ -65,7 +65,7 @@ void ObjectPrototype::put(ExecState* exec, const Identifier& propertyName, JSVal if (m_hasNoPropertiesWithUInt32Names) { bool isUInt32; - propertyName.toStrictUInt32(&isUInt32); + propertyName.toUInt32(isUInt32); m_hasNoPropertiesWithUInt32Names = !isUInt32; } } @@ -81,75 +81,85 @@ bool ObjectPrototype::getOwnPropertySlot(ExecState* exec, unsigned propertyName, // ECMA 15.2.4.2, 15.2.4.4, 15.2.4.5, 15.2.4.7 -JSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL objectProtoFuncValueOf(ExecState* exec) { - return thisValue.toThisObject(exec); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisObject(exec)); } -JSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectProtoFuncHasOwnProperty(ExecState* exec) { - return jsBoolean(thisValue.toThisObject(exec)->hasOwnProperty(exec, Identifier(exec, args.at(0).toString(exec)))); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(jsBoolean(thisValue.toThisObject(exec)->hasOwnProperty(exec, Identifier(exec, exec->argument(0).toString(exec))))); } -JSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectProtoFuncIsPrototypeOf(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); JSObject* thisObj = thisValue.toThisObject(exec); - if (!args.at(0).isObject()) - return jsBoolean(false); + if (!exec->argument(0).isObject()) + return JSValue::encode(jsBoolean(false)); - JSValue v = asObject(args.at(0))->prototype(); + JSValue v = asObject(exec->argument(0))->prototype(); while (true) { if (!v.isObject()) - return jsBoolean(false); - if (v == thisObj) - return jsBoolean(true); + return JSValue::encode(jsBoolean(false)); + if (v == thisObj) + return JSValue::encode(jsBoolean(true)); v = asObject(v)->prototype(); } } -JSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineGetter(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); CallData callData; - if (args.at(1).getCallData(callData) == CallTypeNone) - return throwError(exec, SyntaxError, "invalid getter usage"); - thisValue.toThisObject(exec)->defineGetter(exec, Identifier(exec, args.at(0).toString(exec)), asObject(args.at(1))); - return jsUndefined(); + if (getCallData(exec->argument(1), callData) == CallTypeNone) + return throwVMError(exec, createSyntaxError(exec, "invalid getter usage")); + thisValue.toThisObject(exec)->defineGetter(exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1))); + return JSValue::encode(jsUndefined()); } -JSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectProtoFuncDefineSetter(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); CallData callData; - if (args.at(1).getCallData(callData) == CallTypeNone) - return throwError(exec, SyntaxError, "invalid setter usage"); - thisValue.toThisObject(exec)->defineSetter(exec, Identifier(exec, args.at(0).toString(exec)), asObject(args.at(1))); - return jsUndefined(); + if (getCallData(exec->argument(1), callData) == CallTypeNone) + return throwVMError(exec, createSyntaxError(exec, "invalid setter usage")); + thisValue.toThisObject(exec)->defineSetter(exec, Identifier(exec, exec->argument(0).toString(exec)), asObject(exec->argument(1))); + return JSValue::encode(jsUndefined()); } -JSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec) { - return thisValue.toThisObject(exec)->lookupGetter(exec, Identifier(exec, args.at(0).toString(exec))); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisObject(exec)->lookupGetter(exec, Identifier(exec, exec->argument(0).toString(exec)))); } -JSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupSetter(ExecState* exec) { - return thisValue.toThisObject(exec)->lookupSetter(exec, Identifier(exec, args.at(0).toString(exec))); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisObject(exec)->lookupSetter(exec, Identifier(exec, exec->argument(0).toString(exec)))); } -JSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL objectProtoFuncPropertyIsEnumerable(ExecState* exec) { - return jsBoolean(thisValue.toThisObject(exec)->propertyIsEnumerable(exec, Identifier(exec, args.at(0).toString(exec)))); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(jsBoolean(thisValue.toThisObject(exec)->propertyIsEnumerable(exec, Identifier(exec, exec->argument(0).toString(exec))))); } -JSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL objectProtoFuncToLocaleString(ExecState* exec) { - return thisValue.toThisJSString(exec); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(thisValue.toThisJSString(exec)); } -JSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState* exec) { - return jsMakeNontrivialString(exec, "[object ", thisValue.toThisObject(exec)->className(), "]"); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisValue.toThisObject(exec)->className(), "]")); } } // namespace JSC diff --git a/JavaScriptCore/runtime/ObjectPrototype.h b/JavaScriptCore/runtime/ObjectPrototype.h index 489d962..0382ae4 100644 --- a/JavaScriptCore/runtime/ObjectPrototype.h +++ b/JavaScriptCore/runtime/ObjectPrototype.h @@ -27,7 +27,7 @@ namespace JSC { class ObjectPrototype : public JSObject { public: - ObjectPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); + ObjectPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); private: virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&); @@ -36,7 +36,7 @@ namespace JSC { bool m_hasNoPropertiesWithUInt32Names; }; - JSValue JSC_HOST_CALL objectProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); + EncodedJSValue JSC_HOST_CALL objectProtoFuncToString(ExecState*); } // namespace JSC diff --git a/JavaScriptCore/runtime/Operations.cpp b/JavaScriptCore/runtime/Operations.cpp index cf236bf..f129a80 100644 --- a/JavaScriptCore/runtime/Operations.cpp +++ b/JavaScriptCore/runtime/Operations.cpp @@ -55,7 +55,7 @@ NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2) if (p2.isString()) return jsString(callFrame, p1.toString(callFrame), asString(p2)); - return jsNumber(callFrame, p1.toNumber(callFrame) + p2.toNumber(callFrame)); + return jsNumber(p1.toNumber(callFrame) + p2.toNumber(callFrame)); } JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v) @@ -85,7 +85,7 @@ bool jsIsObjectType(JSValue v) if (!v.isCell()) return v.isNull(); - JSType type = asCell(v)->structure()->typeInfo().type(); + JSType type = v.asCell()->structure()->typeInfo().type(); if (type == NumberType || type == StringType) return false; if (type == ObjectType) { diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h index 9b27074..1252345 100644 --- a/JavaScriptCore/runtime/Operations.h +++ b/JavaScriptCore/runtime/Operations.h @@ -37,132 +37,203 @@ namespace JSC { ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) { - if (!s1->length()) + unsigned length1 = s1->length(); + if (!length1) return s2; - if (!s2->length()) + unsigned length2 = s2->length(); + if (!length2) return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); - unsigned ropeLength = s1->ropeLength() + s2->ropeLength(); + unsigned fiberCount = s1->fiberCount() + s2->fiberCount(); JSGlobalData* globalData = &exec->globalData(); - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, s1, s2); + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, s1, s2); - unsigned index = 0; - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - rope->append(index, s1); - rope->append(index, s2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + ropeBuilder.append(s1); + ropeBuilder.append(s2); + return new (globalData) JSString(globalData, ropeBuilder.release()); } ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2) { - unsigned ropeLength = 1 + s2->ropeLength(); + unsigned length1 = u1.length(); + if (!length1) + return s2; + unsigned length2 = s2->length(); + if (!length2) + return jsString(exec, u1); + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + unsigned fiberCount = 1 + s2->fiberCount(); JSGlobalData* globalData = &exec->globalData(); - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, u1, s2); + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, u1, s2); - unsigned index = 0; - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - rope->append(index, u1); - rope->append(index, s2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + ropeBuilder.append(u1); + ropeBuilder.append(s2); + return new (globalData) JSString(globalData, ropeBuilder.release()); } ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2) { - unsigned ropeLength = s1->ropeLength() + 1; + unsigned length1 = s1->length(); + if (!length1) + return jsString(exec, u2); + unsigned length2 = u2.length(); + if (!length2) + return s1; + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + unsigned fiberCount = s1->fiberCount() + 1; JSGlobalData* globalData = &exec->globalData(); - if (ropeLength <= JSString::s_maxInternalRopeLength) - return new (globalData) JSString(globalData, ropeLength, s1, u2); + if (fiberCount <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, fiberCount, s1, u2); - unsigned index = 0; - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - rope->append(index, s1); - rope->append(index, u2); - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + ropeBuilder.append(s1); + ropeBuilder.append(u2); + return new (globalData) JSString(globalData, ropeBuilder.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2) + { + unsigned length1 = u1.length(); + if (!length1) + return jsString(exec, u2); + unsigned length2 = u2.length(); + if (!length2) + return jsString(exec, u1); + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + + JSGlobalData* globalData = &exec->globalData(); + return new (globalData) JSString(globalData, u1, u2); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, const UString& u2, const UString& u3) + { + unsigned length1 = u1.length(); + unsigned length2 = u2.length(); + unsigned length3 = u3.length(); + if (!length1) + return jsString(exec, u2, u3); + if (!length2) + return jsString(exec, u1, u3); + if (!length3) + return jsString(exec, u1, u2); + + if ((length1 + length2) < length1) + return throwOutOfMemoryError(exec); + if ((length1 + length2 + length3) < length3) + return throwOutOfMemoryError(exec); + + JSGlobalData* globalData = &exec->globalData(); + return new (globalData) JSString(globalData, u1, u2, u3); } ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) { ASSERT(count >= 3); - unsigned ropeLength = 0; + unsigned fiberCount = 0; for (unsigned i = 0; i < count; ++i) { JSValue v = strings[i].jsValue(); if (LIKELY(v.isString())) - ropeLength += asString(v)->ropeLength(); + fiberCount += asString(v)->fiberCount(); else - ++ropeLength; + ++fiberCount; } JSGlobalData* globalData = &exec->globalData(); - if (ropeLength == 3) + if (fiberCount == 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)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - unsigned index = 0; + unsigned length = 0; + bool overflow = false; + for (unsigned i = 0; i < count; ++i) { JSValue v = strings[i].jsValue(); if (LIKELY(v.isString())) - rope->append(index, asString(v)); + ropeBuilder.append(asString(v)); else - rope->append(index, v.toString(exec)); + ropeBuilder.append(v.toString(exec)); + + unsigned newLength = ropeBuilder.length(); + if (newLength < length) + overflow = true; + length = newLength; } - ASSERT(index == ropeLength); - return new (globalData) JSString(globalData, rope.release()); + if (overflow) + return throwOutOfMemoryError(exec); + + return new (globalData) JSString(globalData, ropeBuilder.release()); } - ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue, const ArgList& args) + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSValue thisValue) { - unsigned ropeLength = 0; + unsigned fiberCount = 0; if (LIKELY(thisValue.isString())) - ropeLength += asString(thisValue)->ropeLength(); + fiberCount += asString(thisValue)->fiberCount(); else - ++ropeLength; - for (unsigned i = 0; i < args.size(); ++i) { - JSValue v = args.at(i); + ++fiberCount; + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); if (LIKELY(v.isString())) - ropeLength += asString(v)->ropeLength(); + fiberCount += asString(v)->fiberCount(); else - ++ropeLength; + ++fiberCount; } - RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); - if (UNLIKELY(!rope)) + JSString::RopeBuilder ropeBuilder(fiberCount); + if (UNLIKELY(ropeBuilder.isOutOfMemory())) return throwOutOfMemoryError(exec); - unsigned index = 0; if (LIKELY(thisValue.isString())) - rope->append(index, asString(thisValue)); + ropeBuilder.append(asString(thisValue)); else - rope->append(index, thisValue.toString(exec)); - for (unsigned i = 0; i < args.size(); ++i) { - JSValue v = args.at(i); + ropeBuilder.append(thisValue.toString(exec)); + + unsigned length = 0; + bool overflow = false; + + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + JSValue v = exec->argument(i); if (LIKELY(v.isString())) - rope->append(index, asString(v)); + ropeBuilder.append(asString(v)); else - rope->append(index, v.toString(exec)); + ropeBuilder.append(v.toString(exec)); + + unsigned newLength = ropeBuilder.length(); + if (newLength < length) + overflow = true; + length = newLength; } - ASSERT(index == ropeLength); + + if (overflow) + return throwOutOfMemoryError(exec); JSGlobalData* globalData = &exec->globalData(); - return new (globalData) JSString(globalData, rope.release()); + return new (globalData) JSString(globalData, ropeBuilder.release()); } // ECMA 11.9.3 @@ -327,7 +398,7 @@ namespace JSC { { double left = 0.0, right; if (v1.getNumber(left) && v2.getNumber(right)) - return jsNumber(callFrame, left + right); + return jsNumber(left + right); if (v1.isString()) { return v2.isString() @@ -341,7 +412,7 @@ namespace JSC { inline size_t normalizePrototypeChain(CallFrame* callFrame, JSValue base, JSValue slotBase, const Identifier& propertyName, size_t& slotOffset) { - JSCell* cell = asCell(base); + JSCell* cell = base.asCell(); size_t count = 0; while (slotBase != cell) { @@ -353,7 +424,7 @@ namespace JSC { if (v.isNull()) return 0; - cell = asCell(v); + cell = v.asCell(); // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. @@ -378,7 +449,7 @@ namespace JSC { if (v.isNull()) return count; - base = asCell(v); + base = v.asCell(); // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. @@ -389,7 +460,7 @@ namespace JSC { } } - ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain) + ALWAYS_INLINE JSValue resolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain, bool isStrictPut) { ScopeChainIterator iter = scopeChain->begin(); ScopeChainIterator next = iter; @@ -401,7 +472,9 @@ namespace JSC { JSObject* base; while (true) { base = *iter; - if (next == end || base->getPropertySlot(callFrame, property, slot)) + if (next == end) + return isStrictPut ? JSValue() : base; + if (base->getPropertySlot(callFrame, property, slot)) return base; iter = next; diff --git a/JavaScriptCore/runtime/PropertyMapHashTable.h b/JavaScriptCore/runtime/PropertyMapHashTable.h index 44dc2b8..bd452b6 100644 --- a/JavaScriptCore/runtime/PropertyMapHashTable.h +++ b/JavaScriptCore/runtime/PropertyMapHashTable.h @@ -27,13 +27,13 @@ namespace JSC { struct PropertyMapEntry { - UString::Rep* key; + StringImpl* key; unsigned offset; unsigned attributes; JSCell* specificValue; unsigned index; - PropertyMapEntry(UString::Rep* key, unsigned attributes, JSCell* specificValue) + PropertyMapEntry(StringImpl* key, unsigned attributes, JSCell* specificValue) : key(key) , offset(0) , attributes(attributes) @@ -42,7 +42,7 @@ namespace JSC { { } - PropertyMapEntry(UString::Rep* key, unsigned offset, unsigned attributes, JSCell* specificValue, unsigned index) + PropertyMapEntry(StringImpl* key, unsigned offset, unsigned attributes, JSCell* specificValue, unsigned index) : key(key) , offset(offset) , attributes(attributes) diff --git a/JavaScriptCore/runtime/PropertyNameArray.cpp b/JavaScriptCore/runtime/PropertyNameArray.cpp index 4937b7c..afb41be 100644 --- a/JavaScriptCore/runtime/PropertyNameArray.cpp +++ b/JavaScriptCore/runtime/PropertyNameArray.cpp @@ -28,20 +28,20 @@ namespace JSC { static const size_t setThreshold = 20; -void PropertyNameArray::add(UString::Rep* identifier) +void PropertyNameArray::add(StringImpl* identifier) { - ASSERT(identifier == &UString::Rep::empty() || identifier->isIdentifier()); + ASSERT(!identifier || identifier == StringImpl::empty() || identifier->isIdentifier()); size_t size = m_data->propertyNameVector().size(); if (size < setThreshold) { for (size_t i = 0; i < size; ++i) { - if (identifier == m_data->propertyNameVector()[i].ustring().rep()) + if (identifier == m_data->propertyNameVector()[i].impl()) return; } } else { if (m_set.isEmpty()) { for (size_t i = 0; i < size; ++i) - m_set.add(m_data->propertyNameVector()[i].ustring().rep()); + m_set.add(m_data->propertyNameVector()[i].impl()); } if (!m_set.add(identifier).second) return; diff --git a/JavaScriptCore/runtime/PropertyNameArray.h b/JavaScriptCore/runtime/PropertyNameArray.h index 3dbcc9d..0da930f 100644 --- a/JavaScriptCore/runtime/PropertyNameArray.h +++ b/JavaScriptCore/runtime/PropertyNameArray.h @@ -68,9 +68,9 @@ namespace JSC { JSGlobalData* globalData() { return m_globalData; } - void add(const Identifier& identifier) { add(identifier.ustring().rep()); } - void add(UString::Rep*); - void addKnownUnique(UString::Rep* identifier) { m_data->propertyNameVector().append(Identifier(m_globalData, identifier)); } + void add(const Identifier& identifier) { add(identifier.impl()); } + void add(StringImpl*); + void addKnownUnique(StringImpl* identifier) { m_data->propertyNameVector().append(Identifier(m_globalData, identifier)); } Identifier& operator[](unsigned i) { return m_data->propertyNameVector()[i]; } const Identifier& operator[](unsigned i) const { return m_data->propertyNameVector()[i]; } @@ -86,7 +86,7 @@ namespace JSC { const_iterator end() const { return m_data->propertyNameVector().end(); } private: - typedef HashSet<UString::Rep*, PtrHash<UString::Rep*> > IdentifierSet; + typedef HashSet<StringImpl*, PtrHash<StringImpl*> > IdentifierSet; RefPtr<PropertyNameArrayData> m_data; IdentifierSet m_set; diff --git a/JavaScriptCore/runtime/PropertySlot.cpp b/JavaScriptCore/runtime/PropertySlot.cpp index a0a2f48..fd16c0c 100644 --- a/JavaScriptCore/runtime/PropertySlot.cpp +++ b/JavaScriptCore/runtime/PropertySlot.cpp @@ -26,19 +26,15 @@ namespace JSC { -JSValue PropertySlot::functionGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue PropertySlot::functionGetter(ExecState* exec) const { // Prevent getter functions from observing execution if an exception is pending. if (exec->hadException()) return exec->exception(); CallData callData; - CallType callType = slot.m_data.getterFunc->getCallData(callData); - if (callType == CallTypeHost) - return callData.native.function(exec, slot.m_data.getterFunc, slot.slotBase(), exec->emptyList()); - ASSERT(callType == CallTypeJS); - // FIXME: Can this be done more efficiently using the callData? - return asFunction(slot.m_data.getterFunc)->call(exec, slot.slotBase(), exec->emptyList()); + CallType callType = m_data.getterFunc->getCallData(callData); + return call(exec, m_data.getterFunc, callType, callData, thisValue(), exec->emptyList()); } } // namespace JSC diff --git a/JavaScriptCore/runtime/PropertySlot.h b/JavaScriptCore/runtime/PropertySlot.h index 15d9034..de9ddc9 100644 --- a/JavaScriptCore/runtime/PropertySlot.h +++ b/JavaScriptCore/runtime/PropertySlot.h @@ -34,10 +34,20 @@ namespace JSC { #define JSC_VALUE_SLOT_MARKER 0 #define JSC_REGISTER_SLOT_MARKER reinterpret_cast<GetValueFunc>(1) +#define INDEX_GETTER_MARKER reinterpret_cast<GetValueFunc>(2) +#define GETTER_FUNCTION_MARKER reinterpret_cast<GetValueFunc>(3) class PropertySlot { public: + enum CachedPropertyType { + Uncacheable, + Getter, + Custom, + Value + }; + PropertySlot() + : m_cachedPropertyType(Uncacheable) { clearBase(); clearOffset(); @@ -46,12 +56,14 @@ namespace JSC { explicit PropertySlot(const JSValue base) : m_slotBase(base) + , m_cachedPropertyType(Uncacheable) { clearOffset(); clearValue(); } - typedef JSValue (*GetValueFunc)(ExecState*, const Identifier&, const PropertySlot&); + typedef JSValue (*GetValueFunc)(ExecState*, JSValue slotBase, const Identifier&); + typedef JSValue (*GetIndexValueFunc)(ExecState*, JSValue slotBase, unsigned); JSValue getValue(ExecState* exec, const Identifier& propertyName) const { @@ -59,7 +71,11 @@ namespace JSC { return *m_data.valueSlot; if (m_getValue == JSC_REGISTER_SLOT_MARKER) return (*m_data.registerSlot).jsValue(); - return m_getValue(exec, propertyName, *this); + if (m_getValue == INDEX_GETTER_MARKER) + return m_getIndexValue(exec, slotBase(), index()); + if (m_getValue == GETTER_FUNCTION_MARKER) + return functionGetter(exec); + return m_getValue(exec, slotBase(), propertyName); } JSValue getValue(ExecState* exec, unsigned propertyName) const @@ -68,10 +84,16 @@ namespace JSC { return *m_data.valueSlot; if (m_getValue == JSC_REGISTER_SLOT_MARKER) return (*m_data.registerSlot).jsValue(); - return m_getValue(exec, Identifier::from(exec, propertyName), *this); + if (m_getValue == INDEX_GETTER_MARKER) + return m_getIndexValue(exec, m_slotBase, m_data.index); + if (m_getValue == GETTER_FUNCTION_MARKER) + return functionGetter(exec); + return m_getValue(exec, slotBase(), Identifier::from(exec, propertyName)); } - bool isCacheable() const { return m_offset != WTF::notFound; } + CachedPropertyType cachedPropertyType() const { return m_cachedPropertyType; } + bool isCacheable() const { return m_cachedPropertyType != Uncacheable; } + bool isCacheableValue() const { return m_cachedPropertyType == Value; } size_t cachedOffset() const { ASSERT(isCacheable()); @@ -102,6 +124,7 @@ namespace JSC { m_slotBase = slotBase; m_data.valueSlot = valueSlot; m_offset = offset; + m_cachedPropertyType = Value; } void setValue(JSValue value) @@ -128,25 +151,49 @@ namespace JSC { ASSERT(slotBase); ASSERT(getValue); m_getValue = getValue; + m_getIndexValue = 0; m_slotBase = slotBase; } - - void setCustomIndex(JSValue slotBase, unsigned index, GetValueFunc getValue) + + void setCacheableCustom(JSValue slotBase, GetValueFunc getValue) { ASSERT(slotBase); ASSERT(getValue); m_getValue = getValue; + m_getIndexValue = 0; + m_slotBase = slotBase; + m_cachedPropertyType = Custom; + } + + void setCustomIndex(JSValue slotBase, unsigned index, GetIndexValueFunc getIndexValue) + { + ASSERT(slotBase); + ASSERT(getIndexValue); + m_getValue = INDEX_GETTER_MARKER; + m_getIndexValue = getIndexValue; m_slotBase = slotBase; m_data.index = index; } - + void setGetterSlot(JSObject* getterFunc) { ASSERT(getterFunc); - m_getValue = functionGetter; + m_thisValue = m_slotBase; + m_getValue = GETTER_FUNCTION_MARKER; m_data.getterFunc = getterFunc; } - + + void setCacheableGetterSlot(JSValue slotBase, JSObject* getterFunc, unsigned offset) + { + ASSERT(getterFunc); + m_getValue = GETTER_FUNCTION_MARKER; + m_thisValue = m_slotBase; + m_slotBase = slotBase; + m_data.getterFunc = getterFunc; + m_offset = offset; + m_cachedPropertyType = Getter; + } + void setUndefined() { setValue(jsUndefined()); @@ -182,15 +229,24 @@ namespace JSC { { // Clear offset even in release builds, in case this PropertySlot has been used before. // (For other data members, we don't need to clear anything because reuse would meaningfully overwrite them.) - m_offset = WTF::notFound; + m_offset = 0; + m_cachedPropertyType = Uncacheable; } unsigned index() const { return m_data.index; } + JSValue thisValue() const { return m_thisValue; } + + GetValueFunc customGetter() const + { + ASSERT(m_cachedPropertyType == Custom); + return m_getValue; + } private: - static JSValue functionGetter(ExecState*, const Identifier&, const PropertySlot&); + JSValue functionGetter(ExecState*) const; GetValueFunc m_getValue; + GetIndexValueFunc m_getIndexValue; JSValue m_slotBase; union { @@ -201,8 +257,10 @@ namespace JSC { } m_data; JSValue m_value; + JSValue m_thisValue; size_t m_offset; + CachedPropertyType m_cachedPropertyType; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/Protect.h b/JavaScriptCore/runtime/Protect.h index a0d5443..06cf97f 100644 --- a/JavaScriptCore/runtime/Protect.h +++ b/JavaScriptCore/runtime/Protect.h @@ -52,13 +52,13 @@ namespace JSC { inline void gcProtect(JSValue value) { if (value && value.isCell()) - gcProtect(asCell(value)); + gcProtect(value.asCell()); } inline void gcUnprotect(JSValue value) { if (value && value.isCell()) - gcUnprotect(asCell(value)); + gcUnprotect(value.asCell()); } // FIXME: Share more code with RefPtr template? The only differences are the ref/deref operation diff --git a/JavaScriptCore/runtime/PrototypeFunction.cpp b/JavaScriptCore/runtime/PrototypeFunction.cpp index 38f8adb..3529080 100644 --- a/JavaScriptCore/runtime/PrototypeFunction.cpp +++ b/JavaScriptCore/runtime/PrototypeFunction.cpp @@ -32,20 +32,20 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(PrototypeFunction); -PrototypeFunction::PrototypeFunction(ExecState* exec, int length, const Identifier& name, NativeFunction function) - : InternalFunction(&exec->globalData(), exec->lexicalGlobalObject()->prototypeFunctionStructure(), name) +PrototypeFunction::PrototypeFunction(ExecState* exec, JSGlobalObject* globalObject, int length, const Identifier& name, NativeFunction function) + : InternalFunction(&exec->globalData(), globalObject, exec->lexicalGlobalObject()->prototypeFunctionStructure(), name) , m_function(function) { ASSERT_ARG(function, function); - putDirect(exec->propertyNames().length, jsNumber(exec, length), DontDelete | ReadOnly | DontEnum); + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); } -PrototypeFunction::PrototypeFunction(ExecState* exec, NonNullPassRefPtr<Structure> prototypeFunctionStructure, int length, const Identifier& name, NativeFunction function) - : InternalFunction(&exec->globalData(), prototypeFunctionStructure, name) +PrototypeFunction::PrototypeFunction(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> prototypeFunctionStructure, int length, const Identifier& name, NativeFunction function) + : InternalFunction(&exec->globalData(), globalObject, prototypeFunctionStructure, name) , m_function(function) { ASSERT_ARG(function, function); - putDirect(exec->propertyNames().length, jsNumber(exec, length), DontDelete | ReadOnly | DontEnum); + putDirect(exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); } CallType PrototypeFunction::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/PrototypeFunction.h b/JavaScriptCore/runtime/PrototypeFunction.h index 70ee034..6ca2342 100644 --- a/JavaScriptCore/runtime/PrototypeFunction.h +++ b/JavaScriptCore/runtime/PrototypeFunction.h @@ -31,8 +31,8 @@ namespace JSC { class PrototypeFunction : public InternalFunction { public: - PrototypeFunction(ExecState*, int length, const Identifier&, NativeFunction); - PrototypeFunction(ExecState*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); + PrototypeFunction(ExecState*, JSGlobalObject*, int length, const Identifier&, NativeFunction); + PrototypeFunction(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, int length, const Identifier&, NativeFunction); private: virtual CallType getCallData(CallData&); diff --git a/JavaScriptCore/runtime/PutPropertySlot.h b/JavaScriptCore/runtime/PutPropertySlot.h index eb8ea8a..4b0b394 100644 --- a/JavaScriptCore/runtime/PutPropertySlot.h +++ b/JavaScriptCore/runtime/PutPropertySlot.h @@ -38,9 +38,10 @@ namespace JSC { public: enum Type { Uncachable, ExistingProperty, NewProperty }; - PutPropertySlot() + PutPropertySlot(bool isStrictMode = false) : m_type(Uncachable) , m_base(0) + , m_isStrictMode(isStrictMode) { } @@ -61,6 +62,7 @@ namespace JSC { Type type() const { return m_type; } JSObject* base() const { return m_base; } + bool isStrictMode() const { return m_isStrictMode; } bool isCacheable() const { return m_type != Uncachable; } size_t cachedOffset() const { ASSERT(isCacheable()); @@ -70,6 +72,7 @@ namespace JSC { Type m_type; JSObject* m_base; size_t m_offset; + bool m_isStrictMode; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/RegExp.cpp b/JavaScriptCore/runtime/RegExp.cpp index 4e958f4..a33fa91 100644 --- a/JavaScriptCore/runtime/RegExp.cpp +++ b/JavaScriptCore/runtime/RegExp.cpp @@ -28,9 +28,6 @@ #include <wtf/Assertions.h> #include <wtf/OwnArrayPtr.h> - -#if ENABLE(YARR) - #include "yarr/RegexCompiler.h" #if ENABLE(YARR_JIT) #include "yarr/RegexJIT.h" @@ -38,75 +35,59 @@ #include "yarr/RegexInterpreter.h" #endif -#else - -#if ENABLE(WREC) -#include "JIT.h" -#include "WRECGenerator.h" -#endif -#include <pcre/pcre.h> - -#endif - namespace JSC { -#if ENABLE(WREC) -using namespace WREC; +struct RegExpRepresentation { +#if ENABLE(YARR_JIT) + Yarr::RegexCodeBlock m_regExpJITCode; +#else + OwnPtr<Yarr::BytecodePattern> m_regExpBytecode; #endif - -inline RegExp::RegExp(JSGlobalData* globalData, const UString& pattern) - : m_pattern(pattern) - , m_flagBits(0) - , m_constructionError(0) - , m_numSubpatterns(0) -{ - compile(globalData); -} +}; inline RegExp::RegExp(JSGlobalData* globalData, const UString& pattern, const UString& flags) : m_pattern(pattern) , m_flagBits(0) , m_constructionError(0) , m_numSubpatterns(0) +#if ENABLE(REGEXP_TRACING) + , m_rtMatchCallCount(0) + , m_rtMatchFoundCount(0) +#endif + , m_representation(adoptPtr(new RegExpRepresentation)) { // NOTE: The global flag is handled on a case-by-case basis by functions like // String::match and RegExpObject::match. - if (flags.find('g') != -1) - m_flagBits |= Global; - if (flags.find('i') != -1) - m_flagBits |= IgnoreCase; - if (flags.find('m') != -1) - m_flagBits |= Multiline; - + if (!flags.isNull()) { + if (flags.find('g') != notFound) + m_flagBits |= Global; + if (flags.find('i') != notFound) + m_flagBits |= IgnoreCase; + if (flags.find('m') != notFound) + m_flagBits |= Multiline; + } compile(globalData); } -#if !ENABLE(YARR) RegExp::~RegExp() { - jsRegExpFree(m_regExp); -} -#endif - -PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& pattern) -{ - return adoptRef(new RegExp(globalData, pattern)); } PassRefPtr<RegExp> RegExp::create(JSGlobalData* globalData, const UString& pattern, const UString& flags) { - return adoptRef(new RegExp(globalData, pattern, flags)); + RefPtr<RegExp> res = adoptRef(new RegExp(globalData, pattern, flags)); +#if ENABLE(REGEXP_TRACING) + globalData->addRegExpToTrace(res); +#endif + return res.release(); } -#if ENABLE(YARR) - void RegExp::compile(JSGlobalData* globalData) { #if ENABLE(YARR_JIT) - Yarr::jitCompileRegex(globalData, m_regExpJITCode, m_pattern, m_numSubpatterns, m_constructionError, ignoreCase(), multiline()); + Yarr::jitCompileRegex(globalData, m_representation->m_regExpJITCode, m_pattern, m_numSubpatterns, m_constructionError, &globalData->m_regexAllocator, ignoreCase(), multiline()); #else - UNUSED_PARAM(globalData); - m_regExpBytecode.set(Yarr::byteCompileRegex(m_pattern, m_numSubpatterns, m_constructionError, ignoreCase(), multiline())); + m_representation->m_regExpBytecode = Yarr::byteCompileRegex(m_pattern, m_numSubpatterns, m_constructionError, &globalData->m_regexAllocator, ignoreCase(), multiline()); #endif } @@ -114,18 +95,20 @@ int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) { if (startOffset < 0) startOffset = 0; - if (ovector) - ovector->clear(); + +#if ENABLE(REGEXP_TRACING) + m_rtMatchCallCount++; +#endif - if (startOffset > s.size() || s.isNull()) + if (static_cast<unsigned>(startOffset) > s.length() || s.isNull()) return -1; #if ENABLE(YARR_JIT) - if (!!m_regExpJITCode) { + if (!!m_representation->m_regExpJITCode) { #else - if (m_regExpBytecode) { + if (m_representation->m_regExpBytecode) { #endif - int offsetVectorSize = (m_numSubpatterns + 1) * 3; // FIXME: should be 2 - but adding temporary fallback to pcre. + int offsetVectorSize = (m_numSubpatterns + 1) * 2; int* offsetVector; Vector<int, 32> nonReturnedOvector; if (ovector) { @@ -137,123 +120,58 @@ int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) } ASSERT(offsetVector); - for (int j = 0; j < offsetVectorSize; ++j) + // Initialize offsetVector with the return value (index 0) and the + // first subpattern start indicies (even index values) set to -1. + // No need to init the subpattern end indicies. + for (unsigned j = 0, i = 0; i < m_numSubpatterns + 1; j += 2, i++) offsetVector[j] = -1; - #if ENABLE(YARR_JIT) - int result = Yarr::executeRegex(m_regExpJITCode, s.data(), startOffset, s.size(), offsetVector, offsetVectorSize); + int result = Yarr::executeRegex(m_representation->m_regExpJITCode, s.characters(), startOffset, s.length(), offsetVector); #else - int result = Yarr::interpretRegex(m_regExpBytecode.get(), s.data(), startOffset, s.size(), offsetVector); + int result = Yarr::interpretRegex(m_representation->m_regExpBytecode.get(), s.characters(), startOffset, s.length(), offsetVector); #endif - if (result < 0) { -#ifndef NDEBUG - // TODO: define up a symbol, rather than magic -1 - if (result != -1) - fprintf(stderr, "jsRegExpExecute failed with result %d\n", result); + ASSERT(result >= -1);; + +#if ENABLE(REGEXP_TRACING) + if (result != -1) + m_rtMatchFoundCount++; #endif - if (ovector) - ovector->clear(); - } + return result; } return -1; } -#else - -void RegExp::compile(JSGlobalData* globalData) -{ - m_regExp = 0; -#if ENABLE(WREC) - m_wrecFunction = Generator::compileRegExp(globalData, m_pattern, &m_numSubpatterns, &m_constructionError, m_executablePool, ignoreCase(), multiline()); - if (m_wrecFunction || m_constructionError) - return; - // Fall through to non-WREC case. -#else - UNUSED_PARAM(globalData); -#endif - - JSRegExpIgnoreCaseOption ignoreCaseOption = ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase; - JSRegExpMultilineOption multilineOption = multiline() ? JSRegExpMultiline : JSRegExpSingleLine; - m_regExp = jsRegExpCompile(reinterpret_cast<const UChar*>(m_pattern.data()), m_pattern.size(), ignoreCaseOption, multilineOption, &m_numSubpatterns, &m_constructionError); -} - -int RegExp::match(const UString& s, int startOffset, Vector<int, 32>* ovector) -{ - if (startOffset < 0) - startOffset = 0; - if (ovector) - ovector->clear(); - - if (startOffset > s.size() || s.isNull()) - return -1; - -#if ENABLE(WREC) - if (m_wrecFunction) { - int offsetVectorSize = (m_numSubpatterns + 1) * 2; - int* offsetVector; - Vector<int, 32> nonReturnedOvector; - if (ovector) { - ovector->resize(offsetVectorSize); - offsetVector = ovector->data(); - } else { - nonReturnedOvector.resize(offsetVectorSize); - offsetVector = nonReturnedOvector.data(); - } - ASSERT(offsetVector); - for (int j = 0; j < offsetVectorSize; ++j) - offsetVector[j] = -1; - - int result = m_wrecFunction(s.data(), startOffset, s.size(), offsetVector); +#if ENABLE(REGEXP_TRACING) + void RegExp::printTraceData() + { + char formattedPattern[41]; + char rawPattern[41]; + + strncpy(rawPattern, m_pattern.utf8().data(), 40); + rawPattern[40]= '\0'; + + int pattLen = strlen(rawPattern); + + snprintf(formattedPattern, 41, (pattLen <= 38) ? "/%.38s/" : "/%.36s...", rawPattern); - if (result < 0) { -#ifndef NDEBUG - // TODO: define up a symbol, rather than magic -1 - if (result != -1) - fprintf(stderr, "jsRegExpExecute failed with result %d\n", result); -#endif - if (ovector) - ovector->clear(); - } - return result; - } else -#endif - if (m_regExp) { - // Set up the offset vector for the result. - // First 2/3 used for result, the last third used by PCRE. - int* offsetVector; - int offsetVectorSize; - int fixedSizeOffsetVector[3]; - if (!ovector) { - offsetVectorSize = 3; - offsetVector = fixedSizeOffsetVector; - } else { - offsetVectorSize = (m_numSubpatterns + 1) * 3; - ovector->resize(offsetVectorSize); - offsetVector = ovector->data(); - } +#if ENABLE(YARR_JIT) + Yarr::RegexCodeBlock& codeBlock = m_representation->m_regExpJITCode; - int numMatches = jsRegExpExecute(m_regExp, reinterpret_cast<const UChar*>(s.data()), s.size(), startOffset, offsetVector, offsetVectorSize); - - if (numMatches < 0) { -#ifndef NDEBUG - if (numMatches != JSRegExpErrorNoMatch) - fprintf(stderr, "jsRegExpExecute failed with result %d\n", numMatches); + char jitAddr[20]; + if (codeBlock.getFallback()) + sprintf(jitAddr, "fallback"); + else + sprintf(jitAddr, "0x%014lx", reinterpret_cast<unsigned long int>(codeBlock.getAddr())); +#else + const char* jitAddr = "JIT Off"; #endif - if (ovector) - ovector->clear(); - return -1; - } - - return offsetVector[0]; + + printf("%-40.40s %16.16s %10d %10d\n", formattedPattern, jitAddr, m_rtMatchCallCount, m_rtMatchFoundCount); } - - return -1; -} - #endif - + } // namespace JSC diff --git a/JavaScriptCore/runtime/RegExp.h b/JavaScriptCore/runtime/RegExp.h index 61ab0bc..e6e2fbc 100644 --- a/JavaScriptCore/runtime/RegExp.h +++ b/JavaScriptCore/runtime/RegExp.h @@ -23,26 +23,19 @@ #define RegExp_h #include "UString.h" -#include "WREC.h" #include "ExecutableAllocator.h" #include <wtf/Forward.h> #include <wtf/RefCounted.h> -#include "yarr/RegexJIT.h" -#include "yarr/RegexInterpreter.h" - -struct JSRegExp; namespace JSC { + struct RegExpRepresentation; class JSGlobalData; class RegExp : public RefCounted<RegExp> { public: - static PassRefPtr<RegExp> create(JSGlobalData* globalData, const UString& pattern); static PassRefPtr<RegExp> create(JSGlobalData* globalData, const UString& pattern, const UString& flags); -#if !ENABLE(YARR) ~RegExp(); -#endif bool global() const { return m_flagBits & Global; } bool ignoreCase() const { return m_flagBits & IgnoreCase; } @@ -55,9 +48,12 @@ namespace JSC { int match(const UString&, int startOffset, Vector<int, 32>* ovector = 0); unsigned numSubpatterns() const { return m_numSubpatterns; } + +#if ENABLE(REGEXP_TRACING) + void printTraceData(); +#endif private: - RegExp(JSGlobalData* globalData, const UString& pattern); RegExp(JSGlobalData* globalData, const UString& pattern, const UString& flags); void compile(JSGlobalData*); @@ -68,18 +64,12 @@ namespace JSC { int m_flagBits; const char* m_constructionError; unsigned m_numSubpatterns; - -#if ENABLE(YARR_JIT) - Yarr::RegexCodeBlock m_regExpJITCode; -#elif ENABLE(YARR) - OwnPtr<Yarr::BytecodePattern> m_regExpBytecode; -#else -#if ENABLE(WREC) - WREC::CompiledRegExp m_wrecFunction; - RefPtr<ExecutablePool> m_executablePool; -#endif - JSRegExp* m_regExp; +#if ENABLE(REGEXP_TRACING) + unsigned m_rtMatchCallCount; + unsigned m_rtMatchFoundCount; #endif + + OwnPtr<RegExpRepresentation> m_representation; }; } // namespace JSC diff --git a/JavaScriptCore/runtime/RegExpCache.cpp b/JavaScriptCore/runtime/RegExpCache.cpp new file mode 100644 index 0000000..d101758 --- /dev/null +++ b/JavaScriptCore/runtime/RegExpCache.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "RegExpCache.h" + +namespace JSC { + +PassRefPtr<RegExp> RegExpCache::lookupOrCreate(const UString& patternString, const UString& flags) +{ + if (patternString.length() < maxCacheablePatternLength) { + pair<RegExpCacheMap::iterator, bool> result = m_cacheMap.add(RegExpKey(flags, patternString), 0); + if (!result.second) + return result.first->second; + else + return create(patternString, flags, result.first); + } + return create(patternString, flags, m_cacheMap.end()); +} + +PassRefPtr<RegExp> RegExpCache::create(const UString& patternString, const UString& flags, RegExpCacheMap::iterator iterator) +{ + RefPtr<RegExp> regExp = RegExp::create(m_globalData, patternString, flags); + + if (patternString.length() >= maxCacheablePatternLength) + return regExp; + + RegExpKey key = RegExpKey(flags, patternString); + iterator->first = key; + iterator->second = regExp; + + ++m_nextKeyToEvict; + if (m_nextKeyToEvict == maxCacheableEntries) { + m_nextKeyToEvict = 0; + m_isFull = true; + } + if (m_isFull) + m_cacheMap.remove(RegExpKey(patternKeyArray[m_nextKeyToEvict].flagsValue, patternKeyArray[m_nextKeyToEvict].pattern)); + + patternKeyArray[m_nextKeyToEvict].flagsValue = key.flagsValue; + patternKeyArray[m_nextKeyToEvict].pattern = patternString.impl(); + return regExp; +} + +RegExpCache::RegExpCache(JSGlobalData* globalData) + : m_globalData(globalData) + , m_nextKeyToEvict(-1) + , m_isFull(false) +{ +} + +} diff --git a/JavaScriptCore/runtime/RegExpCache.h b/JavaScriptCore/runtime/RegExpCache.h new file mode 100644 index 0000000..e897b43 --- /dev/null +++ b/JavaScriptCore/runtime/RegExpCache.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "RegExp.h" +#include "RegExpKey.h" +#include "UString.h" +#include <wtf/FixedArray.h> +#include <wtf/HashMap.h> + +#ifndef RegExpCache_h +#define RegExpCache_h + +namespace JSC { + +class RegExpCache { + +typedef HashMap<RegExpKey, RefPtr<RegExp> > RegExpCacheMap; + +public: + PassRefPtr<RegExp> lookupOrCreate(const UString& patternString, const UString& flags); + PassRefPtr<RegExp> create(const UString& patternString, const UString& flags, RegExpCacheMap::iterator iterator); + RegExpCache(JSGlobalData* globalData); + +private: + static const unsigned maxCacheablePatternLength = 256; + static const int maxCacheableEntries = 256; + + FixedArray<RegExpKey, maxCacheableEntries> patternKeyArray; + RegExpCacheMap m_cacheMap; + JSGlobalData* m_globalData; + int m_nextKeyToEvict; + bool m_isFull; +}; + +} // namespace JSC + +#endif // RegExpCache_h diff --git a/JavaScriptCore/runtime/RegExpConstructor.cpp b/JavaScriptCore/runtime/RegExpConstructor.cpp index 6f00142..21ca170 100644 --- a/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -24,32 +24,37 @@ #include "ArrayPrototype.h" #include "Error.h" +#include "ExceptionHelpers.h" #include "JSArray.h" #include "JSFunction.h" #include "JSString.h" +#include "Lookup.h" #include "ObjectPrototype.h" #include "RegExpMatchesArray.h" #include "RegExpObject.h" #include "RegExpPrototype.h" #include "RegExp.h" +#include "RegExpCache.h" +#include "UStringConcatenate.h" +#include <wtf/PassOwnPtr.h> namespace JSC { -static JSValue regExpConstructorInput(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorMultiline(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorLastMatch(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorLastParen(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorLeftContext(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorRightContext(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar1(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar2(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar3(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar4(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar5(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar6(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar7(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar8(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpConstructorDollar9(ExecState*, const Identifier&, const PropertySlot&); +static JSValue regExpConstructorInput(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorMultiline(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorLastMatch(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorLastParen(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorLeftContext(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorRightContext(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar1(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar2(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar3(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar4(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar5(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar6(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar7(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar8(ExecState*, JSValue, const Identifier&); +static JSValue regExpConstructorDollar9(ExecState*, JSValue, const Identifier&); static void setRegExpConstructorInput(ExecState*, JSObject*, JSValue); static void setRegExpConstructorMultiline(ExecState*, JSObject*, JSValue); @@ -90,19 +95,19 @@ const ClassInfo RegExpConstructor::info = { "Function", &InternalFunction::info, @end */ -RegExpConstructor::RegExpConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, "RegExp")) - , d(new RegExpConstructorPrivate) +RegExpConstructor::RegExpConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "RegExp")) + , d(adoptPtr(new RegExpConstructorPrivate)) { // ECMA 15.10.5.1 RegExp.prototype putDirectWithoutTransition(exec->propertyNames().prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly); // no. of arguments for constructor - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 2), ReadOnly | DontDelete | DontEnum); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(2), ReadOnly | DontDelete | DontEnum); } RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate* data) - : JSArray(exec->lexicalGlobalObject()->regExpMatchesArrayStructure(), data->lastNumSubPatterns + 1) + : JSArray(exec->lexicalGlobalObject()->regExpMatchesArrayStructure(), data->lastNumSubPatterns + 1, CreateInitialized) { RegExpConstructorPrivate* d = new RegExpConstructorPrivate; d->input = data->lastInput; @@ -113,17 +118,17 @@ RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate memcpy(d->lastOvector().data(), data->lastOvector().data(), offsetVectorSize * sizeof(int)); // d->multiline is not needed, and remains uninitialized - setLazyCreationData(d); + setSubclassData(d); } RegExpMatchesArray::~RegExpMatchesArray() { - delete static_cast<RegExpConstructorPrivate*>(lazyCreationData()); + delete static_cast<RegExpConstructorPrivate*>(subclassData()); } void RegExpMatchesArray::fillArrayInstance(ExecState* exec) { - RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(lazyCreationData()); + RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(subclassData()); ASSERT(d); unsigned lastNumSubpatterns = d->lastNumSubPatterns; @@ -137,11 +142,11 @@ void RegExpMatchesArray::fillArrayInstance(ExecState* exec) } PutPropertySlot slot; - JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector()[0]), slot); + JSArray::put(exec, exec->propertyNames().index, jsNumber(d->lastOvector()[0]), slot); JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->input), slot); delete d; - setLazyCreationData(0); + setSubclassData(0); } JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const @@ -181,7 +186,7 @@ JSValue RegExpConstructor::getLeftContext(ExecState* exec) const JSValue RegExpConstructor::getRightContext(ExecState* exec) const { if (!d->lastOvector().isEmpty()) - return jsSubstring(exec, d->lastInput, d->lastOvector()[1], d->lastInput.size() - d->lastOvector()[1]); + return jsSubstring(exec, d->lastInput, d->lastOvector()[1], d->lastInput.length() - d->lastOvector()[1]); return jsEmptyString(exec); } @@ -195,79 +200,79 @@ bool RegExpConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifi return getStaticValueDescriptor<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, descriptor); } -JSValue regExpConstructorDollar1(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar1(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 1); + return asRegExpConstructor(slotBase)->getBackref(exec, 1); } -JSValue regExpConstructorDollar2(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar2(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 2); + return asRegExpConstructor(slotBase)->getBackref(exec, 2); } -JSValue regExpConstructorDollar3(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar3(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 3); + return asRegExpConstructor(slotBase)->getBackref(exec, 3); } -JSValue regExpConstructorDollar4(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar4(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 4); + return asRegExpConstructor(slotBase)->getBackref(exec, 4); } -JSValue regExpConstructorDollar5(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar5(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 5); + return asRegExpConstructor(slotBase)->getBackref(exec, 5); } -JSValue regExpConstructorDollar6(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar6(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 6); + return asRegExpConstructor(slotBase)->getBackref(exec, 6); } -JSValue regExpConstructorDollar7(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar7(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 7); + return asRegExpConstructor(slotBase)->getBackref(exec, 7); } -JSValue regExpConstructorDollar8(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar8(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 8); + return asRegExpConstructor(slotBase)->getBackref(exec, 8); } -JSValue regExpConstructorDollar9(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorDollar9(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 9); + return asRegExpConstructor(slotBase)->getBackref(exec, 9); } -JSValue regExpConstructorInput(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorInput(ExecState* exec, JSValue slotBase, const Identifier&) { - return jsString(exec, asRegExpConstructor(slot.slotBase())->input()); + return jsString(exec, asRegExpConstructor(slotBase)->input()); } -JSValue regExpConstructorMultiline(ExecState*, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorMultiline(ExecState*, JSValue slotBase, const Identifier&) { - return jsBoolean(asRegExpConstructor(slot.slotBase())->multiline()); + return jsBoolean(asRegExpConstructor(slotBase)->multiline()); } -JSValue regExpConstructorLastMatch(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorLastMatch(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getBackref(exec, 0); + return asRegExpConstructor(slotBase)->getBackref(exec, 0); } -JSValue regExpConstructorLastParen(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorLastParen(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getLastParen(exec); + return asRegExpConstructor(slotBase)->getLastParen(exec); } -JSValue regExpConstructorLeftContext(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorLeftContext(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getLeftContext(exec); + return asRegExpConstructor(slotBase)->getLeftContext(exec); } -JSValue regExpConstructorRightContext(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpConstructorRightContext(ExecState* exec, JSValue slotBase, const Identifier&) { - return asRegExpConstructor(slot.slotBase())->getRightContext(exec); + return asRegExpConstructor(slotBase)->getRightContext(exec); } void RegExpConstructor::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) @@ -293,22 +298,23 @@ JSObject* constructRegExp(ExecState* exec, const ArgList& args) if (arg0.inherits(&RegExpObject::info)) { if (!arg1.isUndefined()) - return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another."); + return throwError(exec, createTypeError(exec, "Cannot supply flags when constructing one RegExp from another.")); return asObject(arg0); } UString pattern = arg0.isUndefined() ? UString("") : arg0.toString(exec); UString flags = arg1.isUndefined() ? UString("") : arg1.toString(exec); - RefPtr<RegExp> regExp = RegExp::create(&exec->globalData(), pattern, flags); + RefPtr<RegExp> regExp = exec->globalData().regExpCache()->lookupOrCreate(pattern, flags); if (!regExp->isValid()) - return throwError(exec, SyntaxError, makeString("Invalid regular expression: ", regExp->errorMessage())); - return new (exec) RegExpObject(exec->lexicalGlobalObject()->regExpStructure(), regExp.release()); + return throwError(exec, createSyntaxError(exec, makeUString("Invalid regular expression: ", regExp->errorMessage()))); + return new (exec) RegExpObject(exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp.release()); } -static JSObject* constructWithRegExpConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec) { - return constructRegExp(exec, args); + ArgList args(exec); + return JSValue::encode(constructRegExp(exec, args)); } ConstructType RegExpConstructor::getConstructData(ConstructData& constructData) @@ -318,9 +324,10 @@ ConstructType RegExpConstructor::getConstructData(ConstructData& constructData) } // ECMA 15.10.3 -static JSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec) { - return constructRegExp(exec, args); + ArgList args(exec); + return JSValue::encode(constructRegExp(exec, args)); } CallType RegExpConstructor::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/RegExpConstructor.h b/JavaScriptCore/runtime/RegExpConstructor.h index 8f4be71..58abde5 100644 --- a/JavaScriptCore/runtime/RegExpConstructor.h +++ b/JavaScriptCore/runtime/RegExpConstructor.h @@ -55,7 +55,7 @@ namespace JSC { class RegExpConstructor : public InternalFunction { public: - RegExpConstructor(ExecState*, NonNullPassRefPtr<Structure>, RegExpPrototype*); + RegExpConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, RegExpPrototype*); static PassRefPtr<Structure> createStructure(JSValue prototype) { @@ -109,7 +109,7 @@ namespace JSC { expression matching through the performMatch function. We use cached results to calculate, e.g., RegExp.lastMatch and RegExp.leftParen. */ - inline void RegExpConstructor::performMatch(RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector) + ALWAYS_INLINE void RegExpConstructor::performMatch(RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector) { position = r->match(s, startOffset, &d->tempOvector()); diff --git a/JavaScriptCore/runtime/RegExpKey.h b/JavaScriptCore/runtime/RegExpKey.h new file mode 100644 index 0000000..cd1368d --- /dev/null +++ b/JavaScriptCore/runtime/RegExpKey.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 University of Szeged + * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu) + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 "UString.h" +#include <wtf/text/StringHash.h> + +#ifndef RegExpKey_h +#define RegExpKey_h + +namespace JSC { + +struct RegExpKey { + int flagsValue; + RefPtr<StringImpl> pattern; + + RegExpKey() + : flagsValue(0) + { + } + + RegExpKey(int flags) + : flagsValue(flags) + { + } + + RegExpKey(int flags, const UString& pattern) + : flagsValue(flags) + , pattern(pattern.impl()) + { + } + + RegExpKey(int flags, const PassRefPtr<StringImpl> pattern) + : flagsValue(flags) + , pattern(pattern) + { + } + + RegExpKey(int flags, const RefPtr<StringImpl>& pattern) + : flagsValue(flags) + , pattern(pattern) + { + } + + RegExpKey(const UString& flags, const UString& pattern) + : pattern(pattern.impl()) + { + flagsValue = getFlagsValue(flags); + } + + int getFlagsValue(const UString flags) + { + flagsValue = 0; + if (flags.find('g') != notFound) + flagsValue += 4; + if (flags.find('i') != notFound) + flagsValue += 2; + if (flags.find('m') != notFound) + flagsValue += 1; + return flagsValue; + } +}; + +inline bool operator==(const RegExpKey& a, const RegExpKey& b) +{ + if (a.flagsValue != b.flagsValue) + return false; + if (!a.pattern) + return !b.pattern; + if (!b.pattern) + return false; + return equal(a.pattern.get(), b.pattern.get()); +} + +} // namespace JSC + +namespace WTF { +template<typename T> struct DefaultHash; +template<typename T> struct RegExpHash; + +template<> struct RegExpHash<JSC::RegExpKey> { + static unsigned hash(const JSC::RegExpKey& key) { return key.pattern->hash(); } + static bool equal(const JSC::RegExpKey& a, const JSC::RegExpKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +template<> struct DefaultHash<JSC::RegExpKey> { + typedef RegExpHash<JSC::RegExpKey> Hash; +}; + +template<> struct HashTraits<JSC::RegExpKey> : GenericHashTraits<JSC::RegExpKey> { + static void constructDeletedValue(JSC::RegExpKey& slot) { slot.flagsValue = -1; } + static bool isDeletedValue(const JSC::RegExpKey& value) { return value.flagsValue == -1; } +}; +} // namespace WTF + +#endif // RegExpKey_h diff --git a/JavaScriptCore/runtime/RegExpMatchesArray.h b/JavaScriptCore/runtime/RegExpMatchesArray.h index 38d3cb4..b823621 100644 --- a/JavaScriptCore/runtime/RegExpMatchesArray.h +++ b/JavaScriptCore/runtime/RegExpMatchesArray.h @@ -32,56 +32,56 @@ namespace JSC { private: virtual bool getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); return JSArray::getOwnPropertySlot(exec, propertyName, slot); } virtual bool getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); return JSArray::getOwnPropertySlot(exec, propertyName, slot); } virtual bool getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); return JSArray::getOwnPropertyDescriptor(exec, propertyName, descriptor); } virtual void put(ExecState* exec, const Identifier& propertyName, JSValue v, PutPropertySlot& slot) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); JSArray::put(exec, propertyName, v, slot); } virtual void put(ExecState* exec, unsigned propertyName, JSValue v) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); JSArray::put(exec, propertyName, v); } virtual bool deleteProperty(ExecState* exec, const Identifier& propertyName) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); return JSArray::deleteProperty(exec, propertyName); } virtual bool deleteProperty(ExecState* exec, unsigned propertyName) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); return JSArray::deleteProperty(exec, propertyName); } virtual void getOwnPropertyNames(ExecState* exec, PropertyNameArray& arr, EnumerationMode mode = ExcludeDontEnumProperties) { - if (lazyCreationData()) + if (subclassData()) fillArrayInstance(exec); JSArray::getOwnPropertyNames(exec, arr, mode); } diff --git a/JavaScriptCore/runtime/RegExpObject.cpp b/JavaScriptCore/runtime/RegExpObject.cpp index 42bfcef..7fda5b1 100644 --- a/JavaScriptCore/runtime/RegExpObject.cpp +++ b/JavaScriptCore/runtime/RegExpObject.cpp @@ -22,19 +22,23 @@ #include "RegExpObject.h" #include "Error.h" +#include "ExceptionHelpers.h" #include "JSArray.h" #include "JSGlobalObject.h" #include "JSString.h" +#include "Lookup.h" #include "RegExpConstructor.h" #include "RegExpPrototype.h" +#include "UStringConcatenate.h" +#include <wtf/PassOwnPtr.h> namespace JSC { -static JSValue regExpObjectGlobal(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectIgnoreCase(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectMultiline(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectSource(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectLastIndex(ExecState*, const Identifier&, const PropertySlot&); +static JSValue regExpObjectGlobal(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectIgnoreCase(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectMultiline(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectSource(ExecState*, JSValue, const Identifier&); +static JSValue regExpObjectLastIndex(ExecState*, JSValue, const Identifier&); static void setRegExpObjectLastIndex(ExecState*, JSObject*, JSValue); } // namespace JSC @@ -57,9 +61,9 @@ const ClassInfo RegExpObject::info = { "RegExp", 0, 0, ExecState::regExpTable }; @end */ -RegExpObject::RegExpObject(NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<RegExp> regExp) - : JSObject(structure) - , d(new RegExpObjectData(regExp, 0)) +RegExpObject::RegExpObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, NonNullPassRefPtr<RegExp> regExp) + : JSObjectWithGlobalObject(globalObject, structure) + , d(adoptPtr(new RegExpObjectData(regExp, 0))) { } @@ -77,29 +81,29 @@ bool RegExpObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& p return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), this, propertyName, descriptor); } -JSValue regExpObjectGlobal(ExecState*, const Identifier&, const PropertySlot& slot) +JSValue regExpObjectGlobal(ExecState*, JSValue slotBase, const Identifier&) { - return jsBoolean(asRegExpObject(slot.slotBase())->regExp()->global()); + return jsBoolean(asRegExpObject(slotBase)->regExp()->global()); } -JSValue regExpObjectIgnoreCase(ExecState*, const Identifier&, const PropertySlot& slot) +JSValue regExpObjectIgnoreCase(ExecState*, JSValue slotBase, const Identifier&) { - return jsBoolean(asRegExpObject(slot.slotBase())->regExp()->ignoreCase()); + return jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase()); } -JSValue regExpObjectMultiline(ExecState*, const Identifier&, const PropertySlot& slot) +JSValue regExpObjectMultiline(ExecState*, JSValue slotBase, const Identifier&) { - return jsBoolean(asRegExpObject(slot.slotBase())->regExp()->multiline()); + return jsBoolean(asRegExpObject(slotBase)->regExp()->multiline()); } -JSValue regExpObjectSource(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpObjectSource(ExecState* exec, JSValue slotBase, const Identifier&) { - return jsString(exec, asRegExpObject(slot.slotBase())->regExp()->pattern()); + return jsString(exec, asRegExpObject(slotBase)->regExp()->pattern()); } -JSValue regExpObjectLastIndex(ExecState* exec, const Identifier&, const PropertySlot& slot) +JSValue regExpObjectLastIndex(ExecState*, JSValue slotBase, const Identifier&) { - return jsNumber(exec, asRegExpObject(slot.slotBase())->lastIndex()); + return jsNumber(asRegExpObject(slotBase)->lastIndex()); } void RegExpObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) @@ -112,21 +116,21 @@ void setRegExpObjectLastIndex(ExecState* exec, JSObject* baseObject, JSValue val asRegExpObject(baseObject)->setLastIndex(value.toInteger(exec)); } -JSValue RegExpObject::test(ExecState* exec, const ArgList& args) +JSValue RegExpObject::test(ExecState* exec) { - return jsBoolean(match(exec, args)); + return jsBoolean(match(exec)); } -JSValue RegExpObject::exec(ExecState* exec, const ArgList& args) +JSValue RegExpObject::exec(ExecState* exec) { - if (match(exec, args)) + if (match(exec)) return exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec); return jsNull(); } -static JSValue JSC_HOST_CALL callRegExpObject(ExecState* exec, JSObject* function, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callRegExpObject(ExecState* exec) { - return asRegExpObject(function)->exec(exec, args); + return JSValue::encode(asRegExpObject(exec->callee())->exec(exec)); } CallType RegExpObject::getCallData(CallData& callData) @@ -136,13 +140,13 @@ CallType RegExpObject::getCallData(CallData& callData) } // Shared implementation used by test and exec. -bool RegExpObject::match(ExecState* exec, const ArgList& args) +bool RegExpObject::match(ExecState* exec) { RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); - UString input = args.isEmpty() ? regExpConstructor->input() : args.at(0).toString(exec); + UString input = !exec->argumentCount() ? regExpConstructor->input() : exec->argument(0).toString(exec); if (input.isNull()) { - throwError(exec, GeneralError, makeString("No input to ", toString(exec), ".")); + throwError(exec, createError(exec, makeUString("No input to ", toString(exec), "."))); return false; } @@ -153,7 +157,7 @@ bool RegExpObject::match(ExecState* exec, const ArgList& args) return position >= 0; } - if (d->lastIndex < 0 || d->lastIndex > input.size()) { + if (d->lastIndex < 0 || d->lastIndex > input.length()) { d->lastIndex = 0; return false; } diff --git a/JavaScriptCore/runtime/RegExpObject.h b/JavaScriptCore/runtime/RegExpObject.h index 4ad11ef..19de929 100644 --- a/JavaScriptCore/runtime/RegExpObject.h +++ b/JavaScriptCore/runtime/RegExpObject.h @@ -21,14 +21,14 @@ #ifndef RegExpObject_h #define RegExpObject_h -#include "JSObject.h" +#include "JSObjectWithGlobalObject.h" #include "RegExp.h" namespace JSC { - class RegExpObject : public JSObject { + class RegExpObject : public JSObjectWithGlobalObject { public: - RegExpObject(NonNullPassRefPtr<Structure>, NonNullPassRefPtr<RegExp>); + RegExpObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure>, NonNullPassRefPtr<RegExp>); virtual ~RegExpObject(); void setRegExp(PassRefPtr<RegExp> r) { d->regExp = r; } @@ -37,15 +37,15 @@ namespace JSC { void setLastIndex(double lastIndex) { d->lastIndex = lastIndex; } double lastIndex() const { return d->lastIndex; } - JSValue test(ExecState*, const ArgList&); - JSValue exec(ExecState*, const ArgList&); + JSValue test(ExecState*); + JSValue exec(ExecState*); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); virtual const ClassInfo* classInfo() const { return &info; } - static const ClassInfo info; + static JS_EXPORTDATA const ClassInfo info; static PassRefPtr<Structure> createStructure(JSValue prototype) { @@ -53,10 +53,10 @@ namespace JSC { } protected: - static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSObject::StructureFlags; - + static const unsigned StructureFlags = OverridesGetOwnPropertySlot | JSObjectWithGlobalObject::StructureFlags; + private: - bool match(ExecState*, const ArgList&); + bool match(ExecState*); virtual CallType getCallData(CallData&); diff --git a/JavaScriptCore/runtime/RegExpPrototype.cpp b/JavaScriptCore/runtime/RegExpPrototype.cpp index dd5fe02..0a4c8bf 100644 --- a/JavaScriptCore/runtime/RegExpPrototype.cpp +++ b/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -33,78 +33,82 @@ #include "PrototypeFunction.h" #include "RegExpObject.h" #include "RegExp.h" +#include "RegExpCache.h" +#include "UStringConcatenate.h" namespace JSC { ASSERT_CLASS_FITS_IN_CELL(RegExpPrototype); -static JSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState*); +static EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*); // ECMA 15.10.5 -const ClassInfo RegExpPrototype::info = { "RegExpPrototype", 0, 0, 0 }; - -RegExpPrototype::RegExpPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) - : JSObject(structure) +RegExpPrototype::RegExpPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure) + : RegExpObject(globalObject, structure, RegExp::create(&exec->globalData(), "", "")) { - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().compile, regExpProtoFuncCompile), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().exec, regExpProtoFuncExec), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().test, regExpProtoFuncTest), DontEnum); - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, regExpProtoFuncToString), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().compile, regExpProtoFuncCompile), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().exec, regExpProtoFuncExec), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().test, regExpProtoFuncTest), DontEnum); + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 0, exec->propertyNames().toString, regExpProtoFuncToString), DontEnum); } // ------------------------------ Functions --------------------------- - -JSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) + +EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::info)) - return throwError(exec, TypeError); - return asRegExpObject(thisValue)->test(exec, args); + return throwVMTypeError(exec); + return JSValue::encode(asRegExpObject(thisValue)->test(exec)); } -JSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::info)) - return throwError(exec, TypeError); - return asRegExpObject(thisValue)->exec(exec, args); + return throwVMTypeError(exec); + return JSValue::encode(asRegExpObject(thisValue)->exec(exec)); } -JSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::info)) - return throwError(exec, TypeError); + return throwVMTypeError(exec); RefPtr<RegExp> regExp; - JSValue arg0 = args.at(0); - JSValue arg1 = args.at(1); + JSValue arg0 = exec->argument(0); + JSValue arg1 = exec->argument(1); if (arg0.inherits(&RegExpObject::info)) { if (!arg1.isUndefined()) - return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another."); + return throwVMError(exec, createTypeError(exec, "Cannot supply flags when constructing one RegExp from another.")); regExp = asRegExpObject(arg0)->regExp(); } else { - UString pattern = args.isEmpty() ? UString("") : arg0.toString(exec); + UString pattern = !exec->argumentCount() ? UString("") : arg0.toString(exec); UString flags = arg1.isUndefined() ? UString("") : arg1.toString(exec); - regExp = RegExp::create(&exec->globalData(), pattern, flags); + regExp = exec->globalData().regExpCache()->lookupOrCreate(pattern, flags); } if (!regExp->isValid()) - return throwError(exec, SyntaxError, makeString("Invalid regular expression: ", regExp->errorMessage())); + return throwVMError(exec, createSyntaxError(exec, makeUString("Invalid regular expression: ", regExp->errorMessage()))); asRegExpObject(thisValue)->setRegExp(regExp.release()); asRegExpObject(thisValue)->setLastIndex(0); - return jsUndefined(); + return JSValue::encode(jsUndefined()); } -JSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); if (!thisValue.inherits(&RegExpObject::info)) { if (thisValue.inherits(&RegExpPrototype::info)) - return jsNontrivialString(exec, "//"); - return throwError(exec, TypeError); + return JSValue::encode(jsNontrivialString(exec, "//")); + return throwVMTypeError(exec); } char postfix[5] = { '/', 0, 0, 0, 0 }; @@ -117,7 +121,7 @@ JSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec, JSObject*, JSValu postfix[index] = 'm'; UString source = asRegExpObject(thisValue)->get(exec, exec->propertyNames().source).toString(exec); // If source is empty, use "/(?:)/" to avoid colliding with comment syntax - return jsMakeNontrivialString(exec, "/", source.size() ? source : UString("(?:)"), postfix); + return JSValue::encode(jsMakeNontrivialString(exec, "/", source.length() ? source : UString("(?:)"), postfix)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/RegExpPrototype.h b/JavaScriptCore/runtime/RegExpPrototype.h index d3979bd..eb4ae00 100644 --- a/JavaScriptCore/runtime/RegExpPrototype.h +++ b/JavaScriptCore/runtime/RegExpPrototype.h @@ -21,16 +21,14 @@ #ifndef RegExpPrototype_h #define RegExpPrototype_h +#include "RegExpObject.h" #include "JSObject.h" namespace JSC { - class RegExpPrototype : public JSObject { + class RegExpPrototype : public RegExpObject { public: - RegExpPrototype(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); - - virtual const ClassInfo* classInfo() const { return &info; } - static const ClassInfo info; + RegExpPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure); }; } // namespace JSC diff --git a/JavaScriptCore/runtime/StringBuilder.h b/JavaScriptCore/runtime/RopeImpl.cpp index 27dbbd7..09c24a9 100644 --- a/JavaScriptCore/runtime/StringBuilder.h +++ b/JavaScriptCore/runtime/RopeImpl.cpp @@ -23,61 +23,40 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef StringBuilder_h -#define StringBuilder_h - -#include <wtf/Vector.h> +#include "config.h" +#include "RopeImpl.h" namespace JSC { -class StringBuilder { -public: - void append(const UChar u) - { - buffer.append(u); - } - - void append(const char* str) - { - append(str, strlen(str)); - } - - void append(const char* str, size_t len) - { - buffer.reserveCapacity(buffer.size() + len); - for (size_t i = 0; i < len; i++) - buffer.append(static_cast<unsigned char>(str[i])); - } - - void append(const UChar* str, size_t len) - { - buffer.append(str, len); - } - - void append(const UString& str) - { - buffer.append(str.data(), str.size()); +void RopeImpl::derefFibersNonRecursive(Vector<RopeImpl*, 32>& workQueue) +{ + unsigned fiberCount = this->fiberCount(); + for (unsigned i = 0; i < fiberCount; ++i) { + Fiber& fiber = m_fibers[i]; + if (isRope(fiber)) { + RopeImpl* nextRope = static_cast<RopeImpl*>(fiber); + if (nextRope->hasOneRef()) + workQueue.append(nextRope); + else + nextRope->deref(); + } else + static_cast<StringImpl*>(fiber)->deref(); } +} - bool isEmpty() { return buffer.isEmpty(); } - void reserveCapacity(size_t newCapacity) { buffer.reserveCapacity(newCapacity); } - void resize(size_t size) { buffer.resize(size); } - size_t size() const { return buffer.size(); } +void RopeImpl::destructNonRecursive() +{ + Vector<RopeImpl*, 32> workQueue; - UChar operator[](size_t i) const { return buffer.at(i); } + derefFibersNonRecursive(workQueue); + delete this; - UString build() - { - buffer.shrinkToFit(); - if (buffer.size() && !buffer.data()) - CRASH(); - return UString::adopt(buffer); + while (!workQueue.isEmpty()) { + RopeImpl* rope = workQueue.last(); + workQueue.removeLast(); + rope->derefFibersNonRecursive(workQueue); + delete rope; } - -protected: - Vector<UChar, 64> buffer; -}; - } -#endif +} // namespace JSC diff --git a/JavaScriptCore/runtime/RopeImpl.h b/JavaScriptCore/runtime/RopeImpl.h new file mode 100644 index 0000000..dfacbf5 --- /dev/null +++ b/JavaScriptCore/runtime/RopeImpl.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009, 2010 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 RopeImpl_h +#define RopeImpl_h + +#include <wtf/text/StringImpl.h> + +namespace JSC { + +class RopeImpl : public StringImplBase { +public: + // A RopeImpl is composed from a set of smaller strings called Fibers. + // Each Fiber in a rope is either StringImpl or another RopeImpl. + typedef StringImplBase* Fiber; + + // Creates a RopeImpl comprising of 'fiberCount' Fibers. + // The RopeImpl is constructed in an uninitialized state - initialize must be called for each Fiber in the RopeImpl. + static PassRefPtr<RopeImpl> tryCreateUninitialized(unsigned fiberCount) + { + void* allocation; + if (tryFastMalloc(sizeof(RopeImpl) + (fiberCount - 1) * sizeof(Fiber)).getValue(allocation)) + return adoptRef(new (allocation) RopeImpl(fiberCount)); + return 0; + } + + static bool isRope(Fiber fiber) + { + return !fiber->isStringImpl(); + } + + static void deref(Fiber fiber) + { + if (isRope(fiber)) + static_cast<RopeImpl*>(fiber)->deref(); + else + static_cast<StringImpl*>(fiber)->deref(); + } + + void initializeFiber(unsigned &index, Fiber fiber) + { + m_fibers[index++] = fiber; + fiber->ref(); + m_length += fiber->length(); + } + + unsigned fiberCount() { return m_size; } + Fiber* fibers() { return m_fibers; } + + ALWAYS_INLINE void deref() + { + m_refCountAndFlags -= s_refCountIncrement; + if (!(m_refCountAndFlags & s_refCountMask)) + destructNonRecursive(); + } + +private: + RopeImpl(unsigned fiberCount) + : StringImplBase(ConstructNonStringImpl) + , m_size(fiberCount) + { + } + + void destructNonRecursive(); + void derefFibersNonRecursive(Vector<RopeImpl*, 32>& workQueue); + + bool hasOneRef() { return (m_refCountAndFlags & s_refCountMask) == s_refCountIncrement; } + + unsigned m_size; + Fiber m_fibers[1]; +}; + +} + +#endif diff --git a/JavaScriptCore/runtime/ScopeChain.cpp b/JavaScriptCore/runtime/ScopeChain.cpp index 981794b..54c5082 100644 --- a/JavaScriptCore/runtime/ScopeChain.cpp +++ b/JavaScriptCore/runtime/ScopeChain.cpp @@ -43,7 +43,7 @@ void ScopeChainNode::print() const fprintf(stderr, "----- [scope %p] -----\n", o); for (PropertyNameArray::const_iterator propIter = propertyNames.begin(); propIter != propEnd; propIter++) { Identifier name = *propIter; - fprintf(stderr, "%s, ", name.ascii()); + fprintf(stderr, "%s, ", name.ustring().utf8().data()); } fprintf(stderr, "\n"); } diff --git a/JavaScriptCore/runtime/SmallStrings.cpp b/JavaScriptCore/runtime/SmallStrings.cpp index d9d4377..f358727 100644 --- a/JavaScriptCore/runtime/SmallStrings.cpp +++ b/JavaScriptCore/runtime/SmallStrings.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2010 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,10 +28,11 @@ #include "JSGlobalObject.h" #include "JSString.h" - #include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> namespace JSC { + static const unsigned numCharactersToStore = 0x100; static inline bool isMarked(JSString* string) @@ -43,19 +44,19 @@ class SmallStringsStorage : public Noncopyable { public: SmallStringsStorage(); - UString::Rep* rep(unsigned char character) { return &m_reps[character]; } + StringImpl* rep(unsigned char character) { return m_reps[character].get(); } private: - UString::Rep m_reps[numCharactersToStore]; + RefPtr<StringImpl> m_reps[numCharactersToStore]; }; SmallStringsStorage::SmallStringsStorage() { UChar* characterBuffer = 0; - RefPtr<UStringImpl> baseString = UStringImpl::createUninitialized(numCharactersToStore, characterBuffer); + RefPtr<StringImpl> baseString = StringImpl::createUninitialized(numCharactersToStore, characterBuffer); for (unsigned i = 0; i < numCharactersToStore; ++i) { characterBuffer[i] = i; - new (&m_reps[i]) UString::Rep(&characterBuffer[i], 1, PassRefPtr<UStringImpl>(baseString)); + m_reps[i] = StringImpl::create(baseString, i, 1); } } @@ -83,7 +84,7 @@ void SmallStrings::markChildren(MarkStack& markStack) bool isAnyStringMarked = isMarked(m_emptyString); for (unsigned i = 0; i < numCharactersToStore && !isAnyStringMarked; ++i) - isAnyStringMarked |= isMarked(m_singleCharacterStrings[i]); + isAnyStringMarked = isMarked(m_singleCharacterStrings[i]); if (!isAnyStringMarked) { clear(); @@ -126,15 +127,15 @@ void SmallStrings::createEmptyString(JSGlobalData* globalData) void SmallStrings::createSingleCharacterString(JSGlobalData* globalData, unsigned char character) { if (!m_storage) - m_storage.set(new SmallStringsStorage); + m_storage = adoptPtr(new SmallStringsStorage); ASSERT(!m_singleCharacterStrings[character]); - m_singleCharacterStrings[character] = new (globalData) JSString(globalData, m_storage->rep(character), JSString::HasOtherOwner); + m_singleCharacterStrings[character] = new (globalData) JSString(globalData, PassRefPtr<StringImpl>(m_storage->rep(character)), JSString::HasOtherOwner); } -UString::Rep* SmallStrings::singleCharacterStringRep(unsigned char character) +StringImpl* SmallStrings::singleCharacterStringRep(unsigned char character) { if (!m_storage) - m_storage.set(new SmallStringsStorage); + m_storage = adoptPtr(new SmallStringsStorage); return m_storage->rep(character); } diff --git a/JavaScriptCore/runtime/SmallStrings.h b/JavaScriptCore/runtime/SmallStrings.h index cc11d0a..d1ebfb1 100644 --- a/JavaScriptCore/runtime/SmallStrings.h +++ b/JavaScriptCore/runtime/SmallStrings.h @@ -27,6 +27,7 @@ #define SmallStrings_h #include "UString.h" +#include <wtf/FixedArray.h> #include <wtf/OwnPtr.h> namespace JSC { @@ -54,19 +55,21 @@ namespace JSC { return m_singleCharacterStrings[character]; } - UString::Rep* singleCharacterStringRep(unsigned char character); + StringImpl* singleCharacterStringRep(unsigned char character); void markChildren(MarkStack&); void clear(); unsigned count() const; - +#if ENABLE(JIT) + JSString** singleCharacterStrings() { return m_singleCharacterStrings.data(); } +#endif private: void createEmptyString(JSGlobalData*); void createSingleCharacterString(JSGlobalData*, unsigned char); JSString* m_emptyString; - JSString* m_singleCharacterStrings[0x100]; + FixedArray<JSString*, 0x100> m_singleCharacterStrings; OwnPtr<SmallStringsStorage> m_storage; }; diff --git a/JavaScriptCore/runtime/StrictEvalActivation.cpp b/JavaScriptCore/runtime/StrictEvalActivation.cpp new file mode 100644 index 0000000..5bb013b --- /dev/null +++ b/JavaScriptCore/runtime/StrictEvalActivation.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 "StrictEvalActivation.h" + +namespace JSC { + +StrictEvalActivation::StrictEvalActivation(ExecState* exec) + : JSObject(exec->globalData().strictEvalActivationStructure) +{ +} + +bool StrictEvalActivation::deleteProperty(ExecState*, const Identifier&) +{ + return false; +} + +JSObject* StrictEvalActivation::toThisObject(ExecState* exec) const +{ + return exec->globalThisValue(); +} + +JSValue StrictEvalActivation::toStrictThisObject(ExecState*) const +{ + return jsNull(); +} + +} diff --git a/JavaScriptCore/runtime/StrictEvalActivation.h b/JavaScriptCore/runtime/StrictEvalActivation.h new file mode 100644 index 0000000..1385eec --- /dev/null +++ b/JavaScriptCore/runtime/StrictEvalActivation.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``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 ITS 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 StrictEvalActivation_h +#define StrictEvalActivation_h + +#include "JSObject.h" + +namespace JSC { + +class StrictEvalActivation : public JSObject { +public: + StrictEvalActivation(ExecState*); + virtual bool deleteProperty(ExecState*, const Identifier&); + virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; +}; + +} // namespace JSC + +#endif // StrictEvalActivation_h diff --git a/JavaScriptCore/runtime/StringConstructor.cpp b/JavaScriptCore/runtime/StringConstructor.cpp index c7b62bf..101650c 100644 --- a/JavaScriptCore/runtime/StringConstructor.cpp +++ b/JavaScriptCore/runtime/StringConstructor.cpp @@ -21,6 +21,8 @@ #include "config.h" #include "StringConstructor.h" +#include "Executable.h" +#include "JITCode.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "PrototypeFunction.h" @@ -28,44 +30,47 @@ namespace JSC { -static NEVER_INLINE JSValue stringFromCharCodeSlowCase(ExecState* exec, const ArgList& args) +static NEVER_INLINE JSValue stringFromCharCodeSlowCase(ExecState* exec) { - unsigned length = args.size(); + unsigned length = exec->argumentCount(); UChar* buf; - PassRefPtr<UStringImpl> impl = UStringImpl::createUninitialized(length, buf); + PassRefPtr<StringImpl> impl = StringImpl::createUninitialized(length, buf); for (unsigned i = 0; i < length; ++i) - buf[i] = static_cast<UChar>(args.at(i).toUInt32(exec)); + buf[i] = static_cast<UChar>(exec->argument(i).toUInt32(exec)); return jsString(exec, impl); } -static JSValue JSC_HOST_CALL stringFromCharCode(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL stringFromCharCode(ExecState* exec) { - if (LIKELY(args.size() == 1)) - return jsSingleCharacterString(exec, args.at(0).toUInt32(exec)); - return stringFromCharCodeSlowCase(exec, args); + if (LIKELY(exec->argumentCount() == 1)) + return JSValue::encode(jsSingleCharacterString(exec, exec->argument(0).toUInt32(exec))); + return JSValue::encode(stringFromCharCodeSlowCase(exec)); } ASSERT_CLASS_FITS_IN_CELL(StringConstructor); -StringConstructor::StringConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, StringPrototype* stringPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, stringPrototype->classInfo()->className)) +StringConstructor::StringConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, Structure* prototypeFunctionStructure, StringPrototype* stringPrototype) + : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, stringPrototype->classInfo()->className)) { // ECMA 15.5.3.1 String.prototype putDirectWithoutTransition(exec->propertyNames().prototype, stringPrototype, ReadOnly | DontEnum | DontDelete); // ECMA 15.5.3.2 fromCharCode() - putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().fromCharCode, stringFromCharCode), DontEnum); - +#if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL) + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().fromCharCode, exec->globalData().getHostFunction(stringFromCharCode, fromCharCodeThunkGenerator)), DontEnum); +#else + putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().fromCharCode, stringFromCharCode), DontEnum); +#endif // no. of arguments for constructor - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); } // ECMA 15.5.2 -static JSObject* constructWithStringConstructor(ExecState* exec, JSObject*, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL constructWithStringConstructor(ExecState* exec) { - if (args.isEmpty()) - return new (exec) StringObject(exec, exec->lexicalGlobalObject()->stringObjectStructure()); - return new (exec) StringObject(exec, exec->lexicalGlobalObject()->stringObjectStructure(), args.at(0).toString(exec)); + if (!exec->argumentCount()) + return JSValue::encode(new (exec) StringObject(exec, exec->lexicalGlobalObject()->stringObjectStructure())); + return JSValue::encode(new (exec) StringObject(exec, exec->lexicalGlobalObject()->stringObjectStructure(), exec->argument(0).toString(exec))); } ConstructType StringConstructor::getConstructData(ConstructData& constructData) @@ -75,11 +80,11 @@ ConstructType StringConstructor::getConstructData(ConstructData& constructData) } // ECMA 15.5.1 -static JSValue JSC_HOST_CALL callStringConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) +static EncodedJSValue JSC_HOST_CALL callStringConstructor(ExecState* exec) { - if (args.isEmpty()) - return jsEmptyString(exec); - return jsString(exec, args.at(0).toString(exec)); + if (!exec->argumentCount()) + return JSValue::encode(jsEmptyString(exec)); + return JSValue::encode(jsString(exec, exec->argument(0).toString(exec))); } CallType StringConstructor::getCallData(CallData& callData) diff --git a/JavaScriptCore/runtime/StringConstructor.h b/JavaScriptCore/runtime/StringConstructor.h index e511f7b..20f3a52 100644 --- a/JavaScriptCore/runtime/StringConstructor.h +++ b/JavaScriptCore/runtime/StringConstructor.h @@ -29,7 +29,7 @@ namespace JSC { class StringConstructor : public InternalFunction { public: - StringConstructor(ExecState*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, StringPrototype*); + StringConstructor(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>, Structure* prototypeFunctionStructure, StringPrototype*); virtual ConstructType getConstructData(ConstructData&); virtual CallType getCallData(CallData&); diff --git a/JavaScriptCore/runtime/StringObject.cpp b/JavaScriptCore/runtime/StringObject.cpp index f8e0e87..dc27618 100644 --- a/JavaScriptCore/runtime/StringObject.cpp +++ b/JavaScriptCore/runtime/StringObject.cpp @@ -80,7 +80,7 @@ bool StringObject::deleteProperty(ExecState* exec, const Identifier& propertyNam if (propertyName == exec->propertyNames().length) return false; bool isStrictUInt32; - unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); + unsigned i = propertyName.toUInt32(isStrictUInt32); if (isStrictUInt32 && internalValue()->canGetIndex(i)) return false; return JSObject::deleteProperty(exec, propertyName); @@ -90,7 +90,7 @@ void StringObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& prope { int size = internalValue()->length(); for (int i = 0; i < size; ++i) - propertyNames.add(Identifier(exec, UString::from(i))); + propertyNames.add(Identifier(exec, UString::number(i))); if (mode == IncludeDontEnumProperties) propertyNames.add(exec->propertyNames().length); return JSObject::getOwnPropertyNames(exec, propertyNames, mode); diff --git a/JavaScriptCore/runtime/StringPrototype.cpp b/JavaScriptCore/runtime/StringPrototype.cpp index 8c014ec..b5ea8fa 100644 --- a/JavaScriptCore/runtime/StringPrototype.cpp +++ b/JavaScriptCore/runtime/StringPrototype.cpp @@ -29,9 +29,11 @@ #include "JSArray.h" #include "JSFunction.h" #include "JSStringBuilder.h" +#include "Lookup.h" #include "ObjectPrototype.h" #include "Operations.h" #include "PropertyNameArray.h" +#include "RegExpCache.h" #include "RegExpConstructor.h" #include "RegExpObject.h" #include <wtf/ASCIICType.h> @@ -44,40 +46,38 @@ namespace JSC { ASSERT_CLASS_FITS_IN_CELL(StringPrototype); -static JSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*, JSObject*, JSValue, const ArgList&); - -static JSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*, JSObject*, JSValue, const ArgList&); - -static JSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*); +static EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*); } @@ -131,11 +131,12 @@ const ClassInfo StringPrototype::info = { "String", &StringObject::info, 0, Exec */ // ECMA 15.5.4 -StringPrototype::StringPrototype(ExecState* exec, NonNullPassRefPtr<Structure> structure) +StringPrototype::StringPrototype(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure) : StringObject(exec, structure) { + putAnonymousValue(0, globalObject); // The constructor will be added later, after StringConstructor has been built - putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 0), DontDelete | ReadOnly | DontEnum); + putDirectWithoutTransition(exec->propertyNames().length, jsNumber(0), DontDelete | ReadOnly | DontEnum); } bool StringPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) @@ -150,19 +151,19 @@ bool StringPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier // ------------------------------ Functions -------------------------- -static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, int i) +static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacement, const UString& source, const int* ovector, RegExp* reg, size_t i) { Vector<UChar> substitutedReplacement; int offset = 0; do { - if (i + 1 == replacement.size()) + if (i + 1 == replacement.length()) break; UChar ref = replacement[i + 1]; if (ref == '$') { // "$$" -> "$" ++i; - substitutedReplacement.append(replacement.data() + offset, i - offset); + substitutedReplacement.append(replacement.characters() + offset, i - offset); offset = i + 1; continue; } @@ -178,13 +179,13 @@ static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacem backrefLength = ovector[0]; } else if (ref == '\'') { backrefStart = ovector[1]; - backrefLength = source.size() - backrefStart; + backrefLength = source.length() - backrefStart; } else if (reg && ref >= '0' && ref <= '9') { // 1- and 2-digit back references are allowed unsigned backrefIndex = ref - '0'; if (backrefIndex > reg->numSubpatterns()) continue; - if (replacement.size() > i + 2) { + if (replacement.length() > i + 2) { ref = replacement[i + 2]; if (ref >= '0' && ref <= '9') { backrefIndex = 10 * backrefIndex + ref - '0'; @@ -202,14 +203,15 @@ static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacem continue; if (i - offset) - substitutedReplacement.append(replacement.data() + offset, i - offset); + substitutedReplacement.append(replacement.characters() + offset, i - offset); i += 1 + advance; offset = i + 1; - substitutedReplacement.append(source.data() + backrefStart, backrefLength); - } while ((i = replacement.find('$', i + 1)) != -1); + if (backrefStart >= 0) + substitutedReplacement.append(source.characters() + backrefStart, backrefLength); + } while ((i = replacement.find('$', i + 1)) != notFound); - if (replacement.size() - offset) - substitutedReplacement.append(replacement.data() + offset, replacement.size() - offset); + if (replacement.length() - offset) + substitutedReplacement.append(replacement.characters() + offset, replacement.length() - offset); substitutedReplacement.shrinkToFit(); return UString::adopt(substitutedReplacement); @@ -217,15 +219,15 @@ static NEVER_INLINE UString substituteBackreferencesSlow(const UString& replacem static inline UString substituteBackreferences(const UString& replacement, const UString& source, const int* ovector, RegExp* reg) { - int i = replacement.find('$', 0); - if (UNLIKELY(i != -1)) + size_t i = replacement.find('$', 0); + if (UNLIKELY(i != notFound)) return substituteBackreferencesSlow(replacement, source, ovector, reg, i); return replacement; } static inline int localeCompare(const UString& a, const UString& b) { - return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.data()), a.size(), reinterpret_cast<const ::UChar*>(b.data()), b.size()); + return Collator::userDefault()->collate(reinterpret_cast<const ::UChar*>(a.characters()), a.length(), reinterpret_cast<const ::UChar*>(b.characters()), b.length()); } struct StringRange { @@ -244,30 +246,29 @@ public: int length; }; -JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount); -JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount) +static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const UString& source, const StringRange* substringRanges, int rangeCount, const UString* separators, int separatorCount) { if (rangeCount == 1 && separatorCount == 0) { - int sourceSize = source.size(); + int sourceSize = source.length(); int position = substringRanges[0].position; int length = substringRanges[0].length; if (position <= 0 && length >= sourceSize) return sourceVal; // We could call UString::substr, but this would result in redundant checks - return jsString(exec, UStringImpl::create(source.rep(), max(0, position), min(sourceSize, length))); + return jsString(exec, StringImpl::create(source.impl(), max(0, position), min(sourceSize, length))); } int totalLength = 0; for (int i = 0; i < rangeCount; i++) totalLength += substringRanges[i].length; for (int i = 0; i < separatorCount; i++) - totalLength += separators[i].size(); + totalLength += separators[i].length(); if (totalLength == 0) return jsString(exec, ""); UChar* buffer; - PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(totalLength, buffer); + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); if (!impl) return throwOutOfMemoryError(exec); @@ -275,61 +276,47 @@ JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, c int bufferPos = 0; for (int i = 0; i < maxCount; i++) { if (i < rangeCount) { - UStringImpl::copyChars(buffer + bufferPos, source.data() + substringRanges[i].position, substringRanges[i].length); - bufferPos += substringRanges[i].length; + if (int srcLen = substringRanges[i].length) { + StringImpl::copyChars(buffer + bufferPos, source.characters() + substringRanges[i].position, srcLen); + bufferPos += srcLen; + } } if (i < separatorCount) { - UStringImpl::copyChars(buffer + bufferPos, separators[i].data(), separators[i].size()); - bufferPos += separators[i].size(); + if (int sepLen = separators[i].length()) { + StringImpl::copyChars(buffer + bufferPos, separators[i].characters(), sepLen); + bufferPos += sepLen; + } } } return jsString(exec, impl); } -JSValue jsReplaceRange(ExecState* exec, const UString& source, int rangeStart, int rangeLength, const UString& replacement); -JSValue jsReplaceRange(ExecState* exec, const UString& source, int rangeStart, int rangeLength, const UString& replacement) -{ - int replacementLength = replacement.size(); - int totalLength = source.size() - rangeLength + replacementLength; - if (totalLength == 0) - return jsString(exec, ""); - - UChar* buffer; - PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(totalLength, buffer); - if (!impl) - return throwOutOfMemoryError(exec); - - UStringImpl::copyChars(buffer, source.data(), rangeStart); - UStringImpl::copyChars(buffer + rangeStart, replacement.data(), replacementLength); - int rangeEnd = rangeStart + rangeLength; - UStringImpl::copyChars(buffer + rangeStart + replacementLength, source.data() + rangeEnd, source.size() - rangeEnd); - - return jsString(exec, impl); -} - -JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); JSString* sourceVal = thisValue.toThisJSString(exec); - const UString& source = sourceVal->value(exec); + JSValue pattern = exec->argument(0); + JSValue replacement = exec->argument(1); - JSValue pattern = args.at(0); - - JSValue replacement = args.at(1); UString replacementString; CallData callData; - CallType callType = replacement.getCallData(callData); + CallType callType = getCallData(replacement, callData); if (callType == CallTypeNone) replacementString = replacement.toString(exec); if (pattern.inherits(&RegExpObject::info)) { + const UString& source = sourceVal->value(exec); + unsigned sourceLen = source.length(); + if (exec->hadException()) + return JSValue::encode(JSValue()); RegExp* reg = asRegExpObject(pattern)->regExp(); bool global = reg->global(); RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int lastIndex = 0; - int startPosition = 0; + unsigned startPosition = 0; Vector<StringRange, 16> sourceRanges; Vector<UString, 16> replacements; @@ -339,9 +326,9 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue int argCount = reg->numSubpatterns() + 1 + 2; JSFunction* func = asFunction(replacement); - CachedCall cachedCall(exec, func, argCount, exec->exceptionSlot()); + CachedCall cachedCall(exec, func, argCount); if (exec->hadException()) - return jsNull(); + return JSValue::encode(jsNull()); while (true) { int matchIndex; int matchLen = 0; @@ -349,7 +336,7 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); if (matchIndex < 0) break; - + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); int completeMatchStart = ovector[0]; @@ -364,12 +351,15 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue cachedCall.setArgument(i, jsSubstring(exec, source, matchStart, matchLen)); } - cachedCall.setArgument(i++, jsNumber(exec, completeMatchStart)); + cachedCall.setArgument(i++, jsNumber(completeMatchStart)); cachedCall.setArgument(i++, sourceVal); - + cachedCall.setThis(exec->globalThisValue()); JSValue result = cachedCall.call(); - replacements.append(result.toString(cachedCall.newCallFrame(exec))); + if (LIKELY(result.isString())) + replacements.append(asString(result)->value(exec)); + else + replacements.append(result.toString(cachedCall.newCallFrame(exec))); if (exec->hadException()) break; @@ -379,10 +369,10 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue // special case of empty match if (matchLen == 0) { startPosition++; - if (startPosition > source.size()) + if (startPosition > sourceLen) break; } - } + } } else { do { int matchIndex; @@ -392,30 +382,39 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue if (matchIndex < 0) break; - sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); - if (callType != CallTypeNone) { + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + int completeMatchStart = ovector[0]; MarkedArgumentBuffer args; for (unsigned i = 0; i < reg->numSubpatterns() + 1; ++i) { int matchStart = ovector[i * 2]; int matchLen = ovector[i * 2 + 1] - matchStart; - + if (matchStart < 0) args.append(jsUndefined()); else args.append(jsSubstring(exec, source, matchStart, matchLen)); } - args.append(jsNumber(exec, completeMatchStart)); + args.append(jsNumber(completeMatchStart)); args.append(sourceVal); replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec)); if (exec->hadException()) break; - } else - replacements.append(substituteBackreferences(replacementString, source, ovector, reg)); + } else { + int replLen = replacementString.length(); + if (lastIndex < matchIndex || replLen) { + sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex)); + + if (replLen) + replacements.append(substituteBackreferences(replacementString, source, ovector, reg)); + else + replacements.append(UString()); + } + } lastIndex = matchIndex + matchLen; startPosition = lastIndex; @@ -423,109 +422,127 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue // special case of empty match if (matchLen == 0) { startPosition++; - if (startPosition > source.size()) + if (startPosition > sourceLen) break; } } while (global); } if (!lastIndex && replacements.isEmpty()) - return sourceVal; + return JSValue::encode(sourceVal); - if (lastIndex < source.size()) - sourceRanges.append(StringRange(lastIndex, source.size() - lastIndex)); + if (static_cast<unsigned>(lastIndex) < sourceLen) + sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); - return jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()); + return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, sourceVal, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); } // Not a regular expression, so treat the pattern as a string. UString patternString = pattern.toString(exec); - int matchPos = source.find(patternString); + // Special case for single character patterns without back reference replacement + if (patternString.length() == 1 && callType == CallTypeNone && replacementString.find('$', 0) == notFound) + return JSValue::encode(sourceVal->replaceCharacter(exec, patternString[0], replacementString)); + + const UString& source = sourceVal->value(exec); + size_t matchPos = source.find(patternString); - if (matchPos == -1) - return sourceVal; + if (matchPos == notFound) + return JSValue::encode(sourceVal); - int matchLen = patternString.size(); + int matchLen = patternString.length(); if (callType != CallTypeNone) { MarkedArgumentBuffer args; args.append(jsSubstring(exec, source, matchPos, matchLen)); - args.append(jsNumber(exec, matchPos)); + args.append(jsNumber(matchPos)); args.append(sourceVal); replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec); } - - int ovector[2] = { matchPos, matchPos + matchLen }; - return jsReplaceRange(exec, source, matchPos, matchLen, substituteBackreferences(replacementString, source, ovector, 0)); + + size_t matchEnd = matchPos + matchLen; + int ovector[2] = { matchPos, matchEnd }; + return JSValue::encode(jsString(exec, source.substringSharingImpl(0, matchPos), substituteBackreferences(replacementString, source, ovector, 0), source.substringSharingImpl(matchEnd))); } -JSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); // Also used for valueOf. if (thisValue.isString()) - return thisValue; + return JSValue::encode(thisValue); if (thisValue.inherits(&StringObject::info)) - return asStringObject(thisValue)->internalValue(); + return JSValue::encode(asStringObject(thisValue)->internalValue()); - return throwError(exec, TypeError); + return throwVMTypeError(exec); } -JSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - unsigned len = s.size(); - JSValue a0 = args.at(0); + unsigned len = s.length(); + JSValue a0 = exec->argument(0); if (a0.isUInt32()) { uint32_t i = a0.asUInt32(); if (i < len) - return jsSingleCharacterSubstring(exec, s, i); - return jsEmptyString(exec); + return JSValue::encode(jsSingleCharacterSubstring(exec, s, i)); + return JSValue::encode(jsEmptyString(exec)); } double dpos = a0.toInteger(exec); if (dpos >= 0 && dpos < len) - return jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos)); - return jsEmptyString(exec); + return JSValue::encode(jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos))); + return JSValue::encode(jsEmptyString(exec)); } -JSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - unsigned len = s.size(); - JSValue a0 = args.at(0); + unsigned len = s.length(); + JSValue a0 = exec->argument(0); if (a0.isUInt32()) { uint32_t i = a0.asUInt32(); if (i < len) - return jsNumber(exec, s.data()[i]); - return jsNaN(exec); + return JSValue::encode(jsNumber(s.characters()[i])); + return JSValue::encode(jsNaN()); } double dpos = a0.toInteger(exec); if (dpos >= 0 && dpos < len) - return jsNumber(exec, s[static_cast<int>(dpos)]); - return jsNaN(exec); + return JSValue::encode(jsNumber(s[static_cast<int>(dpos)])); + return JSValue::encode(jsNaN()); } -JSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec) { - if (thisValue.isString() && (args.size() == 1)) { - JSValue v = args.at(0); - return v.isString() + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isString() && (exec->argumentCount() == 1)) { + JSValue v = exec->argument(0); + return JSValue::encode(v.isString() ? jsString(exec, asString(thisValue), asString(v)) - : jsString(exec, asString(thisValue), v.toString(exec)); + : jsString(exec, asString(thisValue), v.toString(exec))); } - - return jsString(exec, thisValue, args); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); + return JSValue::encode(jsString(exec, thisValue)); } -JSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - int len = s.size(); + int len = s.length(); - JSValue a0 = args.at(0); - JSValue a1 = args.at(1); + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); UString u2 = a0.toString(exec); int pos; if (a1.isUndefined()) @@ -541,16 +558,22 @@ JSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue pos = static_cast<int>(dpos); } - return jsNumber(exec, s.find(u2, pos)); + size_t result = s.find(u2, pos); + if (result == notFound) + return JSValue::encode(jsNumber(-1)); + return JSValue::encode(jsNumber(result)); } -JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - int len = s.size(); + int len = s.length(); - JSValue a0 = args.at(0); - JSValue a1 = args.at(1); + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); UString u2 = a0.toString(exec); double dpos = a1.toIntegerPreserveNaN(exec); @@ -563,14 +586,21 @@ JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSV else if (isnan(dpos)) dpos = len; #endif - return jsNumber(exec, s.rfind(u2, static_cast<int>(dpos))); + + size_t result = s.reverseFind(u2, static_cast<unsigned>(dpos)); + if (result == notFound) + return JSValue::encode(jsNumber(-1)); + return JSValue::encode(jsNumber(result)); } -JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); + JSValue a0 = exec->argument(0); UString u = s; RefPtr<RegExp> reg; @@ -583,7 +613,7 @@ JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue t * If regexp is not an object whose [[Class]] property is "RegExp", it is * replaced with the result of the expression new RegExp(regexp). */ - reg = RegExp::create(&exec->globalData(), a0.toString(exec)); + reg = exec->globalData().regExpCache()->lookupOrCreate(a0.toString(exec), UString()); } RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int pos; @@ -592,8 +622,8 @@ JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue t if (!(reg->global())) { // case without 'g' flag is handled like RegExp.prototype.exec if (pos < 0) - return jsNull(); - return regExpConstructor->arrayOfMatches(exec); + return JSValue::encode(jsNull()); + return JSValue::encode(regExpConstructor->arrayOfMatches(exec)); } // return array of matches @@ -611,17 +641,20 @@ JSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec, JSObject*, JSValue t // if there are no matches at all, it's important to return // Null instead of an empty array, because this matches // other browsers and because Null is a false value. - return jsNull(); + return JSValue::encode(jsNull()); } - return constructArray(exec, list); + return JSValue::encode(constructArray(exec, list)); } -JSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); + JSValue a0 = exec->argument(0); UString u = s; RefPtr<RegExp> reg; @@ -633,22 +666,25 @@ JSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec, JSObject*, JSValue * If regexp is not an object whose [[Class]] property is "RegExp", it is * replaced with the result of the expression new RegExp(regexp). */ - reg = RegExp::create(&exec->globalData(), a0.toString(exec)); + reg = exec->globalData().regExpCache()->lookupOrCreate(a0.toString(exec), UString()); } RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int pos; int matchLength = 0; regExpConstructor->performMatch(reg.get(), u, 0, pos, matchLength); - return jsNumber(exec, pos); + return JSValue::encode(jsNumber(pos)); } -JSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - int len = s.size(); + int len = s.length(); - JSValue a0 = args.at(0); - JSValue a1 = args.at(1); + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); // The arg processing is very much like ArrayProtoFunc::Slice double start = a0.toInteger(exec); @@ -660,38 +696,41 @@ JSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec, JSObject*, JSValue t from = 0; if (to > len) to = len; - return jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)); + return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from))); } - return jsEmptyString(exec); + return JSValue::encode(jsEmptyString(exec)); } -JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); - JSValue a1 = args.at(1); + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); JSArray* result = constructEmptyArray(exec); unsigned i = 0; - int p0 = 0; + unsigned p0 = 0; unsigned limit = a1.isUndefined() ? 0xFFFFFFFFU : a1.toUInt32(exec); if (a0.inherits(&RegExpObject::info)) { RegExp* reg = asRegExpObject(a0)->regExp(); if (s.isEmpty() && reg->match(s, 0) >= 0) { // empty string matched by regexp -> empty array - return result; + return JSValue::encode(result); } - int pos = 0; - while (i != limit && pos < s.size()) { + unsigned pos = 0; + while (i != limit && pos < s.length()) { Vector<int, 32> ovector; int mpos = reg->match(s, pos, &ovector); if (mpos < 0) break; int mlen = ovector[1] - ovector[0]; pos = mpos + (mlen == 0 ? 1 : mlen); - if (mpos != p0 || mlen) { + if (static_cast<unsigned>(mpos) != p0 || mlen) { result->put(exec, i++, jsSubstring(exec, s, p0, mpos - p0)); p0 = mpos + mlen; } @@ -708,38 +747,41 @@ JSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec, JSObject*, JSValue t if (u2.isEmpty()) { if (s.isEmpty()) { // empty separator matches empty string -> empty array - return result; + return JSValue::encode(result); } - while (i != limit && p0 < s.size() - 1) + while (i != limit && p0 < s.length() - 1) result->put(exec, i++, jsSingleCharacterSubstring(exec, s, p0++)); } else { - int pos; - while (i != limit && (pos = s.find(u2, p0)) >= 0) { + size_t pos; + while (i != limit && (pos = s.find(u2, p0)) != notFound) { result->put(exec, i++, jsSubstring(exec, s, p0, pos - p0)); - p0 = pos + u2.size(); + p0 = pos + u2.length(); } } } // add remaining string if (i != limit) - result->put(exec, i++, jsSubstring(exec, s, p0, s.size() - p0)); + result->put(exec, i++, jsSubstring(exec, s, p0, s.length() - p0)); - return result; + return JSValue::encode(result); } -JSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - int len = s.size(); + int len = s.length(); - JSValue a0 = args.at(0); - JSValue a1 = args.at(1); + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); double start = a0.toInteger(exec); double length = a1.isUndefined() ? len : a1.toInteger(exec); if (start >= len || length <= 0) - return jsEmptyString(exec); + return JSValue::encode(jsEmptyString(exec)); if (start < 0) { start += len; if (start < 0) @@ -747,51 +789,56 @@ JSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec, JSObject*, JSValue } if (start + length > len) length = len - start; - return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(length)); + return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(length))); } -JSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - int len = s.size(); + int len = s.length(); - JSValue a0 = args.at(0); - JSValue a1 = args.at(1); + JSValue a0 = exec->argument(0); + JSValue a1 = exec->argument(1); double start = a0.toNumber(exec); - double end = a1.toNumber(exec); - if (isnan(start)) + double end; + if (!(start >= 0)) // check for negative values or NaN start = 0; - if (isnan(end)) - end = 0; - if (start < 0) - start = 0; - if (end < 0) - end = 0; - if (start > len) + else if (start > len) start = len; - if (end > len) - end = len; if (a1.isUndefined()) end = len; + else { + end = a1.toNumber(exec); + if (!(end >= 0)) // check for negative values or NaN + end = 0; + else if (end > len) + end = len; + } if (start > end) { double temp = end; end = start; start = temp; } - return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(end) - static_cast<unsigned>(start)); + return JSValue::encode(jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(end) - static_cast<unsigned>(start))); } -JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); JSString* sVal = thisValue.toThisJSString(exec); const UString& s = sVal->value(exec); - int sSize = s.size(); + int sSize = s.length(); if (!sSize) - return sVal; + return JSValue::encode(sVal); - const UChar* sData = s.data(); + const UChar* sData = s.characters(); Vector<UChar> buffer(sSize); UChar ored = 0; @@ -801,7 +848,7 @@ JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSV buffer[i] = toASCIILower(c); } if (!(ored & ~0x7f)) - return jsString(exec, UString::adopt(buffer)); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); bool error; int length = Unicode::toLower(buffer.data(), sSize, sData, sSize, &error); @@ -809,26 +856,29 @@ JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSV buffer.resize(length); length = Unicode::toLower(buffer.data(), length, sData, sSize, &error); if (error) - return sVal; + return JSValue::encode(sVal); } if (length == sSize) { if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0) - return sVal; + return JSValue::encode(sVal); } else buffer.resize(length); - return jsString(exec, UString::adopt(buffer)); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); } -JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); JSString* sVal = thisValue.toThisJSString(exec); const UString& s = sVal->value(exec); - int sSize = s.size(); + int sSize = s.length(); if (!sSize) - return sVal; + return JSValue::encode(sVal); - const UChar* sData = s.data(); + const UChar* sData = s.characters(); Vector<UChar> buffer(sSize); UChar ored = 0; @@ -838,7 +888,7 @@ JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSV buffer[i] = toASCIIUpper(c); } if (!(ored & ~0x7f)) - return jsString(exec, UString::adopt(buffer)); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); bool error; int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error); @@ -846,100 +896,115 @@ JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSV buffer.resize(length); length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error); if (error) - return sVal; + return JSValue::encode(sVal); } if (length == sSize) { if (memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0) - return sVal; + return JSValue::encode(sVal); } else buffer.resize(length); - return jsString(exec, UString::adopt(buffer)); + return JSValue::encode(jsString(exec, UString::adopt(buffer))); } -JSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec) { - if (args.size() < 1) - return jsNumber(exec, 0); + if (exec->argumentCount() < 1) + return JSValue::encode(jsNumber(0)); + + JSValue thisValue = exec->hostThisValue(); + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwVMTypeError(exec); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); - return jsNumber(exec, localeCompare(s, a0.toString(exec))); + JSValue a0 = exec->argument(0); + return JSValue::encode(jsNumber(localeCompare(s, a0.toString(exec)))); } -JSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<big>", s, "</big>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>")); } -JSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<small>", s, "</small>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>")); } -JSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<blink>", s, "</blink>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>")); } -JSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<b>", s, "</b>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>")); } -JSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<tt>", s, "</tt>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>")); } -JSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<i>", s, "</i>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>")); } -JSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<strike>", s, "</strike>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>")); } -JSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<sub>", s, "</sub>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>")); } -JSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - return jsMakeNontrivialString(exec, "<sup>", s, "</sup>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>")); } -JSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); - return jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec), "\">", s, "</font>"); + JSValue a0 = exec->argument(0); + return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", a0.toString(exec), "\">", s, "</font>")); } -JSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); + JSValue a0 = exec->argument(0); uint32_t smallInteger; if (a0.getUInt32(smallInteger) && smallInteger <= 9) { - unsigned stringSize = s.size(); + unsigned stringSize = s.length(); unsigned bufferSize = 22 + stringSize; UChar* buffer; - PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(bufferSize, buffer); + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer); if (!impl) - return jsUndefined(); + return JSValue::encode(jsUndefined()); buffer[0] = '<'; buffer[1] = 'f'; buffer[2] = 'o'; @@ -955,7 +1020,7 @@ JSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec, JSObject*, JSValu buffer[12] = '0' + smallInteger; buffer[13] = '"'; buffer[14] = '>'; - memcpy(&buffer[15], s.data(), stringSize * sizeof(UChar)); + memcpy(&buffer[15], s.characters(), stringSize * sizeof(UChar)); buffer[15 + stringSize] = '<'; buffer[16 + stringSize] = '/'; buffer[17 + stringSize] = 'f'; @@ -963,32 +1028,34 @@ JSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec, JSObject*, JSValu buffer[19 + stringSize] = 'n'; buffer[20 + stringSize] = 't'; buffer[21 + stringSize] = '>'; - return jsNontrivialString(exec, impl); + return JSValue::encode(jsNontrivialString(exec, impl)); } - return jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec), "\">", s, "</font>"); + return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", a0.toString(exec), "\">", s, "</font>")); } -JSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); - return jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec), "\">", s, "</a>"); + JSValue a0 = exec->argument(0); + return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", a0.toString(exec), "\">", s, "</a>")); } -JSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec) { + JSValue thisValue = exec->hostThisValue(); UString s = thisValue.toThisString(exec); - JSValue a0 = args.at(0); + JSValue a0 = exec->argument(0); UString linkText = a0.toString(exec); - unsigned linkTextSize = linkText.size(); - unsigned stringSize = s.size(); + unsigned linkTextSize = linkText.length(); + unsigned stringSize = s.length(); unsigned bufferSize = 15 + linkTextSize + stringSize; UChar* buffer; - PassRefPtr<UStringImpl> impl = UStringImpl::tryCreateUninitialized(bufferSize, buffer); + PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer); if (!impl) - return jsUndefined(); + return JSValue::encode(jsUndefined()); buffer[0] = '<'; buffer[1] = 'a'; buffer[2] = ' '; @@ -998,15 +1065,15 @@ JSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec, JSObject*, JSValue th buffer[6] = 'f'; buffer[7] = '='; buffer[8] = '"'; - memcpy(&buffer[9], linkText.data(), linkTextSize * sizeof(UChar)); + memcpy(&buffer[9], linkText.characters(), linkTextSize * sizeof(UChar)); buffer[9 + linkTextSize] = '"'; buffer[10 + linkTextSize] = '>'; - memcpy(&buffer[11 + linkTextSize], s.data(), stringSize * sizeof(UChar)); + memcpy(&buffer[11 + linkTextSize], s.characters(), stringSize * sizeof(UChar)); buffer[11 + linkTextSize + stringSize] = '<'; buffer[12 + linkTextSize + stringSize] = '/'; buffer[13 + linkTextSize + stringSize] = 'a'; buffer[14 + linkTextSize + stringSize] = '>'; - return jsNontrivialString(exec, impl); + return JSValue::encode(jsNontrivialString(exec, impl)); } enum { @@ -1021,38 +1088,43 @@ static inline bool isTrimWhitespace(UChar c) static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind) { + if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible + return throwTypeError(exec); UString str = thisValue.toThisString(exec); - int left = 0; + unsigned left = 0; if (trimKind & TrimLeft) { - while (left < str.size() && isTrimWhitespace(str[left])) + while (left < str.length() && isTrimWhitespace(str[left])) left++; } - int right = str.size(); + unsigned right = str.length(); if (trimKind & TrimRight) { while (right > left && isTrimWhitespace(str[right - 1])) right--; } // Don't gc allocate a new string if we don't have to. - if (left == 0 && right == str.size() && thisValue.isString()) + if (left == 0 && right == str.length() && thisValue.isString()) return thisValue; - return jsString(exec, str.substr(left, right - left)); + return jsString(exec, str.substringSharingImpl(left, right - left)); } -JSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState* exec) { - return trimString(exec, thisValue, TrimLeft | TrimRight); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimLeft | TrimRight)); } -JSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState* exec) { - return trimString(exec, thisValue, TrimLeft); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimLeft)); } -JSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState* exec) { - return trimString(exec, thisValue, TrimRight); + JSValue thisValue = exec->hostThisValue(); + return JSValue::encode(trimString(exec, thisValue, TrimRight)); } diff --git a/JavaScriptCore/runtime/StringPrototype.h b/JavaScriptCore/runtime/StringPrototype.h index 3a6a2a3..4b0f88f 100644 --- a/JavaScriptCore/runtime/StringPrototype.h +++ b/JavaScriptCore/runtime/StringPrototype.h @@ -29,7 +29,7 @@ namespace JSC { class StringPrototype : public StringObject { public: - StringPrototype(ExecState*, NonNullPassRefPtr<Structure>); + StringPrototype(ExecState*, JSGlobalObject*, NonNullPassRefPtr<Structure>); virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp index 546e2bf..d06a239 100644 --- a/JavaScriptCore/runtime/Structure.cpp +++ b/JavaScriptCore/runtime/Structure.cpp @@ -79,6 +79,106 @@ static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); static int comparePropertyMapEntryIndices(const void* a, const void* b); +inline void Structure::setTransitionTable(TransitionTable* table) +{ + ASSERT(m_isUsingSingleSlot); +#ifndef NDEBUG + setSingleTransition(0); +#endif + m_isUsingSingleSlot = false; + m_transitions.m_table = table; + // This implicitly clears the flag that indicates we're using a single transition + ASSERT(!m_isUsingSingleSlot); +} + +// The contains and get methods accept imprecise matches, so if an unspecialised transition exists +// for the given key they will consider that transition to be a match. If a specialised transition +// exists and it matches the provided specificValue, get will return the specific transition. +inline bool Structure::transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + Structure* existingTransition = singleTransition(); + return existingTransition && existingTransition->m_nameInPrevious.get() == key.first + && existingTransition->m_attributesInPrevious == key.second + && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0); + } + TransitionTable::iterator find = transitionTable()->find(key); + if (find == transitionTable()->end()) + return false; + + return find->second.first || find->second.second->transitionedFor(specificValue); +} + +inline Structure* Structure::transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const +{ + if (m_isUsingSingleSlot) { + Structure* existingTransition = singleTransition(); + if (existingTransition && existingTransition->m_nameInPrevious.get() == key.first + && existingTransition->m_attributesInPrevious == key.second + && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) + return existingTransition; + return 0; + } + + Transition transition = transitionTable()->get(key); + if (transition.second && transition.second->transitionedFor(specificValue)) + return transition.second; + return transition.first; +} + +inline bool Structure::transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const +{ + if (m_isUsingSingleSlot) { + Structure* transition = singleTransition(); + return transition && transition->m_nameInPrevious == key.first + && transition->m_attributesInPrevious == key.second; + } + return transitionTable()->contains(key); +} + +inline void Structure::transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + ASSERT(transitionTableContains(key, specificValue)); + setSingleTransition(0); + return; + } + TransitionTable::iterator find = transitionTable()->find(key); + if (!specificValue) + find->second.first = 0; + else + find->second.second = 0; + if (!find->second.first && !find->second.second) + transitionTable()->remove(find); +} + +inline void Structure::transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue) +{ + if (m_isUsingSingleSlot) { + if (!singleTransition()) { + setSingleTransition(structure); + return; + } + Structure* existingTransition = singleTransition(); + TransitionTable* transitionTable = new TransitionTable; + setTransitionTable(transitionTable); + if (existingTransition) + transitionTableAdd(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); + } + if (!specificValue) { + TransitionTable::iterator find = transitionTable()->find(key); + if (find == transitionTable()->end()) + transitionTable()->add(key, Transition(structure, static_cast<Structure*>(0))); + else + find->second.first = structure; + } else { + // If we're adding a transition to a specific value, then there cannot be + // an existing transition + ASSERT(!transitionTable()->contains(key)); + transitionTable()->add(key, Transition(static_cast<Structure*>(0), structure)); + } +} + void Structure::dumpStatistics() { #if DUMP_STRUCTURE_ID_STATISTICS @@ -136,7 +236,10 @@ Structure::Structure(JSValue prototype, const TypeInfo& typeInfo, unsigned anony , m_attributesInPrevious(0) , m_specificFunctionThrashCount(0) , m_anonymousSlotCount(anonymousSlotCount) + , m_isUsingSingleSlot(true) { + m_transitions.m_singleTransition = 0; + ASSERT(m_prototype); ASSERT(m_prototype.isObject() || m_prototype.isNull()); @@ -159,17 +262,15 @@ Structure::~Structure() { if (m_previous) { ASSERT(m_nameInPrevious); - m_previous->table.remove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); + m_previous->transitionTableRemove(make_pair(m_nameInPrevious.get(), m_attributesInPrevious), m_specificValueInPrevious); } - - if (m_enumerationCache) - m_enumerationCache->setCachedStructure(0); + ASSERT(!m_enumerationCache.hasDeadObject()); if (m_propertyTable) { unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; for (unsigned i = 1; i <= entryCount; i++) { - if (UString::Rep* key = m_propertyTable->entries()[i].key) + if (StringImpl* key = m_propertyTable->entries()[i].key) key->deref(); } @@ -177,6 +278,9 @@ Structure::~Structure() fastFree(m_propertyTable); } + if (!m_isUsingSingleSlot) + delete transitionTable(); + #ifndef NDEBUG #if ENABLE(JSC_MULTIPLE_THREADS) MutexLocker protect(ignoreSetMutex); @@ -291,7 +395,7 @@ void Structure::growPropertyStorageCapacity() void Structure::despecifyDictionaryFunction(const Identifier& propertyName) { - const UString::Rep* rep = propertyName._ustring.rep(); + const StringImpl* rep = propertyName.impl(); materializePropertyMapIfNecessary(); @@ -340,7 +444,7 @@ PassRefPtr<Structure> Structure::addPropertyTransitionToExistingStructure(Struct ASSERT(!structure->isDictionary()); ASSERT(structure->typeInfo().type() == ObjectType); - if (Structure* existingTransition = structure->table.get(make_pair(propertyName.ustring().rep(), attributes), specificValue)) { + if (Structure* existingTransition = structure->transitionTableGet(make_pair(propertyName.impl(), attributes), specificValue)) { ASSERT(existingTransition->m_offset != noOffset); offset = existingTransition->m_offset + existingTransition->m_anonymousSlotCount; ASSERT(offset >= structure->m_anonymousSlotCount); @@ -375,7 +479,7 @@ PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, con transition->m_cachedPrototypeChain = structure->m_cachedPrototypeChain; transition->m_previous = structure; - transition->m_nameInPrevious = propertyName.ustring().rep(); + transition->m_nameInPrevious = propertyName.impl(); transition->m_attributesInPrevious = attributes; transition->m_specificValueInPrevious = specificValue; transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; @@ -405,7 +509,7 @@ PassRefPtr<Structure> Structure::addPropertyTransition(Structure* structure, con transition->m_offset = offset - structure->m_anonymousSlotCount; ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount()); - structure->table.add(make_pair(propertyName.ustring().rep(), attributes), transition.get(), specificValue); + structure->transitionTableAdd(make_pair(propertyName.impl(), attributes), transition.get(), specificValue); return transition.release(); } @@ -634,7 +738,7 @@ PropertyMapHashTable* Structure::copyPropertyTable() unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; for (unsigned i = 1; i <= entryCount; ++i) { - if (UString::Rep* key = newTable->entries()[i].key) + if (StringImpl* key = newTable->entries()[i].key) key->ref(); } @@ -645,7 +749,7 @@ PropertyMapHashTable* Structure::copyPropertyTable() return newTable; } -size_t Structure::get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue) +size_t Structure::get(const StringImpl* rep, unsigned& attributes, JSCell*& specificValue) { materializePropertyMapIfNecessary(); if (!m_propertyTable) @@ -702,7 +806,7 @@ bool Structure::despecifyFunction(const Identifier& propertyName) if (!m_propertyTable) return false; - UString::Rep* rep = propertyName._ustring.rep(); + StringImpl* rep = propertyName.impl(); unsigned i = rep->existingHash(); @@ -766,7 +870,7 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCel if (attributes & DontEnum) m_hasNonEnumerableProperties = true; - UString::Rep* rep = propertyName._ustring.rep(); + StringImpl* rep = propertyName.impl(); if (!m_propertyTable) createPropertyMapHashTable(); @@ -850,9 +954,9 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCel return newOffset; } -bool Structure::hasTransition(UString::Rep* rep, unsigned attributes) +bool Structure::hasTransition(StringImpl* rep, unsigned attributes) { - return table.hasTransition(make_pair(rep, attributes)); + return transitionTableHasTransition(make_pair(rep, attributes)); } size_t Structure::remove(const Identifier& propertyName) @@ -861,7 +965,7 @@ size_t Structure::remove(const Identifier& propertyName) checkConsistency(); - UString::Rep* rep = propertyName._ustring.rep(); + StringImpl* rep = propertyName.impl(); if (!m_propertyTable) return notFound; @@ -875,7 +979,7 @@ size_t Structure::remove(const Identifier& propertyName) unsigned i = rep->existingHash(); unsigned k = 0; unsigned entryIndex; - UString::Rep* key = 0; + StringImpl* key = 0; while (1) { entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask]; if (entryIndex == emptyEntryIndex) @@ -1137,7 +1241,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; + StringImpl* rep = m_propertyTable->entries()[c].key; ASSERT(m_propertyTable->entries()[c].offset >= m_anonymousSlotCount); if (!rep) continue; diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h index 95cf94c..f480051 100644 --- a/JavaScriptCore/runtime/Structure.h +++ b/JavaScriptCore/runtime/Structure.h @@ -106,20 +106,20 @@ namespace JSC { bool isUsingInlineStorage() const; size_t get(const Identifier& propertyName); - size_t get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue); + size_t get(const StringImpl* rep, unsigned& attributes, JSCell*& specificValue); size_t get(const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue) { ASSERT(!propertyName.isNull()); - return get(propertyName.ustring().rep(), attributes, specificValue); + return get(propertyName.impl(), attributes, specificValue); } bool transitionedFor(const JSCell* specificValue) { return m_specificValueInPrevious == specificValue; } - bool hasTransition(UString::Rep*, unsigned attributes); + bool hasTransition(StringImpl*, unsigned attributes); bool hasTransition(const Identifier& propertyName, unsigned attributes) { - return hasTransition(propertyName._ustring.rep(), attributes); + return hasTransition(propertyName.impl(), attributes); } bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } @@ -179,6 +179,20 @@ namespace JSC { // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both. return m_offset == noOffset ? 0 : m_offset + 1; } + + typedef std::pair<Structure*, Structure*> Transition; + typedef HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> TransitionTable; + + inline bool transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue); + inline void transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue); + inline void transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue); + inline bool transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const; + inline Structure* transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const; + + TransitionTable* transitionTable() const { ASSERT(!m_isUsingSingleSlot); return m_transitions.m_table; } + inline void setTransitionTable(TransitionTable* table); + Structure* singleTransition() const { ASSERT(m_isUsingSingleSlot); return m_transitions.m_singleTransition; } + void setSingleTransition(Structure* structure) { ASSERT(m_isUsingSingleSlot); m_transitions.m_singleTransition = structure; } bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; @@ -196,10 +210,14 @@ namespace JSC { mutable RefPtr<StructureChain> m_cachedPrototypeChain; RefPtr<Structure> m_previous; - RefPtr<UString::Rep> m_nameInPrevious; + RefPtr<StringImpl> m_nameInPrevious; JSCell* m_specificValueInPrevious; - StructureTransitionTable table; + // 'm_isUsingSingleSlot' indicates whether we are using the single transition optimisation. + union { + TransitionTable* m_table; + Structure* m_singleTransition; + } m_transitions; WeakGCPtr<JSPropertyNameIterator> m_enumerationCache; @@ -224,7 +242,8 @@ namespace JSC { #endif unsigned m_specificFunctionThrashCount : 2; unsigned m_anonymousSlotCount : 5; - // 5 free bits + unsigned m_isUsingSingleSlot : 1; + // 4 free bits }; inline size_t Structure::get(const Identifier& propertyName) @@ -235,7 +254,7 @@ namespace JSC { if (!m_propertyTable) return WTF::notFound; - UString::Rep* rep = propertyName._ustring.rep(); + StringImpl* rep = propertyName.impl(); unsigned i = rep->existingHash(); @@ -271,58 +290,7 @@ namespace JSC { return m_propertyTable->entries()[entryIndex - 1].offset; } } - - bool StructureTransitionTable::contains(const StructureTransitionTableHash::Key& key, JSCell* specificValue) - { - if (usingSingleTransitionSlot()) { - Structure* existingTransition = singleTransition(); - return existingTransition && existingTransition->m_nameInPrevious.get() == key.first - && existingTransition->m_attributesInPrevious == key.second - && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0); - } - TransitionTable::iterator find = table()->find(key); - if (find == table()->end()) - return false; - - return find->second.first || find->second.second->transitionedFor(specificValue); - } - Structure* StructureTransitionTable::get(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const - { - if (usingSingleTransitionSlot()) { - Structure* existingTransition = singleTransition(); - if (existingTransition && existingTransition->m_nameInPrevious.get() == key.first - && existingTransition->m_attributesInPrevious == key.second - && (existingTransition->m_specificValueInPrevious == specificValue || existingTransition->m_specificValueInPrevious == 0)) - return existingTransition; - return 0; - } - - Transition transition = table()->get(key); - if (transition.second && transition.second->transitionedFor(specificValue)) - return transition.second; - return transition.first; - } - - bool StructureTransitionTable::hasTransition(const StructureTransitionTableHash::Key& key) const - { - if (usingSingleTransitionSlot()) { - Structure* transition = singleTransition(); - return transition && transition->m_nameInPrevious == key.first - && transition->m_attributesInPrevious == key.second; - } - return table()->contains(key); - } - - void StructureTransitionTable::reifySingleTransition() - { - ASSERT(usingSingleTransitionSlot()); - Structure* existingTransition = singleTransition(); - TransitionTable* transitionTable = new TransitionTable; - setTransitionTable(transitionTable); - if (existingTransition) - add(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); - } } // namespace JSC #endif // Structure_h diff --git a/JavaScriptCore/runtime/StructureTransitionTable.h b/JavaScriptCore/runtime/StructureTransitionTable.h index 320dbdd..7e9d7ff 100644 --- a/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/JavaScriptCore/runtime/StructureTransitionTable.h @@ -30,7 +30,6 @@ #include <wtf/HashFunctions.h> #include <wtf/HashMap.h> #include <wtf/HashTraits.h> -#include <wtf/PtrAndFlags.h> #include <wtf/OwnPtr.h> #include <wtf/RefPtr.h> @@ -39,7 +38,7 @@ namespace JSC { class Structure; struct StructureTransitionTableHash { - typedef std::pair<RefPtr<UString::Rep>, unsigned> Key; + typedef std::pair<RefPtr<StringImpl>, unsigned> Key; static unsigned hash(const Key& p) { return p.first->existingHash(); @@ -54,7 +53,7 @@ namespace JSC { }; struct StructureTransitionTableHashTraits { - typedef WTF::HashTraits<RefPtr<UString::Rep> > FirstTraits; + typedef WTF::HashTraits<RefPtr<StringImpl> > FirstTraits; typedef WTF::GenericHashTraits<unsigned> SecondTraits; typedef std::pair<FirstTraits::TraitType, SecondTraits::TraitType > TraitType; @@ -67,99 +66,6 @@ namespace JSC { static bool isDeletedValue(const TraitType& value) { return FirstTraits::isDeletedValue(value.first); } }; - class StructureTransitionTable { - typedef std::pair<Structure*, Structure*> Transition; - typedef HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> TransitionTable; - public: - StructureTransitionTable() { - m_transitions.m_singleTransition.set(0); - m_transitions.m_singleTransition.setFlag(usingSingleSlot); - } - - ~StructureTransitionTable() { - if (!usingSingleTransitionSlot()) - delete table(); - } - - // The contains and get methods accept imprecise matches, so if an unspecialised transition exists - // for the given key they will consider that transition to be a match. If a specialised transition - // exists and it matches the provided specificValue, get will return the specific transition. - inline bool contains(const StructureTransitionTableHash::Key&, JSCell* specificValue); - inline Structure* get(const StructureTransitionTableHash::Key&, JSCell* specificValue) const; - inline bool hasTransition(const StructureTransitionTableHash::Key& key) const; - void remove(const StructureTransitionTableHash::Key& key, JSCell* specificValue) - { - if (usingSingleTransitionSlot()) { - ASSERT(contains(key, specificValue)); - setSingleTransition(0); - return; - } - TransitionTable::iterator find = table()->find(key); - if (!specificValue) - find->second.first = 0; - else - find->second.second = 0; - if (!find->second.first && !find->second.second) - table()->remove(find); - } - void add(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue) - { - if (usingSingleTransitionSlot()) { - if (!singleTransition()) { - setSingleTransition(structure); - return; - } - reifySingleTransition(); - } - if (!specificValue) { - TransitionTable::iterator find = table()->find(key); - if (find == table()->end()) - table()->add(key, Transition(structure, 0)); - else - find->second.first = structure; - } else { - // If we're adding a transition to a specific value, then there cannot be - // an existing transition - ASSERT(!table()->contains(key)); - table()->add(key, Transition(0, structure)); - } - } - - private: - TransitionTable* table() const { ASSERT(!usingSingleTransitionSlot()); return m_transitions.m_table; } - Structure* singleTransition() const { - ASSERT(usingSingleTransitionSlot()); - return m_transitions.m_singleTransition.get(); - } - bool usingSingleTransitionSlot() const { return m_transitions.m_singleTransition.isFlagSet(usingSingleSlot); } - void setSingleTransition(Structure* structure) - { - ASSERT(usingSingleTransitionSlot()); - m_transitions.m_singleTransition.set(structure); - } - - void setTransitionTable(TransitionTable* table) - { - ASSERT(usingSingleTransitionSlot()); -#ifndef NDEBUG - setSingleTransition(0); -#endif - m_transitions.m_table = table; - // This implicitly clears the flag that indicates we're using a single transition - ASSERT(!usingSingleTransitionSlot()); - } - inline void reifySingleTransition(); - - enum UsingSingleSlot { - usingSingleSlot - }; - // Last bit indicates whether we are using the single transition optimisation - union { - TransitionTable* m_table; - PtrAndFlagsBase<Structure, UsingSingleSlot> m_singleTransition; - } m_transitions; - }; - } // namespace JSC #endif // StructureTransitionTable_h diff --git a/JavaScriptCore/runtime/SymbolTable.h b/JavaScriptCore/runtime/SymbolTable.h index f5e2669..1b1636d 100644 --- a/JavaScriptCore/runtime/SymbolTable.h +++ b/JavaScriptCore/runtime/SymbolTable.h @@ -119,10 +119,13 @@ namespace JSC { static const bool needsDestruction = false; }; - typedef HashMap<RefPtr<UString::Rep>, SymbolTableEntry, IdentifierRepHash, HashTraits<RefPtr<UString::Rep> >, SymbolTableIndexHashTraits> SymbolTable; + typedef HashMap<RefPtr<StringImpl>, SymbolTableEntry, IdentifierRepHash, HashTraits<RefPtr<StringImpl> >, SymbolTableIndexHashTraits> SymbolTable; - class SharedSymbolTable : public SymbolTable, public RefCounted<SharedSymbolTable> - { + class SharedSymbolTable : public SymbolTable, public RefCounted<SharedSymbolTable> { + public: + static PassRefPtr<SharedSymbolTable> create() { return adoptRef(new SharedSymbolTable); } + private: + SharedSymbolTable() { } }; } // namespace JSC diff --git a/JavaScriptCore/runtime/Terminator.h b/JavaScriptCore/runtime/Terminator.h new file mode 100644 index 0000000..6b0f236 --- /dev/null +++ b/JavaScriptCore/runtime/Terminator.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google 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. + * 3. Neither the name of Google Inc. ("Google") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 Terminator_h +#define Terminator_h + +namespace JSC { + +class Terminator { +public: + Terminator() : m_shouldTerminate(false) { } + + void terminateSoon() { m_shouldTerminate = true; } + bool shouldTerminate() const { return m_shouldTerminate; } + +private: + bool m_shouldTerminate; +}; + +} // namespace JSC + +#endif // Terminator_h diff --git a/JavaScriptCore/runtime/TimeoutChecker.cpp b/JavaScriptCore/runtime/TimeoutChecker.cpp index 250fdaf..04d904d 100644 --- a/JavaScriptCore/runtime/TimeoutChecker.cpp +++ b/JavaScriptCore/runtime/TimeoutChecker.cpp @@ -84,6 +84,13 @@ static inline unsigned getCPUTime() GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime.fileTime, &userTime.fileTime); return userTime.fileTimeAsLong / 10000 + kernelTime.fileTimeAsLong / 10000; +#elif OS(SYMBIAN) + RThread current; + TTimeIntervalMicroSeconds cpuTime; + + TInt err = current.GetCpuTime(cpuTime); + ASSERT_WITH_MESSAGE(err == KErrNone, "GetCpuTime failed with %d", err); + return cpuTime.Int64() / 1000; #elif PLATFORM(BREWMP) // This function returns a continuously and linearly increasing millisecond // timer from the time the device was powered on. @@ -91,7 +98,10 @@ static inline unsigned getCPUTime() return GETUPTIMEMS(); #else // FIXME: We should return the time the current thread has spent executing. - return currentTime() * 1000; + + // use a relative time from first call in order to avoid an overflow + static double firstTime = currentTime(); + return (currentTime() - firstTime) * 1000; #endif } diff --git a/JavaScriptCore/runtime/TimeoutChecker.h b/JavaScriptCore/runtime/TimeoutChecker.h index 18bc36b..71ce169 100644 --- a/JavaScriptCore/runtime/TimeoutChecker.h +++ b/JavaScriptCore/runtime/TimeoutChecker.h @@ -44,6 +44,7 @@ namespace JSC { TimeoutChecker(); void setTimeoutInterval(unsigned timeoutInterval) { m_timeoutInterval = timeoutInterval; } + unsigned timeoutInterval() const { return m_timeoutInterval; } unsigned ticksUntilNextCheck() { return m_ticksUntilNextCheck; } diff --git a/JavaScriptCore/runtime/UString.cpp b/JavaScriptCore/runtime/UString.cpp index 4a89a23..b3cd40c 100644 --- a/JavaScriptCore/runtime/UString.cpp +++ b/JavaScriptCore/runtime/UString.cpp @@ -26,23 +26,20 @@ #include "JSGlobalObjectFunctions.h" #include "Collector.h" -#include "dtoa.h" #include "Identifier.h" #include "Operations.h" #include <ctype.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/DecimalNumber.h> #include <wtf/MathExtras.h> #include <wtf/StringExtras.h> #include <wtf/Vector.h> #include <wtf/unicode/UTF8.h> -#include <wtf/StringExtras.h> #if HAVE(STRINGS_H) #include <strings.h> @@ -57,140 +54,50 @@ namespace JSC { extern const double NaN; extern const double Inf; -CString::CString(const char* c) - : m_length(strlen(c)) - , m_data(new char[m_length + 1]) -{ - memcpy(m_data, c, m_length + 1); -} +COMPILE_ASSERT(sizeof(UString) == sizeof(void*), UString_should_stay_small); -CString::CString(const char* c, size_t length) - : m_length(length) - , m_data(new char[length + 1]) +// Construct a string with UTF-16 data. +UString::UString(const UChar* characters, unsigned length) + : m_impl(characters ? StringImpl::create(characters, length) : 0) { - memcpy(m_data, c, m_length); - m_data[m_length] = 0; } -CString::CString(const CString& b) +// Construct a string with UTF-16 data, from a null-terminated source. +UString::UString(const UChar* characters) { - m_length = b.m_length; - if (b.m_data) { - m_data = new char[m_length + 1]; - memcpy(m_data, b.m_data, m_length + 1); - } else - m_data = 0; -} + if (!characters) + return; -CString::~CString() -{ - delete [] m_data; -} - -CString CString::adopt(char* c, size_t length) -{ - CString s; - s.m_data = c; - s.m_length = length; - return s; -} + int length = 0; + while (characters[length] != UChar(0)) + ++length; -CString& CString::append(const CString& t) -{ - char* n; - n = new char[m_length + t.m_length + 1]; - if (m_length) - memcpy(n, m_data, m_length); - if (t.m_length) - memcpy(n + m_length, t.m_data, t.m_length); - m_length += t.m_length; - n[m_length] = 0; - - delete [] m_data; - m_data = n; - - return *this; + m_impl = StringImpl::create(characters, length); } -CString& CString::operator=(const char* c) +// Construct a string with latin1 data. +UString::UString(const char* characters, unsigned length) + : m_impl(characters ? StringImpl::create(characters, length) : 0) { - if (m_data) - delete [] m_data; - m_length = strlen(c); - m_data = new char[m_length + 1]; - memcpy(m_data, c, m_length + 1); - - return *this; } -CString& CString::operator=(const CString& str) +// Construct a string with latin1 data, from a null-terminated source. +UString::UString(const char* characters) + : m_impl(characters ? StringImpl::create(characters) : 0) { - if (this == &str) - return *this; - - if (m_data) - delete [] m_data; - m_length = str.m_length; - if (str.m_data) { - m_data = new char[m_length + 1]; - memcpy(m_data, str.m_data, m_length + 1); - } else - m_data = 0; - - return *this; } -bool operator==(const CString& c1, const CString& c2) -{ - size_t len = c1.size(); - return len == c2.size() && (len == 0 || memcmp(c1.c_str(), c2.c_str(), len) == 0); -} - -// These static strings are immutable, except for rc, whose initial value is chosen to -// reduce the possibility of it becoming zero due to ref/deref not being thread-safe. -static UChar sharedEmptyChar; -UStringImpl* UStringImpl::s_empty; - -UString::Rep* UString::s_nullRep; -UString* UString::s_nullUString; - -void initializeUString() -{ - UStringImpl::s_empty = new UStringImpl(&sharedEmptyChar, 0, UStringImpl::ConstructStaticString); - - UString::s_nullRep = new UStringImpl(0, 0, UStringImpl::ConstructStaticString); - UString::s_nullUString = new UString; -} - -UString::UString(const char* c) - : m_rep(Rep::create(c)) -{ -} - -UString::UString(const char* c, int length) - : m_rep(Rep::create(c, length)) -{ -} - -UString::UString(const UChar* c, int length) -{ - if (length == 0) - m_rep = &Rep::empty(); - else - m_rep = Rep::create(c, length); -} - -UString UString::from(int i) +UString UString::number(int i) { UChar buf[1 + sizeof(i) * 3]; - UChar* end = buf + sizeof(buf) / sizeof(UChar); + UChar* end = buf + WTF_ARRAY_LENGTH(buf); UChar* p = end; if (i == 0) *--p = '0'; else if (i == INT_MIN) { char minBuf[1 + sizeof(i) * 3]; - sprintf(minBuf, "%d", INT_MIN); + snprintf(minBuf, sizeof(minBuf), "%d", INT_MIN); return UString(minBuf); } else { bool negative = false; @@ -206,13 +113,13 @@ UString UString::from(int i) *--p = '-'; } - return UString(p, static_cast<int>(end - p)); + return UString(p, static_cast<unsigned>(end - p)); } -UString UString::from(long long i) +UString UString::number(long long i) { UChar buf[1 + sizeof(i) * 3]; - UChar* end = buf + sizeof(buf) / sizeof(UChar); + UChar* end = buf + WTF_ARRAY_LENGTH(buf); UChar* p = end; if (i == 0) @@ -220,9 +127,9 @@ UString UString::from(long long i) else if (i == std::numeric_limits<long long>::min()) { char minBuf[1 + sizeof(i) * 3]; #if OS(WINDOWS) - snprintf(minBuf, sizeof(minBuf) - 1, "%I64d", std::numeric_limits<long long>::min()); + snprintf(minBuf, sizeof(minBuf), "%I64d", std::numeric_limits<long long>::min()); #else - snprintf(minBuf, sizeof(minBuf) - 1, "%lld", std::numeric_limits<long long>::min()); + snprintf(minBuf, sizeof(minBuf), "%lld", std::numeric_limits<long long>::min()); #endif return UString(minBuf); } else { @@ -239,13 +146,13 @@ UString UString::from(long long i) *--p = '-'; } - return UString(p, static_cast<int>(end - p)); + return UString(p, static_cast<unsigned>(end - p)); } -UString UString::from(unsigned int u) +UString UString::number(unsigned u) { UChar buf[sizeof(u) * 3]; - UChar* end = buf + sizeof(buf) / sizeof(UChar); + UChar* end = buf + WTF_ARRAY_LENGTH(buf); UChar* p = end; if (u == 0) @@ -257,20 +164,20 @@ UString UString::from(unsigned int u) } } - return UString(p, static_cast<int>(end - p)); + return UString(p, static_cast<unsigned>(end - p)); } -UString UString::from(long l) +UString UString::number(long l) { UChar buf[1 + sizeof(l) * 3]; - UChar* end = buf + sizeof(buf) / sizeof(UChar); + UChar* end = buf + WTF_ARRAY_LENGTH(buf); UChar* p = end; if (l == 0) *--p = '0'; else if (l == LONG_MIN) { char minBuf[1 + sizeof(l) * 3]; - sprintf(minBuf, "%ld", LONG_MIN); + snprintf(minBuf, sizeof(minBuf), "%ld", LONG_MIN); return UString(minBuf); } else { bool negative = false; @@ -286,360 +193,27 @@ UString UString::from(long l) *--p = '-'; } - return UString(p, static_cast<int>(end - p)); + return UString(p, end - p); } -UString UString::from(double d) +UString UString::number(double d) { - DtoaBuffer buffer; - unsigned length; - doubleToStringInJavaScriptFormat(d, buffer, &length); + NumberToStringBuffer buffer; + unsigned length = numberToString(d, buffer); return UString(buffer, length); } -bool UString::getCString(CStringBuffer& buffer) const +UString UString::substringSharingImpl(unsigned offset, unsigned length) const { - int length = size(); - int neededSize = length + 1; - buffer.resize(neededSize); - char* buf = buffer.data(); - - UChar ored = 0; - const UChar* p = data(); - char* q = buf; - const UChar* limit = p + length; - while (p != limit) { - UChar c = p[0]; - ored |= c; - *q = static_cast<char>(c); - ++p; - ++q; - } - *q = '\0'; + // FIXME: We used to check against a limit of Heap::minExtraCost / sizeof(UChar). - return !(ored & 0xFF00); -} + unsigned stringLength = this->length(); + offset = min(offset, stringLength); + length = min(length, stringLength - offset); -char* UString::ascii() const -{ - static char* asciiBuffer = 0; - - int length = size(); - int neededSize = length + 1; - delete[] asciiBuffer; - asciiBuffer = new char[neededSize]; - - const UChar* p = data(); - char* q = asciiBuffer; - const UChar* limit = p + length; - while (p != limit) { - *q = static_cast<char>(p[0]); - ++p; - ++q; - } - *q = '\0'; - - return asciiBuffer; -} - -bool UString::is8Bit() const -{ - const UChar* u = data(); - const UChar* limit = u + size(); - while (u < limit) { - if (u[0] > 0xFF) - return false; - ++u; - } - - return true; -} - -UChar UString::operator[](int pos) const -{ - if (pos >= size()) - return '\0'; - return data()[pos]; -} - -double UString::toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const -{ - if (size() == 1) { - UChar c = data()[0]; - if (isASCIIDigit(c)) - return c - '0'; - if (isASCIISpace(c) && tolerateEmptyString) - return 0; - return NaN; - } - - // FIXME: If tolerateTrailingJunk is true, then we want to tolerate non-8-bit junk - // after the number, so this is too strict a check. - CStringBuffer s; - if (!getCString(s)) - return NaN; - const char* c = s.data(); - - // skip leading white space - while (isASCIISpace(*c)) - c++; - - // empty string ? - if (*c == '\0') - return tolerateEmptyString ? 0.0 : NaN; - - double d; - - // hex number ? - if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X')) { - const char* firstDigitPosition = c + 2; - c++; - d = 0.0; - while (*(++c)) { - if (*c >= '0' && *c <= '9') - d = d * 16.0 + *c - '0'; - else if ((*c >= 'A' && *c <= 'F') || (*c >= 'a' && *c <= 'f')) - d = d * 16.0 + (*c & 0xdf) - 'A' + 10.0; - else - break; - } - - if (d >= mantissaOverflowLowerBound) - d = parseIntOverflow(firstDigitPosition, c - firstDigitPosition, 16); - } else { - // regular number ? - char* end; - d = WTF::strtod(c, &end); - if ((d != 0.0 || end != c) && d != Inf && d != -Inf) { - c = end; - } else { - double sign = 1.0; - - if (*c == '+') - c++; - else if (*c == '-') { - sign = -1.0; - c++; - } - - // We used strtod() to do the conversion. However, strtod() handles - // infinite values slightly differently than JavaScript in that it - // converts the string "inf" with any capitalization to infinity, - // whereas the ECMA spec requires that it be converted to NaN. - - if (c[0] == 'I' && c[1] == 'n' && c[2] == 'f' && c[3] == 'i' && c[4] == 'n' && c[5] == 'i' && c[6] == 't' && c[7] == 'y') { - d = sign * Inf; - c += 8; - } else if ((d == Inf || d == -Inf) && *c != 'I' && *c != 'i') - c = end; - else - return NaN; - } - } - - // allow trailing white space - while (isASCIISpace(*c)) - c++; - // don't allow anything after - unless tolerant=true - if (!tolerateTrailingJunk && *c != '\0') - d = NaN; - - return d; -} - -double UString::toDouble(bool tolerateTrailingJunk) const -{ - return toDouble(tolerateTrailingJunk, true); -} - -double UString::toDouble() const -{ - return toDouble(false, true); -} - -uint32_t UString::toUInt32(bool* ok) const -{ - double d = toDouble(); - bool b = true; - - if (d != static_cast<uint32_t>(d)) { - b = false; - d = 0; - } - - if (ok) - *ok = b; - - return static_cast<uint32_t>(d); -} - -uint32_t UString::toUInt32(bool* ok, bool tolerateEmptyString) const -{ - double d = toDouble(false, tolerateEmptyString); - bool b = true; - - if (d != static_cast<uint32_t>(d)) { - b = false; - d = 0; - } - - if (ok) - *ok = b; - - return static_cast<uint32_t>(d); -} - -uint32_t UString::toStrictUInt32(bool* ok) const -{ - if (ok) - *ok = false; - - // Empty string is not OK. - int len = m_rep->size(); - if (len == 0) - return 0; - const UChar* p = m_rep->data(); - unsigned short c = p[0]; - - // If the first digit is 0, only 0 itself is OK. - if (c == '0') { - if (len == 1 && ok) - *ok = true; - return 0; - } - - // Convert to UInt32, checking for overflow. - uint32_t i = 0; - while (1) { - // Process character, turning it into a digit. - if (c < '0' || c > '9') - return 0; - const unsigned d = c - '0'; - - // Multiply by 10, checking for overflow out of 32 bits. - if (i > 0xFFFFFFFFU / 10) - return 0; - i *= 10; - - // Add in the digit, checking for overflow out of 32 bits. - const unsigned max = 0xFFFFFFFFU - d; - if (i > max) - return 0; - i += d; - - // Handle end of string. - if (--len == 0) { - if (ok) - *ok = true; - return i; - } - - // Get next character. - c = *(++p); - } -} - -int UString::find(const UString& f, int pos) const -{ - int fsz = f.size(); - - if (pos < 0) - pos = 0; - - if (fsz == 1) { - UChar ch = f[0]; - const UChar* end = data() + size(); - for (const UChar* c = data() + pos; c < end; c++) { - if (*c == ch) - return static_cast<int>(c - data()); - } - return -1; - } - - int sz = size(); - if (sz < fsz) - return -1; - if (fsz == 0) - return pos; - const UChar* end = data() + sz - fsz; - int fsizeminusone = (fsz - 1) * sizeof(UChar); - const UChar* fdata = f.data(); - unsigned short fchar = fdata[0]; - ++fdata; - for (const UChar* c = data() + pos; c <= end; c++) { - if (c[0] == fchar && !memcmp(c + 1, fdata, fsizeminusone)) - return static_cast<int>(c - data()); - } - - return -1; -} - -int UString::find(UChar ch, int pos) const -{ - if (pos < 0) - pos = 0; - const UChar* end = data() + size(); - for (const UChar* c = data() + pos; c < end; c++) { - if (*c == ch) - return static_cast<int>(c - data()); - } - - return -1; -} - -int UString::rfind(const UString& f, int pos) const -{ - int sz = size(); - int fsz = f.size(); - if (sz < fsz) - return -1; - if (pos < 0) - pos = 0; - if (pos > sz - fsz) - pos = sz - fsz; - if (fsz == 0) - return pos; - int fsizeminusone = (fsz - 1) * sizeof(UChar); - const UChar* fdata = f.data(); - for (const UChar* c = data() + pos; c >= data(); c--) { - if (*c == *fdata && !memcmp(c + 1, fdata + 1, fsizeminusone)) - return static_cast<int>(c - data()); - } - - return -1; -} - -int UString::rfind(UChar ch, int pos) const -{ - if (isEmpty()) - return -1; - if (pos + 1 >= size()) - pos = size() - 1; - for (const UChar* c = data() + pos; c >= data(); c--) { - if (*c == ch) - return static_cast<int>(c - data()); - } - - return -1; -} - -UString UString::substr(int pos, int len) const -{ - int s = size(); - - if (pos < 0) - pos = 0; - else if (pos >= s) - pos = s; - if (len < 0) - len = s; - if (pos + len >= s) - len = s - pos; - - if (pos == 0 && len == s) + if (!offset && length == stringLength) return *this; - - return UString(Rep::create(m_rep, pos, len)); + return UString(StringImpl::create(m_impl, offset, length)); } bool operator==(const UString& s1, const char *s2) @@ -647,8 +221,8 @@ bool operator==(const UString& s1, const char *s2) if (s2 == 0) return s1.isEmpty(); - const UChar* u = s1.data(); - const UChar* uend = u + s1.size(); + const UChar* u = s1.characters(); + const UChar* uend = u + s1.length(); while (u != uend && *s2) { if (u[0] != (unsigned char)*s2) return false; @@ -661,12 +235,12 @@ bool operator==(const UString& s1, const char *s2) bool operator<(const UString& s1, const UString& s2) { - const int l1 = s1.size(); - const int l2 = s2.size(); - const int lmin = l1 < l2 ? l1 : l2; - const UChar* c1 = s1.data(); - const UChar* c2 = s2.data(); - int l = 0; + const unsigned l1 = s1.length(); + const unsigned l2 = s2.length(); + const unsigned lmin = l1 < l2 ? l1 : l2; + const UChar* c1 = s1.characters(); + const UChar* c2 = s2.characters(); + unsigned l = 0; while (l < lmin && *c1 == *c2) { c1++; c2++; @@ -680,12 +254,12 @@ bool operator<(const UString& s1, const UString& s2) bool operator>(const UString& s1, const UString& s2) { - const int l1 = s1.size(); - const int l2 = s2.size(); - const int lmin = l1 < l2 ? l1 : l2; - const UChar* c1 = s1.data(); - const UChar* c2 = s2.data(); - int l = 0; + const unsigned l1 = s1.length(); + const unsigned l2 = s2.length(); + const unsigned lmin = l1 < l2 ? l1 : l2; + const UChar* c1 = s1.characters(); + const UChar* c2 = s2.characters(); + unsigned l = 0; while (l < lmin && *c1 == *c2) { c1++; c2++; @@ -697,57 +271,96 @@ bool operator>(const UString& s1, const UString& s2) return (l1 > l2); } -int compare(const UString& s1, const UString& s2) +CString UString::ascii() const { - const int l1 = s1.size(); - const int l2 = s2.size(); - const int lmin = l1 < l2 ? l1 : l2; - const UChar* c1 = s1.data(); - const UChar* c2 = s2.data(); - int l = 0; - while (l < lmin && *c1 == *c2) { - c1++; - c2++; - l++; - } + // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are + // preserved, characters outside of this range are converted to '?'. - if (l < lmin) - return (c1[0] > c2[0]) ? 1 : -1; + unsigned length = this->length(); + const UChar* characters = this->characters(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); - if (l1 == l2) - return 0; + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch && (ch < 0x20 || ch >= 0x7f) ? '?' : ch; + } - return (l1 > l2) ? 1 : -1; + return result; } -bool equal(const UString::Rep* r, const UString::Rep* b) +CString UString::latin1() const { - int length = r->size(); - if (length != b->size()) - return false; - const UChar* d = r->data(); - const UChar* s = b->data(); - for (int i = 0; i != length; ++i) { - if (d[i] != s[i]) - return false; + // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are + // preserved, characters outside of this range are converted to '?'. + + unsigned length = this->length(); + const UChar* characters = this->characters(); + + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); + + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch > 0xff ? '?' : ch; } - return true; + + return result; } -CString UString::UTF8String(bool strict) const +// Helper to write a three-byte UTF-8 code point to the buffer, caller must check room is available. +static inline void putUTF8Triple(char*& buffer, UChar ch) { - // Allocate a buffer big enough to hold all the characters. - const int length = size(); - Vector<char, 1024> buffer(length * 3); - - // Convert to runs of 8-bit characters. - char* p = buffer.data(); - const UChar* d = reinterpret_cast<const UChar*>(&data()[0]); - ConversionResult result = convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), strict); - if (result != conversionOK) + ASSERT(ch >= 0x0800); + *buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0); + *buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80); + *buffer++ = static_cast<char>((ch & 0x3F) | 0x80); +} + +CString UString::utf8(bool strict) const +{ + unsigned length = this->length(); + const UChar* characters = this->characters(); + + // Allocate a buffer big enough to hold all the characters + // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes). + // Optimization ideas, if we find this function is hot: + // * We could speculatively create a CStringBuffer to contain 'length' + // characters, and resize if necessary (i.e. if the buffer contains + // non-ascii characters). (Alternatively, scan the buffer first for + // ascii characters, so we know this will be sufficient). + // * We could allocate a CStringBuffer with an appropriate size to + // have a good chance of being able to write the string into the + // buffer without reallocing (say, 1.5 x length). + if (length > numeric_limits<unsigned>::max() / 3) + return CString(); + Vector<char, 1024> bufferVector(length * 3); + + char* buffer = bufferVector.data(); + ConversionResult result = convertUTF16ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size(), strict); + ASSERT(result != targetExhausted); // (length * 3) should be sufficient for any conversion + + // Only produced from strict conversion. + if (result == sourceIllegal) return CString(); - return CString(buffer.data(), p - buffer.data()); + // Check for an unconverted high surrogate. + if (result == sourceExhausted) { + if (strict) + return CString(); + // This should be one unpaired high surrogate. Treat it the same + // was as an unpaired high surrogate would have been handled in + // the middle of a string with non-strict conversion - which is + // to say, simply encode it to UTF-8. + ASSERT((characters + 1) == (this->characters() + length)); + ASSERT((*characters >= 0xD800) && (*characters <= 0xDBFF)); + // There should be room left, since one UChar hasn't been converted. + ASSERT((buffer + 3) <= (buffer + bufferVector.size())); + putUTF8Triple(buffer, *characters); + } + + return CString(bufferVector.data(), buffer - bufferVector.data()); } } // namespace JSC diff --git a/JavaScriptCore/runtime/UString.h b/JavaScriptCore/runtime/UString.h index 7d9ec49..8f6c083 100644 --- a/JavaScriptCore/runtime/UString.h +++ b/JavaScriptCore/runtime/UString.h @@ -1,631 +1,263 @@ /* - * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. * */ #ifndef UString_h #define UString_h -#include "Collector.h" -#include "UStringImpl.h" -#include <stdint.h> -#include <string.h> -#include <wtf/Assertions.h> -#include <wtf/CrossThreadRefCounted.h> -#include <wtf/OwnFastMallocPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/PtrAndFlags.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> -#include <wtf/unicode/Unicode.h> +#include <wtf/text/StringImpl.h> namespace JSC { - using WTF::PlacementNewAdoptType; - using WTF::PlacementNewAdopt; +class UString { +public: + // Construct a null string, distinguishable from an empty string. + UString() { } - class CString { - public: - CString() - : m_length(0) - , m_data(0) - { - } - - CString(const char*); - CString(const char*, size_t); - CString(const CString&); - - ~CString(); - - static CString adopt(char*, size_t); // buffer should be allocated with new[]. - - CString& append(const CString&); - CString& operator=(const char* c); - CString& operator=(const CString&); - CString& operator+=(const CString& c) { return append(c); } - - size_t size() const { return m_length; } - const char* c_str() const { return m_data; } - - private: - size_t m_length; - char* m_data; - }; - - bool operator==(const CString&, const CString&); - - typedef Vector<char, 32> CStringBuffer; - - class UString { - friend class JIT; - - public: - typedef UStringImpl Rep; - - public: - UString(); - UString(const char*); // Constructor for null-terminated string. - UString(const char*, int length); - UString(const UChar*, int length); - UString(const Vector<UChar>& buffer); - - UString(const UString& s) - : m_rep(s.m_rep) - { - } - - // Special constructor for cases where we overwrite an object in place. - UString(PlacementNewAdoptType) - : m_rep(PlacementNewAdopt) - { - } - - ~UString() - { - } - - template<size_t inlineCapacity> - static PassRefPtr<UStringImpl> adopt(Vector<UChar, inlineCapacity>& vector) - { - return Rep::adopt(vector); - } - - static UString from(int); - static UString from(long long); - static UString from(unsigned int); - static UString from(long); - static UString from(double); - - bool getCString(CStringBuffer&) const; - - // NOTE: This method should only be used for *debugging* purposes as it - // is neither Unicode safe nor free from side effects nor thread-safe. - char* ascii() const; - - /** - * Convert the string to UTF-8, assuming it is UTF-16 encoded. - * In non-strict mode, this function is tolerant of badly formed UTF-16, it - * can create UTF-8 strings that are invalid because they have characters in - * the range U+D800-U+DDFF, U+FFFE, or U+FFFF, but the UTF-8 string is - * guaranteed to be otherwise valid. - * In strict mode, error is returned as null CString. - */ - CString UTF8String(bool strict = false) const; - - const UChar* data() const { return m_rep->data(); } - - bool isNull() const { return m_rep == s_nullRep; } - bool isEmpty() const { return !m_rep->size(); } - - bool is8Bit() const; - - int size() const { return m_rep->size(); } - - UChar operator[](int pos) const; - - double toDouble(bool tolerateTrailingJunk, bool tolerateEmptyString) const; - double toDouble(bool tolerateTrailingJunk) const; - double toDouble() const; - - uint32_t toUInt32(bool* ok = 0) const; - uint32_t toUInt32(bool* ok, bool tolerateEmptyString) const; - uint32_t toStrictUInt32(bool* ok = 0) const; - - unsigned toArrayIndex(bool* ok = 0) const; - - int find(const UString& f, int pos = 0) const; - int find(UChar, int pos = 0) const; - int rfind(const UString& f, int pos) const; - int rfind(UChar, int pos) const; - - UString substr(int pos = 0, int len = -1) const; - - static const UString& null() { return *s_nullUString; } - - Rep* rep() const { return m_rep.get(); } - - UString(PassRefPtr<Rep> r) - : m_rep(r) - { - ASSERT(m_rep); - } - - size_t cost() const { return m_rep->cost(); } - - private: - RefPtr<Rep> m_rep; - - JS_EXPORTDATA static Rep* s_nullRep; - static UString* s_nullUString; - - friend void initializeUString(); - friend bool operator==(const UString&, const UString&); - }; - - ALWAYS_INLINE bool operator==(const UString& s1, const UString& s2) - { - int size = s1.size(); - switch (size) { - case 0: - return !s2.size(); - case 1: - return s2.size() == 1 && s1.data()[0] == s2.data()[0]; - case 2: { - if (s2.size() != 2) - return false; - const UChar* d1 = s1.data(); - const UChar* d2 = s2.data(); - return (d1[0] == d2[0]) & (d1[1] == d2[1]); - } - default: - return s2.size() == size && memcmp(s1.data(), s2.data(), size * sizeof(UChar)) == 0; - } - } - - - inline bool operator!=(const UString& s1, const UString& s2) - { - return !JSC::operator==(s1, s2); - } - - bool operator<(const UString& s1, const UString& s2); - bool operator>(const UString& s1, const UString& s2); - - bool operator==(const UString& s1, const char* s2); - - inline bool operator!=(const UString& s1, const char* s2) - { - return !JSC::operator==(s1, s2); - } - - inline bool operator==(const char *s1, const UString& s2) - { - return operator==(s2, s1); - } - - inline bool operator!=(const char *s1, const UString& s2) - { - return !JSC::operator==(s1, s2); - } + // Construct a string with UTF-16 data. + UString(const UChar* characters, unsigned length); - int compare(const UString&, const UString&); + // Construct a string with UTF-16 data, from a null-terminated source. + UString(const UChar*); - inline UString::UString() - : m_rep(s_nullRep) - { - } + // Construct a string with latin1 data. + UString(const char* characters, unsigned length); - // Rule from ECMA 15.2 about what an array index is. - // Must exactly match string form of an unsigned integer, and be less than 2^32 - 1. - inline unsigned UString::toArrayIndex(bool* ok) const - { - unsigned i = toStrictUInt32(ok); - if (ok && i >= 0xFFFFFFFFU) - *ok = false; - return i; - } + // Construct a string with latin1 data, from a null-terminated source. + UString(const char* characters); - // We'd rather not do shared substring append for small strings, since - // this runs too much risk of a tiny initial string holding down a - // huge buffer. - // FIXME: this should be size_t but that would cause warnings until we - // fix UString sizes to be size_t instead of int - static const int minShareSize = Heap::minExtraCost / sizeof(UChar); - - struct IdentifierRepHash : PtrHash<RefPtr<JSC::UString::Rep> > { - static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->existingHash(); } - static unsigned hash(JSC::UString::Rep* key) { return key->existingHash(); } - }; - - void initializeUString(); - - template<typename StringType> - class StringTypeAdapter { - }; - - template<> - class StringTypeAdapter<char*> { - public: - StringTypeAdapter<char*>(char* buffer) - : m_buffer((unsigned char*)buffer) - , m_length(strlen(buffer)) - { - } + // Construct a string referencing an existing StringImpl. + UString(StringImpl* impl) : m_impl(impl) { } + UString(PassRefPtr<StringImpl> impl) : m_impl(impl) { } + UString(RefPtr<StringImpl> impl) : m_impl(impl) { } - unsigned length() { return m_length; } + // Inline the destructor. + ALWAYS_INLINE ~UString() { } - void writeTo(UChar* destination) - { - for (unsigned i = 0; i < m_length; ++i) - destination[i] = m_buffer[i]; - } - - private: - const unsigned char* m_buffer; - unsigned m_length; - }; - - template<> - class StringTypeAdapter<const char*> { - public: - StringTypeAdapter<const char*>(const char* buffer) - : m_buffer((unsigned char*)buffer) - , m_length(strlen(buffer)) - { - } - - unsigned length() { return m_length; } - - void writeTo(UChar* destination) - { - for (unsigned i = 0; i < m_length; ++i) - destination[i] = m_buffer[i]; - } - - private: - const unsigned char* m_buffer; - unsigned m_length; - }; - - template<> - class StringTypeAdapter<UString> { - public: - StringTypeAdapter<UString>(UString& string) - : m_data(string.data()) - , m_length(string.size()) - { - } + void swap(UString& o) { m_impl.swap(o.m_impl); } - unsigned length() { return m_length; } + template<size_t inlineCapacity> + static UString adopt(Vector<UChar, inlineCapacity>& vector) { return StringImpl::adopt(vector); } - void writeTo(UChar* destination) - { - for (unsigned i = 0; i < m_length; ++i) - destination[i] = m_data[i]; - } + bool isNull() const { return !m_impl; } + bool isEmpty() const { return !m_impl || !m_impl->length(); } - private: - const UChar* m_data; - unsigned m_length; - }; + StringImpl* impl() const { return m_impl.get(); } - template<typename StringType1, typename StringType2> - PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2) + unsigned length() const { - StringTypeAdapter<StringType1> adapter1(string1); - StringTypeAdapter<StringType2> adapter2(string2); - - UChar* buffer; - unsigned length = adapter1.length() + adapter2.length(); - PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); - if (!resultImpl) + if (!m_impl) return 0; - - UChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - - return resultImpl; + return m_impl->length(); } - template<typename StringType1, typename StringType2, typename StringType3> - PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3) + const UChar* characters() const { - StringTypeAdapter<StringType1> adapter1(string1); - StringTypeAdapter<StringType2> adapter2(string2); - StringTypeAdapter<StringType3> adapter3(string3); - - UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length(); - PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); - if (!resultImpl) + if (!m_impl) return 0; - - UChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - result += adapter2.length(); - adapter3.writeTo(result); - - return resultImpl; + return m_impl->characters(); } - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> - PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) - { - StringTypeAdapter<StringType1> adapter1(string1); - StringTypeAdapter<StringType2> adapter2(string2); - StringTypeAdapter<StringType3> adapter3(string3); - StringTypeAdapter<StringType4> adapter4(string4); - - UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length(); - PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); - if (!resultImpl) - return 0; + CString ascii() const; + CString latin1() const; + CString utf8(bool strict = false) const; - UChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - result += adapter2.length(); - adapter3.writeTo(result); - result += adapter3.length(); - adapter4.writeTo(result); - - return resultImpl; - } - - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> - PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) + UChar operator[](unsigned index) const { - StringTypeAdapter<StringType1> adapter1(string1); - StringTypeAdapter<StringType2> adapter2(string2); - StringTypeAdapter<StringType3> adapter3(string3); - StringTypeAdapter<StringType4> adapter4(string4); - StringTypeAdapter<StringType5> adapter5(string5); - - UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length(); - PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); - if (!resultImpl) + if (!m_impl || index >= m_impl->length()) return 0; + return m_impl->characters()[index]; + } - UChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - result += adapter2.length(); - adapter3.writeTo(result); - result += adapter3.length(); - adapter4.writeTo(result); - result += adapter4.length(); - adapter5.writeTo(result); - - return resultImpl; + static UString number(int); + static UString number(unsigned); + static UString number(long); + static UString number(long long); + static UString number(double); + + // Find a single character or string, also with match function & latin1 forms. + size_t find(UChar c, unsigned start = 0) const + { return m_impl ? m_impl->find(c, start) : notFound; } + size_t find(const UString& str, unsigned start = 0) const + { return m_impl ? m_impl->find(str.impl(), start) : notFound; } + size_t find(const char* str, unsigned start = 0) const + { return m_impl ? m_impl->find(str, start) : notFound; } + + // Find the last instance of a single character or string. + size_t reverseFind(UChar c, unsigned start = UINT_MAX) const + { return m_impl ? m_impl->reverseFind(c, start) : notFound; } + size_t reverseFind(const UString& str, unsigned start = UINT_MAX) const + { return m_impl ? m_impl->reverseFind(str.impl(), start) : notFound; } + + UString substringSharingImpl(unsigned pos, unsigned len = UINT_MAX) const; + +private: + RefPtr<StringImpl> m_impl; +}; + +ALWAYS_INLINE bool operator==(const UString& s1, const UString& s2) +{ + StringImpl* rep1 = s1.impl(); + StringImpl* rep2 = s2.impl(); + unsigned size1 = 0; + unsigned size2 = 0; + + if (rep1 == rep2) // If they're the same rep, they're equal. + return true; + + if (rep1) + size1 = rep1->length(); + + if (rep2) + size2 = rep2->length(); + + if (size1 != size2) // If the lengths are not the same, we're done. + return false; + + if (!size1) + return true; + + // At this point we know + // (a) that the strings are the same length and + // (b) that they are greater than zero length. + const UChar* d1 = rep1->characters(); + const UChar* d2 = rep2->characters(); + + if (d1 == d2) // Check to see if the data pointers are the same. + return true; + + // Do quick checks for sizes 1 and 2. + switch (size1) { + case 1: + return d1[0] == d2[0]; + case 2: + return (d1[0] == d2[0]) & (d1[1] == d2[1]); + default: + return memcmp(d1, d2, size1 * sizeof(UChar)) == 0; } +} - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> - PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) - { - StringTypeAdapter<StringType1> adapter1(string1); - StringTypeAdapter<StringType2> adapter2(string2); - StringTypeAdapter<StringType3> adapter3(string3); - StringTypeAdapter<StringType4> adapter4(string4); - StringTypeAdapter<StringType5> adapter5(string5); - StringTypeAdapter<StringType6> adapter6(string6); - - UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length(); - PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); - if (!resultImpl) - return 0; - UChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - result += adapter2.length(); - adapter3.writeTo(result); - result += adapter3.length(); - adapter4.writeTo(result); - result += adapter4.length(); - adapter5.writeTo(result); - result += adapter5.length(); - adapter6.writeTo(result); - - return resultImpl; - } +inline bool operator!=(const UString& s1, const UString& s2) +{ + return !JSC::operator==(s1, s2); +} - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7> - PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7) - { - StringTypeAdapter<StringType1> adapter1(string1); - StringTypeAdapter<StringType2> adapter2(string2); - StringTypeAdapter<StringType3> adapter3(string3); - StringTypeAdapter<StringType4> adapter4(string4); - StringTypeAdapter<StringType5> adapter5(string5); - StringTypeAdapter<StringType6> adapter6(string6); - StringTypeAdapter<StringType7> adapter7(string7); - - UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length() + adapter7.length(); - PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); - if (!resultImpl) - return 0; +bool operator<(const UString& s1, const UString& s2); +bool operator>(const UString& s1, const UString& s2); - UChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - result += adapter2.length(); - adapter3.writeTo(result); - result += adapter3.length(); - adapter4.writeTo(result); - result += adapter4.length(); - adapter5.writeTo(result); - result += adapter5.length(); - adapter6.writeTo(result); - result += adapter6.length(); - adapter7.writeTo(result); - - return resultImpl; - } +bool operator==(const UString& s1, const char* s2); - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8> - PassRefPtr<UStringImpl> tryMakeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8) - { - StringTypeAdapter<StringType1> adapter1(string1); - StringTypeAdapter<StringType2> adapter2(string2); - StringTypeAdapter<StringType3> adapter3(string3); - StringTypeAdapter<StringType4> adapter4(string4); - StringTypeAdapter<StringType5> adapter5(string5); - StringTypeAdapter<StringType6> adapter6(string6); - StringTypeAdapter<StringType7> adapter7(string7); - StringTypeAdapter<StringType8> adapter8(string8); - - UChar* buffer; - unsigned length = adapter1.length() + adapter2.length() + adapter3.length() + adapter4.length() + adapter5.length() + adapter6.length() + adapter7.length() + adapter8.length(); - PassRefPtr<UStringImpl> resultImpl = UStringImpl::tryCreateUninitialized(length, buffer); - if (!resultImpl) - return 0; +inline bool operator!=(const UString& s1, const char* s2) +{ + return !JSC::operator==(s1, s2); +} - UChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - result += adapter2.length(); - adapter3.writeTo(result); - result += adapter3.length(); - adapter4.writeTo(result); - result += adapter4.length(); - adapter5.writeTo(result); - result += adapter5.length(); - adapter6.writeTo(result); - result += adapter6.length(); - adapter7.writeTo(result); - result += adapter7.length(); - adapter8.writeTo(result); - - return resultImpl; - } +inline bool operator==(const char *s1, const UString& s2) +{ + return operator==(s2, s1); +} - template<typename StringType1, typename StringType2> - UString makeString(StringType1 string1, StringType2 string2) - { - PassRefPtr<UStringImpl> resultImpl = tryMakeString(string1, string2); - if (!resultImpl) - CRASH(); - return resultImpl; - } +inline bool operator!=(const char *s1, const UString& s2) +{ + return !JSC::operator==(s1, s2); +} - template<typename StringType1, typename StringType2, typename StringType3> - UString makeString(StringType1 string1, StringType2 string2, StringType3 string3) - { - PassRefPtr<UStringImpl> resultImpl = tryMakeString(string1, string2, string3); - if (!resultImpl) - CRASH(); - return resultImpl; - } +inline int codePointCompare(const UString& s1, const UString& s2) +{ + return codePointCompare(s1.impl(), s2.impl()); +} - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> - UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) +struct UStringHash { + static unsigned hash(StringImpl* key) { return key->hash(); } + static bool equal(const StringImpl* a, const StringImpl* b) { - PassRefPtr<UStringImpl> resultImpl = tryMakeString(string1, string2, string3, string4); - if (!resultImpl) - CRASH(); - return resultImpl; - } + if (a == b) + return true; + if (!a || !b) + return false; + + unsigned aLength = a->length(); + unsigned bLength = b->length(); + if (aLength != bLength) + return false; + + // FIXME: perhaps we should have a more abstract macro that indicates when + // going 4 bytes at a time is unsafe +#if CPU(ARM) || CPU(SH4) || CPU(MIPS) + const UChar* aChars = a->characters(); + const UChar* bChars = b->characters(); + for (unsigned i = 0; i != aLength; ++i) { + if (*aChars++ != *bChars++) + return false; + } + return true; +#else + /* Do it 4-bytes-at-a-time on architectures where it's safe */ + const uint32_t* aChars = reinterpret_cast<const uint32_t*>(a->characters()); + const uint32_t* bChars = reinterpret_cast<const uint32_t*>(b->characters()); + + unsigned halfLength = aLength >> 1; + for (unsigned i = 0; i != halfLength; ++i) + if (*aChars++ != *bChars++) + return false; - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> - UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) - { - PassRefPtr<UStringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5); - if (!resultImpl) - CRASH(); - return resultImpl; - } + if (aLength & 1 && *reinterpret_cast<const uint16_t*>(aChars) != *reinterpret_cast<const uint16_t*>(bChars)) + return false; - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> - UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) - { - PassRefPtr<UStringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5, string6); - if (!resultImpl) - CRASH(); - return resultImpl; + return true; +#endif } - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7> - UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7) + static unsigned hash(const RefPtr<StringImpl>& key) { return key->hash(); } + static bool equal(const RefPtr<StringImpl>& a, const RefPtr<StringImpl>& b) { - PassRefPtr<UStringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5, string6, string7); - if (!resultImpl) - CRASH(); - return resultImpl; + return equal(a.get(), b.get()); } - template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8> - UString makeString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8) + static unsigned hash(const UString& key) { return key.impl()->hash(); } + static bool equal(const UString& a, const UString& b) { - PassRefPtr<UStringImpl> resultImpl = tryMakeString(string1, string2, string3, string4, string5, string6, string7, string8); - if (!resultImpl) - CRASH(); - return resultImpl; + return equal(a.impl(), b.impl()); } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + } // namespace JSC namespace WTF { - template<typename T> struct DefaultHash; - template<typename T> struct StrHash; - - template<> struct StrHash<JSC::UString::Rep*> { - static unsigned hash(const JSC::UString::Rep* key) { return key->hash(); } - static bool equal(const JSC::UString::Rep* a, const JSC::UString::Rep* b) { return JSC::equal(a, b); } - static const bool safeToCompareToEmptyOrDeleted = false; - }; - - template<> struct StrHash<RefPtr<JSC::UString::Rep> > : public StrHash<JSC::UString::Rep*> { - using StrHash<JSC::UString::Rep*>::hash; - static unsigned hash(const RefPtr<JSC::UString::Rep>& key) { return key->hash(); } - using StrHash<JSC::UString::Rep*>::equal; - static bool equal(const RefPtr<JSC::UString::Rep>& a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a.get(), b.get()); } - static bool equal(const JSC::UString::Rep* a, const RefPtr<JSC::UString::Rep>& b) { return JSC::equal(a, b.get()); } - static bool equal(const RefPtr<JSC::UString::Rep>& a, const JSC::UString::Rep* b) { return JSC::equal(a.get(), b); } - - static const bool safeToCompareToEmptyOrDeleted = false; - }; +// UStringHash is the default hash for UString +template<typename T> struct DefaultHash; +template<> struct DefaultHash<JSC::UString> { + typedef JSC::UStringHash Hash; +}; - template<> struct DefaultHash<JSC::UString::Rep*> { - typedef StrHash<JSC::UString::Rep*> Hash; - }; - - template<> struct DefaultHash<RefPtr<JSC::UString::Rep> > { - typedef StrHash<RefPtr<JSC::UString::Rep> > Hash; - - }; +template <> struct VectorTraits<JSC::UString> : SimpleClassVectorTraits +{ + static const bool canInitializeWithMemset = true; +}; } // namespace WTF #endif + diff --git a/JavaScriptCore/runtime/UStringBuilder.h b/JavaScriptCore/runtime/UStringBuilder.h new file mode 100644 index 0000000..31ccf38 --- /dev/null +++ b/JavaScriptCore/runtime/UStringBuilder.h @@ -0,0 +1,43 @@ +/* + * 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 UStringBuilder_h +#define UStringBuilder_h + +#include <wtf/text/StringBuilder.h> + +namespace JSC { + +class UStringBuilder : public StringBuilder { +public: + using StringBuilder::append; + void append(const UString& str) { append(String(str.impl())); } + + UString toUString() { return toString().impl(); } +}; + +} // namespace JSC + +#endif // UStringBuilder_h diff --git a/JavaScriptCore/runtime/UStringConcatenate.h b/JavaScriptCore/runtime/UStringConcatenate.h new file mode 100644 index 0000000..0990c72 --- /dev/null +++ b/JavaScriptCore/runtime/UStringConcatenate.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 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 UStringConcatenate_h +#define UStringConcatenate_h + +#include "UString.h" +#include <wtf/text/StringConcatenate.h> + +namespace WTF { + +template<> +class StringTypeAdapter<JSC::UString> { +public: + StringTypeAdapter<JSC::UString>(JSC::UString& string) + : m_data(string.characters()) + , m_length(string.length()) + { + } + + unsigned length() { return m_length; } + + void writeTo(UChar* destination) + { + for (unsigned i = 0; i < m_length; ++i) + destination[i] = m_data[i]; + } + +private: + const UChar* m_data; + unsigned m_length; +}; + +}; // namespace WTF + +namespace JSC { + +template<typename StringType1, typename StringType2> +UString makeUString(StringType1 string1, StringType2 string2) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5, string6); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5, string6, string7); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +template<typename StringType1, typename StringType2, typename StringType3, typename StringType4, typename StringType5, typename StringType6, typename StringType7, typename StringType8> +UString makeUString(StringType1 string1, StringType2 string2, StringType3 string3, StringType4 string4, StringType5 string5, StringType6 string6, StringType7 string7, StringType8 string8) +{ + PassRefPtr<StringImpl> resultImpl = WTF::tryMakeString(string1, string2, string3, string4, string5, string6, string7, string8); + if (!resultImpl) + CRASH(); + return resultImpl; +} + +} // namespace JSC + +#endif diff --git a/JavaScriptCore/runtime/UStringImpl.cpp b/JavaScriptCore/runtime/UStringImpl.cpp deleted file mode 100644 index 9882007..0000000 --- a/JavaScriptCore/runtime/UStringImpl.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 "UStringImpl.h" - -#include "Identifier.h" -#include "UString.h" -#include <wtf/unicode/UTF8.h> - -using namespace WTF::Unicode; -using namespace std; - -namespace JSC { - -PassRefPtr<UStringImpl> UStringImpl::create(const char* c) -{ - ASSERT(c); - - if (!c[0]) - return &UStringImpl::empty(); - - size_t length = strlen(c); - UChar* d; - PassRefPtr<UStringImpl> result = UStringImpl::createUninitialized(length, d); - for (size_t i = 0; i < length; i++) - d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend - return result; -} - -PassRefPtr<UStringImpl> UStringImpl::create(const char* c, int length) -{ - ASSERT(c); - - if (!length) - return &UStringImpl::empty(); - - UChar* d; - PassRefPtr<UStringImpl> result = UStringImpl::createUninitialized(length, d); - 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 result; -} - -PassRefPtr<UStringImpl> UStringImpl::create(const UChar* buffer, int length) -{ - UChar* newBuffer; - PassRefPtr<UStringImpl> impl = createUninitialized(length, newBuffer); - copyChars(newBuffer, buffer, length); - return impl; -} - -SharedUChar* UStringImpl::baseSharedBuffer() -{ - ASSERT((bufferOwnership() == BufferShared) - || ((bufferOwnership() == BufferOwned) && !m_dataBuffer.asPtr<void*>())); - - if (bufferOwnership() != BufferShared) - m_dataBuffer = UntypedPtrAndBitfield(SharedUChar::create(new OwnFastMallocPtr<UChar>(m_data)).releaseRef(), BufferShared); - - return m_dataBuffer.asPtr<SharedUChar*>(); -} - -SharedUChar* UStringImpl::sharedBuffer() -{ - if (m_length < s_minLengthToShare) - return 0; - ASSERT(!isStatic()); - - UStringImpl* owner = bufferOwnerString(); - if (owner->bufferOwnership() == BufferInternal) - return 0; - - return owner->baseSharedBuffer(); -} - -UStringImpl::~UStringImpl() -{ - ASSERT(!isStatic()); - checkConsistency(); - - if (isIdentifier()) - Identifier::remove(this); - - if (bufferOwnership() != BufferInternal) { - if (bufferOwnership() == BufferOwned) - fastFree(m_data); - else if (bufferOwnership() == BufferSubstring) - m_dataBuffer.asPtr<UStringImpl*>()->deref(); - else { - ASSERT(bufferOwnership() == BufferShared); - m_dataBuffer.asPtr<SharedUChar*>()->deref(); - } - } -} - -} diff --git a/JavaScriptCore/runtime/UStringImpl.h b/JavaScriptCore/runtime/UStringImpl.h deleted file mode 100644 index bbea0aa..0000000 --- a/JavaScriptCore/runtime/UStringImpl.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * 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 UStringImpl_h -#define UStringImpl_h - -#include <limits> -#include <wtf/CrossThreadRefCounted.h> -#include <wtf/OwnFastMallocPtr.h> -#include <wtf/PossiblyNull.h> -#include <wtf/StringHashFunctions.h> -#include <wtf/Vector.h> -#include <wtf/unicode/Unicode.h> - -namespace JSC { - -class IdentifierTable; - -typedef CrossThreadRefCounted<OwnFastMallocPtr<UChar> > SharedUChar; - -class UntypedPtrAndBitfield { -public: - UntypedPtrAndBitfield() {} - - UntypedPtrAndBitfield(void* ptrValue, uintptr_t bitValue) - : m_value(reinterpret_cast<uintptr_t>(ptrValue) | bitValue) -#ifndef NDEBUG - , m_leaksPtr(ptrValue) -#endif - { - ASSERT(ptrValue == asPtr<void*>()); - ASSERT((*this & ~s_alignmentMask) == bitValue); - } - - template<typename T> - T asPtr() const { return reinterpret_cast<T>(m_value & s_alignmentMask); } - - UntypedPtrAndBitfield& operator&=(uintptr_t bits) - { - m_value &= bits | s_alignmentMask; - return *this; - } - - UntypedPtrAndBitfield& operator|=(uintptr_t bits) - { - m_value |= bits & ~s_alignmentMask; - return *this; - } - - uintptr_t operator&(uintptr_t mask) const - { - return m_value & mask & ~s_alignmentMask; - } - -private: - static const uintptr_t s_alignmentMask = ~static_cast<uintptr_t>(0x7); - uintptr_t m_value; -#ifndef NDEBUG - void* m_leaksPtr; // Only used to allow tools like leaks on OSX to detect that the memory is referenced. -#endif -}; - -class UStringImpl : Noncopyable { -public: - template<size_t inlineCapacity> - static PassRefPtr<UStringImpl> adopt(Vector<UChar, inlineCapacity>& vector) - { - if (unsigned length = vector.size()) { - ASSERT(vector.data()); - return adoptRef(new UStringImpl(vector.releaseBuffer(), length, BufferOwned)); - } - return &empty(); - } - - static PassRefPtr<UStringImpl> create(const char* c); - static PassRefPtr<UStringImpl> create(const char* c, int length); - static PassRefPtr<UStringImpl> create(const UChar* buffer, int length); - - static PassRefPtr<UStringImpl> create(PassRefPtr<UStringImpl> rep, int offset, int length) - { - ASSERT(rep); - rep->checkConsistency(); - return adoptRef(new UStringImpl(rep->m_data + offset, length, rep->bufferOwnerString())); - } - - static PassRefPtr<UStringImpl> create(PassRefPtr<SharedUChar> sharedBuffer, UChar* buffer, int length) - { - return adoptRef(new UStringImpl(buffer, length, sharedBuffer)); - } - - static PassRefPtr<UStringImpl> createUninitialized(unsigned length, UChar*& output) - { - if (!length) { - output = 0; - return &empty(); - } - - if (length > ((std::numeric_limits<size_t>::max() - sizeof(UStringImpl)) / sizeof(UChar))) - CRASH(); - UStringImpl* resultImpl = static_cast<UStringImpl*>(fastMalloc(sizeof(UChar) * length + sizeof(UStringImpl))); - output = reinterpret_cast<UChar*>(resultImpl + 1); - return adoptRef(new(resultImpl) UStringImpl(output, length, BufferInternal)); - } - - static PassRefPtr<UStringImpl> tryCreateUninitialized(unsigned length, UChar*& output) - { - if (!length) { - output = 0; - return &empty(); - } - - if (length > ((std::numeric_limits<size_t>::max() - sizeof(UStringImpl)) / sizeof(UChar))) - return 0; - UStringImpl* resultImpl; - if (!tryFastMalloc(sizeof(UChar) * length + sizeof(UStringImpl)).getValue(resultImpl)) - return 0; - output = reinterpret_cast<UChar*>(resultImpl + 1); - return adoptRef(new(resultImpl) UStringImpl(output, length, BufferInternal)); - } - - SharedUChar* sharedBuffer(); - UChar* data() const { return m_data; } - int size() const { return m_length; } - size_t cost() - { - // For substrings, return the cost of the base string. - if (bufferOwnership() == BufferSubstring) - return m_dataBuffer.asPtr<UStringImpl*>()->cost(); - - if (m_dataBuffer & s_reportedCostBit) - return 0; - m_dataBuffer |= s_reportedCostBit; - return m_length; - } - unsigned hash() const { if (!m_hash) m_hash = computeHash(data(), m_length); return m_hash; } - unsigned existingHash() const { ASSERT(m_hash); return m_hash; } // fast path for Identifiers - void setHash(unsigned hash) { ASSERT(hash == computeHash(data(), m_length)); m_hash = hash; } // fast path for Identifiers - bool isIdentifier() const { return m_isIdentifier; } - void setIsIdentifier(bool isIdentifier) { m_isIdentifier = isIdentifier; } - - UStringImpl* ref() { m_refCount += s_refCountIncrement; return this; } - ALWAYS_INLINE void deref() { if (!(m_refCount -= s_refCountIncrement)) delete this; } - - static void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) - { - if (numCharacters <= s_copyCharsInlineCutOff) { - for (unsigned i = 0; i < numCharacters; ++i) - destination[i] = source[i]; - } else - memcpy(destination, source, numCharacters * sizeof(UChar)); - } - - static unsigned computeHash(const UChar* s, int length) { ASSERT(length >= 0); return WTF::stringHash(s, length); } - static unsigned computeHash(const char* s, int length) { ASSERT(length >= 0); return WTF::stringHash(s, length); } - static unsigned computeHash(const char* s) { return WTF::stringHash(s); } - - static UStringImpl& empty() { return *s_empty; } - - ALWAYS_INLINE void checkConsistency() const - { - // There is no recursion of substrings. - ASSERT(bufferOwnerString()->bufferOwnership() != BufferSubstring); - // Static strings cannot be put in identifier tables, because they are globally shared. - ASSERT(!isStatic() || !isIdentifier()); - } - -private: - enum BufferOwnership { - BufferInternal, - BufferOwned, - BufferSubstring, - BufferShared, - }; - - // For SmallStringStorage, which allocates an array and uses an in-place new. - UStringImpl() { } - - // Used to construct normal strings with an internal or external buffer. - UStringImpl(UChar* data, int length, BufferOwnership ownership) - : m_data(data) - , m_length(length) - , m_refCount(s_refCountIncrement) - , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, ownership) - { - ASSERT((ownership == BufferInternal) || (ownership == BufferOwned)); - checkConsistency(); - } - - // Used to construct static strings, which have an special refCount that can never hit zero. - // This means that the static string will never be destroyed, which is important because - // static strings will be shared across threads & ref-counted in a non-threadsafe manner. - enum StaticStringConstructType { ConstructStaticString }; - UStringImpl(UChar* data, int length, StaticStringConstructType) - : m_data(data) - , m_length(length) - , m_refCount(s_staticRefCountInitialValue) - , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, BufferOwned) - { - checkConsistency(); - } - - // Used to create new strings that are a substring of an existing string. - UStringImpl(UChar* data, int length, PassRefPtr<UStringImpl> base) - : m_data(data) - , m_length(length) - , m_refCount(s_refCountIncrement) - , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(base.releaseRef(), BufferSubstring) - { - // Do use static strings as a base for substrings; UntypedPtrAndBitfield assumes - // that all pointers will be at least 8-byte aligned, we cannot guarantee that of - // UStringImpls that are not heap allocated. - ASSERT(m_dataBuffer.asPtr<UStringImpl*>()->size()); - ASSERT(!m_dataBuffer.asPtr<UStringImpl*>()->isStatic()); - checkConsistency(); - } - - // Used to construct new strings sharing an existing shared buffer. - UStringImpl(UChar* data, int length, PassRefPtr<SharedUChar> sharedBuffer) - : m_data(data) - , m_length(length) - , m_refCount(s_refCountIncrement) - , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(sharedBuffer.releaseRef(), BufferShared) - { - checkConsistency(); - } - - using Noncopyable::operator new; - void* operator new(size_t, void* inPlace) { return inPlace; } - - ~UStringImpl(); - - // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. - static const int s_minLengthToShare = 10; - static const unsigned s_copyCharsInlineCutOff = 20; - static const uintptr_t s_bufferOwnershipMask = 3; - static const uintptr_t s_reportedCostBit = 4; - // We initialize and increment/decrement the refCount for all normal (non-static) strings by the value 2. - // We initialize static strings with an odd number (specifically, 1), such that the refCount cannot reach zero. - static const int s_refCountIncrement = 2; - static const int s_staticRefCountInitialValue = 1; - - UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } - const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } - SharedUChar* baseSharedBuffer(); - unsigned bufferOwnership() const { return m_dataBuffer & s_bufferOwnershipMask; } - bool isStatic() const { return m_refCount & 1; } - - // unshared data - UChar* m_data; - int m_length; - unsigned m_refCount; - mutable unsigned m_hash : 31; - mutable unsigned m_isIdentifier : 1; - UntypedPtrAndBitfield m_dataBuffer; - - JS_EXPORTDATA static UStringImpl* s_empty; - - friend class JIT; - friend class SmallStringsStorage; - friend void initializeUString(); -}; - -bool equal(const UStringImpl*, const UStringImpl*); - -} - -#endif diff --git a/JavaScriptCore/runtime/WeakGCPtr.h b/JavaScriptCore/runtime/WeakGCPtr.h index 3ed4645..ac77cf3 100644 --- a/JavaScriptCore/runtime/WeakGCPtr.h +++ b/JavaScriptCore/runtime/WeakGCPtr.h @@ -27,6 +27,7 @@ #define WeakGCPtr_h #include "Collector.h" +#include "GCHandle.h" #include <wtf/Noncopyable.h> namespace JSC { @@ -34,20 +35,34 @@ namespace JSC { // A smart pointer whose get() function returns 0 for cells awaiting destruction. template <typename T> class WeakGCPtr : Noncopyable { public: - WeakGCPtr() : m_ptr(0) { } + WeakGCPtr() + : m_ptr(0) + { + } + WeakGCPtr(T* ptr) { assign(ptr); } + ~WeakGCPtr() + { + if (m_ptr) + m_ptr->pool()->free(m_ptr); + } + T* get() const { - if (!m_ptr || !Heap::isCellMarked(m_ptr)) - return 0; - return m_ptr; + if (m_ptr && m_ptr->isValidPtr()) + return static_cast<T*>(m_ptr->get()); + return 0; } - void clear(JSCell* ptr) + bool clear(JSCell* p) { - if (ptr == m_ptr) - m_ptr = 0; + if (!m_ptr || m_ptr->get() != p) + return false; + + m_ptr->pool()->free(m_ptr); + m_ptr = 0; + return true; } T& operator*() const { return *get(); } @@ -59,21 +74,27 @@ public: #if COMPILER(WINSCW) operator bool() const { return m_ptr; } #else - typedef T* WeakGCPtr::*UnspecifiedBoolType; + typedef WeakGCHandle* WeakGCPtr::*UnspecifiedBoolType; operator UnspecifiedBoolType() const { return get() ? &WeakGCPtr::m_ptr : 0; } #endif WeakGCPtr& operator=(T*); +#if !ASSERT_DISABLED + bool hasDeadObject() const { return !!m_ptr; } +#endif + private: - void assign(T* ptr) + void assign(JSCell* ptr) { - if (ptr) - Heap::markCell(ptr); - m_ptr = ptr; + ASSERT(ptr); + if (m_ptr) + m_ptr->set(ptr); + else + m_ptr = Heap::heap(ptr)->addWeakGCHandle(ptr); } - T* m_ptr; + WeakGCHandle* m_ptr; }; template <typename T> inline WeakGCPtr<T>& WeakGCPtr<T>::operator=(T* optr) @@ -122,7 +143,7 @@ template <typename T, typename U> inline WeakGCPtr<T> const_pointer_cast(const W return WeakGCPtr<T>(const_cast<T*>(p.get())); } -template <typename T> inline T* getPtr(const WeakGCPtr<T>& p) +template <typename T> inline T* get(const WeakGCPtr<T>& p) { return p.get(); } |
