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

Last change on this file since 43383 was 43153, checked in by [email protected], 16 years ago

JavaScriptCore:

2009-05-02 Geoffrey Garen <[email protected]>

Reviewed by Sam Weinig.

Simplified null-ish JSValues.


Replaced calls to noValue() with calls to JSValue() (which is what
noValue() returned). Removed noValue().


Replaced almost all uses of jsImpossibleValue() with uses of JSValue().
Its one remaining use is for construction of hash table deleted values.
For that specific task, I made a new, private constructor with a special
tag. Removed jsImpossibleValue().


Removed "JSValue()" initialiazers, since default construction happens...
by default.

  • API/JSCallbackObjectFunctions.h: (JSC::::call):
  • bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::emitLoad):
  • bytecompiler/BytecodeGenerator.h:
  • debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluate):
  • debugger/DebuggerCallFrame.h: (JSC::DebuggerCallFrame::DebuggerCallFrame):
  • interpreter/CallFrame.h: (JSC::ExecState::clearException):
  • interpreter/Interpreter.cpp: (JSC::Interpreter::privateExecute): (JSC::Interpreter::retrieveLastCaller):
  • interpreter/Register.h: (JSC::Register::Register):
  • jit/JITCall.cpp: (JSC::JIT::unlinkCall): (JSC::JIT::compileOpCallInitializeCallFrame): (JSC::JIT::compileOpCall):
  • jit/JITStubs.cpp: (JSC::JITStubs::cti_op_call_eval): (JSC::JITStubs::cti_vm_throw):
  • profiler/Profiler.cpp: (JSC::Profiler::willExecute): (JSC::Profiler::didExecute):
  • runtime/ArrayPrototype.cpp: (JSC::getProperty):
  • runtime/Completion.cpp: (JSC::evaluate):
  • runtime/Completion.h: (JSC::Completion::Completion):
  • runtime/GetterSetter.cpp: (JSC::GetterSetter::getPrimitiveNumber):
  • runtime/JSArray.cpp: (JSC::JSArray::putSlowCase): (JSC::JSArray::deleteProperty): (JSC::JSArray::increaseVectorLength): (JSC::JSArray::setLength): (JSC::JSArray::pop): (JSC::JSArray::sort): (JSC::JSArray::compactForSorting):
  • runtime/JSCell.cpp: (JSC::JSCell::getJSNumber):
  • runtime/JSCell.h: (JSC::JSValue::getJSNumber):
  • runtime/JSGlobalData.cpp: (JSC::JSGlobalData::JSGlobalData):
  • runtime/JSImmediate.h: (JSC::JSImmediate::fromNumberOutsideIntegerRange): (JSC::JSImmediate::from):
  • runtime/JSNumberCell.cpp: (JSC::jsNumberCell):
  • runtime/JSObject.cpp: (JSC::callDefaultValueFunction):
  • runtime/JSObject.h: (JSC::JSObject::getDirect):
  • runtime/JSPropertyNameIterator.cpp: (JSC::JSPropertyNameIterator::toPrimitive):
  • runtime/JSPropertyNameIterator.h: (JSC::JSPropertyNameIterator::next):
  • runtime/JSValue.h: (JSC::JSValue::): (JSC::JSValueHashTraits::constructDeletedValue): (JSC::JSValueHashTraits::isDeletedValue): (JSC::JSValue::JSValue):
  • runtime/JSWrapperObject.h: (JSC::JSWrapperObject::JSWrapperObject):
  • runtime/Operations.h: (JSC::resolveBase):
  • runtime/PropertySlot.h: (JSC::PropertySlot::clearBase): (JSC::PropertySlot::clearValue):

WebCore:

2009-05-02 Geoffrey Garen <[email protected]>

Reviewed by Sam Weinig.

Simplified null-ish JSValues.


Replaced calls to noValue() with calls to JSValue() (which is what
noValue() returned). Removed noValue().


Removed "JSValue()" initialiazers, since default construction happens...
by default.

  • bindings/js/JSDOMBinding.cpp: (WebCore::setDOMException):
  • bindings/js/JSDOMWindowCustom.cpp: (WebCore::JSDOMWindow::open): (WebCore::JSDOMWindow::showModalDialog):
  • bindings/js/JSEventListener.cpp: (WebCore::JSEventListener::handleEvent):
  • bindings/js/JSJavaScriptCallFrameCustom.cpp: (WebCore::JSJavaScriptCallFrame::evaluate):
  • bindings/js/JSSQLResultSetRowListCustom.cpp: (WebCore::JSSQLResultSetRowList::item):
  • bindings/js/ScriptController.cpp: (WebCore::ScriptController::evaluate):
  • bindings/js/ScriptValue.h: (WebCore::ScriptValue::ScriptValue): (WebCore::ScriptValue::hasNoValue):
  • bindings/js/WorkerScriptController.cpp: (WebCore::WorkerScriptController::evaluate):
  • bridge/jni/jni_instance.cpp: (JavaInstance::invokeMethod):
  • bridge/jni/jni_runtime.cpp: (JavaField::dispatchValueFromInstance): (JavaField::dispatchSetValueToInstance):
  • bridge/runtime.h: (JSC::Bindings::Instance::invokeConstruct):

WebKit/mac:

2009-05-02 Geoffrey Garen <[email protected]>

Reviewed by Sam Weinig.

Simplified null-ish JSValues.


Replaced calls to noValue() with calls to JSValue() (which is what
noValue() returned). Removed noValue().


Removed "JSValue()" initialiazers, since default construction happens...
by default.

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