1 /*
2 * This file is part of the KDE libraries
3 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
4 * Copyright (C) 2003, 2007, 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2003 Peter Kelly (pmk@post.com)
6 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 */
23
24 #include "array_instance.h"
25
26 #include "PropertyNameArray.h"
27 #include "JSVariableObject.h"
28 #include <wtf/Assertions.h>
29 #include <wtf/HashMap.h>
30
31 #include <algorithm>
32 #include <stdio.h>
33
34 using std::min;
35 using std::max;
36
37 namespace KJS
38 {
39
40 struct ArrayEntity {
ArrayEntityKJS::ArrayEntity41 ArrayEntity()
42 : value(nullptr),
43 attributes(0)
44 {
45 }
46
ArrayEntityKJS::ArrayEntity47 ArrayEntity(JSValue *jsVal, uint32_t attr)
48 : value(jsVal),
49 attributes(attr)
50 {
51 }
52
53 JSValue *value;
54 uint32_t attributes;
55 };
56
57 typedef HashMap<unsigned, ArrayEntity> SparseArrayValueMap;
58
59 struct ArrayStorage {
60 unsigned m_numValuesInVector;
61 SparseArrayValueMap *m_sparseValueMap;
62 ArrayEntity m_vector[1];
63 };
64
65 // (2^32)-1
66 static const unsigned maxArrayLength = 0xFFFFFFFFU;
67 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer
68 static const unsigned maxArrayIndex = 0xFFFFFFFEU;
69
70 // Our policy for when to use a vector and when to use a sparse map.
71 // For all array indices under sparseArrayCutoff, we always use a vector.
72 // When indices greater than sparseArrayCutoff are involved, we use a vector
73 // as long as it is 1/8 full. If more sparse than that, we use a map.
74 static const unsigned sparseArrayCutoff = 10000;
75 static const unsigned minDensityMultiplier = 8;
76
77 static const unsigned mergeSortCutoff = 10000;
78
79 const ClassInfo ArrayInstance::info = {"Array", nullptr, nullptr, nullptr};
80
storageSize(unsigned vectorLength)81 static inline size_t storageSize(unsigned vectorLength)
82 {
83 return sizeof(ArrayStorage) - sizeof(ArrayEntity) + vectorLength * sizeof(ArrayEntity);
84 }
85
increasedVectorLength(unsigned newLength)86 static inline unsigned increasedVectorLength(unsigned newLength)
87 {
88 return (newLength * 3 + 1) / 2;
89 }
90
isDenseEnoughForVector(unsigned length,unsigned numValues)91 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
92 {
93 return length / minDensityMultiplier <= numValues;
94 }
95
ArrayInstance(JSObject * prototype,unsigned initialLength)96 ArrayInstance::ArrayInstance(JSObject *prototype, unsigned initialLength)
97 : JSObject(prototype)
98 {
99 unsigned initialCapacity = min(initialLength, sparseArrayCutoff);
100
101 m_length = initialLength;
102 m_vectorLength = initialCapacity;
103 m_storage = static_cast<ArrayStorage *>(fastCalloc(storageSize(initialCapacity), 1));
104 m_lengthAttributes = DontDelete | DontEnum;
105
106 Collector::reportExtraMemoryCost(initialCapacity * sizeof(ArrayEntity));
107 }
108
ArrayInstance(JSObject * prototype,const List & list)109 ArrayInstance::ArrayInstance(JSObject *prototype, const List &list)
110 : JSObject(prototype)
111 {
112 unsigned length = list.size();
113
114 m_length = length;
115 m_vectorLength = length;
116 m_lengthAttributes = DontDelete | DontEnum;
117
118 ArrayStorage *storage = static_cast<ArrayStorage *>(fastMalloc(storageSize(length)));
119
120 storage->m_numValuesInVector = length;
121 storage->m_sparseValueMap = nullptr;
122
123 ListIterator it = list.begin();
124 for (unsigned i = 0; i < length; ++i) {
125 storage->m_vector[i].value = it++;
126 storage->m_vector[i].attributes = 0;
127 }
128
129 m_storage = storage;
130
131 // When the array is created non-empty, its cells are filled, so it's really no worse than
132 // a property map. Therefore don't report extra memory cost.
133 }
134
~ArrayInstance()135 ArrayInstance::~ArrayInstance()
136 {
137 delete m_storage->m_sparseValueMap;
138 fastFree(m_storage);
139 }
140
getItem(unsigned i) const141 JSValue *ArrayInstance::getItem(unsigned i) const
142 {
143 ASSERT(i <= maxArrayIndex);
144
145 ArrayEntity *ent = getArrayEntity(i);
146 if (ent) {
147 return ent->value;
148 }
149 return jsUndefined();
150 }
151
lengthGetter(ExecState *,JSObject *,const Identifier &,const PropertySlot & slot)152 JSValue *ArrayInstance::lengthGetter(ExecState *, JSObject *, const Identifier &, const PropertySlot &slot)
153 {
154 return jsNumber(static_cast<ArrayInstance *>(slot.slotBase())->m_length);
155 }
156
inlineGetOwnPropertySlot(ExecState * exec,unsigned i,PropertySlot & slot)157 ALWAYS_INLINE bool ArrayInstance::inlineGetOwnPropertySlot(ExecState *exec, unsigned i, PropertySlot &slot)
158 {
159 if (i >= m_length) {
160 if (i > maxArrayIndex) {
161 return getOwnPropertySlot(exec, Identifier::from(i), slot);
162 }
163 return false;
164 }
165
166 ArrayEntity *ent = getArrayEntity(i);
167 if (ent) {
168 if (ent->attributes & GetterSetter) {
169 GetterSetterImp *gs = static_cast<GetterSetterImp *>(ent->value);
170 JSObject *getterFunc = gs->getGetter();
171 if (getterFunc) {
172 slot.setGetterSlot(this, getterFunc);
173 } else {
174 slot.setUndefined(this);
175 }
176 return true;
177 }
178 slot.setValueSlot(this, &ent->value);
179 return true;
180 }
181
182 return false;
183 }
184
getArrayEntity(unsigned int i) const185 ArrayEntity *ArrayInstance::getArrayEntity(unsigned int i) const
186 {
187 if (i >= m_length) {
188 return nullptr;
189 }
190
191 ArrayStorage *storage = m_storage;
192 if (i < m_vectorLength) {
193 if (storage->m_vector[i].value) {
194 return &storage->m_vector[i];
195 }
196 }
197
198 SparseArrayValueMap *map = storage->m_sparseValueMap;
199 if (map && i > 0 && i <= maxArrayIndex) {
200 SparseArrayValueMap::iterator it = map->find(i);
201 if (it != map->end()) {
202 return &it->second;
203 }
204 }
205
206 return nullptr;
207 }
208
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)209 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
210 {
211 if (propertyName == exec->propertyNames().length) {
212 slot.setCustom(this, lengthGetter);
213 return true;
214 }
215
216 bool isArrayIndex;
217 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
218 if (isArrayIndex) {
219 return inlineGetOwnPropertySlot(exec, i, slot);
220 }
221
222 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
223 }
224
getOwnPropertySlot(ExecState * exec,unsigned i,PropertySlot & slot)225 bool ArrayInstance::getOwnPropertySlot(ExecState *exec, unsigned i, PropertySlot &slot)
226 {
227 return inlineGetOwnPropertySlot(exec, i, slot);
228 }
229
230 // ECMA 15.4.5.1
put(ExecState * exec,const Identifier & propertyName,JSValue * value,int attributes)231 void ArrayInstance::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attributes)
232 {
233 bool isArrayIndex;
234 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
235 if (isArrayIndex) {
236 put(exec, i, value, attributes);
237 return;
238 }
239
240 if (propertyName == exec->propertyNames().length) {
241 if (m_lengthAttributes & ReadOnly) {
242 return;
243 }
244 unsigned newLength = JSValue::toUInt32(value, exec);
245 if (JSValue::toNumber(value, exec) != static_cast<double>(newLength)) {
246 throwError(exec, RangeError, "Invalid array length.");
247 return;
248 }
249 m_lengthAttributes = attributes;
250 setLength(newLength);
251 return;
252 }
253
254 JSObject::put(exec, propertyName, value, attributes);
255 }
256
put(ExecState * exec,unsigned i,JSValue * value,int attributes)257 void ArrayInstance::put(ExecState *exec, unsigned i, JSValue *value, int attributes)
258 {
259 ArrayEntity *ent = getArrayEntity(i);
260 if (ent) {
261 if (ent->attributes & ReadOnly) {
262 return;
263 }
264 attributes |= ent->attributes;
265
266 JSValue *gs = ent->value;
267 if (gs && !JSValue::isUndefined(gs)) {
268 if (ent->attributes & GetterSetter) {
269 JSObject *setterFunc = static_cast<GetterSetterImp *>(gs)->getSetter();
270
271 if (!setterFunc) {
272 if (false) { //if strict is true
273 throwError(exec, TypeError, "setting a property that has only a getter");
274 }
275 return;
276 }
277
278 List args;
279 args.append(value);
280
281 setterFunc->call(exec, this, args);
282 return;
283 }
284 }
285 }
286
287 KJS::ArrayInstance::putDirect(i, value, attributes);
288 }
289
deleteProperty(ExecState * exec,const Identifier & propertyName)290 bool ArrayInstance::deleteProperty(ExecState *exec, const Identifier &propertyName)
291 {
292 bool isArrayIndex;
293 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
294 if (isArrayIndex) {
295 return deleteProperty(exec, i);
296 }
297
298 if (propertyName == exec->propertyNames().length) {
299 return false;
300 }
301
302 return JSObject::deleteProperty(exec, propertyName);
303 }
304
deleteProperty(ExecState * exec,unsigned i)305 bool ArrayInstance::deleteProperty(ExecState *exec, unsigned i)
306 {
307 ArrayStorage *storage = m_storage;
308
309 if (i < m_vectorLength) {
310 ArrayEntity *ent = &storage->m_vector[i];
311 if (ent->value) {
312 if (ent->attributes & DontDelete) {
313 return false;
314 }
315
316 JSValue *&valueSlot = ent->value;
317 bool hadValue = valueSlot;
318 valueSlot = nullptr;
319 storage->m_numValuesInVector -= hadValue;
320 ent->value = nullptr;
321 return hadValue;
322 }
323 }
324
325 if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
326 SparseArrayValueMap::iterator it = map->find(i);
327 if (it != map->end()) {
328 if ((*it).second.attributes & DontDelete) {
329 return false;
330 }
331 map->remove(it->first);
332 return true;
333 }
334 }
335
336 if (i > maxArrayIndex) {
337 return JSObject::deleteProperty(exec, Identifier::from(i));
338 }
339
340 return true;
341 }
342
getOwnPropertyNames(ExecState * exec,PropertyNameArray & propertyNames,PropertyMap::PropertyMode mode)343 void ArrayInstance::getOwnPropertyNames(ExecState *exec, PropertyNameArray &propertyNames, PropertyMap::PropertyMode mode)
344 {
345 // FIXME: Filling PropertyNameArray with an identifier for every integer
346 // is incredibly inefficient for large arrays. We need a different approach.
347
348 ArrayStorage *storage = m_storage;
349
350 unsigned usedVectorLength = min(m_length, m_vectorLength);
351 for (unsigned i = 0; i < usedVectorLength; ++i) {
352 if (storage->m_vector[i].value &&
353 (!(storage->m_vector[i].attributes & DontEnum) ||
354 mode == PropertyMap::IncludeDontEnumProperties)) {
355 propertyNames.add(Identifier::from(i));
356 }
357 }
358
359 if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
360 SparseArrayValueMap::iterator end = map->end();
361 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
362 if (!((*it).second.attributes & DontEnum) ||
363 mode == PropertyMap::IncludeDontEnumProperties) {
364 propertyNames.add(Identifier::from(it->first));
365 }
366 }
367
368 if (mode == PropertyMap::IncludeDontEnumProperties) {
369 propertyNames.add(exec->propertyNames().length);
370 }
371
372 JSObject::getOwnPropertyNames(exec, propertyNames, mode);
373 }
374
getOwnPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & descriptor)375 bool ArrayInstance::getOwnPropertyDescriptor(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &descriptor)
376 {
377 if (propertyName == exec->propertyNames().length) {
378 descriptor.setPropertyDescriptorValues(exec, jsNumber(m_length), m_lengthAttributes);
379 return true;
380 }
381
382 bool isArrayIndex;
383 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
384
385 if (isArrayIndex) {
386 ArrayEntity *ent = getArrayEntity(i);
387 if (ent) {
388 descriptor.setPropertyDescriptorValues(exec, ent->value, ent->attributes);
389 return true;
390 }
391 }
392 return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor);
393 }
394
395 //ECMAScript Edition 5.1r6 - 15.4.5.1
defineOwnProperty(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & desc,bool shouldThrow)396 bool ArrayInstance::defineOwnProperty(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc, bool shouldThrow)
397 {
398 PropertyDescriptor oldLenDesc;
399 getOwnPropertyDescriptor(exec, exec->propertyNames().length, oldLenDesc);
400 unsigned int oldLen = JSValue::toUInt32(oldLenDesc.value(), exec);
401
402 //4a
403 bool isArrayIndex;
404 unsigned index = propertyName.toArrayIndex(&isArrayIndex);
405
406 //Step 3
407 if (propertyName == exec->propertyNames().length) {
408 //a
409 if (!desc.value()) {
410 return JSObject::defineOwnProperty(exec, propertyName, desc, shouldThrow);
411 }
412
413 //b
414 PropertyDescriptor newLenDesc(desc);
415 //c
416 unsigned int newLen = JSValue::toUInt32(desc.value(), exec);
417 //d
418 if (newLen != JSValue::toNumber(desc.value(), exec) || JSValue::toNumber(desc.value(), exec) > maxArrayLength) {
419 throwError(exec, RangeError, "Index out of range");
420 return false;
421 }
422 //e
423 newLenDesc.setValue(jsNumber(newLen));
424 //f
425 if (newLen >= oldLen) {
426 return JSObject::defineOwnProperty(exec, propertyName, newLenDesc, shouldThrow);
427 }
428 //g
429 if (!oldLenDesc.writable()) {
430 if (shouldThrow) {
431 throwError(exec, TypeError, "length is not writable");
432 }
433 return false;
434 }
435 //h
436 bool newWriteable;
437 if (!newLenDesc.writableSet() || newLenDesc.writable()) {
438 newWriteable = true;
439 } else { //i
440 if (anyItemHasAttribute(DontDelete)) {
441 newLenDesc.setWritable(false);
442 } else {
443 newLenDesc.setWritable(true);
444 }
445 newWriteable = false;
446 }
447 //j
448 bool succeeded = JSObject::defineOwnProperty(exec, propertyName, newLenDesc, shouldThrow);
449 //k
450 if (!succeeded) {
451 return false;
452 }
453 //l
454 while (newLen < oldLen) {
455 --oldLen;
456 bool deleteSucceeded = deleteProperty(exec, oldLen); // 3. argument should be false
457 if (!deleteSucceeded) {
458 newLenDesc.setValue(jsNumber(oldLen + 1));
459 if (!newWriteable) {
460 newLenDesc.setWritable(false);
461 }
462 JSObject::defineOwnProperty(exec, propertyName, newLenDesc, false);
463 if (shouldThrow) {
464 throwError(exec, TypeError);
465 }
466 return false;
467 }
468 }
469 //m
470 if (!newWriteable) {
471 PropertyDescriptor writableDesc;
472 writableDesc.setWritable(false);
473 return JSObject::defineOwnProperty(exec, propertyName, writableDesc, false);
474 }
475 return true;
476 } else if (isArrayIndex) {//Step 4
477 //b
478 if (index >= oldLen && !oldLenDesc.writable()) {
479 if (shouldThrow) {
480 throwError(exec, TypeError);
481 }
482 return false;
483 }
484 //c
485 bool succeeded = JSObject::defineOwnProperty(exec, propertyName, desc, false);
486 //d
487 if (!succeeded) {
488 if (shouldThrow) {
489 throwError(exec, TypeError);
490 }
491 return false;
492 }
493 //e
494 if (index >= oldLen) {
495 oldLenDesc.setValue(jsNumber(index + 1));
496 JSObject::defineOwnProperty(exec, exec->propertyNames().length, oldLenDesc, false);
497 }
498 return true;
499 }
500
501 return JSObject::defineOwnProperty(exec, propertyName, desc, shouldThrow);
502 }
503
getPropertyAttributes(const Identifier & propertyName,unsigned int & attributes) const504 bool ArrayInstance::getPropertyAttributes(const Identifier &propertyName, unsigned int &attributes) const
505 {
506 bool isArrayIndex;
507 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
508
509 if (isArrayIndex) {
510 ArrayEntity *ent = getArrayEntity(i);
511 if (ent) {
512 attributes = ent->attributes;
513 return true;
514 }
515 }
516
517 return KJS::JSObject::getPropertyAttributes(propertyName, attributes);
518 }
519
getDirect(const Identifier & propertyName) const520 JSValue *ArrayInstance::getDirect(const Identifier &propertyName) const
521 {
522 bool isArrayIndex;
523 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
524
525 if (isArrayIndex) {
526 ArrayEntity *ent = getArrayEntity(i);
527 if (ent) {
528 if (ent->value) {
529 return ent->value;
530 }
531 }
532 }
533
534 return KJS::JSObject::getDirect(propertyName);
535 }
536
putDirect(unsigned i,JSValue * value,int attributes)537 void ArrayInstance::putDirect(unsigned i, JSValue *value, int attributes)
538 {
539 unsigned length = m_length;
540
541 if (i >= length) {
542 if (i > maxArrayIndex) {
543 KJS::JSObject::putDirect(Identifier::from(i), value, attributes);
544 return;
545 }
546 length = i + 1;
547 m_length = length;
548 }
549
550 ArrayStorage *storage = m_storage;
551
552 if (i < m_vectorLength) {
553 ArrayEntity *ent = &storage->m_vector[i];
554 if (!ent->value && !isExtensible()) {
555 return;
556 }
557 JSValue *&valueSlot = ent->value;
558 storage->m_numValuesInVector += !valueSlot;
559 valueSlot = value;
560 ent->attributes = attributes;
561 return;
562 }
563
564 if (!isExtensible()) {
565 return;
566 }
567
568 SparseArrayValueMap *map = storage->m_sparseValueMap;
569 if (i >= sparseArrayCutoff) {
570 // If the index is high, go to the map unless we're pretty dense.
571 if (!map) {
572 map = new SparseArrayValueMap;
573 storage->m_sparseValueMap = map;
574
575 // If we create a sparse map, we need to ensure that there is at least one spot
576 // in the vector map, however, since the sparse map can't put/get key 0.
577 // It's safe to do it here, since put(0) will always put it in the vector part,
578 // but we have to do it before a get(0) or it will crash
579 if (!m_vectorLength) {
580 increaseVectorLength(1);
581 }
582 }
583
584 map->set(i, ArrayEntity(value, attributes));
585 return;
586 }
587
588 // note: an invariant here is that indices < sparseArrayCutoff
589 // are always inside the vector portion.
590
591 // lowish indices or high density -> we have decided that we'll put the new item into the vector.
592 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from the sparse map.
593 if (!map || map->isEmpty()) {
594 increaseVectorLength(i + 1);
595 storage = m_storage;
596 ++storage->m_numValuesInVector;
597 storage->m_vector[i].value = value;
598 storage->m_vector[i].attributes = attributes;
599 return;
600 }
601
602 // Decide just how large we want to make the vector to be.
603 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
604 unsigned newVectorLength = increasedVectorLength(i + 1);
605
606 // First, count how much stuff we are guaranteed to move over, now
607 // that we've decided to at least include i in the vector.
608 for (unsigned j = max(m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) {
609 newNumValuesInVector += map->contains(j);
610 }
611 if (i >= sparseArrayCutoff) {
612 newNumValuesInVector -= map->contains(i);
613 }
614
615 // Continue increasing the vector portion as long as the things in the map are dense enough
616 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
617 unsigned proposedNewNumValuesInVector = newNumValuesInVector;
618 while (true) {
619 unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
620 for (unsigned j = max(newVectorLength, sparseArrayCutoff); j < proposedNewVectorLength; ++j) {
621 proposedNewNumValuesInVector += map->contains(j);
622 }
623 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector)) {
624 break;
625 }
626 newVectorLength = proposedNewVectorLength;
627 newNumValuesInVector = proposedNewNumValuesInVector;
628 }
629 }
630
631 storage = static_cast<ArrayStorage *>(fastRealloc(storage, storageSize(newVectorLength)));
632
633 unsigned vectorLength = m_vectorLength;
634
635 // Special case: if we just added a single value, we don't have to scan the map
636 // to see what to remove from it
637 if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
638 for (unsigned j = vectorLength; j < newVectorLength; ++j) {
639 storage->m_vector[j].value = nullptr;
640 }
641 if (i > sparseArrayCutoff) {
642 map->remove(i);
643 }
644 } else {
645 // Move over things from the map to the new array portion
646 for (unsigned j = vectorLength; j < max(vectorLength, sparseArrayCutoff); ++j) {
647 storage->m_vector[j].value = nullptr;
648 }
649 for (unsigned j = max(vectorLength, sparseArrayCutoff); j < newVectorLength; ++j) {
650 storage->m_vector[j] = map->take(j);
651 }
652 }
653
654 storage->m_vector[i].value = value;
655 storage->m_vector[i].attributes = attributes;
656
657 m_vectorLength = newVectorLength;
658 storage->m_numValuesInVector = newNumValuesInVector;
659
660 m_storage = storage;
661 }
662
putDirect(const Identifier & propertyName,JSValue * value,int attr)663 void ArrayInstance::putDirect(const Identifier &propertyName, JSValue *value, int attr)
664 {
665 bool isArrayIndex;
666 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
667
668 if (isArrayIndex) {
669 KJS::ArrayInstance::putDirect(i, value, attr);
670 return;
671 }
672
673 KJS::JSObject::putDirect(propertyName, value, attr);
674 }
675
putDirect(const Identifier & propertyName,int value,int attr)676 void ArrayInstance::putDirect(const Identifier &propertyName, int value, int attr)
677 {
678 bool isArrayIndex;
679 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
680
681 if (isArrayIndex) {
682 KJS::ArrayInstance::putDirect(i, jsNumber(value), attr);
683 return;
684 }
685
686 KJS::JSObject::putDirect(propertyName, jsNumber(value), attr);
687 }
688
removeDirect(const Identifier & propertyName)689 void ArrayInstance::removeDirect(const Identifier &propertyName)
690 {
691 bool isArrayIndex;
692 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
693
694 if (isArrayIndex) {
695 ArrayStorage *storage = m_storage;
696
697 if (i < m_vectorLength) {
698 ArrayEntity *ent = &storage->m_vector[i];
699 if (ent->value) {
700 JSValue *&valueSlot = ent->value;
701 bool hadValue = valueSlot;
702 valueSlot = nullptr;
703 storage->m_numValuesInVector -= hadValue;
704 ent->value = nullptr;
705 return;
706 }
707 }
708
709 if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
710 SparseArrayValueMap::iterator it = map->find(i);
711 if (it != map->end()) {
712 map->remove(it->first);
713 return;
714 }
715 }
716 }
717
718 JSObject::removeDirect(Identifier::from(i));
719 }
720
increaseVectorLength(unsigned newLength)721 void ArrayInstance::increaseVectorLength(unsigned newLength)
722 {
723 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
724 // to the vector. Callers have to account for that, because they can do it more efficiently.
725
726 ArrayStorage *storage = m_storage;
727
728 unsigned vectorLength = m_vectorLength;
729 ASSERT(newLength > vectorLength);
730 unsigned newVectorLength = increasedVectorLength(newLength);
731
732 storage = static_cast<ArrayStorage *>(fastRealloc(storage, storageSize(newVectorLength)));
733 m_vectorLength = newVectorLength;
734
735 for (unsigned i = vectorLength; i < newVectorLength; ++i) {
736 storage->m_vector[i].value = nullptr;
737 }
738
739 m_storage = storage;
740 }
741
setLength(unsigned newLength)742 void ArrayInstance::setLength(unsigned newLength)
743 {
744 ArrayStorage *storage = m_storage;
745
746 unsigned length = m_length;
747 unsigned newLenSet = newLength;
748
749 if (newLength < length) {
750 unsigned usedVectorLength = min(length, m_vectorLength);
751 if (usedVectorLength > 0) {
752 for (unsigned i = usedVectorLength - 1; i >= newLength && i > 0; --i) {
753 ArrayEntity *ent = &storage->m_vector[i];
754 if (ent->value) {
755 if (ent->attributes & DontDelete) {
756 newLenSet = i + 1;
757 break;
758 }
759 JSValue *&valueSlot = ent->value;
760 bool hadValue = valueSlot;
761 valueSlot = nullptr;
762 ent->value = nullptr;
763 storage->m_numValuesInVector -= hadValue;
764 }
765 }
766 }
767
768 if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
769 SparseArrayValueMap copy = *map;
770 SparseArrayValueMap::iterator end = copy.end();
771 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
772 if (it->first >= newLength) {
773 if (it->second.attributes & DontDelete) {
774 newLenSet = it->first + 1;
775 }
776 }
777 }
778
779 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
780 if (it->first >= newLenSet) {
781 map->remove(it->first);
782 }
783 }
784
785 if (map->isEmpty()) {
786 delete map;
787 storage->m_sparseValueMap = nullptr;
788 }
789 }
790 }
791
792 m_length = newLenSet;
793 }
794
mark()795 void ArrayInstance::mark()
796 {
797 JSObject::mark();
798
799 ArrayStorage *storage = m_storage;
800
801 unsigned usedVectorLength = min(m_length, m_vectorLength);
802 for (unsigned i = 0; i < usedVectorLength; ++i) {
803 ArrayEntity *ent = &storage->m_vector[i];
804 if (ent->value && !JSValue::marked(ent->value)) {
805 JSValue::mark(ent->value);
806 }
807 }
808
809 if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
810 SparseArrayValueMap::iterator end = map->end();
811 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
812 ArrayEntity *ent = &it->second;
813 if (!JSValue::marked(ent->value)) {
814 JSValue::mark(ent->value);
815 }
816 }
817 }
818 }
819
820 static ExecState *execForCompareByStringForQSort;
821
compareByStringForQSort(const void * a,const void * b)822 static int compareByStringForQSort(const void *a, const void *b)
823 {
824 ExecState *exec = execForCompareByStringForQSort;
825
826 const ArrayEntity *va = static_cast<const ArrayEntity *>(a);
827 const ArrayEntity *vb = static_cast<const ArrayEntity *>(b);
828
829 ASSERT(va->value && !JSValue::isUndefined(va->value));
830 ASSERT(vb->value && !JSValue::isUndefined(vb->value));
831
832 return compare(JSValue::toString(va->value, exec), JSValue::toString(vb->value, exec));
833 }
834
sort(ExecState * exec)835 void ArrayInstance::sort(ExecState *exec)
836 {
837 unsigned lengthNotIncludingUndefined = compactForSorting();
838
839 ExecState *oldExec = execForCompareByStringForQSort;
840 execForCompareByStringForQSort = exec;
841
842 #if HAVE(MERGESORT)
843 // Because mergesort usually does fewer compares, it is faster than qsort here.
844 // However, because it requires extra copies of the storage buffer, don't use it for very
845 // large arrays.
846
847 // FIXME: Since we sort by string value, a fast algorithm might be to convert all the
848 // values to string once up front, and then use a radix sort. That would be O(N) rather
849 // than O(N log N).
850
851 if (lengthNotIncludingUndefined < mergeSortCutoff) {
852 // During the sort, we could do a garbage collect, and it's important to still
853 // have references to every object in the array for ArrayInstance::mark.
854 // The mergesort algorithm does not guarantee this, so we sort a copy rather
855 // than the original.
856 size_t size = storageSize(m_vectorLength);
857 ArrayStorage *copy = static_cast<ArrayStorage *>(fastMalloc(size));
858 memcpy(copy, m_storage, size);
859 mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareByStringForQSort);
860 fastFree(m_storage);
861 m_storage = copy;
862 execForCompareByStringForQSort = oldExec;
863 return;
864 }
865 #endif
866
867 qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareByStringForQSort);
868 execForCompareByStringForQSort = oldExec;
869 }
870
871 struct CompareWithCompareFunctionArguments {
CompareWithCompareFunctionArgumentsKJS::CompareWithCompareFunctionArguments872 CompareWithCompareFunctionArguments(ExecState *e, JSObject *cf)
873 : exec(e)
874 , compareFunction(cf)
875 , globalObject(e->dynamicInterpreter()->globalObject())
876 {
877 }
878
879 ExecState *exec;
880 JSObject *compareFunction;
881 List arguments;
882 JSObject *globalObject;
883 };
884
885 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments;
886
compareWithCompareFunctionForQSort(const void * a,const void * b)887 static int compareWithCompareFunctionForQSort(const void *a, const void *b)
888 {
889 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
890
891 const ArrayEntity *va = static_cast<const ArrayEntity *>(a);
892 const ArrayEntity *vb = static_cast<const ArrayEntity *>(b);
893
894 ASSERT(va->value && !JSValue::isUndefined(va->value));
895 ASSERT(vb->value && !JSValue::isUndefined(vb->value));
896
897 args->arguments.clear();
898 args->arguments.append(va->value);
899 args->arguments.append(vb->value);
900 double compareResult = JSValue::toNumber(args->compareFunction->call(args->exec, args->globalObject, args->arguments), args->exec);
901 return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;
902 }
903
sort(ExecState * exec,JSObject * compareFunction)904 void ArrayInstance::sort(ExecState *exec, JSObject *compareFunction)
905 {
906 size_t lengthNotIncludingUndefined = compactForSorting();
907
908 CompareWithCompareFunctionArguments *oldArgs = compareWithCompareFunctionArguments;
909 CompareWithCompareFunctionArguments args(exec, compareFunction);
910 compareWithCompareFunctionArguments = &args;
911
912 #if HAVE(MERGESORT)
913 // Because mergesort usually does fewer compares, it is faster than qsort here.
914 // However, because it requires extra copies of the storage buffer, don't use it for very
915 // large arrays.
916
917 // FIXME: A tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even
918 // better job of minimizing compares.
919
920 if (lengthNotIncludingUndefined < mergeSortCutoff) {
921 // During the sort, we could do a garbage collect, and it's important to still
922 // have references to every object in the array for ArrayInstance::mark.
923 // The mergesort algorithm does not guarantee this, so we sort a copy rather
924 // than the original.
925 size_t size = storageSize(m_vectorLength);
926 ArrayStorage *copy = static_cast<ArrayStorage *>(fastMalloc(size));
927 memcpy(copy, m_storage, size);
928 mergesort(copy->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareWithCompareFunctionForQSort);
929 fastFree(m_storage);
930 m_storage = copy;
931 compareWithCompareFunctionArguments = oldArgs;
932 return;
933 }
934 #endif
935
936 qsort(m_storage->m_vector, lengthNotIncludingUndefined, sizeof(ArrayEntity), compareWithCompareFunctionForQSort);
937 compareWithCompareFunctionArguments = oldArgs;
938 }
939
compactForSorting()940 unsigned ArrayInstance::compactForSorting()
941 {
942 ArrayStorage *storage = m_storage;
943
944 unsigned usedVectorLength = min(m_length, m_vectorLength);
945
946 unsigned numDefined = 0;
947 unsigned numUndefined = 0;
948
949 // This compacts normal values (e.g. not undefined) in a contiguous run
950 // at the beginning of the array, and then puts any set undefined values
951 // at the end
952
953 // count the first contiguous run of defined values in the vector store
954 for (; numDefined < usedVectorLength; ++numDefined) {
955 ArrayEntity *v = &storage->m_vector[numDefined];
956 if (!v->value || JSValue::isUndefined(v->value)) {
957 break;
958 }
959 }
960
961 // compact the rest, counting along the way
962 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
963 ArrayEntity v = storage->m_vector[i];
964 if (!v.value || JSValue::isUndefined(v.value)) {
965 ++numUndefined;
966 } else {
967 storage->m_vector[numDefined++] = storage->m_vector[i];
968 }
969 }
970
971 unsigned newUsedVectorLength = numDefined + numUndefined;
972
973 if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
974 newUsedVectorLength += map->size();
975 if (newUsedVectorLength > m_vectorLength) {
976 increaseVectorLength(newUsedVectorLength);
977 storage = m_storage;
978 }
979
980 SparseArrayValueMap::iterator end = map->end();
981 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
982 storage->m_vector[numDefined++] = it->second;
983 }
984
985 delete map;
986 storage->m_sparseValueMap = nullptr;
987 }
988
989 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) {
990 storage->m_vector[i].value = nullptr;
991 }
992 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) {
993 storage->m_vector[i].value = nullptr;
994 }
995
996 return numDefined;
997 }
998
anyItemHasAttribute(unsigned int attributes) const999 bool KJS::ArrayInstance::anyItemHasAttribute(unsigned int attributes) const
1000 {
1001 ArrayStorage *storage = m_storage;
1002
1003 unsigned usedVectorLength = min(m_length, m_vectorLength);
1004 for (unsigned i = 0; i < usedVectorLength; ++i) {
1005 if (storage->m_vector[i].value && storage->m_vector[i].attributes & attributes) {
1006 return true;
1007 }
1008 }
1009
1010 if (SparseArrayValueMap *map = storage->m_sparseValueMap) {
1011 SparseArrayValueMap::iterator end = map->end();
1012 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
1013 if ((*it).second.attributes & attributes) {
1014 return true;
1015 }
1016 }
1017
1018 return false;
1019 }
1020
1021 }
1022