diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-17 16:21:14 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-10-17 16:21:14 +0200 |
commit | 8995b83bcbfbb68245f779b64e5517627c6cc6ea (patch) | |
tree | 17985605dab9263cc2444bd4d45f189e142cca7c /Source/JavaScriptCore/runtime | |
parent | b9c9652036d5e9f1e29c574f40bc73a35c81ace6 (diff) | |
download | qtwebkit-8995b83bcbfbb68245f779b64e5517627c6cc6ea.tar.gz |
Imported WebKit commit cf4f8fc6f19b0629f51860cb2d4b25e139d07e00 (http://svn.webkit.org/repository/webkit/trunk@131592)
New snapshot that includes the build fixes for Mac OS X 10.6 and earlier as well
as the previously cherry-picked changes
Diffstat (limited to 'Source/JavaScriptCore/runtime')
96 files changed, 2051 insertions, 1001 deletions
diff --git a/Source/JavaScriptCore/runtime/Arguments.cpp b/Source/JavaScriptCore/runtime/Arguments.cpp index fdf202192..ba73b2cf2 100644 --- a/Source/JavaScriptCore/runtime/Arguments.cpp +++ b/Source/JavaScriptCore/runtime/Arguments.cpp @@ -33,9 +33,7 @@ using namespace std; namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(Arguments); - -const ClassInfo Arguments::s_info = { "Arguments", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(Arguments) }; +const ClassInfo Arguments::s_info = { "Arguments", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Arguments) }; void Arguments::visitChildren(JSCell* cell, SlotVisitor& visitor) { @@ -406,19 +404,19 @@ void Arguments::tearOffForInlineCallFrame(JSGlobalData& globalData, Register* re JSValue value; Register* location = ®isters[CallFrame::argumentOffset(i)]; switch (recovery.technique()) { - case AlreadyInRegisterFile: + case AlreadyInJSStack: value = location->jsValue(); break; - case AlreadyInRegisterFileAsUnboxedInt32: + case AlreadyInJSStackAsUnboxedInt32: value = jsNumber(location->unboxedInt32()); break; - case AlreadyInRegisterFileAsUnboxedCell: + case AlreadyInJSStackAsUnboxedCell: value = location->unboxedCell(); break; - case AlreadyInRegisterFileAsUnboxedBoolean: + case AlreadyInJSStackAsUnboxedBoolean: value = jsBoolean(location->unboxedBoolean()); break; - case AlreadyInRegisterFileAsUnboxedDouble: + case AlreadyInJSStackAsUnboxedDouble: #if USE(JSVALUE64) value = jsNumber(*bitwise_cast<double*>(location)); #else diff --git a/Source/JavaScriptCore/runtime/Arguments.h b/Source/JavaScriptCore/runtime/Arguments.h index 40063bead..7c8b69bd1 100644 --- a/Source/JavaScriptCore/runtime/Arguments.h +++ b/Source/JavaScriptCore/runtime/Arguments.h @@ -26,6 +26,7 @@ #include "CodeOrigin.h" #include "JSActivation.h" +#include "JSDestructibleObject.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "Interpreter.h" @@ -33,11 +34,11 @@ namespace JSC { - class Arguments : public JSNonFinalObject { + class Arguments : public JSDestructibleObject { friend class JIT; friend class DFG::SpeculativeJIT; public: - typedef JSNonFinalObject Base; + typedef JSDestructibleObject Base; static Arguments* create(JSGlobalData& globalData, CallFrame* callFrame) { @@ -147,12 +148,12 @@ namespace JSC { } inline Arguments::Arguments(CallFrame* callFrame) - : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) + : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) { } inline Arguments::Arguments(CallFrame* callFrame, NoParametersType) - : JSNonFinalObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) + : JSDestructibleObject(callFrame->globalData(), callFrame->lexicalGlobalObject()->argumentsStructure()) { } diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp index eda35e146..a13648442 100644 --- a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp @@ -53,8 +53,6 @@ const ClassInfo ArrayConstructor::s_info = { "Function", &InternalFunction::s_in @end */ -ASSERT_CLASS_FITS_IN_CELL(ArrayConstructor); - ArrayConstructor::ArrayConstructor(JSGlobalObject* globalObject, Structure* structure) : InternalFunction(globalObject, structure) { diff --git a/Source/JavaScriptCore/runtime/ArrayConventions.h b/Source/JavaScriptCore/runtime/ArrayConventions.h index 3ea6b0471..a557b1ef9 100644 --- a/Source/JavaScriptCore/runtime/ArrayConventions.h +++ b/Source/JavaScriptCore/runtime/ArrayConventions.h @@ -79,7 +79,7 @@ static const unsigned minDensityMultiplier = 8; inline bool isDenseEnoughForVector(unsigned length, unsigned numValues) { - return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues; + return length / minDensityMultiplier <= numValues; } inline IndexingHeader indexingHeaderForArray(unsigned length, unsigned vectorLength) diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp index 1eacd1179..6975dc778 100644 --- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -42,8 +42,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(ArrayPrototype); - static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*); static EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*); @@ -194,7 +192,8 @@ static unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument // currentCount) will be shifted to the left or right as appropriate; in the // case of shift this must be removing values, in the case of unshift this // must be introducing new values. -static inline void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) +template<JSArray::ShiftCountMode shiftCountMode> +void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) { ASSERT(currentCount > resultCount); unsigned count = currentCount - resultCount; @@ -202,9 +201,9 @@ static inline void shift(ExecState* exec, JSObject* thisObj, unsigned header, un ASSERT(header <= length); ASSERT(currentCount <= (length - header)); - if (!header && isJSArray(thisObj)) { + if (isJSArray(thisObj)) { JSArray* array = asArray(thisObj); - if (array->length() == length && asArray(thisObj)->shiftCount(exec, count)) + if (array->length() == length && asArray(thisObj)->shiftCount<shiftCountMode>(exec, header, count)) return; } @@ -231,7 +230,8 @@ static inline void shift(ExecState* exec, JSObject* thisObj, unsigned header, un } } } -static inline void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) +template<JSArray::ShiftCountMode shiftCountMode> +void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned currentCount, unsigned resultCount, unsigned length) { ASSERT(resultCount > currentCount); unsigned count = resultCount - currentCount; @@ -245,12 +245,12 @@ static inline void unshift(ExecState* exec, JSObject* thisObj, unsigned header, return; } - if (!header && isJSArray(thisObj)) { + if (isJSArray(thisObj)) { JSArray* array = asArray(thisObj); - if (array->length() == length && asArray(thisObj)->unshiftCount(exec, count)) + if (array->length() == length && array->unshiftCount<shiftCountMode>(exec, header, count)) return; } - + for (unsigned k = length - currentCount; k > header; --k) { unsigned from = k + currentCount - 1; unsigned to = k + resultCount - 1; @@ -526,7 +526,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) array->push(exec, exec->argument(0)); return JSValue::encode(jsNumber(array->length())); } - + JSObject* thisObj = thisValue.toObject(exec); unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (exec->hadException()) @@ -544,6 +544,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec) if (exec->hadException()) return JSValue::encode(jsUndefined()); } + JSValue newLength(static_cast<int64_t>(length) + static_cast<int64_t>(exec->argumentCount())); putProperty(exec, thisObj, exec->propertyNames().length, newLength); return JSValue::encode(newLength); @@ -600,7 +601,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec) result = jsUndefined(); } else { result = thisObj->get(exec, 0); - shift(exec, thisObj, 0, 1, 0, length); + shift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 1, 0, length); if (exec->hadException()) return JSValue::encode(jsUndefined()); putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(length - 1)); @@ -655,7 +656,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec) CallData callData; CallType callType = getCallData(function, callData); - if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseIndexingMode() && !shouldUseSlowPut(thisObj->structure()->indexingType())) { + if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->hasSparseMap() && !shouldUseSlowPut(thisObj->structure()->indexingType())) { if (isNumericCompareFunction(exec, callType, callData)) asArray(thisObj)->sortNumeric(exec, function, callType, callData); else if (callType != CallTypeNone) @@ -730,7 +731,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); - + if (!exec->argumentCount()) return JSValue::encode(constructEmptyArray(exec)); @@ -762,11 +763,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec) unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0); if (additionalArgs < deleteCount) { - shift(exec, thisObj, begin, deleteCount, additionalArgs, length); + shift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length); if (exec->hadException()) return JSValue::encode(jsUndefined()); } else if (additionalArgs > deleteCount) { - unshift(exec, thisObj, begin, deleteCount, additionalArgs, length); + unshift<JSArray::ShiftCountForSplice>(exec, thisObj, begin, deleteCount, additionalArgs, length); if (exec->hadException()) return JSValue::encode(jsUndefined()); } @@ -791,7 +792,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec) unsigned nrArgs = exec->argumentCount(); if (nrArgs) { - unshift(exec, thisObj, 0, 0, nrArgs, length); + unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length); if (exec->hadException()) return JSValue::encode(jsUndefined()); } diff --git a/Source/JavaScriptCore/runtime/ArrayStorage.h b/Source/JavaScriptCore/runtime/ArrayStorage.h index 1ab936335..ffd84b281 100644 --- a/Source/JavaScriptCore/runtime/ArrayStorage.h +++ b/Source/JavaScriptCore/runtime/ArrayStorage.h @@ -41,6 +41,8 @@ namespace JSC { // 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) +// All slots in ArrayStorage (slots from 0 to vectorLength) are expected to be initialized to a JSValue or, +// for hole slots, JSValue(). struct ArrayStorage { WTF_MAKE_NONCOPYABLE(ArrayStorage); private: diff --git a/Source/JavaScriptCore/runtime/BooleanConstructor.cpp b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp index 9b666292c..0485350ce 100644 --- a/Source/JavaScriptCore/runtime/BooleanConstructor.cpp +++ b/Source/JavaScriptCore/runtime/BooleanConstructor.cpp @@ -26,7 +26,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(BooleanConstructor); ASSERT_HAS_TRIVIAL_DESTRUCTOR(BooleanConstructor); const ClassInfo BooleanConstructor::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(BooleanConstructor) }; diff --git a/Source/JavaScriptCore/runtime/BooleanObject.cpp b/Source/JavaScriptCore/runtime/BooleanObject.cpp index bf2655bbb..355993864 100644 --- a/Source/JavaScriptCore/runtime/BooleanObject.cpp +++ b/Source/JavaScriptCore/runtime/BooleanObject.cpp @@ -25,7 +25,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(BooleanObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(BooleanObject); const ClassInfo BooleanObject::s_info = { "Boolean", &JSWrapperObject::s_info, 0, 0, CREATE_METHOD_TABLE(BooleanObject) }; diff --git a/Source/JavaScriptCore/runtime/BooleanPrototype.cpp b/Source/JavaScriptCore/runtime/BooleanPrototype.cpp index c8c77220a..a331c6c15 100644 --- a/Source/JavaScriptCore/runtime/BooleanPrototype.cpp +++ b/Source/JavaScriptCore/runtime/BooleanPrototype.cpp @@ -47,7 +47,6 @@ const ClassInfo BooleanPrototype::s_info = { "Boolean", &BooleanObject::s_info, @end */ -ASSERT_CLASS_FITS_IN_CELL(BooleanPrototype); ASSERT_HAS_TRIVIAL_DESTRUCTOR(BooleanPrototype); BooleanPrototype::BooleanPrototype(ExecState* exec, Structure* structure) diff --git a/Source/JavaScriptCore/runtime/Butterfly.h b/Source/JavaScriptCore/runtime/Butterfly.h index 1926169ba..cb93aea8a 100644 --- a/Source/JavaScriptCore/runtime/Butterfly.h +++ b/Source/JavaScriptCore/runtime/Butterfly.h @@ -35,7 +35,7 @@ namespace JSC { class JSGlobalData; -class SlotVisitor; +class CopyVisitor; struct ArrayStorage; class Butterfly { @@ -56,6 +56,15 @@ public: return reinterpret_cast<Butterfly*>(static_cast<EncodedJSValue*>(base) + preCapacity + propertyCapacity + 1); } + // This method is here not just because it's handy, but to remind you that + // the whole point of butterflies is to do evil pointer arithmetic. + static Butterfly* fromPointer(char* ptr) + { + return reinterpret_cast<Butterfly*>(ptr); + } + + char* pointer() { return reinterpret_cast<char*>(this); } + static ptrdiff_t offsetOfIndexingHeader() { return IndexingHeader::offsetOfIndexingHeader(); } static ptrdiff_t offsetOfPublicLength() { return offsetOfIndexingHeader() + IndexingHeader::offsetOfPublicLength(); } static ptrdiff_t offsetOfVectorLength() { return offsetOfIndexingHeader() + IndexingHeader::offsetOfVectorLength(); } @@ -64,15 +73,27 @@ public: static Butterfly* create(JSGlobalData&, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, const IndexingHeader&, size_t indexingPayloadSizeInBytes); static Butterfly* create(JSGlobalData&, Structure*); - static Butterfly* createUninitializedDuringCollection(SlotVisitor&, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes); + static Butterfly* createUninitializedDuringCollection(CopyVisitor&, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes); IndexingHeader* indexingHeader() { return IndexingHeader::from(this); } const IndexingHeader* indexingHeader() const { return IndexingHeader::from(this); } PropertyStorage propertyStorage() { return indexingHeader()->propertyStorage(); } ConstPropertyStorage propertyStorage() const { return indexingHeader()->propertyStorage(); } + + uint32_t publicLength() { return indexingHeader()->publicLength(); } + uint32_t vectorLength() { return indexingHeader()->vectorLength(); } + void setPublicLength(uint32_t value) { indexingHeader()->setPublicLength(value); } + void setVectorLength(uint32_t value) { indexingHeader()->setVectorLength(value); } + template<typename T> T* indexingPayload() { return reinterpret_cast<T*>(this); } ArrayStorage* arrayStorage() { return indexingPayload<ArrayStorage>(); } + WriteBarrier<Unknown>* contiguous() { return indexingPayload<WriteBarrier<Unknown> >(); } + + static Butterfly* fromContiguous(WriteBarrier<Unknown>* contiguous) + { + return reinterpret_cast<Butterfly*>(contiguous); + } static ptrdiff_t offsetOfPropertyStorage() { return -static_cast<ptrdiff_t>(sizeof(IndexingHeader)); } static int indexOfPropertyStorage() diff --git a/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h b/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h index 049350342..86a836bef 100644 --- a/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h +++ b/Source/JavaScriptCore/runtime/ButterflyInlineMethods.h @@ -29,8 +29,8 @@ #include "ArrayStorage.h" #include "Butterfly.h" #include "CopiedSpaceInlineMethods.h" +#include "CopyVisitor.h" #include "JSGlobalData.h" -#include "SlotVisitor.h" #include "Structure.h" namespace JSC { @@ -59,7 +59,7 @@ inline Butterfly* Butterfly::create(JSGlobalData& globalData, Structure* structu return create(globalData, 0, structure->outOfLineCapacity(), hasIndexingHeader(structure->indexingType()), IndexingHeader(), 0); } -inline Butterfly* Butterfly::createUninitializedDuringCollection(SlotVisitor& visitor, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes) +inline Butterfly* Butterfly::createUninitializedDuringCollection(CopyVisitor& visitor, size_t preCapacity, size_t propertyCapacity, bool hasIndexingHeader, size_t indexingPayloadSizeInBytes) { Butterfly* result = fromBase( visitor.allocateNewSpace(totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes)), @@ -144,7 +144,7 @@ inline Butterfly* Butterfly::resizeArray(JSGlobalData& globalData, Structure* st inline Butterfly* Butterfly::unshift(Structure* structure, size_t numberOfSlots) { - ASSERT(hasIndexingHeader(structure->indexingType())); + ASSERT(hasArrayStorage(structure->indexingType())); ASSERT(numberOfSlots <= indexingHeader()->preCapacity(structure)); unsigned propertyCapacity = structure->outOfLineCapacity(); // FIXME: It would probably be wise to rewrite this as a loop since (1) we know in which @@ -163,7 +163,7 @@ inline Butterfly* Butterfly::unshift(Structure* structure, size_t numberOfSlots) inline Butterfly* Butterfly::shift(Structure* structure, size_t numberOfSlots) { - ASSERT(hasIndexingHeader(structure->indexingType())); + ASSERT(hasArrayStorage(structure->indexingType())); unsigned propertyCapacity = structure->outOfLineCapacity(); // FIXME: See comment in unshift(), above. memmove( diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h index e8823d571..c918621fe 100644 --- a/Source/JavaScriptCore/runtime/ClassInfo.h +++ b/Source/JavaScriptCore/runtime/ClassInfo.h @@ -39,6 +39,9 @@ namespace JSC { typedef void (*VisitChildrenFunctionPtr)(JSCell*, SlotVisitor&); VisitChildrenFunctionPtr visitChildren; + typedef void (*CopyBackingStoreFunctionPtr)(JSCell*, CopyVisitor&); + CopyBackingStoreFunctionPtr copyBackingStore; + typedef CallType (*GetCallDataFunctionPtr)(JSCell*, CallData&); GetCallDataFunctionPtr getCallData; @@ -116,6 +119,7 @@ struct MemberCheck##member { \ #define CREATE_METHOD_TABLE(ClassName) { \ &ClassName::destroy, \ &ClassName::visitChildren, \ + &ClassName::copyBackingStore, \ &ClassName::getCallData, \ &ClassName::getConstructData, \ &ClassName::put, \ diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h index 7edd9091c..2f5159461 100644 --- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h +++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h @@ -43,7 +43,7 @@ namespace JSC { namespace CommonSlowPaths { -ALWAYS_INLINE ExecState* arityCheckFor(ExecState* exec, RegisterFile* registerFile, CodeSpecializationKind kind) +ALWAYS_INLINE ExecState* arityCheckFor(ExecState* exec, JSStack* stack, CodeSpecializationKind kind) { JSFunction* callee = jsCast<JSFunction*>(exec->callee()); ASSERT(!callee->isHostFunction()); @@ -51,7 +51,7 @@ ALWAYS_INLINE ExecState* arityCheckFor(ExecState* exec, RegisterFile* registerFi int argumentCountIncludingThis = exec->argumentCountIncludingThis(); // This ensures enough space for the worst case scenario of zero arguments passed by the caller. - if (!registerFile->grow(exec->registers() + newCodeBlock->numParameters() + newCodeBlock->m_numCalleeRegisters)) + if (!stack->grow(exec->registers() + newCodeBlock->numParameters() + newCodeBlock->m_numCalleeRegisters)) return 0; ASSERT(argumentCountIncludingThis < newCodeBlock->numParameters()); @@ -71,7 +71,7 @@ ALWAYS_INLINE ExecState* arityCheckFor(ExecState* exec, RegisterFile* registerFi dst[i] = jsUndefined(); ExecState* newExec = ExecState::create(dst); - ASSERT((void*)newExec <= registerFile->end()); + ASSERT((void*)newExec <= stack->end()); return newExec; } diff --git a/Source/JavaScriptCore/runtime/DateConstructor.cpp b/Source/JavaScriptCore/runtime/DateConstructor.cpp index e4f89dd37..f78e8bf55 100644 --- a/Source/JavaScriptCore/runtime/DateConstructor.cpp +++ b/Source/JavaScriptCore/runtime/DateConstructor.cpp @@ -71,7 +71,6 @@ const ClassInfo DateConstructor::s_info = { "Function", &InternalFunction::s_inf @end */ -ASSERT_CLASS_FITS_IN_CELL(DateConstructor); ASSERT_HAS_TRIVIAL_DESTRUCTOR(DateConstructor); DateConstructor::DateConstructor(JSGlobalObject* globalObject, Structure* structure) diff --git a/Source/JavaScriptCore/runtime/DatePrototype.cpp b/Source/JavaScriptCore/runtime/DatePrototype.cpp index 9b2cb164f..75da466fb 100644 --- a/Source/JavaScriptCore/runtime/DatePrototype.cpp +++ b/Source/JavaScriptCore/runtime/DatePrototype.cpp @@ -70,8 +70,6 @@ using namespace WTF; namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(DatePrototype); - static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState*); static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState*); static EncodedJSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState*); diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp index 27a729d0a..68ae12d90 100644 --- a/Source/JavaScriptCore/runtime/Error.cpp +++ b/Source/JavaScriptCore/runtime/Error.cpp @@ -177,8 +177,6 @@ JSObject* throwSyntaxError(ExecState* exec) return throwError(exec, createSyntaxError(exec, ASCIILiteral("Syntax error"))); } -ASSERT_CLASS_FITS_IN_CELL(StrictModeTypeErrorFunction); - const ClassInfo StrictModeTypeErrorFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(StrictModeTypeErrorFunction) }; void StrictModeTypeErrorFunction::destroy(JSCell* cell) diff --git a/Source/JavaScriptCore/runtime/ErrorConstructor.cpp b/Source/JavaScriptCore/runtime/ErrorConstructor.cpp index 96272d0cf..f2578a497 100644 --- a/Source/JavaScriptCore/runtime/ErrorConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ErrorConstructor.cpp @@ -27,7 +27,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(ErrorConstructor); ASSERT_HAS_TRIVIAL_DESTRUCTOR(ErrorConstructor); const ClassInfo ErrorConstructor::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ErrorConstructor) }; diff --git a/Source/JavaScriptCore/runtime/ErrorPrototype.cpp b/Source/JavaScriptCore/runtime/ErrorPrototype.cpp index 6c9d6df04..a30efdc31 100644 --- a/Source/JavaScriptCore/runtime/ErrorPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ErrorPrototype.cpp @@ -30,7 +30,7 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(ErrorPrototype); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(ErrorPrototype); static EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState*); @@ -48,8 +48,6 @@ const ClassInfo ErrorPrototype::s_info = { "Error", &ErrorInstance::s_info, 0, E @end */ -ASSERT_CLASS_FITS_IN_CELL(ErrorPrototype); - ErrorPrototype::ErrorPrototype(ExecState* exec, Structure* structure) : ErrorInstance(exec->globalData(), structure) { diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h index 6eeda3a82..76a537da3 100644 --- a/Source/JavaScriptCore/runtime/Executable.h +++ b/Source/JavaScriptCore/runtime/Executable.h @@ -81,6 +81,8 @@ namespace JSC { typedef JSCell Base; #if ENABLE(JIT) + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; static void destroy(JSCell*); #endif diff --git a/Source/JavaScriptCore/runtime/FunctionConstructor.cpp b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp index 570444e3c..bf74c0a97 100644 --- a/Source/JavaScriptCore/runtime/FunctionConstructor.cpp +++ b/Source/JavaScriptCore/runtime/FunctionConstructor.cpp @@ -34,7 +34,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(FunctionConstructor); ASSERT_HAS_TRIVIAL_DESTRUCTOR(FunctionConstructor); const ClassInfo FunctionConstructor::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(FunctionConstructor) }; diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp index dd1628b29..a4b2202c1 100644 --- a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -32,7 +32,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(FunctionPrototype); ASSERT_HAS_TRIVIAL_DESTRUCTOR(FunctionPrototype); const ClassInfo FunctionPrototype::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(FunctionPrototype) }; diff --git a/Source/JavaScriptCore/runtime/Identifier.cpp b/Source/JavaScriptCore/runtime/Identifier.cpp index a9a2a66bf..45dd0bbde 100644 --- a/Source/JavaScriptCore/runtime/Identifier.cpp +++ b/Source/JavaScriptCore/runtime/Identifier.cpp @@ -30,6 +30,7 @@ #include <wtf/Assertions.h> #include <wtf/FastMalloc.h> #include <wtf/HashSet.h> +#include <wtf/text/ASCIIFastPath.h> #include <wtf/text/StringHash.h> using WTF::ThreadSpecific; @@ -80,11 +81,7 @@ struct IdentifierLCharFromUCharTranslator { { LChar* d; StringImpl* r = StringImpl::createUninitialized(buf.length, d).leakRef(); - for (unsigned i = 0; i != buf.length; i++) { - UChar c = buf.s[i]; - ASSERT(c <= 0xff); - d[i] = c; - } + WTF::copyLCharsFromUCharSource(d, buf.s, buf.length); r->setHash(hash); location = r; } @@ -102,7 +99,7 @@ PassRefPtr<StringImpl> Identifier::add(JSGlobalData* globalData, const char* c) const LiteralIdentifierTable::iterator& iter = literalIdentifierTable.find(c); if (iter != literalIdentifierTable.end()) - return iter->second; + return iter->value; HashSet<StringImpl*>::AddResult addResult = identifierTable.add<const LChar*, IdentifierASCIIStringTranslator>(reinterpret_cast<const LChar*>(c)); diff --git a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h b/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h index e1d893b0b..22785ce24 100644 --- a/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h +++ b/Source/JavaScriptCore/runtime/IndexingHeaderInlineMethods.h @@ -42,10 +42,17 @@ inline size_t IndexingHeader::preCapacity(Structure* structure) inline size_t IndexingHeader::indexingPayloadSizeInBytes(Structure* structure) { - if (LIKELY(!hasArrayStorage(structure->indexingType()))) + switch (structure->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + return vectorLength() * sizeof(EncodedJSValue); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return ArrayStorage::sizeFor(arrayStorage()->vectorLength()); + + default: + ASSERT(!hasIndexedProperties(structure->indexingType())); return 0; - - return ArrayStorage::sizeFor(arrayStorage()->vectorLength()); + } } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/IndexingType.cpp b/Source/JavaScriptCore/runtime/IndexingType.cpp new file mode 100644 index 000000000..7261847a2 --- /dev/null +++ b/Source/JavaScriptCore/runtime/IndexingType.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 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 "IndexingType.h" + +#include <stdio.h> +#include <wtf/StringExtras.h> + +namespace JSC { + +const char* indexingTypeToString(IndexingType indexingType) +{ + static char result[128]; + const char* basicName; + switch (indexingType & AllArrayTypes) { + case NonArray: + basicName = "NonArray"; + break; + case NonArrayWithContiguous: + basicName = "NonArrayWithContiguous"; + break; + case NonArrayWithArrayStorage: + basicName = "NonArrayWithArrayStorage"; + break; + case NonArrayWithSlowPutArrayStorage: + basicName = "NonArrayWithSlowPutArrayStorage"; + break; + case ArrayClass: + basicName = "ArrayClass"; + break; + case ArrayWithContiguous: + basicName = "ArrayWithContiguous"; + break; + case ArrayWithArrayStorage: + basicName = "ArrayWithArrayStorage"; + break; + case ArrayWithSlowPutArrayStorage: + basicName = "ArrayWithSlowPutArrayStorage"; + break; + default: + basicName = "Unknown!"; + break; + } + + snprintf( + result, sizeof(result), "%s%s", basicName, + (indexingType & MayHaveIndexedAccessors) ? "|MayHaveIndexedAccessors" : ""); + + return result; +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/runtime/IndexingType.h b/Source/JavaScriptCore/runtime/IndexingType.h index 3b97230ea..4bbe3cfa0 100644 --- a/Source/JavaScriptCore/runtime/IndexingType.h +++ b/Source/JavaScriptCore/runtime/IndexingType.h @@ -26,32 +26,44 @@ #ifndef IndexingType_h #define IndexingType_h +#include <wtf/StdLibExtras.h> + namespace JSC { typedef uint8_t IndexingType; // Flags for testing the presence of capabilities. static const IndexingType IsArray = 1; -static const IndexingType HasArrayStorage = 8; -static const IndexingType HasSlowPutArrayStorage = 16; + +// The shape of the indexed property storage. +static const IndexingType IndexingShapeMask = 30; +static const IndexingType NoIndexingShape = 0; +static const IndexingType ContiguousShape = 26; +static const IndexingType ArrayStorageShape = 28; +static const IndexingType SlowPutArrayStorageShape = 30; // Additional flags for tracking the history of the type. These are usually // masked off unless you ask for them directly. -static const IndexingType HadArrayStorage = 32; // Means that this object did have array storage in the past. -static const IndexingType MayHaveIndexedAccessors = 64; +static const IndexingType MayHaveIndexedAccessors = 32; // List of acceptable array types. static const IndexingType NonArray = 0; -static const IndexingType NonArrayWithArrayStorage = HasArrayStorage; -static const IndexingType NonArrayWithSlowPutArrayStorage = HasSlowPutArrayStorage; +static const IndexingType NonArrayWithContiguous = ContiguousShape; +static const IndexingType NonArrayWithArrayStorage = ArrayStorageShape; +static const IndexingType NonArrayWithSlowPutArrayStorage = SlowPutArrayStorageShape; static const IndexingType ArrayClass = IsArray; // I'd want to call this "Array" but this would lead to disastrous namespace pollution. -static const IndexingType ArrayWithArrayStorage = IsArray | HasArrayStorage; -static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | HasSlowPutArrayStorage; +static const IndexingType ArrayWithContiguous = IsArray | ContiguousShape; +static const IndexingType ArrayWithArrayStorage = IsArray | ArrayStorageShape; +static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | SlowPutArrayStorageShape; #define ALL_BLANK_INDEXING_TYPES \ NonArray: \ case ArrayClass +#define ALL_CONTIGUOUS_INDEXING_TYPES \ + NonArrayWithContiguous: \ + case ArrayWithContiguous + #define ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES \ ArrayWithArrayStorage: \ case ArrayWithSlowPutArrayStorage @@ -63,12 +75,7 @@ static const IndexingType ArrayWithSlowPutArrayStorage = IsArray | HasSlowPut static inline bool hasIndexedProperties(IndexingType indexingType) { - switch (indexingType) { - case ALL_BLANK_INDEXING_TYPES: - return false; - default: - return true; - } + return (indexingType & IndexingShapeMask) != NoIndexingShape; } static inline bool hasIndexingHeader(IndexingType type) @@ -76,16 +83,30 @@ static inline bool hasIndexingHeader(IndexingType type) return hasIndexedProperties(type); } +static inline bool hasContiguous(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == ContiguousShape; +} + +// FIXME: This is an awkward name. This should really be called hasArrayStorage() +// and then next method down should be called hasAnyArrayStorage(). +static inline bool hasFastArrayStorage(IndexingType indexingType) +{ + return (indexingType & IndexingShapeMask) == ArrayStorageShape; +} + static inline bool hasArrayStorage(IndexingType indexingType) { - return !!(indexingType & (HasArrayStorage | HasSlowPutArrayStorage)); + return static_cast<uint8_t>((indexingType & IndexingShapeMask) - ArrayStorageShape) <= static_cast<uint8_t>(SlowPutArrayStorageShape - ArrayStorageShape); } static inline bool shouldUseSlowPut(IndexingType indexingType) { - return !!(indexingType & HasSlowPutArrayStorage); + return (indexingType & IndexingShapeMask) == SlowPutArrayStorageShape; } +const char* indexingTypeToString(IndexingType); + // Mask of all possible types. static const IndexingType AllArrayTypes = 31; diff --git a/Source/JavaScriptCore/runtime/InitializeThreading.cpp b/Source/JavaScriptCore/runtime/InitializeThreading.cpp index 1a7239f60..1a6c84514 100644 --- a/Source/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/Source/JavaScriptCore/runtime/InitializeThreading.cpp @@ -31,6 +31,7 @@ #include "ExecutableAllocator.h" #include "Heap.h" +#include "HeapStatistics.h" #include "Options.h" #include "Identifier.h" #include "JSDateMath.h" @@ -56,13 +57,15 @@ static void initializeThreadingOnce() WTF::initializeThreading(); GlobalJSLock::initialize(); Options::initialize(); + if (Options::recordGCPauseTimes()) + HeapStatistics::initialize(); #if ENABLE(WRITE_BARRIER_PROFILING) WriteBarrierCounters::initialize(); #endif #if ENABLE(ASSEMBLER) ExecutableAllocator::initializeAllocator(); #endif - RegisterFile::initializeThreading(); + JSStack::initializeThreading(); #if ENABLE(LLINT) LLInt::initialize(); #endif diff --git a/Source/JavaScriptCore/runtime/InternalFunction.cpp b/Source/JavaScriptCore/runtime/InternalFunction.cpp index e2de03d92..afb5e6317 100644 --- a/Source/JavaScriptCore/runtime/InternalFunction.cpp +++ b/Source/JavaScriptCore/runtime/InternalFunction.cpp @@ -29,13 +29,12 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(InternalFunction); ASSERT_HAS_TRIVIAL_DESTRUCTOR(InternalFunction); -const ClassInfo InternalFunction::s_info = { "Function", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(InternalFunction) }; +const ClassInfo InternalFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(InternalFunction) }; InternalFunction::InternalFunction(JSGlobalObject* globalObject, Structure* structure) - : JSNonFinalObject(globalObject->globalData(), structure) + : JSDestructibleObject(globalObject->globalData(), structure) { } diff --git a/Source/JavaScriptCore/runtime/InternalFunction.h b/Source/JavaScriptCore/runtime/InternalFunction.h index e26b9f953..daeebc34d 100644 --- a/Source/JavaScriptCore/runtime/InternalFunction.h +++ b/Source/JavaScriptCore/runtime/InternalFunction.h @@ -24,16 +24,16 @@ #ifndef InternalFunction_h #define InternalFunction_h -#include "JSObject.h" #include "Identifier.h" +#include "JSDestructibleObject.h" namespace JSC { class FunctionPrototype; - class InternalFunction : public JSNonFinalObject { + class InternalFunction : public JSDestructibleObject { public: - typedef JSNonFinalObject Base; + typedef JSDestructibleObject Base; static JS_EXPORTDATA const ClassInfo s_info; diff --git a/Source/JavaScriptCore/runtime/JSActivation.cpp b/Source/JavaScriptCore/runtime/JSActivation.cpp index c34e10bc8..3b665962f 100644 --- a/Source/JavaScriptCore/runtime/JSActivation.cpp +++ b/Source/JavaScriptCore/runtime/JSActivation.cpp @@ -37,8 +37,6 @@ using namespace std; namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSActivation); - const ClassInfo JSActivation::s_info = { "JSActivation", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSActivation) }; void JSActivation::visitChildren(JSCell* cell, SlotVisitor& visitor) @@ -49,7 +47,7 @@ void JSActivation::visitChildren(JSCell* cell, SlotVisitor& visitor) ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); - // No need to mark our registers if they're still in the RegisterFile. + // No need to mark our registers if they're still in the JSStack. if (!thisObject->isTornOff()) return; @@ -116,11 +114,11 @@ void JSActivation::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec SymbolTable::const_iterator end = thisObject->symbolTable()->end(); for (SymbolTable::const_iterator it = thisObject->symbolTable()->begin(); it != end; ++it) { - if (it->second.getAttributes() & DontEnum && mode != IncludeDontEnumProperties) + if (it->value.getAttributes() & DontEnum && mode != IncludeDontEnumProperties) continue; - if (!thisObject->isValid(it->second)) + if (!thisObject->isValid(it->value)) continue; - propertyNames.add(Identifier(exec, it->first.get())); + propertyNames.add(Identifier(exec, it->key.get())); } // Skip the JSVariableObject implementation of getOwnNonIndexPropertyNames JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); @@ -133,7 +131,7 @@ inline bool JSActivation::symbolTablePutWithAttributes(JSGlobalData& globalData, SymbolTable::iterator iter = symbolTable()->find(propertyName.publicName()); if (iter == symbolTable()->end()) return false; - SymbolTableEntry& entry = iter->second; + SymbolTableEntry& entry = iter->value; ASSERT(!entry.isNull()); if (!isValid(entry)) return false; diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp index 8398ae77d..7028c3b95 100644 --- a/Source/JavaScriptCore/runtime/JSArray.cpp +++ b/Source/JavaScriptCore/runtime/JSArray.cpp @@ -44,8 +44,6 @@ using namespace WTF; namespace JSC { - -ASSERT_CLASS_FITS_IN_CELL(JSArray); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray); const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)}; @@ -245,8 +243,8 @@ void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, Pro JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); } -// This method makes room in the vector, but leaves the new space uncleared. -bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count) +// This method makes room in the vector, but leaves the new space for count slots uncleared. +bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, bool addToFront, unsigned count) { ArrayStorage* storage = ensureArrayStorage(globalData); Butterfly* butterfly = storage->butterfly(); @@ -254,7 +252,7 @@ bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count) unsigned propertySize = structure()->outOfLineSize(); // If not, we should have handled this on the fast path. - ASSERT(count > storage->m_indexBias); + ASSERT(!addToFront || count > storage->m_indexBias); // Step 1: // Gather 4 key metrics: @@ -278,7 +276,7 @@ bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count) unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1); // Step 2: - // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing on. + // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one. void* newAllocBase = 0; unsigned newStorageCapacity; @@ -297,36 +295,48 @@ bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count) // Work out where we're going to move things to. // Determine how much of the vector to use as pre-capacity, and how much as post-capacity. + // If we're adding to the end, we'll add all the new space to the end. // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any. // If it did, we calculate the amount that will remain based on an atomic decay - leave the // vector with half the post-capacity it had previously. unsigned postCapacity = 0; - if (length < storage->vectorLength()) { + if (!addToFront) + postCapacity = max(newStorageCapacity - requiredVectorLength, count); + else if (length < storage->vectorLength()) { // Atomic decay, + the post-capacity cannot be greater than what is available. postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength); // If we're moving contents within the same allocation, the post-capacity is being reduced. ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length); } - + unsigned newVectorLength = requiredVectorLength + postCapacity; unsigned newIndexBias = newStorageCapacity - newVectorLength; Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity); - - memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength); - memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); - + + if (addToFront) { + ASSERT(count + usedVectorLength <= newVectorLength); + memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength); + memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); + } else if ((newAllocBase != butterfly->base(structure())) || (newIndexBias != storage->m_indexBias)) { + memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0)); + memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength); + + WriteBarrier<Unknown>* newVector = newButterfly->arrayStorage()->m_vector; + for (unsigned i = requiredVectorLength; i < newVectorLength; i++) + newVector[i].clear(); + } + newButterfly->arrayStorage()->setVectorLength(newVectorLength); newButterfly->arrayStorage()->m_indexBias = newIndexBias; - + m_butterfly = newButterfly; return true; } -bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException) +bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage) { - ArrayStorage* storage = ensureArrayStorage(exec->globalData()); unsigned length = storage->length(); // If the length is read only then we enter sparse mode, so should enter the following 'if'. @@ -343,7 +353,7 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException keys.reserveCapacity(min(map->size(), static_cast<size_t>(length - newLength))); SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { - unsigned index = static_cast<unsigned>(it->first); + unsigned index = static_cast<unsigned>(it->key); if (index < length && index >= newLength) keys.append(index); } @@ -358,7 +368,7 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException unsigned index = keys[--i]; SparseArrayValueMap::iterator it = map->find(index); ASSERT(it != map->notFound()); - if (it->second.attributes & DontDelete) { + if (it->value.attributes & DontDelete) { storage->setLength(index + 1); return reject(exec, throwException, "Unable to delete property."); } @@ -389,12 +399,71 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException return true; } +bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException) +{ + switch (structure()->indexingType()) { + case ArrayClass: + if (!newLength) + return true; + if (newLength >= MIN_SPARSE_ARRAY_INDEX) { + return setLengthWithArrayStorage( + exec, newLength, throwException, + convertContiguousToArrayStorage(exec->globalData())); + } + createInitialContiguous(exec->globalData(), newLength); + return true; + + case ArrayWithContiguous: + if (newLength == m_butterfly->publicLength()) + return true; + if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push. + || (newLength >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(newLength, countElementsInContiguous(m_butterfly)))) { + return setLengthWithArrayStorage( + exec, newLength, throwException, + convertContiguousToArrayStorage(exec->globalData())); + } + if (newLength > m_butterfly->publicLength()) { + ensureContiguousLength(exec->globalData(), newLength); + return true; + } + for (unsigned i = m_butterfly->publicLength(); i-- > newLength;) + m_butterfly->contiguous()[i].clear(); + m_butterfly->setPublicLength(newLength); + return true; + + case ArrayWithArrayStorage: + case ArrayWithSlowPutArrayStorage: + return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage()); + + default: + CRASH(); + return false; + } +} + JSValue JSArray::pop(ExecState* exec) { switch (structure()->indexingType()) { case ArrayClass: return jsUndefined(); + case ArrayWithContiguous: { + unsigned length = m_butterfly->publicLength(); + + if (!length--) + return jsUndefined(); + + ASSERT(length < m_butterfly->vectorLength()); + JSValue value = m_butterfly->contiguous()[length].get(); + if (value) { + m_butterfly->contiguous()[length].clear(); + m_butterfly->setPublicLength(length); + return value; + } + break; + } + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); @@ -418,26 +487,28 @@ JSValue JSArray::pop(ExecState* exec) return element; } } - - // Let element be the result of calling the [[Get]] internal method of O with argument indx. - JSValue element = get(exec, index); - if (exec->hadException()) - return jsUndefined(); - // Call the [[Delete]] internal method of O with arguments indx and true. - if (!deletePropertyByIndex(this, exec, index)) { - throwTypeError(exec, "Unable to delete property."); - return jsUndefined(); - } - // Call the [[Put]] internal method of O with arguments "length", indx, and true. - setLength(exec, index, true); - // Return element. - return element; + break; } default: - ASSERT_NOT_REACHED(); + CRASH(); return JSValue(); } + + unsigned index = getArrayLength() - 1; + // Let element be the result of calling the [[Get]] internal method of O with argument indx. + JSValue element = get(exec, index); + if (exec->hadException()) + return jsUndefined(); + // Call the [[Delete]] internal method of O with arguments indx and true. + if (!deletePropertyByIndex(this, exec, index)) { + throwTypeError(exec, "Unable to delete property."); + return jsUndefined(); + } + // Call the [[Put]] internal method of O with arguments "length", indx, and true. + setLength(exec, index, true); + // Return element. + return element; } // Push & putIndex are almost identical, with two small differences. @@ -451,6 +522,26 @@ void JSArray::push(ExecState* exec, JSValue value) break; } + case ArrayWithContiguous: { + unsigned length = m_butterfly->publicLength(); + ASSERT(length <= m_butterfly->vectorLength()); + if (length < m_butterfly->vectorLength()) { + m_butterfly->contiguous()[length].set(exec->globalData(), this, value); + m_butterfly->setPublicLength(length + 1); + return; + } + + if (length > MAX_ARRAY_INDEX) { + methodTable()->putByIndex(this, exec, length, value, true); + if (!exec->hadException()) + throwError(exec, createRangeError(exec, "Invalid array length")); + return; + } + + putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, length, value); + return; + } + case ArrayWithSlowPutArrayStorage: { unsigned oldLength = length(); if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) { @@ -492,59 +583,139 @@ void JSArray::push(ExecState* exec, JSValue value) } } -bool JSArray::shiftCount(ExecState* exec, unsigned count) +bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, ArrayStorage* storage) { - ASSERT(count > 0); - - ArrayStorage* storage = ensureArrayStorage(exec->globalData()); - unsigned oldLength = storage->length(); ASSERT(count <= oldLength); // If the array contains holes or is otherwise in an abnormal state, // use the generic algorithm in ArrayPrototype. - if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode()) + if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode() || shouldUseSlowPut(structure()->indexingType())) return false; if (!oldLength) return true; + unsigned length = oldLength - count; + storage->m_numValuesInVector -= count; - storage->setLength(oldLength - count); + storage->setLength(length); unsigned vectorLength = storage->vectorLength(); + if (!vectorLength) + return true; + + if (startIndex >= vectorLength) + return true; + + if (startIndex + count > vectorLength) + count = vectorLength - startIndex; + + unsigned usedVectorLength = min(vectorLength, oldLength); + + vectorLength -= count; + storage->setVectorLength(vectorLength); + if (vectorLength) { - count = min(vectorLength, (unsigned)count); - - vectorLength -= count; - storage->setVectorLength(vectorLength); - - if (vectorLength) { + if (startIndex < usedVectorLength - (startIndex + count)) { + if (startIndex) { + memmove( + storage->m_vector + count, + storage->m_vector, + sizeof(JSValue) * startIndex); + } m_butterfly = m_butterfly->shift(structure(), count); storage = m_butterfly->arrayStorage(); storage->m_indexBias += count; + } else { + memmove( + storage->m_vector + startIndex, + storage->m_vector + startIndex + count, + sizeof(JSValue) * (usedVectorLength - (startIndex + count))); + for (unsigned i = usedVectorLength - count; i < usedVectorLength; ++i) + storage->m_vector[i].clear(); } } return true; } +bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count) +{ + ASSERT(count > 0); + + switch (structure()->indexingType()) { + case ArrayClass: + return true; + + case ArrayWithContiguous: { + unsigned oldLength = m_butterfly->publicLength(); + ASSERT(count <= oldLength); + + // We may have to walk the entire array to do the shift. We're willing to do + // so only if it's not horribly slow. + if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX) + return shiftCountWithArrayStorage(startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + + unsigned end = oldLength - count; + for (unsigned i = startIndex; i < end; ++i) { + // Storing to a hole is fine since we're still having a good time. But reading + // from a hole is totally not fine, since we might have to read from the proto + // chain. + JSValue v = m_butterfly->contiguous()[i + count].get(); + if (UNLIKELY(!v)) { + // The purpose of this path is to ensure that we don't make the same + // mistake in the future: shiftCountWithArrayStorage() can't do anything + // about holes (at least for now), but it can detect them quickly. So + // we convert to array storage and then allow the array storage path to + // figure it out. + return shiftCountWithArrayStorage(startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + } + // No need for a barrier since we're just moving data around in the same vector. + // This is in line with our standing assumption that we won't have a deletion + // barrier. + m_butterfly->contiguous()[i].setWithoutWriteBarrier(v); + } + for (unsigned i = end; i < oldLength; ++i) + m_butterfly->contiguous()[i].clear(); + + m_butterfly->setPublicLength(oldLength - count); + return true; + } + + case ArrayWithArrayStorage: + case ArrayWithSlowPutArrayStorage: + return shiftCountWithArrayStorage(startIndex, count, arrayStorage()); + + default: + CRASH(); + return false; + } +} + // Returns true if the unshift can be handled, false to fallback. -bool JSArray::unshiftCount(ExecState* exec, unsigned count) +bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage) { - ArrayStorage* storage = ensureArrayStorage(exec->globalData()); unsigned length = storage->length(); + ASSERT(startIndex <= length); + // If the array contains holes or is otherwise in an abnormal state, // use the generic algorithm in ArrayPrototype. - if (length != storage->m_numValuesInVector || storage->inSparseMode()) + if (length != storage->m_numValuesInVector || storage->inSparseMode() || shouldUseSlowPut(structure()->indexingType())) return false; - if (storage->m_indexBias >= count) { + bool moveFront = !startIndex || startIndex < length / 2; + + unsigned vectorLength = storage->vectorLength(); + + if (moveFront && storage->m_indexBias >= count) { m_butterfly = storage->butterfly()->unshift(structure(), count); storage = m_butterfly->arrayStorage(); storage->m_indexBias -= count; - storage->setVectorLength(storage->vectorLength() + count); - } else if (unshiftCountSlowCase(exec->globalData(), count)) + storage->setVectorLength(vectorLength + count); + } else if (!moveFront && vectorLength - length >= count) + storage = storage->butterfly()->arrayStorage(); + else if (unshiftCountSlowCase(exec->globalData(), moveFront, count)) storage = arrayStorage(); else { throwOutOfMemoryError(exec); @@ -552,11 +723,61 @@ bool JSArray::unshiftCount(ExecState* exec, unsigned count) } WriteBarrier<Unknown>* vector = storage->m_vector; + + if (startIndex) { + if (moveFront) + memmove(vector, vector + count, startIndex * sizeof(JSValue)); + else if (length - startIndex) + memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue)); + } + for (unsigned i = 0; i < count; i++) - vector[i].clear(); + vector[i + startIndex].clear(); return true; } +bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count) +{ + switch (structure()->indexingType()) { + case ArrayClass: + // We could handle this. But it shouldn't ever come up, so we won't. + return false; + + case ArrayWithContiguous: { + unsigned oldLength = m_butterfly->publicLength(); + + // We may have to walk the entire array to do the unshift. We're willing to do so + // only if it's not horribly slow. + if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) + return unshiftCountWithArrayStorage(exec, startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + + ensureContiguousLength(exec->globalData(), oldLength + count); + + for (unsigned i = oldLength; i-- > startIndex;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (UNLIKELY(!v)) + return unshiftCountWithArrayStorage(exec, startIndex, count, convertContiguousToArrayStorage(exec->globalData())); + m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v); + } + + // NOTE: we're leaving being garbage in the part of the array that we shifted out + // of. This is fine because the caller is required to store over that area, and + // in contiguous mode storing into a hole is guaranteed to behave exactly the same + // as storing over an existing element. + + return true; + } + + case ArrayWithArrayStorage: + case ArrayWithSlowPutArrayStorage: + return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage()); + + default: + CRASH(); + return false; + } +} + static int compareNumbersForQSort(const void* a, const void* b) { double da = static_cast<const JSValue*>(a)->asNumber(); @@ -571,6 +792,45 @@ static int compareByStringPairForQSort(const void* a, const void* b) return codePointCompare(va->second, vb->second); } +template<IndexingType indexingType> +void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) +{ + ASSERT(indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage); + + unsigned lengthNotIncludingUndefined; + unsigned newRelevantLength; + compactForSorting<indexingType>( + lengthNotIncludingUndefined, + newRelevantLength); + + WriteBarrier<Unknown>* data = indexingData<indexingType>(); + + if (indexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) { + throwOutOfMemoryError(exec); + return; + } + + if (!lengthNotIncludingUndefined) + return; + + bool allValuesAreNumbers = true; + for (size_t i = 0; i < newRelevantLength; ++i) { + if (!data[i].isNumber()) { + allValuesAreNumbers = false; + break; + } + } + + if (!allValuesAreNumbers) + return sort(exec, compareFunction, callType, callData); + + // 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(data, newRelevantLength, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); + return; +} + void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) { ASSERT(!inSparseIndexingMode()); @@ -579,123 +839,129 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal case ArrayClass: return; - case ArrayWithArrayStorage: { - unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); - ArrayStorage* storage = m_butterfly->arrayStorage(); - - if (storage->m_sparseMap.get()) { - throwOutOfMemoryError(exec); - return; - } - - if (!lengthNotIncludingUndefined) - return; - - bool allValuesAreNumbers = true; - size_t size = storage->m_numValuesInVector; - for (size_t i = 0; i < size; ++i) { - if (!storage->m_vector[i].isNumber()) { - allValuesAreNumbers = false; - break; - } - } - - if (!allValuesAreNumbers) - return sort(exec, compareFunction, callType, callData); - - // 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(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort); - + case ArrayWithContiguous: + sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData); + return; + + case ArrayWithArrayStorage: + sortNumericVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData); return; - } default: - ASSERT_NOT_REACHED(); + CRASH(); + return; } } -void JSArray::sort(ExecState* exec) +template<IndexingType indexingType> +void JSArray::sortCompactedVector(ExecState* exec, WriteBarrier<Unknown>* begin, unsigned relevantLength) { - ASSERT(!inSparseIndexingMode()); - - switch (structure()->indexingType()) { - case ArrayClass: + if (!relevantLength) return; + + JSGlobalData& globalData = exec->globalData(); + + // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. + // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary + // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return + // random or otherwise changing results, effectively making compare function inconsistent. - case ArrayWithArrayStorage: { - unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData()); - ArrayStorage* storage = m_butterfly->arrayStorage(); - if (storage->m_sparseMap.get()) { - throwOutOfMemoryError(exec); - return; - } - - if (!lengthNotIncludingUndefined) - return; - - // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. - // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary - // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return - // random or otherwise changing results, effectively making compare function inconsistent. - - Vector<ValueStringPair> values(lengthNotIncludingUndefined); - if (!values.begin()) { - throwOutOfMemoryError(exec); - return; - } + Vector<ValueStringPair> values(relevantLength); + if (!values.begin()) { + throwOutOfMemoryError(exec); + return; + } - Heap::heap(this)->pushTempSortVector(&values); + Heap::heap(this)->pushTempSortVector(&values); - bool isSortingPrimitiveValues = true; - for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { - JSValue value = storage->m_vector[i].get(); - ASSERT(!value.isUndefined()); - values[i].first = value; - isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); - } + bool isSortingPrimitiveValues = true; + for (size_t i = 0; i < relevantLength; i++) { + JSValue value = begin[i].get(); + ASSERT(!value.isUndefined()); + values[i].first = value; + isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive(); + } - // FIXME: The following loop continues to call toString on subsequent values even after - // a toString call raises an exception. + // FIXME: The following loop continues to call toString on subsequent values even after + // a toString call raises an exception. - for (size_t i = 0; i < lengthNotIncludingUndefined; i++) - values[i].second = values[i].first.toWTFStringInline(exec); + for (size_t i = 0; i < relevantLength; i++) + values[i].second = values[i].first.toWTFStringInline(exec); - if (exec->hadException()) { - Heap::heap(this)->popTempSortVector(&values); - return; - } + if (exec->hadException()) { + Heap::heap(this)->popTempSortVector(&values); + return; + } - // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather - // than O(N log N). + // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather + // than O(N log N). #if HAVE(MERGESORT) - if (isSortingPrimitiveValues) - qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); - else - mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); -#else - // FIXME: The qsort library function is likely to not be a stable sort. - // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. + if (isSortingPrimitiveValues) qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); + else + mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); +#else + // FIXME: The qsort library function is likely to not be a stable sort. + // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort. + qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort); #endif + + // If the toString function changed the length of the array or vector storage, + // increase the length to handle the orignal number of actual values. + switch (indexingType) { + case ArrayWithContiguous: + ensureContiguousLength(globalData, relevantLength); + break; - // If the toString function changed the length of the array or vector storage, - // increase the length to handle the orignal number of actual values. - if (storage->vectorLength() < lengthNotIncludingUndefined) { - increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined); - storage = m_butterfly->arrayStorage(); + case ArrayWithArrayStorage: + if (arrayStorage()->vectorLength() < relevantLength) { + increaseVectorLength(exec->globalData(), relevantLength); + begin = arrayStorage()->m_vector; } - if (storage->length() < lengthNotIncludingUndefined) - storage->setLength(lengthNotIncludingUndefined); + if (arrayStorage()->length() < relevantLength) + arrayStorage()->setLength(relevantLength); + break; + + default: + CRASH(); + } + + for (size_t i = 0; i < relevantLength; i++) + begin[i].set(globalData, this, values[i].first); + + Heap::heap(this)->popTempSortVector(&values); +} + +void JSArray::sort(ExecState* exec) +{ + ASSERT(!inSparseIndexingMode()); + + switch (structure()->indexingType()) { + case ArrayClass: + return; - JSGlobalData& globalData = exec->globalData(); - for (size_t i = 0; i < lengthNotIncludingUndefined; i++) - storage->m_vector[i].set(globalData, this, values[i].first); + case ArrayWithContiguous: { + unsigned lengthNotIncludingUndefined; + unsigned newRelevantLength; + compactForSorting<ArrayWithContiguous>( + lengthNotIncludingUndefined, newRelevantLength); - Heap::heap(this)->popTempSortVector(&values); + sortCompactedVector<ArrayWithContiguous>( + exec, m_butterfly->contiguous(), lengthNotIncludingUndefined); + return; + } + + case ArrayWithArrayStorage: { + unsigned lengthNotIncludingUndefined; + unsigned newRelevantLength; + compactForSorting<ArrayWithArrayStorage>( + lengthNotIncludingUndefined, newRelevantLength); + ArrayStorage* storage = m_butterfly->arrayStorage(); + ASSERT(!storage->m_sparseMap); + sortCompactedVector<ArrayWithArrayStorage>( + exec, storage->m_vector, lengthNotIncludingUndefined); return; } @@ -781,122 +1047,116 @@ struct AVLTreeAbstractorForArrayCompare { static handle null() { return 0x7FFFFFFF; } }; -void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) +template<IndexingType indexingType> +void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) { ASSERT(!inSparseIndexingMode()); + ASSERT(indexingType == structure()->indexingType()); - switch (structure()->indexingType()) { - case ArrayClass: - return; - - case ArrayWithArrayStorage: { - ArrayStorage* storage = m_butterfly->arrayStorage(); - - // FIXME: This ignores exceptions raised in the compare function or in toNumber. + // 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(storage->length() <= static_cast<unsigned>(std::numeric_limits<int>::max())); - if (storage->length() > static_cast<unsigned>(std::numeric_limits<int>::max())) - return; - - unsigned usedVectorLength = min(storage->length(), storage->vectorLength()); - unsigned nodeCount = usedVectorLength + (storage->m_sparseMap ? storage->m_sparseMap->size() : 0); - - if (!nodeCount) - return; - - AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items - tree.abstractor().m_exec = exec; - tree.abstractor().m_compareFunction = compareFunction; - tree.abstractor().m_compareCallType = callType; - tree.abstractor().m_compareCallData = &callData; - tree.abstractor().m_nodes.grow(nodeCount); + // The maximum tree depth is compiled in - but the caller is clearly up to no good + // if a larger array is passed. + ASSERT(m_butterfly->publicLength() <= static_cast<unsigned>(std::numeric_limits<int>::max())); + if (m_butterfly->publicLength() > static_cast<unsigned>(std::numeric_limits<int>::max())) + return; - if (callType == CallTypeJS) - tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2)); + unsigned usedVectorLength = relevantLength<indexingType>(); + unsigned nodeCount = usedVectorLength; - if (!tree.abstractor().m_nodes.begin()) { - throwOutOfMemoryError(exec); - return; - } + if (!nodeCount) + return; - // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified - // right out from under us while we're building the tree here. + AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items + tree.abstractor().m_exec = exec; + tree.abstractor().m_compareFunction = compareFunction; + tree.abstractor().m_compareCallType = callType; + tree.abstractor().m_compareCallData = &callData; + tree.abstractor().m_nodes.grow(nodeCount); - unsigned numDefined = 0; - unsigned numUndefined = 0; + if (callType == CallTypeJS) + tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2)); - // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. - for (; numDefined < usedVectorLength; ++numDefined) { - JSValue v = storage->m_vector[numDefined].get(); - if (!v || v.isUndefined()) - break; - tree.abstractor().m_nodes[numDefined].value = v; - tree.insert(numDefined); - } - for (unsigned i = numDefined; i < usedVectorLength; ++i) { - JSValue v = storage->m_vector[i].get(); - if (v) { - if (v.isUndefined()) - ++numUndefined; - else { - tree.abstractor().m_nodes[numDefined].value = v; - tree.insert(numDefined); - ++numDefined; - } - } - } - - unsigned newUsedVectorLength = numDefined + numUndefined; + if (!tree.abstractor().m_nodes.begin()) { + throwOutOfMemoryError(exec); + return; + } - if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { - newUsedVectorLength += map->size(); - if (newUsedVectorLength > storage->vectorLength()) { - // Check that it is possible to allocate an array large enough to hold all the entries. - if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) { - throwOutOfMemoryError(exec); - return; - } - storage = m_butterfly->arrayStorage(); - } - - SparseArrayValueMap::const_iterator end = map->end(); - for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { - tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode(); + // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified + // right out from under us while we're building the tree here. + + unsigned numDefined = 0; + unsigned numUndefined = 0; + + // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. + for (; numDefined < usedVectorLength; ++numDefined) { + if (numDefined > m_butterfly->vectorLength()) + break; + JSValue v = indexingData<indexingType>()[numDefined].get(); + if (!v || v.isUndefined()) + break; + tree.abstractor().m_nodes[numDefined].value = v; + tree.insert(numDefined); + } + for (unsigned i = numDefined; i < usedVectorLength; ++i) { + if (i > m_butterfly->vectorLength()) + break; + JSValue v = indexingData<indexingType>()[i].get(); + if (v) { + if (v.isUndefined()) + ++numUndefined; + else { + tree.abstractor().m_nodes[numDefined].value = v; tree.insert(numDefined); ++numDefined; } - - deallocateSparseIndexMap(); } + } - ASSERT(tree.abstractor().m_nodes.size() >= numDefined); - - // FIXME: If the compare function changed the length of the array, the following might be - // modifying the vector incorrectly. + unsigned newUsedVectorLength = numDefined + numUndefined; - // Copy the values back into m_storage. - AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; - iter.start_iter_least(tree); - JSGlobalData& globalData = exec->globalData(); - for (unsigned i = 0; i < numDefined; ++i) { - storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); - ++iter; - } + // The array size may have changed. Figure out the new bounds. + unsigned newestUsedVectorLength = relevantLength<indexingType>(); - // Put undefined values back in. - for (unsigned i = numDefined; i < newUsedVectorLength; ++i) - storage->m_vector[i].setUndefined(); + unsigned elementsToExtractThreshold = min(min(newestUsedVectorLength, numDefined), static_cast<unsigned>(tree.abstractor().m_nodes.size())); + unsigned undefinedElementsThreshold = min(newestUsedVectorLength, newUsedVectorLength); + unsigned clearElementsThreshold = min(newestUsedVectorLength, usedVectorLength); - // Ensure that unused values in the vector are zeroed out. - for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) - storage->m_vector[i].clear(); - - storage->m_numValuesInVector = newUsedVectorLength; + // Copy the values back into m_storage. + AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; + iter.start_iter_least(tree); + JSGlobalData& globalData = exec->globalData(); + for (unsigned i = 0; i < elementsToExtractThreshold; ++i) { + indexingData<indexingType>()[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); + ++iter; + } + // Put undefined values back in. + for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) + indexingData<indexingType>()[i].setUndefined(); + + // Ensure that unused values in the vector are zeroed out. + for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) + indexingData<indexingType>()[i].clear(); + + if (hasArrayStorage(indexingType)) + arrayStorage()->m_numValuesInVector = newUsedVectorLength; +} + +void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) +{ + ASSERT(!inSparseIndexingMode()); + + switch (structure()->indexingType()) { + case ArrayClass: + return; + case ArrayWithContiguous: + sortVector<ArrayWithContiguous>(exec, compareFunction, callType, callData); + return; + + case ArrayWithArrayStorage: + sortVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData); return; - } default: ASSERT_NOT_REACHED(); @@ -905,129 +1165,127 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) { + unsigned i = 0; + unsigned vectorEnd; + WriteBarrier<Unknown>* vector; + switch (structure()->indexingType()) { case ArrayClass: return; + + case ArrayWithContiguous: { + vectorEnd = m_butterfly->publicLength(); + vector = m_butterfly->contiguous(); + break; + } case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); - WriteBarrier<Unknown>* vector = storage->m_vector; - unsigned vectorEnd = min(storage->length(), storage->vectorLength()); - unsigned i = 0; - for (; i < vectorEnd; ++i) { - WriteBarrier<Unknown>& v = vector[i]; - if (!v) - break; - args.append(v.get()); - } - - for (; i < storage->length(); ++i) - args.append(get(exec, i)); - return; + vector = storage->m_vector; + vectorEnd = min(storage->length(), storage->vectorLength()); + break; } default: - ASSERT_NOT_REACHED(); + CRASH(); + vector = 0; + vectorEnd = 0; + break; } + + for (; i < vectorEnd; ++i) { + WriteBarrier<Unknown>& v = vector[i]; + if (!v) + break; + args.append(v.get()); + } + + for (; i < length(); ++i) + args.append(get(exec, i)); } void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length) { + unsigned i = 0; + WriteBarrier<Unknown>* vector; + unsigned vectorEnd; + ASSERT(length == this->length()); switch (structure()->indexingType()) { case ArrayClass: return; + case ArrayWithContiguous: { + vector = m_butterfly->contiguous(); + vectorEnd = m_butterfly->publicLength(); + break; + } + case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); - unsigned i = 0; - WriteBarrier<Unknown>* vector = storage->m_vector; - unsigned vectorEnd = min(length, storage->vectorLength()); - for (; i < vectorEnd; ++i) { - WriteBarrier<Unknown>& v = vector[i]; - if (!v) - break; - callFrame->setArgument(i, v.get()); - } - - for (; i < length; ++i) - callFrame->setArgument(i, get(exec, i)); - return; + vector = storage->m_vector; + vectorEnd = min(length, storage->vectorLength()); + break; } default: - ASSERT_NOT_REACHED(); + CRASH(); + vector = 0; + vectorEnd = 0; + break; + } + + for (; i < vectorEnd; ++i) { + WriteBarrier<Unknown>& v = vector[i]; + if (!v) + break; + callFrame->setArgument(i, v.get()); } + + for (; i < length; ++i) + callFrame->setArgument(i, get(exec, i)); } -unsigned JSArray::compactForSorting(JSGlobalData& globalData) +template<IndexingType indexingType> +void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLength) { ASSERT(!inSparseIndexingMode()); + ASSERT(indexingType == structure()->indexingType()); - switch (structure()->indexingType()) { - case ArrayClass: - return 0; - - case ArrayWithArrayStorage: { - ArrayStorage* storage = m_butterfly->arrayStorage(); - - unsigned usedVectorLength = min(storage->length(), storage->vectorLength()); - - unsigned numDefined = 0; - unsigned numUndefined = 0; - - for (; numDefined < usedVectorLength; ++numDefined) { - JSValue v = storage->m_vector[numDefined].get(); - if (!v || v.isUndefined()) - break; - } + unsigned myRelevantLength = relevantLength<indexingType>(); + + numDefined = 0; + unsigned numUndefined = 0; - for (unsigned i = numDefined; i < usedVectorLength; ++i) { - JSValue v = storage->m_vector[i].get(); - if (v) { - if (v.isUndefined()) - ++numUndefined; - else - storage->m_vector[numDefined++].setWithoutWriteBarrier(v); - } - } + for (; numDefined < myRelevantLength; ++numDefined) { + JSValue v = indexingData<indexingType>()[numDefined].get(); + if (!v || v.isUndefined()) + break; + } - unsigned newUsedVectorLength = numDefined + numUndefined; - - if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { - newUsedVectorLength += map->size(); - if (newUsedVectorLength > storage->vectorLength()) { - // Check that it is possible to allocate an array large enough to hold all the entries - if not, - // exception is thrown by caller. - if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength)) - return 0; - - storage = m_butterfly->arrayStorage(); - } - - SparseArrayValueMap::const_iterator end = map->end(); - for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) - storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode()); - - deallocateSparseIndexMap(); + for (unsigned i = numDefined; i < myRelevantLength; ++i) { + JSValue v = indexingData<indexingType>()[i].get(); + if (v) { + if (v.isUndefined()) + ++numUndefined; + else + indexingData<indexingType>()[numDefined++].setWithoutWriteBarrier(v); } - - for (unsigned i = numDefined; i < newUsedVectorLength; ++i) - storage->m_vector[i].setUndefined(); - for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) - storage->m_vector[i].clear(); - - storage->m_numValuesInVector = newUsedVectorLength; - - return numDefined; } - default: - ASSERT_NOT_REACHED(); - return 0; - } -} + newRelevantLength = numDefined + numUndefined; + + if (hasArrayStorage(indexingType)) + ASSERT(!arrayStorage()->m_sparseMap); + + for (unsigned i = numDefined; i < newRelevantLength; ++i) + indexingData<indexingType>()[i].setUndefined(); + for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) + indexingData<indexingType>()[i].clear(); + if (hasArrayStorage(indexingType)) + arrayStorage()->m_numValuesInVector = newRelevantLength; +} } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h index 6e539c9db..d4622aacc 100644 --- a/Source/JavaScriptCore/runtime/JSArray.h +++ b/Source/JavaScriptCore/runtime/JSArray.h @@ -71,8 +71,60 @@ namespace JSC { void push(ExecState*, JSValue); JSValue pop(ExecState*); - bool shiftCount(ExecState*, unsigned count); - bool unshiftCount(ExecState*, unsigned count); + enum ShiftCountMode { + // This form of shift hints that we're doing queueing. With this assumption in hand, + // we convert to ArrayStorage, which has queue optimizations. + ShiftCountForShift, + + // This form of shift hints that we're just doing care and feeding on an array that + // is probably typically used for ordinary accesses. With this assumption in hand, + // we try to preserve whatever indexing type it has already. + ShiftCountForSplice + }; + + bool shiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count) + { + return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData())); + } + bool shiftCountForSplice(ExecState* exec, unsigned startIndex, unsigned count) + { + return shiftCountWithAnyIndexingType(exec, startIndex, count); + } + template<ShiftCountMode shiftCountMode> + bool shiftCount(ExecState* exec, unsigned startIndex, unsigned count) + { + switch (shiftCountMode) { + case ShiftCountForShift: + return shiftCountForShift(exec, startIndex, count); + case ShiftCountForSplice: + return shiftCountForSplice(exec, startIndex, count); + default: + CRASH(); + return false; + } + } + + bool unshiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count) + { + return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData())); + } + bool unshiftCountForSplice(ExecState* exec, unsigned startIndex, unsigned count) + { + return unshiftCountWithAnyIndexingType(exec, startIndex, count); + } + template<ShiftCountMode shiftCountMode> + bool unshiftCount(ExecState* exec, unsigned startIndex, unsigned count) + { + switch (shiftCountMode) { + case ShiftCountForShift: + return unshiftCountForShift(exec, startIndex, count); + case ShiftCountForSplice: + return unshiftCountForSplice(exec, startIndex, count); + default: + CRASH(); + return false; + } + } void fillArgList(ExecState*, MarkedArgumentBuffer&); void copyToArguments(ExecState*, CallFrame*, uint32_t length); @@ -94,18 +146,44 @@ namespace JSC { { ArrayStorage* storage = arrayStorageOrNull(); if (!storage) - return false; + return true; SparseArrayValueMap* map = storage->m_sparseMap.get(); return !map || !map->lengthIsReadOnly(); } + + bool shiftCountWithAnyIndexingType(ExecState*, unsigned startIndex, unsigned count); + bool shiftCountWithArrayStorage(unsigned startIndex, unsigned count, ArrayStorage*); - void setLengthWritable(ExecState*, bool writable); + bool unshiftCountWithAnyIndexingType(ExecState*, unsigned startIndex, unsigned count); + bool unshiftCountWithArrayStorage(ExecState*, unsigned startIndex, unsigned count, ArrayStorage*); + bool unshiftCountSlowCase(JSGlobalData&, bool, unsigned); + + template<IndexingType indexingType> + void sortNumericVector(ExecState*, JSValue compareFunction, CallType, const CallData&); + + template<IndexingType indexingType> + void sortCompactedVector(ExecState*, WriteBarrier<Unknown>* begin, unsigned relevantLength); + + template<IndexingType indexingType> + void sortVector(ExecState*, JSValue compareFunction, CallType, const CallData&); - bool unshiftCountSlowCase(JSGlobalData&, unsigned count); + bool setLengthWithArrayStorage(ExecState*, unsigned newLength, bool throwException, ArrayStorage*); + void setLengthWritable(ExecState*, bool writable); - unsigned compactForSorting(JSGlobalData&); + template<IndexingType indexingType> + void compactForSorting(unsigned& numDefined, unsigned& newRelevantLength); }; + inline Butterfly* createContiguousArrayButterfly(JSGlobalData& globalData, unsigned length) + { + IndexingHeader header; + header.setVectorLength(std::max(length, BASE_VECTOR_LEN)); + header.setPublicLength(length); + Butterfly* result = Butterfly::create( + globalData, 0, 0, true, header, header.vectorLength() * sizeof(EncodedJSValue)); + return result; + } + inline Butterfly* createArrayButterfly(JSGlobalData& globalData, unsigned initialLength) { Butterfly* butterfly = Butterfly::create( @@ -121,7 +199,16 @@ namespace JSC { inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength) { - Butterfly* butterfly = createArrayButterfly(globalData, initialLength); + Butterfly* butterfly; + if (LIKELY(structure->indexingType() == ArrayWithContiguous)) { + butterfly = createContiguousArrayButterfly(globalData, initialLength); + ASSERT(initialLength < MIN_SPARSE_ARRAY_INDEX); + } else { + ASSERT( + structure->indexingType() == ArrayWithSlowPutArrayStorage + || (initialLength && structure->indexingType() == ArrayWithArrayStorage)); + butterfly = createArrayButterfly(globalData, initialLength); + } JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly); array->finishCreation(globalData); return array; @@ -133,15 +220,26 @@ namespace JSC { if (vectorLength > MAX_STORAGE_VECTOR_LENGTH) return 0; - void* temp; - if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, ArrayStorage::sizeFor(vectorLength)), &temp)) - return 0; - Butterfly* butterfly = Butterfly::fromBase(temp, 0, 0); - *butterfly->indexingHeader() = indexingHeaderForArray(initialLength, vectorLength); - ArrayStorage* storage = butterfly->arrayStorage(); - storage->m_indexBias = 0; - storage->m_sparseMap.clear(); - storage->m_numValuesInVector = initialLength; + Butterfly* butterfly; + if (LIKELY(structure->indexingType() == ArrayWithContiguous)) { + + void* temp; + if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, vectorLength * sizeof(EncodedJSValue)), &temp)) + return 0; + butterfly = Butterfly::fromBase(temp, 0, 0); + butterfly->setVectorLength(vectorLength); + butterfly->setPublicLength(initialLength); + } else { + void* temp; + if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, ArrayStorage::sizeFor(vectorLength)), &temp)) + return 0; + butterfly = Butterfly::fromBase(temp, 0, 0); + *butterfly->indexingHeader() = indexingHeaderForArray(initialLength, vectorLength); + ArrayStorage* storage = butterfly->arrayStorage(); + storage->m_indexBias = 0; + storage->m_sparseMap.clear(); + storage->m_numValuesInVector = initialLength; + } JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly); array->finishCreation(globalData); diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp index 3815c144e..d8f611477 100644 --- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp @@ -31,7 +31,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSBoundFunction); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSBoundFunction); const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSBoundFunction) }; diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp index 739247fb2..f6f4d716d 100644 --- a/Source/JavaScriptCore/runtime/JSCell.cpp +++ b/Source/JavaScriptCore/runtime/JSCell.cpp @@ -38,6 +38,10 @@ void JSCell::destroy(JSCell* cell) cell->JSCell::~JSCell(); } +void JSCell::copyBackingStore(JSCell*, CopyVisitor&) +{ +} + bool JSCell::getString(ExecState* exec, String& stringValue) const { if (!isString()) diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h index cf6f4ec45..a39af1283 100644 --- a/Source/JavaScriptCore/runtime/JSCell.h +++ b/Source/JavaScriptCore/runtime/JSCell.h @@ -31,12 +31,15 @@ #include "JSValueInlineMethods.h" #include "SlotVisitor.h" #include "SlotVisitorInlineMethods.h" +#include "TypedArrayDescriptor.h" #include "WriteBarrier.h" #include <wtf/Noncopyable.h> #include <wtf/TypeTraits.h> namespace JSC { + class CopyVisitor; + class JSDestructibleObject; class JSGlobalObject; class LLIntOffsetsExtractor; class PropertyDescriptor; @@ -48,19 +51,6 @@ namespace JSC { IncludeDontEnumProperties }; - enum TypedArrayType { - TypedArrayNone, - TypedArrayInt8, - TypedArrayInt16, - TypedArrayInt32, - TypedArrayUint8, - TypedArrayUint8Clamped, - TypedArrayUint16, - TypedArrayUint32, - TypedArrayFloat32, - TypedArrayFloat64 - }; - class JSCell { friend class JSValue; friend class MarkedBlock; @@ -70,6 +60,9 @@ namespace JSC { public: static const unsigned StructureFlags = 0; + static const bool needsDestruction = false; + static const bool hasImmortalStructure = false; + enum CreatingEarlyCellTag { CreatingEarlyCell }; JSCell(CreatingEarlyCellTag); @@ -108,6 +101,7 @@ namespace JSC { JS_EXPORT_PRIVATE JSObject* toObject(ExecState*, JSGlobalObject*) const; static void visitChildren(JSCell*, SlotVisitor&); + JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&); // Object operations, with the toObject operation included. const ClassInfo* classInfo() const; @@ -309,46 +303,29 @@ namespace JSC { return isCell() ? asCell()->toObject(exec, globalObject) : toObjectSlowCase(exec, globalObject); } - template<class T> - struct NeedsDestructor { - static const bool value = !WTF::HasTrivialDestructor<T>::value; - }; - template<typename T> - void* allocateCell(Heap& heap) + void* allocateCell(Heap& heap, size_t size) { + ASSERT(size >= sizeof(T)); #if ENABLE(GC_VALIDATION) ASSERT(!heap.globalData()->isInitializingObject()); heap.globalData()->setInitializingObjectClass(&T::s_info); #endif JSCell* result = 0; - if (NeedsDestructor<T>::value) - result = static_cast<JSCell*>(heap.allocateWithDestructor(sizeof(T))); - else { - ASSERT(T::s_info.methodTable.destroy == JSCell::destroy); - result = static_cast<JSCell*>(heap.allocateWithoutDestructor(sizeof(T))); - } + if (T::needsDestruction && T::hasImmortalStructure) + result = static_cast<JSCell*>(heap.allocateWithImmortalStructureDestructor(size)); + else if (T::needsDestruction && !T::hasImmortalStructure) + result = static_cast<JSCell*>(heap.allocateWithNormalDestructor(size)); + else + result = static_cast<JSCell*>(heap.allocateWithoutDestructor(size)); result->clearStructure(); return result; } template<typename T> - void* allocateCell(Heap& heap, size_t size) + void* allocateCell(Heap& heap) { - ASSERT(size >= sizeof(T)); -#if ENABLE(GC_VALIDATION) - ASSERT(!heap.globalData()->isInitializingObject()); - heap.globalData()->setInitializingObjectClass(&T::s_info); -#endif - JSCell* result = 0; - if (NeedsDestructor<T>::value) - result = static_cast<JSCell*>(heap.allocateWithDestructor(size)); - else { - ASSERT(T::s_info.methodTable.destroy == JSCell::destroy); - result = static_cast<JSCell*>(heap.allocateWithoutDestructor(size)); - } - result->clearStructure(); - return result; + return allocateCell<T>(heap, sizeof(T)); } inline bool isZapped(const JSCell* cell) @@ -362,7 +339,7 @@ namespace JSC { ASSERT(!from || from->JSCell::inherits(&WTF::RemovePointer<To>::Type::s_info)); return static_cast<To>(from); } - + template<typename To> inline To jsCast(JSValue from) { diff --git a/Source/JavaScriptCore/runtime/JSDestructibleObject.h b/Source/JavaScriptCore/runtime/JSDestructibleObject.h new file mode 100644 index 000000000..b8479be62 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSDestructibleObject.h @@ -0,0 +1,43 @@ +#ifndef JSDestructibleObject_h +#define JSDestructibleObject_h + +#include "JSObject.h" + +namespace JSC { + +struct ClassInfo; + +class JSDestructibleObject : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static const bool needsDestruction = true; + + const ClassInfo* classInfo() const { return m_classInfo; } + +protected: + JSDestructibleObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly = 0) + : JSNonFinalObject(globalData, structure, butterfly) + , m_classInfo(structure->classInfo()) + { + ASSERT(m_classInfo); + } + +private: + const ClassInfo* m_classInfo; +}; + +inline const ClassInfo* JSCell::classInfo() const +{ + if (MarkedBlock::blockFor(this)->destructorType() == MarkedBlock::Normal) + return static_cast<const JSDestructibleObject*>(this)->classInfo(); +#if ENABLE(GC_VALIDATION) + return m_structure.unvalidatedGet()->classInfo(); +#else + return m_structure->classInfo(); +#endif +} + +} // namespace JSC + +#endif diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp index 4afe63216..891a23930 100644 --- a/Source/JavaScriptCore/runtime/JSFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSFunction.cpp @@ -48,7 +48,6 @@ EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); } -ASSERT_CLASS_FITS_IN_CELL(JSFunction); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFunction); const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFunction) }; diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp index e30a7913d..bc3d00067 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp @@ -231,7 +231,9 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread interpreter->initialize(this->canUseJIT()); +#if ENABLE(JIT) initializeHostCallReturnValue(); // This is needed to convince the linker not to drop host call return support. +#endif heap.notifyIsSafeToCollect(); diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h index 603f9f82a..6cc0aad8d 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalData.h +++ b/Source/JavaScriptCore/runtime/JSGlobalData.h @@ -44,6 +44,7 @@ #include "Strong.h" #include "Terminator.h" #include "TimeoutChecker.h" +#include "TypedArrayDescriptor.h" #include "WeakRandom.h" #include <wtf/BumpPointerAllocator.h> #include <wtf/Forward.h> @@ -108,24 +109,6 @@ namespace JSC { ThreadStackTypeSmall }; - struct TypedArrayDescriptor { - TypedArrayDescriptor() - : m_classInfo(0) - , m_storageOffset(0) - , m_lengthOffset(0) - { - } - TypedArrayDescriptor(const ClassInfo* classInfo, size_t storageOffset, size_t lengthOffset) - : m_classInfo(classInfo) - , m_storageOffset(storageOffset) - , m_lengthOffset(lengthOffset) - { - } - const ClassInfo* m_classInfo; - size_t m_storageOffset; - size_t m_lengthOffset; - }; - #if ENABLE(DFG_JIT) class ConservativeRoots; @@ -429,6 +412,35 @@ namespace JSC { registerTypedArrayFunction(float32, Float32); registerTypedArrayFunction(float64, Float64); #undef registerTypedArrayFunction + + const TypedArrayDescriptor* typedArrayDescriptor(TypedArrayType type) const + { + switch (type) { + case TypedArrayNone: + return 0; + case TypedArrayInt8: + return &int8ArrayDescriptor(); + case TypedArrayInt16: + return &int16ArrayDescriptor(); + case TypedArrayInt32: + return &int32ArrayDescriptor(); + case TypedArrayUint8: + return &uint8ArrayDescriptor(); + case TypedArrayUint8Clamped: + return &uint8ClampedArrayDescriptor(); + case TypedArrayUint16: + return &uint16ArrayDescriptor(); + case TypedArrayUint32: + return &uint32ArrayDescriptor(); + case TypedArrayFloat32: + return &float32ArrayDescriptor(); + case TypedArrayFloat64: + return &float64ArrayDescriptor(); + default: + CRASH(); + return 0; + } + } JSLock& apiLock() { return m_apiLock; } diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp index b0a0e8122..9eb266135 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp @@ -100,8 +100,6 @@ const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &all @end */ -ASSERT_CLASS_FITS_IN_CELL(JSGlobalObject); - // Default number of ticks before a timeout check should be done. static const int initialTickCountThreshold = 255; @@ -228,10 +226,11 @@ void JSGlobalObject::reset(JSValue prototype) m_callbackFunctionStructure.set(exec->globalData(), this, JSCallbackFunction::createStructure(exec->globalData(), this, m_functionPrototype.get())); m_argumentsStructure.set(exec->globalData(), this, Arguments::createStructure(exec->globalData(), this, m_objectPrototype.get())); m_callbackConstructorStructure.set(exec->globalData(), this, JSCallbackConstructor::createStructure(exec->globalData(), this, m_objectPrototype.get())); - m_callbackObjectStructure.set(exec->globalData(), this, JSCallbackObject<JSNonFinalObject>::createStructure(exec->globalData(), this, m_objectPrototype.get())); + m_callbackObjectStructure.set(exec->globalData(), this, JSCallbackObject<JSDestructibleObject>::createStructure(exec->globalData(), this, m_objectPrototype.get())); m_arrayPrototype.set(exec->globalData(), this, ArrayPrototype::create(exec, this, ArrayPrototype::createStructure(exec->globalData(), this, m_objectPrototype.get()))); - m_arrayStructure.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithArrayStorage)); + m_arrayStructure.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithContiguous)); + m_arrayStructureWithArrayStorage.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithArrayStorage)); m_arrayStructureForSlowPut.set(exec->globalData(), this, JSArray::createStructure(exec->globalData(), this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage)); m_regExpMatchesArrayStructure.set(exec->globalData(), this, RegExpMatchesArray::createStructure(exec->globalData(), this, m_arrayPrototype.get())); @@ -318,6 +317,9 @@ void JSGlobalObject::reset(JSValue prototype) GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete | ReadOnly) }; addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals)); + + m_specialPointers[Special::CallFunction] = m_callFunction.get(); + m_specialPointers[Special::ApplyFunction] = m_applyFunction.get(); if (m_experimentsEnabled) { NamePrototype* privateNamePrototype = NamePrototype::create(exec, NamePrototype::createStructure(exec->globalData(), this, m_objectPrototype.get())); @@ -354,7 +356,8 @@ ObjectsWithBrokenIndexingFinder::ObjectsWithBrokenIndexingFinder( inline bool hasBrokenIndexing(JSObject* object) { // This will change if we have more indexing types. - return !!(object->structure()->indexingType() & HasArrayStorage); + IndexingType type = object->structure()->indexingType(); + return hasContiguous(type) || hasFastArrayStorage(type); } void ObjectsWithBrokenIndexingFinder::operator()(JSCell* cell) @@ -407,6 +410,7 @@ void JSGlobalObject::haveABadTime(JSGlobalData& globalData) // Make sure that all JSArray allocations that load the appropriate structure from // this object now load a structure that uses SlowPut. m_arrayStructure.set(globalData, this, m_arrayStructureForSlowPut.get()); + m_arrayStructureWithArrayStorage.set(globalData, this, m_arrayStructureForSlowPut.get()); // Make sure that all objects that have indexed storage switch to the slow kind of // indexed storage. @@ -482,6 +486,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_nameScopeStructure); visitor.append(&thisObject->m_argumentsStructure); visitor.append(&thisObject->m_arrayStructure); + visitor.append(&thisObject->m_arrayStructureWithArrayStorage); visitor.append(&thisObject->m_arrayStructureForSlowPut); visitor.append(&thisObject->m_booleanObjectStructure); visitor.append(&thisObject->m_callbackConstructorStructure); @@ -502,9 +507,14 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_internalFunctionStructure); } +JSObject* JSGlobalObject::toThisObject(JSCell* cell, ExecState*) +{ + return jsCast<JSGlobalObject*>(cell)->globalThis(); +} + ExecState* JSGlobalObject::globalExec() { - return CallFrame::create(m_globalCallFrame + RegisterFile::CallFrameHeaderSize); + return CallFrame::create(m_globalCallFrame + JSStack::CallFrameHeaderSize); } void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count) diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index ad56783cc..2994aa64b 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -24,10 +24,10 @@ #include "JSArray.h" #include "JSGlobalData.h" -#include "JSGlobalThis.h" #include "JSSegmentedVariableObject.h" #include "JSWeakObjectMapRefInternal.h" #include "NumberPrototype.h" +#include "SpecialPointer.h" #include "StringPrototype.h" #include "StructureChain.h" #include "Watchpoint.h" @@ -46,12 +46,12 @@ namespace JSC { class FunctionPrototype; class GetterSetter; class GlobalCodeBlock; + class JSStack; class LLIntOffsetsExtractor; class NativeErrorConstructor; class ProgramCodeBlock; class RegExpConstructor; class RegExpPrototype; - class RegisterFile; struct ActivationStackNode; struct HashTable; @@ -77,7 +77,6 @@ namespace JSC { class JSGlobalObject : public JSSegmentedVariableObject { private: - typedef JSSegmentedVariableObject Base; typedef HashSet<RefPtr<OpaqueJSWeakObjectMap> > WeakMapSet; struct JSGlobalObjectRareData { @@ -92,7 +91,7 @@ namespace JSC { protected: - Register m_globalCallFrame[RegisterFile::CallFrameHeaderSize]; + Register m_globalCallFrame[JSStack::CallFrameHeaderSize]; WriteBarrier<JSObject> m_globalThis; WriteBarrier<JSObject> m_methodCallDummy; @@ -127,6 +126,7 @@ namespace JSC { WriteBarrier<Structure> m_nameScopeStructure; WriteBarrier<Structure> m_argumentsStructure; WriteBarrier<Structure> m_arrayStructure; // This gets set to m_arrayStructureForSlowPut as soon as we decide to have a bad time. + WriteBarrier<Structure> m_arrayStructureWithArrayStorage; // This gets set to m_arrayStructureForSlowPut as soon as we decide to have a bad time. WriteBarrier<Structure> m_arrayStructureForSlowPut; WriteBarrier<Structure> m_booleanObjectStructure; WriteBarrier<Structure> m_callbackConstructorStructure; @@ -146,6 +146,8 @@ namespace JSC { WriteBarrier<Structure> m_regExpStructure; WriteBarrier<Structure> m_stringObjectStructure; WriteBarrier<Structure> m_internalFunctionStructure; + + void* m_specialPointers[Special::TableSize]; // Special pointers used by the LLInt and JIT. Debugger* m_debugger; @@ -168,14 +170,16 @@ namespace JSC { if (m_rareData) return; m_rareData = adoptPtr(new JSGlobalObjectRareData); - Heap::heap(this)->addFinalizer(this, clearRareData); } public: + typedef JSSegmentedVariableObject Base; + static JSGlobalObject* create(JSGlobalData& globalData, Structure* structure) { JSGlobalObject* globalObject = new (NotNull, allocateCell<JSGlobalObject>(globalData.heap)) JSGlobalObject(globalData, structure); globalObject->finishCreation(globalData); + globalData.heap.addFinalizer(globalObject, destroy); return globalObject; } @@ -192,7 +196,7 @@ namespace JSC { init(this); } - void finishCreation(JSGlobalData& globalData, JSGlobalThis* thisValue) + void finishCreation(JSGlobalData& globalData, JSObject* thisValue) { Base::finishCreation(globalData); structure()->setGlobalObject(globalData, this); @@ -203,6 +207,8 @@ namespace JSC { public: JS_EXPORT_PRIVATE ~JSGlobalObject(); JS_EXPORT_PRIVATE static void destroy(JSCell*); + // We don't need a destructor because we use a finalizer instead. + static const bool needsDestruction = false; JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); @@ -262,7 +268,9 @@ namespace JSC { Structure* nameScopeStructure() const { return m_nameScopeStructure.get(); } Structure* argumentsStructure() const { return m_argumentsStructure.get(); } Structure* arrayStructure() const { return m_arrayStructure.get(); } + Structure* arrayStructureWithArrayStorage() const { return m_arrayStructureWithArrayStorage.get(); } void* addressOfArrayStructure() { return &m_arrayStructure; } + void* addressOfArrayStructureWithArrayStorage() { return &m_arrayStructureWithArrayStorage; } Structure* booleanObjectStructure() const { return m_booleanObjectStructure.get(); } Structure* callbackConstructorStructure() const { return m_callbackConstructorStructure.get(); } Structure* callbackFunctionStructure() const { return m_callbackFunctionStructure.get(); } @@ -282,6 +290,12 @@ namespace JSC { Structure* regExpStructure() const { return m_regExpStructure.get(); } Structure* stringObjectStructure() const { return m_stringObjectStructure.get(); } + void* actualPointerFor(Special::Pointer pointer) + { + ASSERT(pointer < Special::TableSize); + return m_specialPointers[pointer]; + } + WatchpointSet* masqueradesAsUndefinedWatchpoint() { return m_masqueradesAsUndefinedWatchpoint.get(); } WatchpointSet* havingABadTimeWatchpoint() { return m_havingABadTimeWatchpoint.get(); } @@ -366,13 +380,16 @@ namespace JSC { }; JS_EXPORT_PRIVATE void addStaticGlobals(GlobalPropertyInfo*, int count); + JS_EXPORT_PRIVATE static JSC::JSObject* toThisObject(JSC::JSCell*, JSC::ExecState*); + + JS_EXPORT_PRIVATE void setGlobalThis(JSGlobalData&, JSObject* globalThis); + private: friend class LLIntOffsetsExtractor; // FIXME: Fold reset into init. JS_EXPORT_PRIVATE void init(JSObject* thisValue); void reset(JSValue prototype); - void setGlobalThis(JSGlobalData&, JSObject* globalThis); void createThrowTypeError(ExecState*); @@ -465,7 +482,7 @@ namespace JSC { inline JSArray* constructEmptyArray(ExecState* exec, JSGlobalObject* globalObject, unsigned initialLength = 0) { - return JSArray::create(exec->globalData(), globalObject->arrayStructure(), initialLength); + return JSArray::create(exec->globalData(), initialLength >= MIN_SPARSE_ARRAY_INDEX ? globalObject->arrayStructureWithArrayStorage() : globalObject->arrayStructure(), initialLength); } inline JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength = 0) diff --git a/Source/JavaScriptCore/runtime/JSGlobalThis.cpp b/Source/JavaScriptCore/runtime/JSGlobalThis.cpp deleted file mode 100644 index a3f2e7785..000000000 --- a/Source/JavaScriptCore/runtime/JSGlobalThis.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2011 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 "JSGlobalThis.h" - -#include "JSGlobalObject.h" - -namespace JSC { - -ASSERT_CLASS_FITS_IN_CELL(JSGlobalThis); -ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSGlobalThis); - -const ClassInfo JSGlobalThis::s_info = { "JSGlobalThis", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSGlobalThis) }; - -void JSGlobalThis::visitChildren(JSCell* cell, SlotVisitor& visitor) -{ - JSGlobalThis* thisObject = jsCast<JSGlobalThis*>(cell); - ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); - - COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); - ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); - - Base::visitChildren(thisObject, visitor); - visitor.append(&thisObject->m_unwrappedObject); -} - -void JSGlobalThis::setUnwrappedObject(JSGlobalData& globalData, JSGlobalObject* globalObject) -{ - ASSERT_ARG(globalObject, globalObject); - m_unwrappedObject.set(globalData, this, globalObject); - setPrototype(globalData, globalObject->prototype()); - resetInheritorID(globalData); -} - -} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSGlobalThis.h b/Source/JavaScriptCore/runtime/JSGlobalThis.h deleted file mode 100644 index 0ca99414a..000000000 --- a/Source/JavaScriptCore/runtime/JSGlobalThis.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2011 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 JSGlobalThis_h -#define JSGlobalThis_h - -#include "JSObject.h" - -namespace JSC { - -class JSGlobalThis : public JSNonFinalObject { -public: - typedef JSNonFinalObject Base; - - static JSGlobalThis* create(JSGlobalData& globalData, Structure* structure) - { - JSGlobalThis* globalThis = new (NotNull, allocateCell<JSGlobalThis>(globalData.heap)) JSGlobalThis(globalData, structure); - globalThis->finishCreation(globalData); - return globalThis; - } - - static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) - { - return Structure::create(globalData, 0, prototype, TypeInfo(GlobalThisType, StructureFlags), &s_info); - } - - static JS_EXPORTDATA const JSC::ClassInfo s_info; - - JSGlobalObject* unwrappedObject() const { return m_unwrappedObject.get(); } - -protected: - JSGlobalThis(JSGlobalData& globalData, Structure* structure) - : JSNonFinalObject(globalData, structure) - { - } - - void finishCreation(JSGlobalData& globalData) - { - Base::finishCreation(globalData); - } - - static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; - - JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); - - JS_EXPORT_PRIVATE void setUnwrappedObject(JSGlobalData&, JSGlobalObject*); - -private: - WriteBarrier<JSGlobalObject> m_unwrappedObject; -}; - -} // namespace JSC - -#endif // JSGlobalThis_h diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp index 9f02b69b8..85dbdfedb 100644 --- a/Source/JavaScriptCore/runtime/JSLock.cpp +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -140,13 +140,13 @@ bool JSLock::currentThreadIsHoldingLock() // context if the thread leaves JSC by making a call out to an external // function through a callback. // -// All threads using the context share the same JS stack (the RegisterFile). -// Whenever a thread calls into JSC it starts using the RegisterFile from the +// All threads using the context share the same JS stack (the JSStack). +// Whenever a thread calls into JSC it starts using the JSStack from the // previous 'high water mark' - the maximum point the stack has ever grown to -// (returned by RegisterFile::end()). So if a first thread calls out to a +// (returned by JSStack::end()). So if a first thread calls out to a // callback, and a second thread enters JSC, then also exits by calling out // to a callback, we can be left with stackframes from both threads in the -// RegisterFile. As such, a problem may occur should the first thread's +// JSStack. As such, a problem may occur should the first thread's // callback complete first, and attempt to return to JSC. Were we to allow // this to happen, and were its stack to grow further, then it may potentially // write over the second thread's call frames. diff --git a/Source/JavaScriptCore/runtime/JSNameScope.cpp b/Source/JavaScriptCore/runtime/JSNameScope.cpp index 5dc665c44..335631fd0 100644 --- a/Source/JavaScriptCore/runtime/JSNameScope.cpp +++ b/Source/JavaScriptCore/runtime/JSNameScope.cpp @@ -30,8 +30,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSNameScope); - const ClassInfo JSNameScope::s_info = { "NameScope", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSNameScope) }; void JSNameScope::visitChildren(JSCell* cell, SlotVisitor& visitor) diff --git a/Source/JavaScriptCore/runtime/JSNotAnObject.cpp b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp index f75fa1f96..53592ba2b 100644 --- a/Source/JavaScriptCore/runtime/JSNotAnObject.cpp +++ b/Source/JavaScriptCore/runtime/JSNotAnObject.cpp @@ -34,7 +34,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSNotAnObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSNotAnObject); const ClassInfo JSNotAnObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSNotAnObject) }; diff --git a/Source/JavaScriptCore/runtime/JSONObject.cpp b/Source/JavaScriptCore/runtime/JSONObject.cpp index fe894afba..fd939934e 100644 --- a/Source/JavaScriptCore/runtime/JSONObject.cpp +++ b/Source/JavaScriptCore/runtime/JSONObject.cpp @@ -41,7 +41,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSONObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSONObject); static EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*); diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index bf38f6876..6a3fb84e4 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -26,13 +26,14 @@ #include "ButterflyInlineMethods.h" #include "CopiedSpaceInlineMethods.h" +#include "CopyVisitor.h" +#include "CopyVisitorInlineMethods.h" #include "DatePrototype.h" #include "ErrorConstructor.h" #include "GetterSetter.h" #include "IndexingHeaderInlineMethods.h" #include "JSFunction.h" #include "JSGlobalObject.h" -#include "JSGlobalThis.h" #include "Lookup.h" #include "NativeErrorConstructor.h" #include "Nodes.h" @@ -63,10 +64,6 @@ JSCell* getCallableObjectSlow(JSCell* cell) return 0; } -ASSERT_CLASS_FITS_IN_CELL(JSObject); -ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject); -ASSERT_CLASS_FITS_IN_CELL(JSFinalObject); - ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFinalObject); @@ -95,7 +92,7 @@ static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* class } } -ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize) +ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butterfly, size_t storageSize) { ASSERT(butterfly); @@ -112,61 +109,93 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but preCapacity = 0; indexingPayloadSizeInBytes = 0; } - size_t capacityInBytes = Butterfly::totalSize( - preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); - if (visitor.checkIfShouldCopyAndPinOtherwise( - butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) { + size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + if (visitor.checkIfShouldCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) { Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); - // Mark and copy the properties. + // Copy the properties. PropertyStorage currentTarget = newButterfly->propertyStorage(); PropertyStorage currentSource = butterfly->propertyStorage(); - for (size_t count = storageSize; count--;) { - JSValue value = (--currentSource)->get(); - ASSERT(value); - visitor.appendUnbarrieredValue(&value); - (--currentTarget)->setWithoutWriteBarrier(value); - } + for (size_t count = storageSize; count--;) + (--currentTarget)->setWithoutWriteBarrier((--currentSource)->get()); if (UNLIKELY(hasIndexingHeader)) { *newButterfly->indexingHeader() = *butterfly->indexingHeader(); - // Mark and copy the array if appropriate. + // Copy the array if appropriate. + + WriteBarrier<Unknown>* currentTarget; + WriteBarrier<Unknown>* currentSource; + size_t count; + switch (structure->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: { + currentTarget = newButterfly->contiguous(); + currentSource = butterfly->contiguous(); + ASSERT(newButterfly->publicLength() <= newButterfly->vectorLength()); + count = newButterfly->vectorLength(); + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage()); - WriteBarrier<Unknown>* currentTarget = newButterfly->arrayStorage()->m_vector; - WriteBarrier<Unknown>* currentSource = butterfly->arrayStorage()->m_vector; - for (size_t count = newButterfly->arrayStorage()->vectorLength(); count--;) { - JSValue value = (currentSource++)->get(); - if (value) - visitor.appendUnbarrieredValue(&value); - (currentTarget++)->setWithoutWriteBarrier(value); - } - if (newButterfly->arrayStorage()->m_sparseMap) - visitor.append(&newButterfly->arrayStorage()->m_sparseMap); + currentTarget = newButterfly->arrayStorage()->m_vector; + currentSource = butterfly->arrayStorage()->m_vector; + count = newButterfly->arrayStorage()->vectorLength(); break; } default: + CRASH(); + currentTarget = 0; + currentSource = 0; + count = 0; break; } + + while (count--) + (currentTarget++)->setWithoutWriteBarrier((currentSource++)->get()); } m_butterfly = newButterfly; + visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes); + } +} + +ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize) +{ + ASSERT(butterfly); + + Structure* structure = this->structure(); + + size_t propertyCapacity = structure->outOfLineCapacity(); + size_t preCapacity; + size_t indexingPayloadSizeInBytes; + bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType()); + if (UNLIKELY(hasIndexingHeader)) { + preCapacity = butterfly->indexingHeader()->preCapacity(structure); + indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); } else { - // Mark the properties. - visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); - - // Mark the array if appropriate. - switch (structure->indexingType()) { - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); - if (butterfly->arrayStorage()->m_sparseMap) - visitor.append(&butterfly->arrayStorage()->m_sparseMap); - break; - default: - break; - } + preCapacity = 0; + indexingPayloadSizeInBytes = 0; + } + size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); + + // Mark the properties. + visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); + visitor.copyLater(butterfly->base(preCapacity, propertyCapacity), capacityInBytes); + + // Mark the array if appropriate. + switch (structure->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + visitor.appendValues(butterfly->contiguous(), butterfly->publicLength()); + break; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength()); + if (butterfly->arrayStorage()->m_sparseMap) + visitor.append(&butterfly->arrayStorage()->m_sparseMap); + break; + default: + break; } } @@ -183,13 +212,23 @@ void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) Butterfly* butterfly = thisObject->butterfly(); if (butterfly) - thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownNonFinalObject()); + thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); #if !ASSERT_DISABLED visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; #endif } +void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor) +{ + JSObject* thisObject = jsCast<JSObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + + Butterfly* butterfly = thisObject->butterfly(); + if (butterfly) + thisObject->copyButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); +} + void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell); @@ -203,9 +242,9 @@ void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) Butterfly* butterfly = thisObject->butterfly(); if (butterfly) - thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownFinalObject()); + thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); - size_t storageSize = thisObject->structure()->inlineSizeForKnownFinalObject(); + size_t storageSize = thisObject->structure()->inlineSize(); visitor.appendValues(thisObject->inlineStorage(), storageSize); #if !ASSERT_DISABLED @@ -235,6 +274,20 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned case ALL_BLANK_INDEXING_TYPES: break; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + + JSValue value = butterfly->contiguous()[i].get(); + if (value) { + slot.setValue(value); + return true; + } + + return false; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i >= storage->length()) @@ -249,7 +302,7 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { - it->second.get(slot); + it->value.get(slot); return true; } } @@ -352,6 +405,16 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, case ALL_BLANK_INDEXING_TYPES: break; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (propertyName >= butterfly->vectorLength()) + break; + butterfly->contiguous()[propertyName].set(exec->globalData(), thisObject, value); + if (propertyName >= butterfly->publicLength()) + butterfly->setPublicLength(propertyName + 1); + return; + } + case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -426,7 +489,7 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists // This will always be a new entry in the map, so no need to check we can write, // and attributes are default so no need to set them. if (value) - map->add(this, i).iterator->second.set(globalData, this, value); + map->add(this, i).iterator->value.set(globalData, this, value); } Butterfly* newButterfly = storage->butterfly()->resizeArray(globalData, structure(), 0, ArrayStorage::sizeFor(0)); @@ -444,6 +507,11 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize + // this case if we ever cared. + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + break; case ALL_ARRAY_STORAGE_INDEXING_TYPES: enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); break; @@ -466,6 +534,24 @@ void JSObject::notifyPresenceOfIndexedAccessors(JSGlobalData& globalData) globalObject()->haveABadTime(globalData); } +WriteBarrier<Unknown>* JSObject::createInitialContiguous(JSGlobalData& globalData, unsigned length) +{ + ASSERT(length < MAX_ARRAY_INDEX); + IndexingType oldType = structure()->indexingType(); + ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); + ASSERT(!structure()->needsSlowPutIndexing()); + ASSERT(!indexingShouldBeSparse()); + unsigned vectorLength = std::max(length, BASE_VECTOR_LEN); + Butterfly* newButterfly = m_butterfly->growArrayRight( + globalData, structure(), structure()->outOfLineCapacity(), false, 0, + sizeof(EncodedJSValue) * vectorLength); + newButterfly->setPublicLength(length); + newButterfly->setVectorLength(vectorLength); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous); + setButterfly(globalData, newButterfly, newStructure); + return newButterfly->contiguous(); +} + ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned length, unsigned vectorLength) { IndexingType oldType = structure()->indexingType(); @@ -481,7 +567,7 @@ ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned le result->m_sparseMap.clear(); result->m_numValuesInVector = 0; result->m_indexBias = 0; - Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), structure()->suggestedIndexingTransition()); + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), structure()->suggestedArrayStorageTransition()); setButterfly(globalData, newButterfly, newStructure); return result; } @@ -491,9 +577,107 @@ ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData) return createArrayStorage(globalData, 0, BASE_VECTOR_LEN); } +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength) +{ + ASSERT(hasContiguous(structure()->indexingType())); + + unsigned publicLength = m_butterfly->publicLength(); + unsigned propertyCapacity = structure()->outOfLineCapacity(); + unsigned propertySize = structure()->outOfLineSize(); + + Butterfly* newButterfly = Butterfly::createUninitialized( + globalData, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength)); + + memcpy( + newButterfly->propertyStorage() - propertySize, + m_butterfly->propertyStorage() - propertySize, + propertySize * sizeof(EncodedJSValue)); + + ArrayStorage* newStorage = newButterfly->arrayStorage(); + newStorage->setVectorLength(neededLength); + newStorage->setLength(publicLength); + newStorage->m_sparseMap.clear(); + newStorage->m_indexBias = 0; + newStorage->m_numValuesInVector = 0; + for (unsigned i = publicLength; i--;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (!v) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; + } + + Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), transition); + setButterfly(globalData, newButterfly, newStructure); + return newStorage; +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition) +{ + return convertContiguousToArrayStorage(globalData, transition, m_butterfly->vectorLength()); +} + +ArrayStorage* JSObject::convertContiguousToArrayStorage(JSGlobalData& globalData) +{ + return convertContiguousToArrayStorage(globalData, structure()->suggestedArrayStorageTransition()); +} + +WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + return 0; + return createInitialContiguous(globalData, 0); + + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +ArrayStorage* JSObject::ensureArrayStorageSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + ASSERT(!indexingShouldBeSparse()); + ASSERT(!structure()->needsSlowPutIndexing()); + return convertContiguousToArrayStorage(globalData); + + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(indexingShouldBeSparse())) + return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData); + return createInitialArrayStorage(globalData); + + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +Butterfly* JSObject::ensureIndexedStorageSlow(JSGlobalData& globalData) +{ + switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + if (UNLIKELY(structure()->needsSlowPutIndexing())) + return createInitialArrayStorage(globalData)->butterfly(); + if (UNLIKELY(indexingShouldBeSparse())) + return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)->butterfly(); + return Butterfly::fromContiguous(createInitialContiguous(globalData, 0)); + + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + // FIXME: This could be made way more efficient, if we cared. + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, convertContiguousToArrayStorage(globalData)); + case ALL_ARRAY_STORAGE_INDEXING_TYPES: return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage()); @@ -513,6 +697,11 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(J void JSObject::switchToSlowPutArrayStorage(JSGlobalData& globalData) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: { + convertContiguousToArrayStorage(globalData, AllocateSlowPutArrayStorage); + break; + } + case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), SwitchToSlowPutArrayStorage); @@ -556,8 +745,7 @@ void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype) if (shouldUseSlowPut(structure()->indexingType())) return; - newStructure = Structure::nonPropertyTransition(globalData, newStructure, SwitchToSlowPutArrayStorage); - setStructure(globalData, newStructure); + switchToSlowPutArrayStorage(globalData); } bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype) @@ -691,6 +879,14 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) case ALL_BLANK_INDEXING_TYPES: return true; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = thisObject->m_butterfly; + if (i >= butterfly->vectorLength()) + return true; + butterfly->contiguous()[i].clear(); + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); @@ -703,7 +899,7 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { - if (it->second.attributes & DontDelete) + if (it->value.attributes & DontDelete) return false; map->remove(it); } @@ -864,6 +1060,17 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa case ALL_BLANK_INDEXING_TYPES: break; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + if (!butterfly->contiguous()[i]) + continue; + propertyNames.add(Identifier::from(exec, i)); + } + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); @@ -879,8 +1086,8 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { - if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum)) - keys.append(static_cast<unsigned>(it->first)); + if (mode == IncludeDontEnumProperties || !(it->value.attributes & DontEnum)) + keys.append(static_cast<unsigned>(it->key)); } std::sort(keys.begin(), keys.end()); @@ -1089,9 +1296,6 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, Propert { ASSERT(index <= MAX_ARRAY_INDEX); - if (descriptor.attributes() & (ReadOnly | Accessor)) - notifyPresenceOfIndexedAccessors(exec->globalData()); - if (!inSparseIndexingMode()) { // Fast case: we're putting a regular property to a regular array // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false @@ -1101,16 +1305,19 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, Propert ASSERT(!descriptor.isAccessorDescriptor()); return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); } - + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->globalData()); } + if (descriptor.attributes() & (ReadOnly | Accessor)) + notifyPresenceOfIndexedAccessors(exec->globalData()); + SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get(); ASSERT(map); // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. SparseArrayValueMap::AddResult result = map->add(this, index); - SparseArrayEntry* entryInMap = &result.iterator->second; + SparseArrayEntry* entryInMap = &result.iterator->value; // 2. Let extensible be the value of the [[Extensible]] internal property of O. // 3. If current is undefined and extensible is false, then Reject. @@ -1230,8 +1437,8 @@ bool JSObject::attemptToInterceptPutByIndexOnHoleForPrototype(ExecState* exec, J ArrayStorage* storage = current->arrayStorageOrNull(); if (storage && storage->m_sparseMap) { SparseArrayValueMap::iterator iter = storage->m_sparseMap->find(i); - if (iter != storage->m_sparseMap->notFound() && (iter->second.attributes & (Accessor | ReadOnly))) { - iter->second.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow); + if (iter != storage->m_sparseMap->notFound() && (iter->value.attributes & (Accessor | ReadOnly))) { + iter->value.put(exec, thisValue, storage->m_sparseMap.get(), value, shouldThrow); return true; } } @@ -1253,6 +1460,35 @@ bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, J return asObject(prototypeValue)->attemptToInterceptPutByIndexOnHoleForPrototype(exec, this, i, value, shouldThrow); } +void JSObject::putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState* exec, unsigned i, JSValue value) +{ + ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(!indexingShouldBeSparse()); + + // For us to get here, the index is either greater than the public length, or greater than + // or equal to the vector length. + ASSERT(i >= m_butterfly->vectorLength()); + + JSGlobalData& globalData = exec->globalData(); + + if (i >= MAX_ARRAY_INDEX - 1 + || (i >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(i, countElementsInContiguous(m_butterfly)))) { + ASSERT(i <= MAX_ARRAY_INDEX); + convertContiguousToArrayStorage(globalData, AllocateArrayStorage); + SparseArrayValueMap* map = allocateSparseIndexMap(globalData); + map->putEntry(exec, this, i, value, false); + ASSERT(i >= arrayStorage()->length()); + arrayStorage()->setLength(i + 1); + return; + } + + ensureContiguousLength(globalData, i + 1); + + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(globalData, this, value); +} + void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) { JSGlobalData& globalData = exec->globalData(); @@ -1315,7 +1551,7 @@ void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns WriteBarrier<Unknown>* vector = storage->m_vector; SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) - vector[it->first].set(globalData, this, it->second.getNonSparseMode()); + vector[it->key].set(globalData, this, it->value.getNonSparseMode()); deallocateSparseIndexMap(); // Store the new property into the vector. @@ -1335,17 +1571,29 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse()) { - putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); + putByIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, shouldThrow, + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); + break; + } + if (i >= MIN_SPARSE_ARRAY_INDEX) { + putByIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0)); break; } - if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) { - putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0)); + if (structure()->needsSlowPutIndexing()) { + ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); + storage->m_vector[i].set(globalData, this, value); + storage->m_numValuesInVector++; break; } + + createInitialContiguous(globalData, i + 1)[i].set(globalData, this, value); + break; + } - ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); - storage->m_vector[i].set(globalData, this, value); - storage->m_numValuesInVector = 1; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); break; } @@ -1371,8 +1619,10 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage) { JSGlobalData& globalData = exec->globalData(); - + // i should be a valid array index that is outside of the current vector. + ASSERT(hasArrayStorage(structure()->indexingType())); + ASSERT(arrayStorage() == storage); ASSERT(i >= storage->vectorLength() || attributes); ASSERT(i <= MAX_ARRAY_INDEX); @@ -1431,7 +1681,7 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, WriteBarrier<Unknown>* vector = storage->m_vector; SparseArrayValueMap::const_iterator end = map->end(); for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) - vector[it->first].set(globalData, this, it->second.getNonSparseMode()); + vector[it->key].set(globalData, this, it->value.getNonSparseMode()); deallocateSparseIndexMap(); // Store the new property into the vector. @@ -1454,14 +1704,32 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: { - if (indexingShouldBeSparse() || attributes) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); - if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0)); + if (indexingShouldBeSparse() || attributes) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, + ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData)); + } + if (i >= MIN_SPARSE_ARRAY_INDEX) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0)); + } + if (structure()->needsSlowPutIndexing()) { + ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); + storage->m_vector[i].set(globalData, this, value); + storage->m_numValuesInVector++; + return true; + } - ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1)); - storage->m_vector[i].set(globalData, this, value); - storage->m_numValuesInVector = 1; + createInitialContiguous(globalData, i + 1)[i].set(globalData, this, value); + return true; + } + + case ALL_CONTIGUOUS_INDEXING_TYPES: { + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertContiguousToArrayStorage(globalData)); + } + putByIndexBeyondVectorLengthContiguousWithoutAttributes(exec, i, value); return true; } @@ -1486,12 +1754,7 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength else if (!currentVectorLength) increasedLength = std::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); + increasedLength = timesThreePlusOneDividedByTwo(desiredLength); } ASSERT(increasedLength >= desiredLength); @@ -1511,9 +1774,10 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) vectorLength = 0; length = 0; break; + case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: - vectorLength = m_butterfly->arrayStorage()->vectorLength(); - length = m_butterfly->arrayStorage()->length(); + vectorLength = m_butterfly->vectorLength(); + length = m_butterfly->publicLength(); break; default: CRASH(); @@ -1522,6 +1786,16 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) return getNewVectorLength(vectorLength, length, desiredLength); } +unsigned JSObject::countElementsInContiguous(Butterfly* butterfly) +{ + unsigned numValues = 0; + for (unsigned i = butterfly->publicLength(); i--;) { + if (butterfly->contiguous()[i]) + numValues++; + } + return numValues; +} + bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength) { // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map @@ -1530,6 +1804,10 @@ bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength return false; ArrayStorage* storage = arrayStorage(); + + if (newLength >= MIN_SPARSE_ARRAY_INDEX + && !isDenseEnoughForVector(newLength, storage->m_numValuesInVector)) + return false; unsigned indexBias = storage->m_indexBias; unsigned vectorLength = storage->vectorLength(); @@ -1561,6 +1839,22 @@ bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength return true; } +void JSObject::ensureContiguousLengthSlow(JSGlobalData& globalData, unsigned length) +{ + ASSERT(length < MAX_ARRAY_INDEX); + ASSERT(hasContiguous(structure()->indexingType())); + ASSERT(length > m_butterfly->vectorLength()); + + unsigned newVectorLength = std::min( + length << 1, + MAX_STORAGE_VECTOR_LENGTH); + m_butterfly = m_butterfly->growArrayRight( + globalData, structure(), structure()->outOfLineCapacity(), true, + m_butterfly->vectorLength() * sizeof(EncodedJSValue), + newVectorLength * sizeof(EncodedJSValue)); + m_butterfly->setVectorLength(newVectorLength); +} + Butterfly* JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize) { ASSERT(newSize > oldSize); @@ -1589,6 +1883,17 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope case ALL_BLANK_INDEXING_TYPES: return false; + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = object->m_butterfly; + if (i >= butterfly->vectorLength()) + return false; + JSValue value = butterfly->contiguous()[i].get(); + if (!value) + return false; + descriptor.setDescriptor(value, 0); + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = object->m_butterfly->arrayStorage(); if (i >= storage->length()) @@ -1604,7 +1909,7 @@ bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, Prope SparseArrayValueMap::iterator it = map->find(i); if (it == map->notFound()) return false; - it->second.get(descriptor); + it->value.get(descriptor); return true; } return false; diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 4b9cff5ad..9204099cb 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/runtime/JSObject.h @@ -109,7 +109,13 @@ namespace JSC { public: typedef JSCell Base; + static size_t allocationSize(size_t inlineCapacity) + { + return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>); + } + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&); JS_EXPORT_PRIVATE static String className(const JSObject*); @@ -148,8 +154,9 @@ namespace JSC { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: return 0; + case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->length(); + return m_butterfly->publicLength(); default: ASSERT_NOT_REACHED(); return 0; @@ -161,8 +168,9 @@ namespace JSC { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: return 0; + case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->vectorLength(); + return m_butterfly->vectorLength(); default: ASSERT_NOT_REACHED(); return 0; @@ -207,6 +215,8 @@ namespace JSC { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: return false; + case ALL_CONTIGUOUS_INDEXING_TYPES: + return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i]; case ALL_ARRAY_STORAGE_INDEXING_TYPES: return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; default: @@ -218,6 +228,8 @@ namespace JSC { JSValue getIndexQuickly(unsigned i) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + return m_butterfly->contiguous()[i].get(); case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->m_vector[i].get(); default: @@ -231,12 +243,13 @@ namespace JSC { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: break; + case ALL_CONTIGUOUS_INDEXING_TYPES: + if (i < m_butterfly->publicLength()) + return m_butterfly->contiguous()[i].get(); + break; case ALL_ARRAY_STORAGE_INDEXING_TYPES: - if (i < m_butterfly->arrayStorage()->vectorLength()) { - JSValue v = m_butterfly->arrayStorage()->m_vector[i].get(); - if (v) - return v; - } + if (i < m_butterfly->arrayStorage()->vectorLength()) + return m_butterfly->arrayStorage()->m_vector[i].get(); break; default: ASSERT_NOT_REACHED(); @@ -267,9 +280,10 @@ namespace JSC { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: return false; + case ALL_CONTIGUOUS_INDEXING_TYPES: case NonArrayWithArrayStorage: case ArrayWithArrayStorage: - return i < m_butterfly->arrayStorage()->vectorLength(); + return i < m_butterfly->vectorLength(); case NonArrayWithSlowPutArrayStorage: case ArrayWithSlowPutArrayStorage: return i < m_butterfly->arrayStorage()->vectorLength() @@ -285,8 +299,9 @@ namespace JSC { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: return false; + case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return i < m_butterfly->arrayStorage()->vectorLength(); + return i < m_butterfly->vectorLength(); default: ASSERT_NOT_REACHED(); return false; @@ -296,15 +311,23 @@ namespace JSC { void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(globalData, this, v); + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); + break; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - WriteBarrier<Unknown>& x = m_butterfly->arrayStorage()->m_vector[i]; - if (!x) { - ArrayStorage* storage = m_butterfly->arrayStorage(); + ArrayStorage* storage = m_butterfly->arrayStorage(); + WriteBarrier<Unknown>& x = storage->m_vector[i]; + JSValue old = x.get(); + x.set(globalData, this, v); + if (!old) { ++storage->m_numValuesInVector; if (i >= storage->length()) storage->setLength(i + 1); } - x.set(globalData, this, v); break; } default: @@ -315,6 +338,12 @@ namespace JSC { void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v) { switch (structure()->indexingType()) { + case ALL_CONTIGUOUS_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(globalData, this, v); + break; + } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { ArrayStorage* storage = m_butterfly->arrayStorage(); ASSERT(i < storage->length()); @@ -327,10 +356,25 @@ namespace JSC { } } + bool hasSparseMap() + { + switch (structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return false; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->m_sparseMap; + default: + ASSERT_NOT_REACHED(); + return false; + } + } + bool inSparseIndexingMode() { switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: return m_butterfly->arrayStorage()->inSparseMode(); @@ -448,10 +492,10 @@ namespace JSC { { PropertyOffset result; size_t offsetInInlineStorage = location - inlineStorageUnsafe(); - if (offsetInInlineStorage < static_cast<size_t>(inlineStorageCapacity)) + if (offsetInInlineStorage < static_cast<size_t>(firstOutOfLineOffset)) result = offsetInInlineStorage; else - result = outOfLineStorage() - location + (inlineStorageCapacity - 1); + result = outOfLineStorage() - location + (firstOutOfLineOffset - 1); validateOffset(result, structure()->typeInfo().type()); return result; } @@ -481,7 +525,7 @@ namespace JSC { bool isNameScopeObject() const; bool isActivationObject() const; bool isErrorInstance() const; - bool isGlobalThis() const; + bool isProxy() const; void seal(JSGlobalData&); void freeze(JSGlobalData&); @@ -528,23 +572,34 @@ namespace JSC { // foo->attemptToInterceptPutByIndexOnHole(...); bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + // Returns 0 if contiguous storage cannot be created - either because + // indexing should be sparse or because we're having a bad time. + WriteBarrier<Unknown>* ensureContiguous(JSGlobalData& globalData) + { + if (LIKELY(hasContiguous(structure()->indexingType()))) + return m_butterfly->contiguous(); + + return ensureContiguousSlow(globalData); + } + // Ensure that the object is in a mode where it has array storage. Use // this if you're about to perform actions that would have required the // object to be converted to have array storage, if it didn't have it // already. ArrayStorage* ensureArrayStorage(JSGlobalData& globalData) { - switch (structure()->indexingType()) { - case ALL_ARRAY_STORAGE_INDEXING_TYPES: + if (LIKELY(hasArrayStorage(structure()->indexingType()))) return m_butterfly->arrayStorage(); - - case ALL_BLANK_INDEXING_TYPES: - return createInitialArrayStorage(globalData); - - default: - ASSERT_NOT_REACHED(); - return 0; - } + + return ensureArrayStorageSlow(globalData); + } + + Butterfly* ensureIndexedStorage(JSGlobalData& globalData) + { + if (LIKELY(hasIndexedProperties(structure()->indexingType()))) + return m_butterfly; + + return ensureIndexedStorageSlow(globalData); } static size_t offsetOfInlineStorage(); @@ -585,6 +640,7 @@ namespace JSC { void resetInheritorID(JSGlobalData&); void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize); + void copyButterfly(CopyVisitor&, Butterfly*, size_t storageSize); // Call this if you know that the object is in a mode where it has array // storage. This will assert otherwise. @@ -609,11 +665,16 @@ namespace JSC { ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength); ArrayStorage* createInitialArrayStorage(JSGlobalData&); + WriteBarrier<Unknown>* createInitialContiguous(JSGlobalData&, unsigned length); + ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength); + ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition); + ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&); ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&); bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException); + void putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState*, unsigned propertyName, JSValue); void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); bool increaseVectorLength(JSGlobalData&, unsigned newLength); @@ -624,6 +685,56 @@ namespace JSC { void notifyPresenceOfIndexedAccessors(JSGlobalData&); bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow); + + // Call this if you want setIndexQuickly to succeed and you're sure that + // the array is contiguous. + void ensureContiguousLength(JSGlobalData& globalData, unsigned length) + { + ASSERT(length < MAX_ARRAY_INDEX); + ASSERT(hasContiguous(structure()->indexingType())); + + if (m_butterfly->vectorLength() < length) + ensureContiguousLengthSlow(globalData, length); + + if (m_butterfly->publicLength() < length) + m_butterfly->setPublicLength(length); + } + + unsigned countElementsInContiguous(Butterfly*); + + template<IndexingType indexingType> + WriteBarrier<Unknown>* indexingData() + { + switch (indexingType) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + return m_butterfly->contiguous(); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->m_vector; + + default: + CRASH(); + return 0; + } + } + + template<IndexingType indexingType> + unsigned relevantLength() + { + switch (indexingType) { + case ALL_CONTIGUOUS_INDEXING_TYPES: + return m_butterfly->publicLength(); + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return std::min( + m_butterfly->arrayStorage()->length(), + m_butterfly->arrayStorage()->vectorLength()); + + default: + CRASH(); + return 0; + } + } private: friend class LLIntOffsetsExtractor; @@ -658,6 +769,12 @@ namespace JSC { JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&); + void ensureContiguousLengthSlow(JSGlobalData&, unsigned length); + + WriteBarrier<Unknown>* ensureContiguousSlow(JSGlobalData&); + ArrayStorage* ensureArrayStorageSlow(JSGlobalData&); + Butterfly* ensureIndexedStorageSlow(JSGlobalData&); + protected: Butterfly* m_butterfly; }; @@ -677,11 +794,6 @@ namespace JSC { return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); } - static bool hasInlineStorage() - { - return false; - } - protected: explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly = 0) : JSObject(globalData, structure, butterfly) @@ -709,45 +821,43 @@ namespace JSC { static JSFinalObject* create(ExecState*, Structure*); static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) { - return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info); + return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info, NonArray, INLINE_STORAGE_CAPACITY); } JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); static JS_EXPORTDATA const ClassInfo s_info; - static bool hasInlineStorage() - { - return true; - } protected: void visitChildrenCommon(SlotVisitor&); void finishCreation(JSGlobalData& globalData) { Base::finishCreation(globalData); - ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double))); - ASSERT(this->structure()->inlineCapacity() == static_cast<unsigned>(inlineStorageCapacity)); - ASSERT(this->structure()->totalStorageCapacity() == static_cast<unsigned>(inlineStorageCapacity)); + ASSERT(structure()->totalStorageCapacity() == structure()->inlineCapacity()); ASSERT(classInfo()); } private: friend class LLIntOffsetsExtractor; - + explicit JSFinalObject(JSGlobalData& globalData, Structure* structure) : JSObject(globalData, structure) { } static const unsigned StructureFlags = JSObject::StructureFlags; - - WriteBarrierBase<Unknown> m_inlineStorage[INLINE_STORAGE_CAPACITY]; }; inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structure) { - JSFinalObject* finalObject = new (NotNull, allocateCell<JSFinalObject>(*exec->heap())) JSFinalObject(exec->globalData(), structure); + JSFinalObject* finalObject = new ( + NotNull, + allocateCell<JSFinalObject>( + *exec->heap(), + allocationSize(structure->inlineCapacity()) + ) + ) JSFinalObject(exec->globalData(), structure); finalObject->finishCreation(exec->globalData()); return finalObject; } @@ -764,7 +874,7 @@ inline bool isJSFinalObject(JSValue value) inline size_t JSObject::offsetOfInlineStorage() { - return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage); + return sizeof(JSObject); } inline bool JSObject::isGlobalObject() const @@ -792,9 +902,9 @@ inline bool JSObject::isErrorInstance() const return structure()->typeInfo().type() == ErrorInstanceType; } -inline bool JSObject::isGlobalThis() const +inline bool JSObject::isProxy() const { - return structure()->typeInfo().type() == GlobalThisType; + return structure()->typeInfo().type() == ProxyType; } inline void JSObject::setButterfly(JSGlobalData& globalData, Butterfly* butterfly, Structure* structure) @@ -856,14 +966,14 @@ inline JSValue JSObject::prototype() const return structure()->storedPrototype(); } -inline bool JSCell::inherits(const ClassInfo* info) const +inline const MethodTable* JSCell::methodTable() const { - return classInfo()->isSubClassOf(info); + return &classInfo()->methodTable; } -inline const MethodTable* JSCell::methodTable() const +inline bool JSCell::inherits(const ClassInfo* info) const { - return &classInfo()->methodTable; + return classInfo()->isSubClassOf(info); } // this method is here to be after the inline declaration of JSCell::inherits @@ -1257,6 +1367,8 @@ inline int offsetRelativeToBase(PropertyOffset offset) return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue); } +COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment); + } // namespace JSC #endif // JSObject_h diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp index 897ceff8c..225401fbd 100644 --- a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -33,8 +33,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSPropertyNameIterator); - const ClassInfo JSPropertyNameIterator::s_info = { "JSPropertyNameIterator", 0, 0, 0, CREATE_METHOD_TABLE(JSPropertyNameIterator) }; inline JSPropertyNameIterator::JSPropertyNameIterator(ExecState* exec, PropertyNameArrayData* propertyNameArrayData, size_t numCacheableSlots) diff --git a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h index 057ffe293..e59a5c6a4 100644 --- a/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h +++ b/Source/JavaScriptCore/runtime/JSPropertyNameIterator.h @@ -48,6 +48,8 @@ namespace JSC { static JSPropertyNameIterator* create(ExecState*, JSObject*); + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; static void destroy(JSCell*); static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) @@ -57,14 +59,6 @@ namespace JSC { static void visitChildren(JSCell*, SlotVisitor&); - bool getOffset(size_t i, PropertyOffset& offset) - { - if (i >= m_numCacheableSlots) - return false; - offset = i + m_offsetBase; - return true; - } - JSValue get(ExecState*, JSObject*, size_t i); size_t size() { return m_jsStringsSize; } @@ -88,7 +82,7 @@ namespace JSC { PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArrayData->propertyNameVector(); for (size_t i = 0; i < m_jsStringsSize; ++i) m_jsStrings[i].set(exec->globalData(), this, jsOwnedString(exec, propertyNameVector[i].string())); - m_offsetBase = object->structure()->firstValidOffset(); + m_cachedStructureInlineCapacity = object->structure()->inlineCapacity(); } private: @@ -100,7 +94,7 @@ namespace JSC { WriteBarrier<StructureChain> m_cachedPrototypeChain; uint32_t m_numCacheableSlots; uint32_t m_jsStringsSize; - PropertyOffset m_offsetBase; + unsigned m_cachedStructureInlineCapacity; OwnArrayPtr<WriteBarrier<Unknown> > m_jsStrings; }; diff --git a/Source/JavaScriptCore/runtime/JSProxy.cpp b/Source/JavaScriptCore/runtime/JSProxy.cpp new file mode 100644 index 000000000..7108dcfa0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSProxy.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2011, 2012 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 "JSProxy.h" + +#include "JSGlobalObject.h" + +namespace JSC { + +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSProxy); + +const ClassInfo JSProxy::s_info = { "JSProxy", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSProxy) }; + +void JSProxy::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_target); +} + +void JSProxy::setTarget(JSGlobalData& globalData, JSGlobalObject* globalObject) +{ + ASSERT_ARG(globalObject, globalObject); + m_target.set(globalData, this, globalObject); + setPrototype(globalData, globalObject->prototype()); + resetInheritorID(globalData); +} + +String JSProxy::className(const JSObject* object) +{ + const JSProxy* thisObject = jsCast<const JSProxy*>(object); + return thisObject->target()->methodTable()->className(thisObject->target()); +} + +bool JSProxy::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + return thisObject->target()->methodTable()->getOwnPropertySlot(thisObject->target(), exec, propertyName, slot); +} + +bool JSProxy::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + return thisObject->target()->methodTable()->getOwnPropertySlotByIndex(thisObject->target(), exec, propertyName, slot); +} + +bool JSProxy::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + return thisObject->target()->methodTable()->getOwnPropertyDescriptor(thisObject->target(), exec, propertyName, descriptor); +} + +void JSProxy::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + thisObject->target()->methodTable()->put(thisObject->target(), exec, propertyName, value, slot); +} + +void JSProxy::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + thisObject->target()->methodTable()->putByIndex(thisObject->target(), exec, propertyName, value, shouldThrow); +} + +void JSProxy::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + thisObject->target()->putDirectVirtual(thisObject->target(), exec, propertyName, value, attributes); +} + +bool JSProxy::defineOwnProperty(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, JSC::PropertyDescriptor& descriptor, bool shouldThrow) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + return thisObject->target()->methodTable()->defineOwnProperty(thisObject->target(), exec, propertyName, descriptor, shouldThrow); +} + +bool JSProxy::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + return thisObject->target()->methodTable()->deleteProperty(thisObject->target(), exec, propertyName); +} + +bool JSProxy::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) +{ + JSProxy* thisObject = jsCast<JSProxy*>(cell); + return thisObject->target()->methodTable()->deletePropertyByIndex(thisObject->target(), exec, propertyName); +} + +void JSProxy::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + thisObject->target()->methodTable()->getPropertyNames(thisObject->target(), exec, propertyNames, mode); +} + +void JSProxy::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSProxy* thisObject = jsCast<JSProxy*>(object); + thisObject->target()->methodTable()->getOwnPropertyNames(thisObject->target(), exec, propertyNames, mode); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSProxy.h b/Source/JavaScriptCore/runtime/JSProxy.h new file mode 100644 index 000000000..144085a79 --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSProxy.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011 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 JSProxy_h +#define JSProxy_h + +#include "JSDestructibleObject.h" + +namespace JSC { + +class JSProxy : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static JSProxy* create(JSGlobalData& globalData, Structure* structure, JSObject* target) + { + JSProxy* proxy = new (NotNull, allocateCell<JSProxy>(globalData.heap)) JSProxy(globalData, structure); + proxy->finishCreation(globalData, target); + return proxy; + } + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ProxyType, StructureFlags), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; + + JSObject* target() const { return m_target.get(); } + +protected: + JSProxy(JSGlobalData& globalData, Structure* structure) + : JSDestructibleObject(globalData, structure) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData); + } + + void finishCreation(JSGlobalData& globalData, JSObject* target) + { + Base::finishCreation(globalData); + m_target.set(globalData, this, target); + } + + static const unsigned StructureFlags = OverridesVisitChildren | OverridesGetOwnPropertySlot | OverridesGetPropertyNames | Base::StructureFlags; + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + + JS_EXPORT_PRIVATE void setTarget(JSGlobalData&, JSGlobalObject*); + + JS_EXPORT_PRIVATE static String className(const JSObject*); + JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&); + JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned, JSValue, bool shouldThrow); + JS_EXPORT_PRIVATE static void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes); + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned); + JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow); + +private: + WriteBarrier<JSObject> m_target; +}; + +} // namespace JSC + +#endif // JSProxy_h diff --git a/Source/JavaScriptCore/runtime/JSScope.cpp b/Source/JavaScriptCore/runtime/JSScope.cpp index b22211970..8fd49b861 100644 --- a/Source/JavaScriptCore/runtime/JSScope.cpp +++ b/Source/JavaScriptCore/runtime/JSScope.cpp @@ -33,7 +33,7 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSScope); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSScope); void JSScope::visitChildren(JSCell* cell, SlotVisitor& visitor) { diff --git a/Source/JavaScriptCore/runtime/JSString.h b/Source/JavaScriptCore/runtime/JSString.h index 1500636f2..245c48a51 100644 --- a/Source/JavaScriptCore/runtime/JSString.h +++ b/Source/JavaScriptCore/runtime/JSString.h @@ -71,6 +71,8 @@ namespace JSC { typedef JSCell Base; + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; static void destroy(JSCell*); private: diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp b/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp index 765e1d3d4..7dcde4700 100644 --- a/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp +++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp @@ -61,8 +61,8 @@ void JSSymbolTableObject::getOwnNonIndexPropertyNames(JSObject* object, ExecStat JSSymbolTableObject* thisObject = jsCast<JSSymbolTableObject*>(object); SymbolTable::const_iterator end = thisObject->symbolTable()->end(); for (SymbolTable::const_iterator it = thisObject->symbolTable()->begin(); it != end; ++it) { - if (!(it->second.getAttributes() & DontEnum) || (mode == IncludeDontEnumProperties)) - propertyNames.add(Identifier(exec, it->first.get())); + if (!(it->value.getAttributes() & DontEnum) || (mode == IncludeDontEnumProperties)) + propertyNames.add(Identifier(exec, it->key.get())); } JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); diff --git a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h index b4d313c19..913679f80 100644 --- a/Source/JavaScriptCore/runtime/JSSymbolTableObject.h +++ b/Source/JavaScriptCore/runtime/JSSymbolTableObject.h @@ -76,7 +76,7 @@ inline bool symbolTableGet( SymbolTable::iterator iter = symbolTable.find(propertyName.publicName()); if (iter == symbolTable.end()) return false; - SymbolTableEntry::Fast entry = iter->second; + SymbolTableEntry::Fast entry = iter->value; ASSERT(!entry.isNull()); slot.setValue(object->registerAt(entry.getIndex()).get()); return true; @@ -90,7 +90,7 @@ inline bool symbolTableGet( SymbolTable::iterator iter = symbolTable.find(propertyName.publicName()); if (iter == symbolTable.end()) return false; - SymbolTableEntry::Fast entry = iter->second; + SymbolTableEntry::Fast entry = iter->value; ASSERT(!entry.isNull()); descriptor.setDescriptor( object->registerAt(entry.getIndex()).get(), entry.getAttributes() | DontDelete); @@ -106,7 +106,7 @@ inline bool symbolTableGet( SymbolTable::iterator iter = symbolTable.find(propertyName.publicName()); if (iter == symbolTable.end()) return false; - SymbolTableEntry::Fast entry = iter->second; + SymbolTableEntry::Fast entry = iter->value; ASSERT(!entry.isNull()); slot.setValue(object->registerAt(entry.getIndex()).get()); slotIsWriteable = !entry.isReadOnly(); @@ -126,7 +126,7 @@ inline bool symbolTablePut( if (iter == symbolTable.end()) return false; bool wasFat; - SymbolTableEntry::Fast fastEntry = iter->second.getFast(wasFat); + SymbolTableEntry::Fast fastEntry = iter->value.getFast(wasFat); ASSERT(!fastEntry.isNull()); if (fastEntry.isReadOnly()) { if (shouldThrow) @@ -134,7 +134,7 @@ inline bool symbolTablePut( return true; } if (UNLIKELY(wasFat)) - iter->second.notifyWrite(); + iter->value.notifyWrite(); object->registerAt(fastEntry.getIndex()).set(globalData, object, value); return true; } @@ -149,7 +149,7 @@ inline bool symbolTablePutWithAttributes( SymbolTable::iterator iter = object->symbolTable()->find(propertyName.publicName()); if (iter == object->symbolTable()->end()) return false; - SymbolTableEntry& entry = iter->second; + SymbolTableEntry& entry = iter->value; ASSERT(!entry.isNull()); entry.notifyWrite(); entry.setAttributes(attributes); diff --git a/Source/JavaScriptCore/runtime/JSType.h b/Source/JavaScriptCore/runtime/JSType.h index b8ab330f5..03f4a7790 100644 --- a/Source/JavaScriptCore/runtime/JSType.h +++ b/Source/JavaScriptCore/runtime/JSType.h @@ -48,7 +48,7 @@ enum JSType { NameInstanceType, NumberObjectType, ErrorInstanceType, - GlobalThisType, + ProxyType, WithScopeType, NameScopeObjectType, diff --git a/Source/JavaScriptCore/runtime/JSValue.cpp b/Source/JavaScriptCore/runtime/JSValue.cpp index ac00fad3d..651e50cec 100644 --- a/Source/JavaScriptCore/runtime/JSValue.cpp +++ b/Source/JavaScriptCore/runtime/JSValue.cpp @@ -195,7 +195,7 @@ void JSValue::putToPrimitiveByIndex(ExecState* exec, unsigned propertyName, JSVa char* JSValue::description() const { - static const size_t size = 128; + static const size_t size = 256; static char description[size]; if (!*this) @@ -213,9 +213,12 @@ char* JSValue::description() const u.asDouble = asDouble(); snprintf(description, size, "Double: %08x:%08x, %lf", u.asTwoInt32s[1], u.asTwoInt32s[0], asDouble()); #endif - } else if (isCell()) - snprintf(description, size, "Cell: %p", asCell()); - else if (isTrue()) + } else if (isCell()) { + snprintf( + description, size, "Cell: %p (%p: %s, %s)", + asCell(), asCell()->structure(), asCell()->structure()->classInfo()->className, + indexingTypeToString(asCell()->structure()->indexingTypeIncludingHistory())); + } else if (isTrue()) snprintf(description, size, "True"); else if (isFalse()) snprintf(description, size, "False"); diff --git a/Source/JavaScriptCore/runtime/JSVariableObject.h b/Source/JavaScriptCore/runtime/JSVariableObject.h index 55952820e..25961dc09 100644 --- a/Source/JavaScriptCore/runtime/JSVariableObject.h +++ b/Source/JavaScriptCore/runtime/JSVariableObject.h @@ -68,7 +68,7 @@ namespace JSC { { } - WriteBarrierBase<Unknown>* m_registers; // "r" in the register file. + WriteBarrierBase<Unknown>* m_registers; // "r" in the stack. }; } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSWithScope.cpp b/Source/JavaScriptCore/runtime/JSWithScope.cpp index 0c4b6e2cc..7d74e63c3 100644 --- a/Source/JavaScriptCore/runtime/JSWithScope.cpp +++ b/Source/JavaScriptCore/runtime/JSWithScope.cpp @@ -28,8 +28,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSWithScope); - const ClassInfo JSWithScope::s_info = { "WithScope", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSWithScope) }; void JSWithScope::visitChildren(JSCell* cell, SlotVisitor& visitor) diff --git a/Source/JavaScriptCore/runtime/JSWrapperObject.cpp b/Source/JavaScriptCore/runtime/JSWrapperObject.cpp index 4a46c2c69..ff80c1e20 100644 --- a/Source/JavaScriptCore/runtime/JSWrapperObject.cpp +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.cpp @@ -24,7 +24,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(JSWrapperObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSWrapperObject); void JSWrapperObject::visitChildren(JSCell* cell, SlotVisitor& visitor) diff --git a/Source/JavaScriptCore/runtime/JSWrapperObject.h b/Source/JavaScriptCore/runtime/JSWrapperObject.h index 65b4bdb7f..72bc1874c 100644 --- a/Source/JavaScriptCore/runtime/JSWrapperObject.h +++ b/Source/JavaScriptCore/runtime/JSWrapperObject.h @@ -22,15 +22,15 @@ #ifndef JSWrapperObject_h #define JSWrapperObject_h -#include "JSObject.h" +#include "JSDestructibleObject.h" namespace JSC { // This class is used as a base for classes such as String, // Number, Boolean and Date which are wrappers for primitive types. - class JSWrapperObject : public JSNonFinalObject { + class JSWrapperObject : public JSDestructibleObject { public: - typedef JSNonFinalObject Base; + typedef JSDestructibleObject Base; JSValue internalValue() const; void setInternalValue(JSGlobalData&, JSValue); @@ -42,7 +42,7 @@ namespace JSC { protected: explicit JSWrapperObject(JSGlobalData&, Structure*); - static const unsigned StructureFlags = OverridesVisitChildren | JSNonFinalObject::StructureFlags; + static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags; static void visitChildren(JSCell*, SlotVisitor&); @@ -51,7 +51,7 @@ namespace JSC { }; inline JSWrapperObject::JSWrapperObject(JSGlobalData& globalData, Structure* structure) - : JSNonFinalObject(globalData, structure) + : JSDestructibleObject(globalData, structure) { } diff --git a/Source/JavaScriptCore/runtime/MathObject.cpp b/Source/JavaScriptCore/runtime/MathObject.cpp index 2a550a38b..2f4df375a 100644 --- a/Source/JavaScriptCore/runtime/MathObject.cpp +++ b/Source/JavaScriptCore/runtime/MathObject.cpp @@ -32,7 +32,7 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(MathObject); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(MathObject); static EncodedJSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState*); static EncodedJSValue JSC_HOST_CALL mathProtoFuncACos(ExecState*); @@ -59,9 +59,7 @@ static EncodedJSValue JSC_HOST_CALL mathProtoFuncTan(ExecState*); namespace JSC { -ASSERT_HAS_TRIVIAL_DESTRUCTOR(MathObject); - -const ClassInfo MathObject::s_info = { "Math", &JSNonFinalObject::s_info, 0, ExecState::mathTable, CREATE_METHOD_TABLE(MathObject) }; +const ClassInfo MathObject::s_info = { "Math", &Base::s_info, 0, ExecState::mathTable, CREATE_METHOD_TABLE(MathObject) }; /* Source for MathObject.lut.h @begin mathTable diff --git a/Source/JavaScriptCore/runtime/MemoryStatistics.cpp b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp index b35e9fbda..0f8efc604 100644 --- a/Source/JavaScriptCore/runtime/MemoryStatistics.cpp +++ b/Source/JavaScriptCore/runtime/MemoryStatistics.cpp @@ -28,7 +28,7 @@ #include "ExecutableAllocator.h" #include "JSGlobalData.h" -#include "RegisterFile.h" +#include "JSStack.h" namespace JSC { @@ -36,7 +36,7 @@ GlobalMemoryStatistics globalMemoryStatistics() { GlobalMemoryStatistics stats; - stats.stackBytes = RegisterFile::committedByteCount(); + stats.stackBytes = JSStack::committedByteCount(); #if ENABLE(EXECUTABLE_ALLOCATOR_FIXED) || ((PLATFORM(BLACKBERRY) || PLATFORM(EFL)) && ENABLE(JIT)) stats.JITBytes = ExecutableAllocator::committedByteCount(); #else diff --git a/Source/JavaScriptCore/runtime/NameConstructor.cpp b/Source/JavaScriptCore/runtime/NameConstructor.cpp index 63f1f647a..b5facc7cf 100644 --- a/Source/JavaScriptCore/runtime/NameConstructor.cpp +++ b/Source/JavaScriptCore/runtime/NameConstructor.cpp @@ -31,7 +31,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(NameConstructor); ASSERT_HAS_TRIVIAL_DESTRUCTOR(NameConstructor); const ClassInfo NameConstructor::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(NameConstructor) }; diff --git a/Source/JavaScriptCore/runtime/NameInstance.h b/Source/JavaScriptCore/runtime/NameInstance.h index c5931e8ef..129e7c407 100644 --- a/Source/JavaScriptCore/runtime/NameInstance.h +++ b/Source/JavaScriptCore/runtime/NameInstance.h @@ -26,14 +26,14 @@ #ifndef NameInstance_h #define NameInstance_h -#include "JSObject.h" +#include "JSDestructibleObject.h" #include "PrivateName.h" namespace JSC { -class NameInstance : public JSNonFinalObject { +class NameInstance : public JSDestructibleObject { public: - typedef JSNonFinalObject Base; + typedef JSDestructibleObject Base; static const ClassInfo s_info; diff --git a/Source/JavaScriptCore/runtime/NamePrototype.cpp b/Source/JavaScriptCore/runtime/NamePrototype.cpp index 3e52856b6..f14e58522 100644 --- a/Source/JavaScriptCore/runtime/NamePrototype.cpp +++ b/Source/JavaScriptCore/runtime/NamePrototype.cpp @@ -30,8 +30,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(NamePrototype); - static EncodedJSValue JSC_HOST_CALL privateNameProtoFuncToString(ExecState*); } @@ -48,8 +46,6 @@ const ClassInfo NamePrototype::s_info = { "Name", &Base::s_info, 0, ExecState::p @end */ -ASSERT_CLASS_FITS_IN_CELL(NamePrototype); - NamePrototype::NamePrototype(ExecState* exec, Structure* structure) : Base(exec->globalData(), structure, jsEmptyString(exec)) { diff --git a/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp index a4ba240fd..1f1730805 100644 --- a/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp +++ b/Source/JavaScriptCore/runtime/NativeErrorConstructor.cpp @@ -28,7 +28,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(NativeErrorConstructor); ASSERT_HAS_TRIVIAL_DESTRUCTOR(NativeErrorConstructor); const ClassInfo NativeErrorConstructor::s_info = { "Function", &InternalFunction::s_info, 0, 0, CREATE_METHOD_TABLE(NativeErrorConstructor) }; diff --git a/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp b/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp index 7fee213fa..296a86a22 100644 --- a/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp +++ b/Source/JavaScriptCore/runtime/NativeErrorPrototype.cpp @@ -27,8 +27,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(NativeErrorPrototype); - NativeErrorPrototype::NativeErrorPrototype(ExecState* exec, Structure* structure) : ErrorPrototype(exec, structure) { diff --git a/Source/JavaScriptCore/runtime/NumberConstructor.cpp b/Source/JavaScriptCore/runtime/NumberConstructor.cpp index 03d616073..daa643da7 100644 --- a/Source/JavaScriptCore/runtime/NumberConstructor.cpp +++ b/Source/JavaScriptCore/runtime/NumberConstructor.cpp @@ -28,8 +28,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(NumberConstructor); - static JSValue numberConstructorNaNValue(ExecState*, JSValue, PropertyName); static JSValue numberConstructorNegInfinity(ExecState*, JSValue, PropertyName); static JSValue numberConstructorPosInfinity(ExecState*, JSValue, PropertyName); diff --git a/Source/JavaScriptCore/runtime/NumberObject.cpp b/Source/JavaScriptCore/runtime/NumberObject.cpp index 1fea25464..b87753d4d 100644 --- a/Source/JavaScriptCore/runtime/NumberObject.cpp +++ b/Source/JavaScriptCore/runtime/NumberObject.cpp @@ -27,7 +27,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(NumberObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(NumberObject); const ClassInfo NumberObject::s_info = { "Number", &JSWrapperObject::s_info, 0, 0, CREATE_METHOD_TABLE(NumberObject) }; diff --git a/Source/JavaScriptCore/runtime/NumberPrototype.cpp b/Source/JavaScriptCore/runtime/NumberPrototype.cpp index 4a10efd6d..23c9dbfdd 100644 --- a/Source/JavaScriptCore/runtime/NumberPrototype.cpp +++ b/Source/JavaScriptCore/runtime/NumberPrototype.cpp @@ -68,7 +68,6 @@ const ClassInfo NumberPrototype::s_info = { "Number", &NumberObject::s_info, 0, @end */ -ASSERT_CLASS_FITS_IN_CELL(NumberPrototype); ASSERT_HAS_TRIVIAL_DESTRUCTOR(NumberPrototype); NumberPrototype::NumberPrototype(ExecState* exec, Structure* structure) diff --git a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp index 8614b9c45..7df047d28 100644 --- a/Source/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/Source/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -35,8 +35,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); - static EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*); static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); diff --git a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp index b1a5b9fb3..e94edfadf 100644 --- a/Source/JavaScriptCore/runtime/ObjectPrototype.cpp +++ b/Source/JavaScriptCore/runtime/ObjectPrototype.cpp @@ -63,8 +63,6 @@ const ClassInfo ObjectPrototype::s_info = { "Object", &JSNonFinalObject::s_info, @end */ -ASSERT_CLASS_FITS_IN_CELL(ObjectPrototype); - ObjectPrototype::ObjectPrototype(ExecState* exec, Structure* stucture) : JSNonFinalObject(exec->globalData(), stucture) { diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp index ed0720b54..386eb4fcf 100644 --- a/Source/JavaScriptCore/runtime/Options.cpp +++ b/Source/JavaScriptCore/runtime/Options.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "Options.h" +#include "HeapStatistics.h" #include <algorithm> #include <limits> #include <stdio.h> @@ -41,10 +42,6 @@ #include <sys/sysctl.h> #endif -// Set to 1 to control the heuristics using environment variables. -#define ENABLE_RUN_TIME_HEURISTICS 0 - - namespace JSC { static bool parse(const char* string, bool& value) @@ -75,10 +72,10 @@ static bool parse(const char* string, double& value) return sscanf(string, "%lf", &value) == 1; } -#if ENABLE(RUN_TIME_HEURISTICS) template<typename T> void overrideOptionWithHeuristic(T& variable, const char* name) { +#if !OS(WINCE) const char* stringValue = getenv(name); if (!stringValue) return; @@ -87,9 +84,8 @@ void overrideOptionWithHeuristic(T& variable, const char* name) return; fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue); -} #endif - +} static unsigned computeNumberOfGCMarkers(int maxNumberOfGCMarkers) { @@ -130,17 +126,19 @@ void Options::initialize() #if USE(CF) || OS(UNIX) objectsAreImmortal() = !!getenv("JSImmortalZombieEnabled"); useZombieMode() = !!getenv("JSImmortalZombieEnabled") || !!getenv("JSZombieEnabled"); + + gcMaxHeapSize() = getenv("GCMaxHeapSize") ? HeapStatistics::parseMemoryAmount(getenv("GCMaxHeapSize")) : 0; + recordGCPauseTimes() = !!getenv("JSRecordGCPauseTimes"); + logHeapStatisticsAtExit() = gcMaxHeapSize() || recordGCPauseTimes(); #endif // Allow environment vars to override options if applicable. // The evn var should be the name of the option prefixed with // "JSC_". -#if ENABLE(RUN_TIME_HEURISTICS) #define FOR_EACH_OPTION(type_, name_, defaultValue_) \ overrideOptionWithHeuristic(name_(), "JSC_" #name_); JSC_OPTIONS(FOR_EACH_OPTION) #undef FOR_EACH_OPTION -#endif // RUN_TIME_HEURISTICS #if 0 ; // Deconfuse editors that do auto indentation @@ -153,7 +151,7 @@ void Options::initialize() #if !ENABLE(YARR_JIT) useRegExpJIT() = false; #endif - + // Do range checks where needed and make corrections to the options: ASSERT(thresholdForOptimizeAfterLongWarmUp() >= thresholdForOptimizeAfterWarmUp()); ASSERT(thresholdForOptimizeAfterWarmUp() >= thresholdForOptimizeSoon()); diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h index 7571f9138..d6d8c66c8 100644 --- a/Source/JavaScriptCore/runtime/Options.h +++ b/Source/JavaScriptCore/runtime/Options.h @@ -51,9 +51,8 @@ namespace JSC { // purposes, you can do so in Options::initialize() after the default values // are set. // -// Alternatively, you can enable RUN_TIME_HEURISTICS which will allow you -// to override the default values by specifying environment variables of the -// form: JSC_<name of JSC option>. +// Alternatively, you can override the default values by specifying +// environment variables of the form: JSC_<name of JSC option>. // // Note: Options::initialize() tries to ensure some sanity on the option values // which are set by doing some range checks, and value corrections. These @@ -116,14 +115,19 @@ namespace JSC { v(unsigned, gcMarkStackSegmentSize, pageSize()) \ v(unsigned, numberOfGCMarkers, computeNumberOfGCMarkers(7)) \ v(unsigned, opaqueRootMergeThreshold, 1000) \ + v(double, minHeapUtilization, 0.8) \ + v(double, minCopiedBlockUtilization, 0.9) \ \ v(bool, forceWeakRandomSeed, false) \ v(unsigned, forcedWeakRandomSeed, 0) \ \ v(bool, useZombieMode, false) \ v(bool, objectsAreImmortal, false) \ - v(bool, showHeapStatistics, false) - + v(bool, showObjectStatistics, false) \ + \ + v(unsigned, gcMaxHeapSize, 0) \ + v(bool, recordGCPauseTimes, false) \ + v(bool, logHeapStatisticsAtExit, false) class Options { public: @@ -161,7 +165,7 @@ private: boolType, unsignedType, doubleType, - int32Type + int32Type, }; // For storing for an option value: diff --git a/Source/JavaScriptCore/runtime/PropertyMapHashTable.h b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h index 2d0f27a3e..9c6ddb20f 100644 --- a/Source/JavaScriptCore/runtime/PropertyMapHashTable.h +++ b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h @@ -178,7 +178,7 @@ public: PropertyOffset getDeletedOffset(); void addDeletedOffset(PropertyOffset); - PropertyOffset nextOffset(JSType); + PropertyOffset nextOffset(PropertyOffset inlineCapacity); // Copy this PropertyTable, ensuring the copy has at least the capacity provided. PassOwnPtr<PropertyTable> copy(JSGlobalData&, JSCell* owner, unsigned newCapacity); @@ -486,15 +486,12 @@ inline void PropertyTable::addDeletedOffset(PropertyOffset offset) m_deletedOffsets->append(offset); } -inline PropertyOffset PropertyTable::nextOffset(JSType type) +inline PropertyOffset PropertyTable::nextOffset(PropertyOffset inlineCapacity) { if (hasDeletedOffset()) return getDeletedOffset(); - - if (type == FinalObjectType) - return size(); - - return size() + firstOutOfLineOffset; + + return propertyOffsetFor(size(), inlineCapacity); } inline PassOwnPtr<PropertyTable> PropertyTable::copy(JSGlobalData& globalData, JSCell* owner, unsigned newCapacity) diff --git a/Source/JavaScriptCore/runtime/PropertyOffset.h b/Source/JavaScriptCore/runtime/PropertyOffset.h index 2aea2981e..1a2bba446 100644 --- a/Source/JavaScriptCore/runtime/PropertyOffset.h +++ b/Source/JavaScriptCore/runtime/PropertyOffset.h @@ -26,7 +26,6 @@ #ifndef PropertyOffset_h #define PropertyOffset_h -#include "JSType.h" #include <wtf/Platform.h> #include <wtf/StdLibExtras.h> #include <wtf/UnusedParam.h> @@ -42,14 +41,13 @@ namespace JSC { typedef int PropertyOffset; static const PropertyOffset invalidOffset = -1; -static const PropertyOffset inlineStorageCapacity = INLINE_STORAGE_CAPACITY; -static const PropertyOffset firstOutOfLineOffset = inlineStorageCapacity; +static const PropertyOffset firstOutOfLineOffset = 100; // Declare all of the functions because they tend to do forward calls. inline void checkOffset(PropertyOffset); -inline void checkOffset(PropertyOffset, JSType); +inline void checkOffset(PropertyOffset, PropertyOffset inlineCapacity); inline void validateOffset(PropertyOffset); -inline void validateOffset(PropertyOffset, JSType); +inline void validateOffset(PropertyOffset, PropertyOffset inlineCapacity); inline bool isValidOffset(PropertyOffset); inline bool isInlineOffset(PropertyOffset); inline bool isOutOfLineOffset(PropertyOffset); @@ -57,9 +55,7 @@ inline size_t offsetInInlineStorage(PropertyOffset); inline size_t offsetInOutOfLineStorage(PropertyOffset); inline size_t offsetInRespectiveStorage(PropertyOffset); inline size_t numberOfOutOfLineSlotsForLastOffset(PropertyOffset); -inline size_t numberOfSlotsForLastOffset(PropertyOffset, JSType); -inline PropertyOffset nextPropertyOffsetFor(PropertyOffset, JSType); -inline PropertyOffset firstPropertyOffsetFor(JSType); +inline size_t numberOfSlotsForLastOffset(PropertyOffset, PropertyOffset inlineCapacity); inline void checkOffset(PropertyOffset offset) { @@ -67,14 +63,14 @@ inline void checkOffset(PropertyOffset offset) ASSERT(offset >= invalidOffset); } -inline void checkOffset(PropertyOffset offset, JSType type) +inline void checkOffset(PropertyOffset offset, PropertyOffset inlineCapacity) { UNUSED_PARAM(offset); - UNUSED_PARAM(type); + UNUSED_PARAM(inlineCapacity); ASSERT(offset >= invalidOffset); ASSERT(offset == invalidOffset - || type == FinalObjectType - || isOutOfLineOffset(offset)); + || offset < inlineCapacity + || isOutOfLineOffset(offset)); } inline void validateOffset(PropertyOffset offset) @@ -83,9 +79,9 @@ inline void validateOffset(PropertyOffset offset) ASSERT(isValidOffset(offset)); } -inline void validateOffset(PropertyOffset offset, JSType type) +inline void validateOffset(PropertyOffset offset, PropertyOffset inlineCapacity) { - checkOffset(offset, type); + checkOffset(offset, inlineCapacity); ASSERT(isValidOffset(offset)); } @@ -98,7 +94,7 @@ inline bool isValidOffset(PropertyOffset offset) inline bool isInlineOffset(PropertyOffset offset) { checkOffset(offset); - return offset < inlineStorageCapacity; + return offset < firstOutOfLineOffset; } inline bool isOutOfLineOffset(PropertyOffset offset) @@ -136,28 +132,24 @@ inline size_t numberOfOutOfLineSlotsForLastOffset(PropertyOffset offset) return offset - firstOutOfLineOffset + 1; } -inline size_t numberOfSlotsForLastOffset(PropertyOffset offset, JSType type) +inline size_t numberOfSlotsForLastOffset(PropertyOffset offset, PropertyOffset inlineCapacity) { - checkOffset(offset, type); - if (type == FinalObjectType) + checkOffset(offset, inlineCapacity); + if (offset < inlineCapacity) return offset + 1; - return numberOfOutOfLineSlotsForLastOffset(offset); + return inlineCapacity + numberOfOutOfLineSlotsForLastOffset(offset); } -inline PropertyOffset nextPropertyOffsetFor(PropertyOffset offset, JSType type) +inline PropertyOffset propertyOffsetFor(PropertyOffset propertyNumber, PropertyOffset inlineCapacity) { - checkOffset(offset, type); - if (type != FinalObjectType && offset == invalidOffset) - return firstOutOfLineOffset; - return offset + 1; -} - -inline PropertyOffset firstPropertyOffsetFor(JSType type) -{ - return nextPropertyOffsetFor(invalidOffset, type); + PropertyOffset offset = propertyNumber; + if (offset >= inlineCapacity) { + offset += firstOutOfLineOffset; + offset -= inlineCapacity; + } + return offset; } } // namespace JSC #endif // PropertyOffset_h - diff --git a/Source/JavaScriptCore/runtime/RegExp.h b/Source/JavaScriptCore/runtime/RegExp.h index 287444b95..b2b65d90c 100644 --- a/Source/JavaScriptCore/runtime/RegExp.h +++ b/Source/JavaScriptCore/runtime/RegExp.h @@ -47,6 +47,8 @@ namespace JSC { typedef JSCell Base; JS_EXPORT_PRIVATE static RegExp* create(JSGlobalData&, const String& pattern, RegExpFlags); + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; static void destroy(JSCell*); bool global() const { return m_flags & FlagGlobal; } diff --git a/Source/JavaScriptCore/runtime/RegExpCache.cpp b/Source/JavaScriptCore/runtime/RegExpCache.cpp index c67dab8e6..8acafba23 100644 --- a/Source/JavaScriptCore/runtime/RegExpCache.cpp +++ b/Source/JavaScriptCore/runtime/RegExpCache.cpp @@ -80,7 +80,7 @@ void RegExpCache::invalidateCode() RegExpCacheMap::iterator end = m_weakCache.end(); for (RegExpCacheMap::iterator it = m_weakCache.begin(); it != end; ++it) { - RegExp* regExp = it->second.get(); + RegExp* regExp = it->value.get(); if (!regExp) // Skip zombies. continue; regExp->invalidateCode(); diff --git a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp index b8c4cd0b3..cc6ceb1d1 100644 --- a/Source/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/Source/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -53,8 +53,6 @@ static void setRegExpConstructorMultiline(ExecState*, JSObject*, JSValue); namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(RegExpConstructor); - const ClassInfo RegExpConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::regExpConstructorTable, CREATE_METHOD_TABLE(RegExpConstructor) }; /* Source for RegExpConstructor.lut.h diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp index 04fea60e8..ce9c2d2db 100644 --- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp +++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp @@ -30,8 +30,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(RegExpMatchesArray); - const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)}; RegExpMatchesArray::RegExpMatchesArray(JSGlobalData& globalData, Butterfly* butterfly, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result) diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp index bed44f22c..dfbf533f7 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.cpp +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -49,9 +49,9 @@ static JSValue regExpObjectSource(ExecState*, JSValue, PropertyName); namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(RegExpObject); +ASSERT_HAS_TRIVIAL_DESTRUCTOR(RegExpObject); -const ClassInfo RegExpObject::s_info = { "RegExp", &JSNonFinalObject::s_info, 0, ExecState::regExpTable, CREATE_METHOD_TABLE(RegExpObject) }; +const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, 0, ExecState::regExpTable, CREATE_METHOD_TABLE(RegExpObject) }; /* Source for RegExpObject.lut.h @begin regExpTable diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp index 3c742a0d3..e4bf2cf9a 100644 --- a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp +++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp @@ -59,8 +59,6 @@ const ClassInfo RegExpPrototype::s_info = { "RegExp", &RegExpObject::s_info, 0, @end */ -ASSERT_CLASS_FITS_IN_CELL(RegExpPrototype); - RegExpPrototype::RegExpPrototype(JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) : RegExpObject(globalObject, structure, regExp) { diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp index b9ba25735..7f21e2c9f 100644 --- a/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp +++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp @@ -88,7 +88,7 @@ SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSObject* array, unsigne void SparseArrayValueMap::putEntry(ExecState* exec, JSObject* array, unsigned i, JSValue value, bool shouldThrow) { AddResult result = add(array, i); - SparseArrayEntry& entry = result.iterator->second; + SparseArrayEntry& entry = result.iterator->value; // To save a separate find & add, we first always add to the sparse map. // In the uncommon case that this is a new property, and the array is not @@ -106,7 +106,7 @@ void SparseArrayValueMap::putEntry(ExecState* exec, JSObject* array, unsigned i, bool SparseArrayValueMap::putDirect(ExecState* exec, JSObject* array, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode) { AddResult result = add(array, i); - SparseArrayEntry& entry = result.iterator->second; + SparseArrayEntry& entry = result.iterator->value; // To save a separate find & add, we first always add to the sparse map. // In the uncommon case that this is a new property, and the array is not @@ -207,7 +207,7 @@ void SparseArrayValueMap::visitChildren(JSCell* thisObject, SlotVisitor& visitor SparseArrayValueMap* thisMap = jsCast<SparseArrayValueMap*>(thisObject); iterator end = thisMap->m_map.end(); for (iterator it = thisMap->m_map.begin(); it != end; ++it) - visitor.append(&it->second); + visitor.append(&it->value); } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/SparseArrayValueMap.h b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h index 5d8d0577a..366a7b8ba 100644 --- a/Source/JavaScriptCore/runtime/SparseArrayValueMap.h +++ b/Source/JavaScriptCore/runtime/SparseArrayValueMap.h @@ -81,6 +81,8 @@ public: static SparseArrayValueMap* create(JSGlobalData&); + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; static void destroy(JSCell*); static Structure* createStructure(JSGlobalData&, JSGlobalObject*, JSValue prototype); diff --git a/Source/JavaScriptCore/runtime/StringConstructor.cpp b/Source/JavaScriptCore/runtime/StringConstructor.cpp index 460343761..7f36a84be 100644 --- a/Source/JavaScriptCore/runtime/StringConstructor.cpp +++ b/Source/JavaScriptCore/runtime/StringConstructor.cpp @@ -45,7 +45,6 @@ const ClassInfo StringConstructor::s_info = { "Function", &InternalFunction::s_i @end */ -ASSERT_CLASS_FITS_IN_CELL(StringConstructor); ASSERT_HAS_TRIVIAL_DESTRUCTOR(StringConstructor); StringConstructor::StringConstructor(JSGlobalObject* globalObject, Structure* structure) diff --git a/Source/JavaScriptCore/runtime/StringObject.cpp b/Source/JavaScriptCore/runtime/StringObject.cpp index 15900913d..ab7d6cb23 100644 --- a/Source/JavaScriptCore/runtime/StringObject.cpp +++ b/Source/JavaScriptCore/runtime/StringObject.cpp @@ -27,7 +27,6 @@ namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(StringObject); ASSERT_HAS_TRIVIAL_DESTRUCTOR(StringObject); const ClassInfo StringObject::s_info = { "String", &JSWrapperObject::s_info, 0, 0, CREATE_METHOD_TABLE(StringObject) }; diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp index 8daa0f335..1540177be 100644 --- a/Source/JavaScriptCore/runtime/StringPrototype.cpp +++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp @@ -47,7 +47,6 @@ using namespace WTF; namespace JSC { -ASSERT_CLASS_FITS_IN_CELL(StringPrototype); ASSERT_HAS_TRIVIAL_DESTRUCTOR(StringPrototype); static EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*); diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp index a59a0860d..a931def27 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -149,7 +149,7 @@ void Structure::dumpStatistics() #endif } -Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType) +Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, PropertyOffset inlineCapacity) : JSCell(globalData, globalData.structureStructure.get()) , m_typeInfo(typeInfo) , m_indexingType(indexingType) @@ -158,6 +158,7 @@ Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSV , m_classInfo(classInfo) , m_transitionWatchpointSet(InitializedWatching) , m_outOfLineCapacity(0) + , m_inlineCapacity(inlineCapacity) , m_offset(invalidOffset) , m_dictionaryKind(NoneDictionaryKind) , m_isPinnedPropertyTable(false) @@ -182,6 +183,7 @@ Structure::Structure(JSGlobalData& globalData) , m_classInfo(&s_info) , m_transitionWatchpointSet(InitializedWatching) , m_outOfLineCapacity(0) + , m_inlineCapacity(0) , m_offset(invalidOffset) , m_dictionaryKind(NoneDictionaryKind) , m_isPinnedPropertyTable(false) @@ -204,6 +206,7 @@ Structure::Structure(JSGlobalData& globalData, const Structure* previous) , m_classInfo(previous->m_classInfo) , m_transitionWatchpointSet(InitializedWatching) , m_outOfLineCapacity(previous->m_outOfLineCapacity) + , m_inlineCapacity(previous->m_inlineCapacity) , m_offset(invalidOffset) , m_dictionaryKind(previous->m_dictionaryKind) , m_isPinnedPropertyTable(false) @@ -323,11 +326,15 @@ bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const } } -NonPropertyTransition Structure::suggestedIndexingTransition() const +bool Structure::needsSlowPutIndexing() const { - ASSERT(!hasIndexedProperties(indexingType())); - - if (anyObjectInChainMayInterceptIndexedAccesses() || globalObject()->isHavingABadTime()) + return anyObjectInChainMayInterceptIndexedAccesses() + || globalObject()->isHavingABadTime(); +} + +NonPropertyTransition Structure::suggestedArrayStorageTransition() const +{ + if (needsSlowPutIndexing()) return AllocateSlowPutArrayStorage; return AllocateArrayStorage; @@ -546,6 +553,7 @@ Structure* Structure::nonPropertyTransition(JSGlobalData& globalData, Structure* transition->m_previous.set(globalData, transition, structure); transition->m_attributesInPrevious = attributes; transition->m_indexingType = indexingType; + transition->m_offset = structure->m_offset; if (structure->m_propertyTable) { if (structure->m_isPinnedPropertyTable) @@ -608,20 +616,21 @@ Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObj ASSERT(m_propertyTable); size_t propertyCount = m_propertyTable->size(); + + // Holds our values compacted by insertion order. Vector<JSValue> values(propertyCount); - + + // Copies out our values from their hashed locations, compacting property table offsets as we go. unsigned i = 0; - PropertyOffset firstOffset = firstPropertyOffsetFor(m_typeInfo.type()); PropertyTable::iterator end = m_propertyTable->end(); for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter, ++i) { values[i] = object->getDirectOffset(iter->offset); - // Update property table to have the new property offsets - iter->offset = i + firstOffset; + iter->offset = propertyOffsetFor(i, m_inlineCapacity); } - // Copy the original property values into their final locations + // Copies in our values to their compacted locations. for (unsigned i = 0; i < propertyCount; i++) - object->putDirectOffset(globalData, firstOffset + i, values[i]); + object->putDirectOffset(globalData, propertyOffsetFor(i, m_inlineCapacity), values[i]); m_propertyTable->clearDeletedOffsets(); } @@ -759,7 +768,7 @@ PropertyOffset Structure::putSpecificValue(JSGlobalData& globalData, PropertyNam if (!m_propertyTable) createPropertyMap(); - PropertyOffset newOffset = m_propertyTable->nextOffset(m_typeInfo.type()); + PropertyOffset newOffset = m_propertyTable->nextOffset(m_inlineCapacity); m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue)); diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h index e77287b20..f45e9f1d9 100644 --- a/Source/JavaScriptCore/runtime/Structure.h +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -69,7 +69,7 @@ namespace JSC { typedef JSCell Base; - static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0); + static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, PropertyOffset inlineCapacity = 0); protected: void finishCreation(JSGlobalData& globalData) @@ -128,6 +128,8 @@ namespace JSC { Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*); + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; static void destroy(JSCell*); // These should be used with caution. @@ -152,7 +154,8 @@ namespace JSC { bool anyObjectInChainMayInterceptIndexedAccesses() const; - NonPropertyTransition suggestedIndexingTransition() const; + bool needsSlowPutIndexing() const; + NonPropertyTransition suggestedArrayStorageTransition() const; JSGlobalObject* globalObject() const { return m_globalObject.get(); } void setGlobalObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { m_globalObject.set(globalData, this, globalObject); } @@ -177,24 +180,6 @@ namespace JSC { ASSERT(structure()->classInfo() == &s_info); return m_outOfLineCapacity; } - unsigned outOfLineSizeForKnownFinalObject() const - { - ASSERT(m_typeInfo.type() == FinalObjectType); - if (m_propertyTable) { - unsigned totalSize = m_propertyTable->propertyStorageSize(); - if (totalSize < static_cast<unsigned>(inlineStorageCapacity)) - return 0; - return totalSize - inlineStorageCapacity; - } - return numberOfOutOfLineSlotsForLastOffset(m_offset); - } - unsigned outOfLineSizeForKnownNonFinalObject() const - { - ASSERT(m_typeInfo.type() != FinalObjectType); - if (m_propertyTable) - return m_propertyTable->propertyStorageSize(); - return numberOfOutOfLineSlotsForLastOffset(m_offset); - } unsigned outOfLineSize() const { ASSERT(structure()->classInfo() == &s_info); @@ -209,31 +194,20 @@ namespace JSC { } bool hasInlineStorage() const { - return m_typeInfo.type() == FinalObjectType; + return !!m_inlineCapacity; } unsigned inlineCapacity() const { - if (hasInlineStorage()) - return inlineStorageCapacity; - return 0; + return m_inlineCapacity; } - unsigned inlineSizeForKnownFinalObject() const + unsigned inlineSize() const { - ASSERT(m_typeInfo.type() == FinalObjectType); unsigned result; if (m_propertyTable) result = m_propertyTable->propertyStorageSize(); else result = m_offset + 1; - if (result > static_cast<unsigned>(inlineStorageCapacity)) - return inlineStorageCapacity; - return result; - } - unsigned inlineSize() const - { - if (!hasInlineStorage()) - return 0; - return inlineSizeForKnownFinalObject(); + return std::min<unsigned>(result, m_inlineCapacity); } unsigned totalStorageSize() const { @@ -251,16 +225,12 @@ namespace JSC { { if (hasInlineStorage()) return 0; - return inlineStorageCapacity; + return firstOutOfLineOffset; } PropertyOffset lastValidOffset() const { - if (m_propertyTable) { - PropertyOffset size = m_propertyTable->propertyStorageSize(); - if (!hasInlineStorage()) - size += inlineStorageCapacity; - return size - 1; - } + if (m_propertyTable) + return propertyOffsetFor(m_propertyTable->propertyStorageSize() - 1, m_inlineCapacity); return m_offset; } bool isValidOffset(PropertyOffset offset) const @@ -381,7 +351,7 @@ namespace JSC { private: friend class LLIntOffsetsExtractor; - JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0); + JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, PropertyOffset inlineCapacity); Structure(JSGlobalData&); Structure(JSGlobalData&, const Structure*); @@ -457,6 +427,8 @@ namespace JSC { mutable InlineWatchpointSet m_transitionWatchpointSet; uint32_t m_outOfLineCapacity; + uint8_t m_inlineCapacity; + COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits); // m_offset does not account for anonymous slots PropertyOffset m_offset; @@ -473,22 +445,11 @@ namespace JSC { unsigned m_staticFunctionReified; }; - template <> inline void* allocateCell<Structure>(Heap& heap) - { -#if ENABLE(GC_VALIDATION) - ASSERT(!heap.globalData()->isInitializingObject()); - heap.globalData()->setInitializingObjectClass(&Structure::s_info); -#endif - JSCell* result = static_cast<JSCell*>(heap.allocateStructure(sizeof(Structure))); - result->clearStructure(); - return result; - } - - inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType) + inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, PropertyOffset inlineCapacity) { ASSERT(globalData.structureStructure); ASSERT(classInfo); - Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo, indexingType); + Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity); structure->finishCreation(globalData); return structure; } @@ -628,15 +589,6 @@ namespace JSC { ASSERT(m_structure || !globalData.structureStructure); } - inline const ClassInfo* JSCell::classInfo() const - { -#if ENABLE(GC_VALIDATION) - return m_structure.unvalidatedGet()->classInfo(); -#else - return m_structure->classInfo(); -#endif - } - } // namespace JSC #endif // Structure_h diff --git a/Source/JavaScriptCore/runtime/StructureChain.h b/Source/JavaScriptCore/runtime/StructureChain.h index 3b19d4cf1..878f606b4 100644 --- a/Source/JavaScriptCore/runtime/StructureChain.h +++ b/Source/JavaScriptCore/runtime/StructureChain.h @@ -59,6 +59,10 @@ namespace JSC { static ClassInfo s_info; + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; + static void destroy(JSCell*); + protected: void finishCreation(JSGlobalData& globalData, Structure* head) { @@ -78,7 +82,6 @@ namespace JSC { friend class LLIntOffsetsExtractor; StructureChain(JSGlobalData&, Structure*); - static void destroy(JSCell*); OwnArrayPtr<WriteBarrier<Structure> > m_vector; }; diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h index 90cb6a4db..3ab7b2014 100644 --- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h +++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h @@ -43,6 +43,7 @@ static const unsigned FirstInternalAttribute = 1 << 6; // Use for transitions th // Support for attributes used to indicate transitions not related to properties. // If any of these are used, the string portion of the key should be 0. enum NonPropertyTransition { + AllocateContiguous, AllocateArrayStorage, AllocateSlowPutArrayStorage, SwitchToSlowPutArrayStorage, @@ -57,13 +58,18 @@ inline unsigned toAttributes(NonPropertyTransition transition) inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition) { switch (transition) { + case AllocateContiguous: + ASSERT(!hasIndexedProperties(oldType)); + return oldType | ContiguousShape; case AllocateArrayStorage: - return oldType | HasArrayStorage; + ASSERT(!hasIndexedProperties(oldType) || hasContiguous(oldType)); + return (oldType & ~IndexingShapeMask) | ArrayStorageShape; case AllocateSlowPutArrayStorage: - return oldType | HasSlowPutArrayStorage; + ASSERT(!hasIndexedProperties(oldType) || hasContiguous(oldType)); + return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; case SwitchToSlowPutArrayStorage: - ASSERT(oldType & HasArrayStorage); - return (oldType & ~HasArrayStorage) | HasSlowPutArrayStorage; + ASSERT(hasFastArrayStorage(oldType)); + return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; case AddIndexedAccessors: return oldType | MayHaveIndexedAccessors; default: diff --git a/Source/JavaScriptCore/runtime/SymbolTable.h b/Source/JavaScriptCore/runtime/SymbolTable.h index 6063dbab4..debb76499 100644 --- a/Source/JavaScriptCore/runtime/SymbolTable.h +++ b/Source/JavaScriptCore/runtime/SymbolTable.h @@ -344,12 +344,16 @@ namespace JSC { class SharedSymbolTable : public JSCell, public SymbolTable { public: + typedef JSCell Base; + static SharedSymbolTable* create(JSGlobalData& globalData) { SharedSymbolTable* sharedSymbolTable = new (NotNull, allocateCell<SharedSymbolTable>(globalData.heap)) SharedSymbolTable(globalData); sharedSymbolTable->finishCreation(globalData); return sharedSymbolTable; } + static const bool needsDestruction = true; + static const bool hasImmortalStructure = true; static void destroy(JSCell*); static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) @@ -396,6 +400,7 @@ namespace JSC { OwnArrayPtr<SlowArgument> m_slowArguments; }; + } // namespace JSC #endif // SymbolTable_h diff --git a/Source/JavaScriptCore/runtime/TypedArrayDescriptor.h b/Source/JavaScriptCore/runtime/TypedArrayDescriptor.h new file mode 100644 index 000000000..1ae4818be --- /dev/null +++ b/Source/JavaScriptCore/runtime/TypedArrayDescriptor.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 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 TypedArrayDescriptor_h +#define TypedArrayDescriptor_h + +namespace JSC { + +struct ClassInfo; + +enum TypedArrayType { + TypedArrayNone, + TypedArrayInt8, + TypedArrayInt16, + TypedArrayInt32, + TypedArrayUint8, + TypedArrayUint8Clamped, + TypedArrayUint16, + TypedArrayUint32, + TypedArrayFloat32, + TypedArrayFloat64 +}; + +struct TypedArrayDescriptor { + TypedArrayDescriptor() + : m_classInfo(0) + , m_storageOffset(0) + , m_lengthOffset(0) + { + } + TypedArrayDescriptor(const ClassInfo* classInfo, size_t storageOffset, size_t lengthOffset) + : m_classInfo(classInfo) + , m_storageOffset(storageOffset) + , m_lengthOffset(lengthOffset) + { + } + const ClassInfo* m_classInfo; + size_t m_storageOffset; + size_t m_lengthOffset; +}; + +enum TypedArraySignedness { + SignedTypedArray, + UnsignedTypedArray +}; +enum TypedArrayRounding { + TruncateRounding, + ClampRounding +}; + +} // namespace JSC + +#endif // TypedArrayDescriptor_h + diff --git a/Source/JavaScriptCore/runtime/WeakGCMap.h b/Source/JavaScriptCore/runtime/WeakGCMap.h index 6926165a7..52e5e2946 100644 --- a/Source/JavaScriptCore/runtime/WeakGCMap.h +++ b/Source/JavaScriptCore/runtime/WeakGCMap.h @@ -63,7 +63,7 @@ public: { typename MapType::iterator end = m_map.end(); for (typename MapType::iterator ptr = m_map.begin(); ptr != end; ++ptr) - WeakSet::deallocate(ptr->second); + WeakSet::deallocate(ptr->value); m_map.clear(); } @@ -80,8 +80,8 @@ public: ASSERT_UNUSED(globalData, globalData.apiLock().currentThreadIsHoldingLock()); typename MapType::AddResult result = m_map.add(key, 0); if (!result.isNewEntry) - WeakSet::deallocate(result.iterator->second); - result.iterator->second = WeakSet::allocate(value, this, FinalizerCallback::finalizerContextFor(key)); + WeakSet::deallocate(result.iterator->value); + result.iterator->value = WeakSet::allocate(value, this, FinalizerCallback::finalizerContextFor(key)); } void remove(const KeyType& key) |