source: webkit/trunk/JavaScriptCore/runtime/JSArray.cpp@ 38528

Last change on this file since 38528 was 38440, checked in by Darin Adler, 17 years ago

JavaScriptCore:

2008-11-15 Darin Adler <Darin Adler>

Rubber stamped by Geoff Garen.

  • do the long-planned StructureID -> Structure rename
  • API/JSCallbackConstructor.cpp: (JSC::JSCallbackConstructor::JSCallbackConstructor):
  • API/JSCallbackConstructor.h: (JSC::JSCallbackConstructor::createStructure):
  • API/JSCallbackFunction.h: (JSC::JSCallbackFunction::createStructure):
  • API/JSCallbackObject.h: (JSC::JSCallbackObject::createStructure):
  • API/JSCallbackObjectFunctions.h: (JSC::::JSCallbackObject):
  • API/JSValueRef.cpp: (JSValueIsInstanceOfConstructor):
  • GNUmakefile.am:
  • JavaScriptCore.exp:
  • JavaScriptCore.pri:
  • JavaScriptCore.scons:
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • JavaScriptCoreSources.bkl:
  • VM/CTI.cpp: (JSC::CTI::compileBinaryArithOp): (JSC::CTI::privateCompileMainPass): (JSC::CTI::privateCompileGetByIdSelf): (JSC::CTI::privateCompileGetByIdProto): (JSC::CTI::privateCompileGetByIdChain): (JSC::CTI::privateCompilePutByIdReplace): (JSC::transitionWillNeedStorageRealloc): (JSC::CTI::privateCompilePutByIdTransition): (JSC::CTI::patchGetByIdSelf): (JSC::CTI::patchPutByIdReplace):
  • VM/CTI.h: (JSC::CTI::compileGetByIdSelf): (JSC::CTI::compileGetByIdProto): (JSC::CTI::compileGetByIdChain): (JSC::CTI::compilePutByIdReplace): (JSC::CTI::compilePutByIdTransition):
  • VM/CodeBlock.cpp: (JSC::CodeBlock::printStructure): (JSC::CodeBlock::printStructures): (JSC::CodeBlock::dump): (JSC::CodeBlock::~CodeBlock): (JSC::CodeBlock::derefStructures): (JSC::CodeBlock::refStructures):
  • VM/CodeBlock.h:
  • VM/Instruction.h: (JSC::Instruction::Instruction): (JSC::Instruction::):
  • VM/Machine.cpp: (JSC::jsTypeStringForValue): (JSC::jsIsObjectType): (JSC::BytecodeInterpreter::resolveGlobal): (JSC::BytecodeInterpreter::BytecodeInterpreter): (JSC::cachePrototypeChain): (JSC::BytecodeInterpreter::tryCachePutByID): (JSC::BytecodeInterpreter::uncachePutByID): (JSC::BytecodeInterpreter::tryCacheGetByID): (JSC::BytecodeInterpreter::uncacheGetByID): (JSC::BytecodeInterpreter::privateExecute): (JSC::BytecodeInterpreter::tryCTICachePutByID): (JSC::BytecodeInterpreter::tryCTICacheGetByID): (JSC::BytecodeInterpreter::cti_op_instanceof): (JSC::BytecodeInterpreter::cti_op_construct_JSConstruct): (JSC::BytecodeInterpreter::cti_op_resolve_global): (JSC::BytecodeInterpreter::cti_op_is_undefined):
  • runtime/Arguments.h: (JSC::Arguments::createStructure):
  • runtime/ArrayConstructor.cpp: (JSC::ArrayConstructor::ArrayConstructor):
  • runtime/ArrayConstructor.h:
  • runtime/ArrayPrototype.cpp: (JSC::ArrayPrototype::ArrayPrototype):
  • runtime/ArrayPrototype.h:
  • runtime/BatchedTransitionOptimizer.h: (JSC::BatchedTransitionOptimizer::BatchedTransitionOptimizer): (JSC::BatchedTransitionOptimizer::~BatchedTransitionOptimizer):
  • runtime/BooleanConstructor.cpp: (JSC::BooleanConstructor::BooleanConstructor):
  • runtime/BooleanConstructor.h:
  • runtime/BooleanObject.cpp: (JSC::BooleanObject::BooleanObject):
  • runtime/BooleanObject.h:
  • runtime/BooleanPrototype.cpp: (JSC::BooleanPrototype::BooleanPrototype):
  • runtime/BooleanPrototype.h:
  • runtime/DateConstructor.cpp: (JSC::DateConstructor::DateConstructor):
  • runtime/DateConstructor.h:
  • runtime/DateInstance.cpp: (JSC::DateInstance::DateInstance):
  • runtime/DateInstance.h:
  • runtime/DatePrototype.cpp: (JSC::DatePrototype::DatePrototype):
  • runtime/DatePrototype.h: (JSC::DatePrototype::createStructure):
  • runtime/ErrorConstructor.cpp: (JSC::ErrorConstructor::ErrorConstructor):
  • runtime/ErrorConstructor.h:
  • runtime/ErrorInstance.cpp: (JSC::ErrorInstance::ErrorInstance):
  • runtime/ErrorInstance.h:
  • runtime/ErrorPrototype.cpp: (JSC::ErrorPrototype::ErrorPrototype):
  • runtime/ErrorPrototype.h:
  • runtime/FunctionConstructor.cpp: (JSC::FunctionConstructor::FunctionConstructor):
  • runtime/FunctionConstructor.h:
  • runtime/FunctionPrototype.cpp: (JSC::FunctionPrototype::FunctionPrototype): (JSC::FunctionPrototype::addFunctionProperties):
  • runtime/FunctionPrototype.h: (JSC::FunctionPrototype::createStructure):
  • runtime/GlobalEvalFunction.cpp: (JSC::GlobalEvalFunction::GlobalEvalFunction):
  • runtime/GlobalEvalFunction.h:
  • runtime/Identifier.h:
  • runtime/InternalFunction.cpp: (JSC::InternalFunction::InternalFunction):
  • runtime/InternalFunction.h: (JSC::InternalFunction::createStructure): (JSC::InternalFunction::InternalFunction):
  • runtime/JSActivation.cpp: (JSC::JSActivation::JSActivation):
  • runtime/JSActivation.h: (JSC::JSActivation::createStructure):
  • runtime/JSArray.cpp: (JSC::JSArray::JSArray):
  • runtime/JSArray.h: (JSC::JSArray::createStructure):
  • runtime/JSCell.h: (JSC::JSCell::JSCell): (JSC::JSCell::isObject): (JSC::JSCell::isString): (JSC::JSCell::structure): (JSC::JSValue::needsThisConversion):
  • runtime/JSFunction.cpp: (JSC::JSFunction::construct):
  • runtime/JSFunction.h: (JSC::JSFunction::JSFunction): (JSC::JSFunction::createStructure):
  • runtime/JSGlobalData.cpp: (JSC::JSGlobalData::JSGlobalData): (JSC::JSGlobalData::createLeaked):
  • runtime/JSGlobalData.h:
  • runtime/JSGlobalObject.cpp: (JSC::markIfNeeded): (JSC::JSGlobalObject::reset):
  • runtime/JSGlobalObject.h: (JSC::JSGlobalObject::JSGlobalObject): (JSC::JSGlobalObject::argumentsStructure): (JSC::JSGlobalObject::arrayStructure): (JSC::JSGlobalObject::booleanObjectStructure): (JSC::JSGlobalObject::callbackConstructorStructure): (JSC::JSGlobalObject::callbackFunctionStructure): (JSC::JSGlobalObject::callbackObjectStructure): (JSC::JSGlobalObject::dateStructure): (JSC::JSGlobalObject::emptyObjectStructure): (JSC::JSGlobalObject::errorStructure): (JSC::JSGlobalObject::functionStructure): (JSC::JSGlobalObject::numberObjectStructure): (JSC::JSGlobalObject::prototypeFunctionStructure): (JSC::JSGlobalObject::regExpMatchesArrayStructure): (JSC::JSGlobalObject::regExpStructure): (JSC::JSGlobalObject::stringObjectStructure): (JSC::JSGlobalObject::createStructure): (JSC::Structure::prototypeForLookup):
  • runtime/JSNotAnObject.h: (JSC::JSNotAnObject::createStructure):
  • runtime/JSNumberCell.h: (JSC::JSNumberCell::createStructure): (JSC::JSNumberCell::JSNumberCell):
  • runtime/JSObject.cpp: (JSC::JSObject::mark): (JSC::JSObject::put): (JSC::JSObject::deleteProperty): (JSC::JSObject::defineGetter): (JSC::JSObject::defineSetter): (JSC::JSObject::getPropertyAttributes): (JSC::JSObject::getPropertyNames): (JSC::JSObject::removeDirect): (JSC::JSObject::createInheritorID):
  • runtime/JSObject.h: (JSC::JSObject::getDirect): (JSC::JSObject::getDirectLocation): (JSC::JSObject::hasCustomProperties): (JSC::JSObject::hasGetterSetterProperties): (JSC::JSObject::createStructure): (JSC::JSObject::JSObject): (JSC::JSObject::~JSObject): (JSC::JSObject::prototype): (JSC::JSObject::setPrototype): (JSC::JSObject::setStructure): (JSC::JSObject::inheritorID): (JSC::JSObject::inlineGetOwnPropertySlot): (JSC::JSObject::getOwnPropertySlotForWrite): (JSC::JSCell::fastGetOwnPropertySlot): (JSC::JSObject::putDirect): (JSC::JSObject::putDirectWithoutTransition): (JSC::JSObject::transitionTo):
  • runtime/JSPropertyNameIterator.h: (JSC::JSPropertyNameIterator::next):
  • runtime/JSStaticScopeObject.h: (JSC::JSStaticScopeObject::JSStaticScopeObject): (JSC::JSStaticScopeObject::createStructure):
  • runtime/JSString.h: (JSC::JSString::JSString): (JSC::JSString::createStructure):
  • runtime/JSVariableObject.h: (JSC::JSVariableObject::JSVariableObject):
  • runtime/JSWrapperObject.h: (JSC::JSWrapperObject::JSWrapperObject):
  • runtime/MathObject.cpp: (JSC::MathObject::MathObject):
  • runtime/MathObject.h: (JSC::MathObject::createStructure):
  • runtime/NativeErrorConstructor.cpp: (JSC::NativeErrorConstructor::NativeErrorConstructor):
  • runtime/NativeErrorConstructor.h:
  • runtime/NativeErrorPrototype.cpp: (JSC::NativeErrorPrototype::NativeErrorPrototype):
  • runtime/NativeErrorPrototype.h:
  • runtime/NumberConstructor.cpp: (JSC::NumberConstructor::NumberConstructor):
  • runtime/NumberConstructor.h: (JSC::NumberConstructor::createStructure):
  • runtime/NumberObject.cpp: (JSC::NumberObject::NumberObject):
  • runtime/NumberObject.h:
  • runtime/NumberPrototype.cpp: (JSC::NumberPrototype::NumberPrototype):
  • runtime/NumberPrototype.h:
  • runtime/ObjectConstructor.cpp: (JSC::ObjectConstructor::ObjectConstructor):
  • runtime/ObjectConstructor.h:
  • runtime/ObjectPrototype.cpp: (JSC::ObjectPrototype::ObjectPrototype):
  • runtime/ObjectPrototype.h:
  • runtime/Operations.h: (JSC::equalSlowCaseInline):
  • runtime/PropertyNameArray.h: (JSC::PropertyNameArrayData::setCachedStructure): (JSC::PropertyNameArrayData::cachedStructure): (JSC::PropertyNameArrayData::setCachedPrototypeChain): (JSC::PropertyNameArrayData::cachedPrototypeChain): (JSC::PropertyNameArrayData::PropertyNameArrayData):
  • runtime/PrototypeFunction.cpp: (JSC::PrototypeFunction::PrototypeFunction):
  • runtime/PrototypeFunction.h:
  • runtime/RegExpConstructor.cpp: (JSC::RegExpConstructor::RegExpConstructor):
  • runtime/RegExpConstructor.h: (JSC::RegExpConstructor::createStructure):
  • runtime/RegExpObject.cpp: (JSC::RegExpObject::RegExpObject):
  • runtime/RegExpObject.h: (JSC::RegExpObject::createStructure):
  • runtime/RegExpPrototype.cpp: (JSC::RegExpPrototype::RegExpPrototype):
  • runtime/RegExpPrototype.h:
  • runtime/StringConstructor.cpp: (JSC::StringConstructor::StringConstructor):
  • runtime/StringConstructor.h:
  • runtime/StringObject.cpp: (JSC::StringObject::StringObject):
  • runtime/StringObject.h: (JSC::StringObject::createStructure):
  • runtime/StringObjectThatMasqueradesAsUndefined.h: (JSC::StringObjectThatMasqueradesAsUndefined::create): (JSC::StringObjectThatMasqueradesAsUndefined::StringObjectThatMasqueradesAsUndefined): (JSC::StringObjectThatMasqueradesAsUndefined::createStructure):
  • runtime/StringPrototype.cpp: (JSC::StringPrototype::StringPrototype):
  • runtime/StringPrototype.h:
  • runtime/Structure.cpp: Copied from JavaScriptCore/runtime/StructureID.cpp. (JSC::Structure::dumpStatistics): (JSC::Structure::Structure): (JSC::Structure::~Structure): (JSC::Structure::startIgnoringLeaks): (JSC::Structure::stopIgnoringLeaks): (JSC::Structure::materializePropertyMap): (JSC::Structure::getEnumerablePropertyNames): (JSC::Structure::clearEnumerationCache): (JSC::Structure::growPropertyStorageCapacity): (JSC::Structure::addPropertyTransitionToExistingStructure): (JSC::Structure::addPropertyTransition): (JSC::Structure::removePropertyTransition): (JSC::Structure::changePrototypeTransition): (JSC::Structure::getterSetterTransition): (JSC::Structure::toDictionaryTransition): (JSC::Structure::fromDictionaryTransition): (JSC::Structure::addPropertyWithoutTransition): (JSC::Structure::removePropertyWithoutTransition): (JSC::Structure::createCachedPrototypeChain): (JSC::Structure::checkConsistency): (JSC::Structure::copyPropertyTable): (JSC::Structure::get): (JSC::Structure::put): (JSC::Structure::remove): (JSC::Structure::insertIntoPropertyMapHashTable): (JSC::Structure::createPropertyMapHashTable): (JSC::Structure::expandPropertyMapHashTable): (JSC::Structure::rehashPropertyMapHashTable): (JSC::Structure::getEnumerablePropertyNamesInternal):
  • runtime/Structure.h: Copied from JavaScriptCore/runtime/StructureID.h. (JSC::Structure::create): (JSC::Structure::previousID): (JSC::Structure::setCachedPrototypeChain): (JSC::Structure::cachedPrototypeChain): (JSC::Structure::): (JSC::Structure::get):
  • runtime/StructureChain.cpp: Copied from JavaScriptCore/runtime/StructureIDChain.cpp. (JSC::StructureChain::StructureChain): (JSC::structureChainsAreEqual):
  • runtime/StructureChain.h: Copied from JavaScriptCore/runtime/StructureIDChain.h. (JSC::StructureChain::create): (JSC::StructureChain::head):
  • runtime/StructureID.cpp: Removed.
  • runtime/StructureID.h: Removed.
  • runtime/StructureIDChain.cpp: Removed.
  • runtime/StructureIDChain.h: Removed.
  • runtime/StructureIDTransitionTable.h: Removed.
  • runtime/StructureTransitionTable.h: Copied from JavaScriptCore/runtime/StructureIDTransitionTable.h.

JavaScriptGlue:

2008-11-15 Darin Adler <Darin Adler>

Rubber stamped by Geoff Garen.

  • do the long-planned StructureID -> Structure rename
  • JSRun.cpp: (JSGlueGlobalObject::JSGlueGlobalObject): (JSRun::JSRun):
  • JSRun.h: (JSGlueGlobalObject::userObjectStructure):
  • JSUtils.cpp: (getThreadGlobalObject):
  • UserObjectImp.cpp: (UserObjectImp::UserObjectImp):
  • UserObjectImp.h: (UserObjectImp::createStructure):

WebCore:

2008-11-15 Darin Adler <Darin Adler>

Rubber stamped by Geoff Garen.

  • do the long-planned StructureID -> Structure rename
  • ForwardingHeaders/runtime/Structure.h: Copied from WebCore/ForwardingHeaders/runtime/StructureID.h.
  • ForwardingHeaders/runtime/StructureID.h: Removed.
  • bindings/js/JSAudioConstructor.cpp: (WebCore::JSAudioConstructor::JSAudioConstructor):
  • bindings/js/JSDOMBinding.cpp: (WebCore::getCachedDOMStructure): (WebCore::cacheDOMStructure):
  • bindings/js/JSDOMBinding.h: (WebCore::DOMObject::DOMObject): (WebCore::getDOMStructure):
  • bindings/js/JSDOMGlobalObject.cpp: (WebCore::JSDOMGlobalObject::JSDOMGlobalObject):
  • bindings/js/JSDOMGlobalObject.h:
  • bindings/js/JSDOMWindowBase.cpp: (WebCore::JSDOMWindowBase::JSDOMWindowBase):
  • bindings/js/JSDOMWindowBase.h:
  • bindings/js/JSDOMWindowShell.cpp: (WebCore::JSDOMWindowShell::JSDOMWindowShell): (WebCore::JSDOMWindowShell::setWindow):
  • bindings/js/JSDOMWindowShell.h: (WebCore::JSDOMWindowShell::createStructure):
  • bindings/js/JSDedicatedWorkerConstructor.cpp: (WebCore::JSDedicatedWorkerConstructor::JSDedicatedWorkerConstructor):
  • bindings/js/JSHTMLAllCollection.h: (WebCore::JSHTMLAllCollection::JSHTMLAllCollection): (WebCore::JSHTMLAllCollection::createStructure):
  • bindings/js/JSImageConstructor.cpp: (WebCore::JSImageConstructor::JSImageConstructor):
  • bindings/js/JSInspectedObjectWrapper.cpp: (WebCore::JSInspectedObjectWrapper::wrap): (WebCore::JSInspectedObjectWrapper::JSInspectedObjectWrapper):
  • bindings/js/JSInspectedObjectWrapper.h:
  • bindings/js/JSInspectorCallbackWrapper.cpp: (WebCore::leakInspectorCallbackWrapperStructure): (WebCore::JSInspectorCallbackWrapper::wrap): (WebCore::JSInspectorCallbackWrapper::JSInspectorCallbackWrapper):
  • bindings/js/JSInspectorCallbackWrapper.h:
  • bindings/js/JSMessageChannelConstructor.cpp: (WebCore::JSMessageChannelConstructor::JSMessageChannelConstructor):
  • bindings/js/JSNamedNodesCollection.h: (WebCore::JSNamedNodesCollection::createStructure):
  • bindings/js/JSOptionConstructor.cpp: (WebCore::JSOptionConstructor::JSOptionConstructor):
  • bindings/js/JSQuarantinedObjectWrapper.cpp: (WebCore::JSQuarantinedObjectWrapper::JSQuarantinedObjectWrapper):
  • bindings/js/JSQuarantinedObjectWrapper.h: (WebCore::JSQuarantinedObjectWrapper::createStructure):
  • bindings/js/JSRGBColor.h: (WebCore::JSRGBColor::createStructure):
  • bindings/js/JSWorkerContext.cpp: (WebCore::createJSWorkerContextStructure): (WebCore::JSWorkerContext::JSWorkerContext): (WebCore::JSWorkerContext::createPrototype):
  • bindings/js/JSWorkerContext.h: (WebCore::JSWorkerContext::createStructure): (WebCore::JSWorkerContextPrototype::JSWorkerContextPrototype): (WebCore::JSWorkerContextPrototype::createStructure):
  • bindings/js/JSXMLHttpRequestConstructor.cpp: (WebCore::JSXMLHttpRequestConstructor::JSXMLHttpRequestConstructor):
  • bindings/js/JSXSLTProcessorConstructor.cpp: (WebCore::JSXSLTProcessorConstructor::JSXSLTProcessorConstructor):
  • bindings/scripts/CodeGeneratorJS.pm:
  • bridge/objc/objc_runtime.h: (JSC::Bindings::ObjcFallbackObjectImp::createStructure):
  • bridge/qt/qt_runtime.cpp: (JSC::Bindings::QtConnectionObject::execute):
  • bridge/qt/qt_runtime.h: (JSC::Bindings::QtRuntimeMethod::createStructure):
  • bridge/runtime_array.h: (JSC::RuntimeArray::createStructure):
  • bridge/runtime_method.h: (JSC::RuntimeMethod::createStructure):
  • bridge/runtime_object.cpp: (JSC::RuntimeObjectImp::RuntimeObjectImp):
  • bridge/runtime_object.h: (JSC::RuntimeObjectImp::createStructure):

WebKitTools:

2008-11-15 Darin Adler <Darin Adler>

Rubber stamped by Geoff Garen.

  • do the long-planned StructureID -> Structure rename
  • Scripts/check-for-global-initializers: Update name of StructureID.o.
  • Scripts/do-webcore-rename: Renaming script that I used.
  • Property svn:eol-style set to native
File size: 34.0 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2003 Peter Kelly ([email protected])
5 * Copyright (C) 2006 Alexey Proskuryakov ([email protected])
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23#include "config.h"
24#include "JSArray.h"
25
26#include "ArrayPrototype.h"
27#include "PropertyNameArray.h"
28#include <wtf/AVLTree.h>
29#include <wtf/Assertions.h>
30#include <Operations.h>
31
32#define CHECK_ARRAY_CONSISTENCY 0
33
34using namespace std;
35using namespace WTF;
36
37namespace JSC {
38
39ASSERT_CLASS_FITS_IN_CELL(JSArray);
40
41// Overview of JSArray
42//
43// Properties of JSArray objects may be stored in one of three locations:
44// * The regular JSObject property map.
45// * A storage vector.
46// * A sparse map of array entries.
47//
48// Properties with non-numeric identifiers, with identifiers that are not representable
49// as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX
50// (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit
51// integer) are not considered array indices and will be stored in the JSObject property map.
52//
53// All properties with a numeric identifer, representable as an unsigned integer i,
54// where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the
55// storage vector or the sparse map. An array index i will be handled in the following
56// fashion:
57//
58// * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector.
59// * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either
60// be stored in the storage vector or in the sparse array, depending on the density of
61// data that would be stored in the vector (a vector being used where at least
62// (1 / minDensityMultiplier) of the entries would be populated).
63// * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored
64// in the sparse array.
65
66// The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
67// function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
68// size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(JSValue*)) +
69// (vectorLength * sizeof(JSValue*)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
70#define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValue*))) / sizeof(JSValue*))
71
72// These values have to be macros to be used in max() and min() without introducing
73// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
74#define MIN_SPARSE_ARRAY_INDEX 10000U
75#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
76// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
77#define MAX_ARRAY_INDEX 0xFFFFFFFEU
78
79// Our policy for when to use a vector and when to use a sparse map.
80// For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
81// When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
82// as long as it is 1/8 full. If more sparse than that, we use a map.
83static const unsigned minDensityMultiplier = 8;
84
85const ClassInfo JSArray::info = {"Array", 0, 0, 0};
86
87static inline size_t storageSize(unsigned vectorLength)
88{
89 ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
90
91 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
92 // - as asserted above - the following calculation cannot overflow.
93 size_t size = (sizeof(ArrayStorage) - sizeof(JSValue*)) + (vectorLength * sizeof(JSValue*));
94 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
95 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
96 ASSERT(((size - (sizeof(ArrayStorage) - sizeof(JSValue*))) / sizeof(JSValue*) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(JSValue*))));
97
98 return size;
99}
100
101static inline unsigned increasedVectorLength(unsigned newLength)
102{
103 ASSERT(newLength <= MAX_STORAGE_VECTOR_LENGTH);
104
105 // Mathematically equivalent to:
106 // increasedLength = (newLength * 3 + 1) / 2;
107 // or:
108 // increasedLength = (unsigned)ceil(newLength * 1.5));
109 // This form is not prone to internal overflow.
110 unsigned increasedLength = newLength + (newLength >> 1) + (newLength & 1);
111 ASSERT(increasedLength >= newLength);
112
113 return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
114}
115
116static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
117{
118 return length / minDensityMultiplier <= numValues;
119}
120
121#if !CHECK_ARRAY_CONSISTENCY
122
123inline void JSArray::checkConsistency(ConsistencyCheckType)
124{
125}
126
127#endif
128
129JSArray::JSArray(PassRefPtr<Structure> structure)
130 : JSObject(structure)
131{
132 unsigned initialCapacity = 0;
133
134 m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
135 m_fastAccessCutoff = 0;
136 m_storage->m_vectorLength = initialCapacity;
137 m_storage->m_length = 0;
138
139 checkConsistency();
140}
141
142JSArray::JSArray(PassRefPtr<Structure> structure, unsigned initialLength)
143 : JSObject(structure)
144{
145 unsigned initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX);
146
147 m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
148 m_fastAccessCutoff = 0;
149 m_storage->m_vectorLength = initialCapacity;
150 m_storage->m_length = initialLength;
151
152 Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue*));
153
154 checkConsistency();
155}
156
157JSArray::JSArray(ExecState* exec, PassRefPtr<Structure> structure, const ArgList& list)
158 : JSObject(structure)
159{
160 unsigned length = list.size();
161
162 m_fastAccessCutoff = length;
163
164 ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length)));
165
166 storage->m_vectorLength = length;
167 storage->m_numValuesInVector = length;
168 storage->m_sparseValueMap = 0;
169 storage->m_length = length;
170
171 size_t i = 0;
172 ArgList::const_iterator end = list.end();
173 for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i)
174 storage->m_vector[i] = (*it).jsValue(exec);
175
176 m_storage = storage;
177
178 // When the array is created non-empty, its cells are filled, so it's really no worse than
179 // a property map. Therefore don't report extra memory cost.
180
181 checkConsistency();
182}
183
184JSArray::~JSArray()
185{
186 checkConsistency(DestructorConsistencyCheck);
187
188 delete m_storage->m_sparseValueMap;
189 fastFree(m_storage);
190}
191
192bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
193{
194 ArrayStorage* storage = m_storage;
195
196 if (i >= storage->m_length) {
197 if (i > MAX_ARRAY_INDEX)
198 return getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
199 return false;
200 }
201
202 if (i < storage->m_vectorLength) {
203 JSValue*& valueSlot = storage->m_vector[i];
204 if (valueSlot) {
205 slot.setValueSlot(&valueSlot);
206 return true;
207 }
208 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
209 if (i >= MIN_SPARSE_ARRAY_INDEX) {
210 SparseArrayValueMap::iterator it = map->find(i);
211 if (it != map->end()) {
212 slot.setValueSlot(&it->second);
213 return true;
214 }
215 }
216 }
217
218 return false;
219}
220
221bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
222{
223 if (propertyName == exec->propertyNames().length) {
224 slot.setValue(jsNumber(exec, length()));
225 return true;
226 }
227
228 bool isArrayIndex;
229 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
230 if (isArrayIndex)
231 return JSArray::getOwnPropertySlot(exec, i, slot);
232
233 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
234}
235
236// ECMA 15.4.5.1
237void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
238{
239 bool isArrayIndex;
240 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
241 if (isArrayIndex) {
242 put(exec, i, value);
243 return;
244 }
245
246 if (propertyName == exec->propertyNames().length) {
247 unsigned newLength = value->toUInt32(exec);
248 if (value->toNumber(exec) != static_cast<double>(newLength)) {
249 throwError(exec, RangeError, "Invalid array length.");
250 return;
251 }
252 setLength(newLength);
253 return;
254 }
255
256 JSObject::put(exec, propertyName, value, slot);
257}
258
259void JSArray::put(ExecState* exec, unsigned i, JSValue* value)
260{
261 checkConsistency();
262
263 unsigned length = m_storage->m_length;
264 if (i >= length && i <= MAX_ARRAY_INDEX) {
265 length = i + 1;
266 m_storage->m_length = length;
267 }
268
269 if (i < m_storage->m_vectorLength) {
270 JSValue*& valueSlot = m_storage->m_vector[i];
271 if (valueSlot) {
272 valueSlot = value;
273 checkConsistency();
274 return;
275 }
276 valueSlot = value;
277 if (++m_storage->m_numValuesInVector == m_storage->m_length)
278 m_fastAccessCutoff = m_storage->m_length;
279 checkConsistency();
280 return;
281 }
282
283 putSlowCase(exec, i, value);
284}
285
286NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue* value)
287{
288 ArrayStorage* storage = m_storage;
289 SparseArrayValueMap* map = storage->m_sparseValueMap;
290
291 if (i >= MIN_SPARSE_ARRAY_INDEX) {
292 if (i > MAX_ARRAY_INDEX) {
293 PutPropertySlot slot;
294 put(exec, Identifier::from(exec, i), value, slot);
295 return;
296 }
297
298 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
299 // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
300 if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
301 if (!map) {
302 map = new SparseArrayValueMap;
303 storage->m_sparseValueMap = map;
304 }
305 map->set(i, value);
306 return;
307 }
308 }
309
310 // We have decided that we'll put the new item into the vector.
311 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
312 if (!map || map->isEmpty()) {
313 if (increaseVectorLength(i + 1)) {
314 storage = m_storage;
315 storage->m_vector[i] = value;
316 if (++storage->m_numValuesInVector == storage->m_length)
317 m_fastAccessCutoff = storage->m_length;
318 checkConsistency();
319 } else
320 throwOutOfMemoryError(exec);
321 return;
322 }
323
324 // Decide how many values it would be best to move from the map.
325 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
326 unsigned newVectorLength = increasedVectorLength(i + 1);
327 for (unsigned j = max(storage->m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
328 newNumValuesInVector += map->contains(j);
329 if (i >= MIN_SPARSE_ARRAY_INDEX)
330 newNumValuesInVector -= map->contains(i);
331 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
332 unsigned proposedNewNumValuesInVector = newNumValuesInVector;
333 // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
334 while (newVectorLength < MAX_STORAGE_VECTOR_LENGTH) {
335 unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
336 for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j)
337 proposedNewNumValuesInVector += map->contains(j);
338 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector))
339 break;
340 newVectorLength = proposedNewVectorLength;
341 newNumValuesInVector = proposedNewNumValuesInVector;
342 }
343 }
344
345 storage = static_cast<ArrayStorage*>(tryFastRealloc(storage, storageSize(newVectorLength)));
346 if (!storage) {
347 throwOutOfMemoryError(exec);
348 return;
349 }
350
351 unsigned vectorLength = storage->m_vectorLength;
352 if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
353 for (unsigned j = vectorLength; j < newVectorLength; ++j)
354 storage->m_vector[j] = noValue();
355 if (i > MIN_SPARSE_ARRAY_INDEX)
356 map->remove(i);
357 } else {
358 for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j)
359 storage->m_vector[j] = noValue();
360 for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
361 storage->m_vector[j] = map->take(j);
362 }
363
364 storage->m_vector[i] = value;
365
366 storage->m_vectorLength = newVectorLength;
367 storage->m_numValuesInVector = newNumValuesInVector;
368
369 m_storage = storage;
370
371 checkConsistency();
372}
373
374bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName)
375{
376 bool isArrayIndex;
377 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
378 if (isArrayIndex)
379 return deleteProperty(exec, i);
380
381 if (propertyName == exec->propertyNames().length)
382 return false;
383
384 return JSObject::deleteProperty(exec, propertyName);
385}
386
387bool JSArray::deleteProperty(ExecState* exec, unsigned i)
388{
389 checkConsistency();
390
391 ArrayStorage* storage = m_storage;
392
393 if (i < storage->m_vectorLength) {
394 JSValue*& valueSlot = storage->m_vector[i];
395 if (!valueSlot) {
396 checkConsistency();
397 return false;
398 }
399 valueSlot = noValue();
400 --storage->m_numValuesInVector;
401 if (m_fastAccessCutoff > i)
402 m_fastAccessCutoff = i;
403 checkConsistency();
404 return true;
405 }
406
407 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
408 if (i >= MIN_SPARSE_ARRAY_INDEX) {
409 SparseArrayValueMap::iterator it = map->find(i);
410 if (it != map->end()) {
411 map->remove(it);
412 checkConsistency();
413 return true;
414 }
415 }
416 }
417
418 checkConsistency();
419
420 if (i > MAX_ARRAY_INDEX)
421 return deleteProperty(exec, Identifier::from(exec, i));
422
423 return false;
424}
425
426void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
427{
428 // FIXME: Filling PropertyNameArray with an identifier for every integer
429 // is incredibly inefficient for large arrays. We need a different approach,
430 // which almost certainly means a different structure for PropertyNameArray.
431
432 ArrayStorage* storage = m_storage;
433
434 unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength);
435 for (unsigned i = 0; i < usedVectorLength; ++i) {
436 if (storage->m_vector[i])
437 propertyNames.add(Identifier::from(exec, i));
438 }
439
440 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
441 SparseArrayValueMap::iterator end = map->end();
442 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
443 propertyNames.add(Identifier::from(exec, it->first));
444 }
445
446 JSObject::getPropertyNames(exec, propertyNames);
447}
448
449bool JSArray::increaseVectorLength(unsigned newLength)
450{
451 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
452 // to the vector. Callers have to account for that, because they can do it more efficiently.
453
454 ArrayStorage* storage = m_storage;
455
456 unsigned vectorLength = storage->m_vectorLength;
457 ASSERT(newLength > vectorLength);
458 ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX);
459 unsigned newVectorLength = increasedVectorLength(newLength);
460
461 storage = static_cast<ArrayStorage*>(tryFastRealloc(storage, storageSize(newVectorLength)));
462 if (!storage)
463 return false;
464
465 storage->m_vectorLength = newVectorLength;
466
467 for (unsigned i = vectorLength; i < newVectorLength; ++i)
468 storage->m_vector[i] = noValue();
469
470 m_storage = storage;
471 return true;
472}
473
474void JSArray::setLength(unsigned newLength)
475{
476 checkConsistency();
477
478 ArrayStorage* storage = m_storage;
479
480 unsigned length = m_storage->m_length;
481
482 if (newLength < length) {
483 if (m_fastAccessCutoff > newLength)
484 m_fastAccessCutoff = newLength;
485
486 unsigned usedVectorLength = min(length, storage->m_vectorLength);
487 for (unsigned i = newLength; i < usedVectorLength; ++i) {
488 JSValue*& valueSlot = storage->m_vector[i];
489 bool hadValue = valueSlot;
490 valueSlot = noValue();
491 storage->m_numValuesInVector -= hadValue;
492 }
493
494 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
495 SparseArrayValueMap copy = *map;
496 SparseArrayValueMap::iterator end = copy.end();
497 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
498 if (it->first >= newLength)
499 map->remove(it->first);
500 }
501 if (map->isEmpty()) {
502 delete map;
503 storage->m_sparseValueMap = 0;
504 }
505 }
506 }
507
508 m_storage->m_length = newLength;
509
510 checkConsistency();
511}
512
513JSValue* JSArray::pop()
514{
515 checkConsistency();
516
517 unsigned length = m_storage->m_length;
518 if (!length)
519 return jsUndefined();
520
521 --length;
522
523 JSValue* result;
524
525 if (m_fastAccessCutoff > length) {
526 JSValue*& valueSlot = m_storage->m_vector[length];
527 result = valueSlot;
528 ASSERT(result);
529 valueSlot = noValue();
530 --m_storage->m_numValuesInVector;
531 m_fastAccessCutoff = length;
532 } else if (length < m_storage->m_vectorLength) {
533 JSValue*& valueSlot = m_storage->m_vector[length];
534 result = valueSlot;
535 valueSlot = noValue();
536 if (result)
537 --m_storage->m_numValuesInVector;
538 else
539 result = jsUndefined();
540 } else {
541 result = jsUndefined();
542 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
543 SparseArrayValueMap::iterator it = map->find(length);
544 if (it != map->end()) {
545 result = it->second;
546 map->remove(it);
547 if (map->isEmpty()) {
548 delete map;
549 m_storage->m_sparseValueMap = 0;
550 }
551 }
552 }
553 }
554
555 m_storage->m_length = length;
556
557 checkConsistency();
558
559 return result;
560}
561
562void JSArray::push(ExecState* exec, JSValue* value)
563{
564 checkConsistency();
565
566 if (m_storage->m_length < m_storage->m_vectorLength) {
567 ASSERT(!m_storage->m_vector[m_storage->m_length]);
568 m_storage->m_vector[m_storage->m_length] = value;
569 if (++m_storage->m_numValuesInVector == ++m_storage->m_length)
570 m_fastAccessCutoff = m_storage->m_length;
571 checkConsistency();
572 return;
573 }
574
575 if (m_storage->m_length < MIN_SPARSE_ARRAY_INDEX) {
576 SparseArrayValueMap* map = m_storage->m_sparseValueMap;
577 if (!map || map->isEmpty()) {
578 if (increaseVectorLength(m_storage->m_length + 1)) {
579 m_storage->m_vector[m_storage->m_length] = value;
580 if (++m_storage->m_numValuesInVector == ++m_storage->m_length)
581 m_fastAccessCutoff = m_storage->m_length;
582 checkConsistency();
583 return;
584 }
585 checkConsistency();
586 throwOutOfMemoryError(exec);
587 return;
588 }
589 }
590
591 putSlowCase(exec, m_storage->m_length++, value);
592}
593
594void JSArray::mark()
595{
596 JSObject::mark();
597
598 ArrayStorage* storage = m_storage;
599
600 unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength);
601 for (unsigned i = 0; i < usedVectorLength; ++i) {
602 JSValue* value = storage->m_vector[i];
603 if (value && !value->marked())
604 value->mark();
605 }
606
607 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
608 SparseArrayValueMap::iterator end = map->end();
609 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
610 JSValue* value = it->second;
611 if (!value->marked())
612 value->mark();
613 }
614 }
615}
616
617typedef std::pair<JSValue*, UString> ArrayQSortPair;
618
619static int compareByStringPairForQSort(const void* a, const void* b)
620{
621 const ArrayQSortPair* va = static_cast<const ArrayQSortPair*>(a);
622 const ArrayQSortPair* vb = static_cast<const ArrayQSortPair*>(b);
623 return compare(va->second, vb->second);
624}
625
626void JSArray::sort(ExecState* exec)
627{
628 unsigned lengthNotIncludingUndefined = compactForSorting();
629 if (m_storage->m_sparseValueMap) {
630 throwOutOfMemoryError(exec);
631 return;
632 }
633
634 if (!lengthNotIncludingUndefined)
635 return;
636
637 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
638 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
639 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
640 // random or otherwise changing results, effectively making compare function inconsistent.
641
642 Vector<ArrayQSortPair> values(lengthNotIncludingUndefined);
643 if (!values.begin()) {
644 throwOutOfMemoryError(exec);
645 return;
646 }
647
648 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
649 JSValue* value = m_storage->m_vector[i];
650 ASSERT(!value->isUndefined());
651 values[i].first = value;
652 }
653
654 // FIXME: While calling these toString functions, the array could be mutated.
655 // In that case, objects pointed to by values in this vector might get garbage-collected!
656
657 // FIXME: The following loop continues to call toString on subsequent values even after
658 // a toString call raises an exception.
659
660 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
661 values[i].second = values[i].first->toString(exec);
662
663 if (exec->hadException())
664 return;
665
666 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
667 // than O(N log N).
668
669#if HAVE(MERGESORT)
670 mergesort(values.begin(), values.size(), sizeof(ArrayQSortPair), compareByStringPairForQSort);
671#else
672 // FIXME: The qsort library function is likely to not be a stable sort.
673 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
674 qsort(values.begin(), values.size(), sizeof(ArrayQSortPair), compareByStringPairForQSort);
675#endif
676
677 // FIXME: If the toString function changed the length of the array, this might be
678 // modifying the vector incorrectly.
679
680 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
681 m_storage->m_vector[i] = values[i].first;
682
683 checkConsistency(SortConsistencyCheck);
684}
685
686struct AVLTreeNodeForArrayCompare {
687 JSValue* value;
688
689 // Child pointers. The high bit of gt is robbed and used as the
690 // balance factor sign. The high bit of lt is robbed and used as
691 // the magnitude of the balance factor.
692 int32_t gt;
693 int32_t lt;
694};
695
696struct AVLTreeAbstractorForArrayCompare {
697 typedef int32_t handle; // Handle is an index into m_nodes vector.
698 typedef JSValue* key;
699 typedef int32_t size;
700
701 Vector<AVLTreeNodeForArrayCompare> m_nodes;
702 ExecState* m_exec;
703 JSValue* m_compareFunction;
704 CallType m_compareCallType;
705 const CallData* m_compareCallData;
706 JSValue* m_globalThisValue;
707
708 handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
709 void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
710 handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
711 void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
712
713 int get_balance_factor(handle h)
714 {
715 if (m_nodes[h].gt & 0x80000000)
716 return -1;
717 return static_cast<unsigned>(m_nodes[h].lt) >> 31;
718 }
719
720 void set_balance_factor(handle h, int bf)
721 {
722 if (bf == 0) {
723 m_nodes[h].lt &= 0x7FFFFFFF;
724 m_nodes[h].gt &= 0x7FFFFFFF;
725 } else {
726 m_nodes[h].lt |= 0x80000000;
727 if (bf < 0)
728 m_nodes[h].gt |= 0x80000000;
729 else
730 m_nodes[h].gt &= 0x7FFFFFFF;
731 }
732 }
733
734 int compare_key_key(key va, key vb)
735 {
736 ASSERT(!va->isUndefined());
737 ASSERT(!vb->isUndefined());
738
739 if (m_exec->hadException())
740 return 1;
741
742 ArgList arguments;
743 arguments.append(va);
744 arguments.append(vb);
745 double compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments)->toNumber(m_exec);
746 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
747 }
748
749 int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
750 int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
751
752 static handle null() { return 0x7FFFFFFF; }
753};
754
755void JSArray::sort(ExecState* exec, JSValue* compareFunction, CallType callType, const CallData& callData)
756{
757 checkConsistency();
758
759 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
760
761 // The maximum tree depth is compiled in - but the caller is clearly up to no good
762 // if a larger array is passed.
763 ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
764 if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
765 return;
766
767 if (!m_storage->m_length)
768 return;
769
770 unsigned usedVectorLength = min(m_storage->m_length, m_storage->m_vectorLength);
771
772 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
773 tree.abstractor().m_exec = exec;
774 tree.abstractor().m_compareFunction = compareFunction;
775 tree.abstractor().m_compareCallType = callType;
776 tree.abstractor().m_compareCallData = &callData;
777 tree.abstractor().m_globalThisValue = exec->globalThisValue();
778 tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0));
779
780 if (!tree.abstractor().m_nodes.begin()) {
781 throwOutOfMemoryError(exec);
782 return;
783 }
784
785 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
786 // right out from under us while we're building the tree here.
787
788 unsigned numDefined = 0;
789 unsigned numUndefined = 0;
790
791 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
792 for (; numDefined < usedVectorLength; ++numDefined) {
793 JSValue* v = m_storage->m_vector[numDefined];
794 if (!v || v->isUndefined())
795 break;
796 tree.abstractor().m_nodes[numDefined].value = v;
797 tree.insert(numDefined);
798 }
799 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
800 if (JSValue* v = m_storage->m_vector[i]) {
801 if (v->isUndefined())
802 ++numUndefined;
803 else {
804 tree.abstractor().m_nodes[numDefined].value = v;
805 tree.insert(numDefined);
806 ++numDefined;
807 }
808 }
809 }
810
811 unsigned newUsedVectorLength = numDefined + numUndefined;
812
813 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
814 newUsedVectorLength += map->size();
815 if (newUsedVectorLength > m_storage->m_vectorLength) {
816 // Check that it is possible to allocate an array large enough to hold all the entries.
817 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) {
818 throwOutOfMemoryError(exec);
819 return;
820 }
821 }
822
823 SparseArrayValueMap::iterator end = map->end();
824 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
825 tree.abstractor().m_nodes[numDefined].value = it->second;
826 tree.insert(numDefined);
827 ++numDefined;
828 }
829
830 delete map;
831 m_storage->m_sparseValueMap = 0;
832 }
833
834 ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
835
836 // FIXME: If the compare function changed the length of the array, the following might be
837 // modifying the vector incorrectly.
838
839 // Copy the values back into m_storage.
840 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
841 iter.start_iter_least(tree);
842 for (unsigned i = 0; i < numDefined; ++i) {
843 m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value;
844 ++iter;
845 }
846
847 // Put undefined values back in.
848 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
849 m_storage->m_vector[i] = jsUndefined();
850
851 // Ensure that unused values in the vector are zeroed out.
852 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
853 m_storage->m_vector[i] = noValue();
854
855 m_fastAccessCutoff = newUsedVectorLength;
856 m_storage->m_numValuesInVector = newUsedVectorLength;
857
858 checkConsistency(SortConsistencyCheck);
859}
860
861void JSArray::fillArgList(ExecState* exec, ArgList& args)
862{
863 unsigned fastAccessLength = min(m_storage->m_length, m_fastAccessCutoff);
864 unsigned i = 0;
865 for (; i < fastAccessLength; ++i)
866 args.append(getIndex(i));
867 for (; i < m_storage->m_length; ++i)
868 args.append(get(exec, i));
869}
870
871unsigned JSArray::compactForSorting()
872{
873 checkConsistency();
874
875 ArrayStorage* storage = m_storage;
876
877 unsigned usedVectorLength = min(m_storage->m_length, storage->m_vectorLength);
878
879 unsigned numDefined = 0;
880 unsigned numUndefined = 0;
881
882 for (; numDefined < usedVectorLength; ++numDefined) {
883 JSValue* v = storage->m_vector[numDefined];
884 if (!v || v->isUndefined())
885 break;
886 }
887 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
888 if (JSValue* v = storage->m_vector[i]) {
889 if (v->isUndefined())
890 ++numUndefined;
891 else
892 storage->m_vector[numDefined++] = v;
893 }
894 }
895
896 unsigned newUsedVectorLength = numDefined + numUndefined;
897
898 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
899 newUsedVectorLength += map->size();
900 if (newUsedVectorLength > storage->m_vectorLength) {
901 // Check that it is possible to allocate an array large enough to hold all the entries - if not,
902 // exception is thrown by caller.
903 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength))
904 return 0;
905 storage = m_storage;
906 }
907
908 SparseArrayValueMap::iterator end = map->end();
909 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
910 storage->m_vector[numDefined++] = it->second;
911
912 delete map;
913 storage->m_sparseValueMap = 0;
914 }
915
916 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
917 storage->m_vector[i] = jsUndefined();
918 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
919 storage->m_vector[i] = noValue();
920
921 m_fastAccessCutoff = newUsedVectorLength;
922 storage->m_numValuesInVector = newUsedVectorLength;
923
924 checkConsistency(SortConsistencyCheck);
925
926 return numDefined;
927}
928
929void* JSArray::lazyCreationData()
930{
931 return m_storage->lazyCreationData;
932}
933
934void JSArray::setLazyCreationData(void* d)
935{
936 m_storage->lazyCreationData = d;
937}
938
939#if CHECK_ARRAY_CONSISTENCY
940
941void JSArray::checkConsistency(ConsistencyCheckType type)
942{
943 ASSERT(m_storage);
944 if (type == SortConsistencyCheck)
945 ASSERT(!m_storage->m_sparseValueMap);
946
947 ASSERT(m_fastAccessCutoff <= m_storage->m_length);
948 ASSERT(m_fastAccessCutoff <= m_storage->m_numValuesInVector);
949
950 unsigned numValuesInVector = 0;
951 for (unsigned i = 0; i < m_storage->m_vectorLength; ++i) {
952 if (JSValue* value = m_storage->m_vector[i]) {
953 ASSERT(i < m_storage->m_length);
954 if (type != DestructorConsistencyCheck)
955 value->type(); // Likely to crash if the object was deallocated.
956 ++numValuesInVector;
957 } else {
958 ASSERT(i >= m_fastAccessCutoff);
959 if (type == SortConsistencyCheck)
960 ASSERT(i >= m_storage->m_numValuesInVector);
961 }
962 }
963 ASSERT(numValuesInVector == m_storage->m_numValuesInVector);
964
965 if (m_storage->m_sparseValueMap) {
966 SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end();
967 for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) {
968 unsigned index = it->first;
969 ASSERT(index < m_storage->m_length);
970 ASSERT(index >= m_storage->m_vectorLength);
971 ASSERT(index <= MAX_ARRAY_INDEX);
972 ASSERT(it->second);
973 if (type != DestructorConsistencyCheck)
974 it->second->type(); // Likely to crash if the object was deallocated.
975 }
976 }
977}
978
979#endif
980
981JSArray* constructEmptyArray(ExecState* exec)
982{
983 return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure());
984}
985
986JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength)
987{
988 return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength);
989}
990
991JSArray* constructArray(ExecState* exec, JSValue* singleItemValue)
992{
993 ArgList values;
994 values.append(singleItemValue);
995 return new (exec) JSArray(exec, exec->lexicalGlobalObject()->arrayStructure(), values);
996}
997
998JSArray* constructArray(ExecState* exec, const ArgList& values)
999{
1000 return new (exec) JSArray(exec, exec->lexicalGlobalObject()->arrayStructure(), values);
1001}
1002
1003} // namespace JSC
Note: See TracBrowser for help on using the repository browser.