Changeset 33967 in webkit
- Timestamp:
- May 21, 2008 10:17:37 AM (16 years ago)
- Location:
- trunk
- Files:
-
- 10 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/ChangeLog
r33965 r33967 1 2008-05-21 Alexey Proskuryakov <ap@webkit.org> 2 3 Reviewed by Darin. 4 5 <rdar://problem/5908520> REGRESSION (3.1.1-r33033): Crash in WebKit when opening or 6 refreshing page on people.com 7 8 The problem was that STL algorithms do not work with non-conformant comparators, and the 9 site used sort(function() { return 0.5 - Math.random(); } to randomly shuffle an array. 10 11 https://bugs.webkit.org/show_bug.cgi?id=18687 12 REGRESSION(r32220): ecma/Array/15.4.4.5-3.js test now fails in GMT(BST) 13 14 Besides relying on sort stability, this test was just broken, and kept failing with the 15 new stable sort. 16 17 Tests: fast/js/sort-randomly.html 18 fast/js/sort-stability.html 19 fast/js/comparefn-sort-stability.html 20 21 * kjs/avl_tree.h: Added an AVL tree implementation. 22 23 * JavaScriptCore.xcodeproj/project.pbxproj: 24 * wtf/AVLTree.h: Added. 25 Added an AVL tree implementation. 26 27 * kjs/array_instance.cpp: 28 (KJS::ArrayInstance::increaseVectorLength): 29 (KJS::ArrayInstance::sort): 30 (KJS::AVLTreeAbstractorForArrayCompare::get_less): 31 (KJS::AVLTreeAbstractorForArrayCompare::set_less): 32 (KJS::AVLTreeAbstractorForArrayCompare::get_greater): 33 (KJS::AVLTreeAbstractorForArrayCompare::set_greater): 34 (KJS::AVLTreeAbstractorForArrayCompare::get_balance_factor): 35 (KJS::AVLTreeAbstractorForArrayCompare::set_balance_factor): 36 (KJS::AVLTreeAbstractorForArrayCompare::compare_key_key): 37 (KJS::AVLTreeAbstractorForArrayCompare::compare_key_node): 38 (KJS::AVLTreeAbstractorForArrayCompare::compare_node_node): 39 (KJS::AVLTreeAbstractorForArrayCompare::null): 40 (KJS::ArrayInstance::compactForSorting): 41 42 * kjs/array_instance.h: increaseVectorLength() now returns a bool to indicate whether it was 43 successful. 44 45 * wtf/Vector.h: 46 (WTF::Vector::Vector): 47 (WTF::::operator=): 48 (WTF::::fill): 49 Make these methods fail instead of crash when allocation fails, matching resize() and 50 reserveCapacity(), which already had this behavior. Callers need to check for null buffer 51 after making any Vector call that can try to allocate. 52 53 * tests/mozilla/ecma/Array/15.4.4.5-3.js: Fixed the test to use a consistent sort function, 54 as suggested in comments to a Mozilla bug filed about it (I'll keep tracking the bug to see 55 what the final resolution is). 56 1 57 2008-05-20 Kevin McCullough <kmccullough@apple.com> 2 58 -
trunk/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
r33466 r33967 204 204 E195679609E7CF1200B89D13 /* UnicodeIcu.h in Headers */ = {isa = PBXBuildFile; fileRef = E195678F09E7CF1200B89D13 /* UnicodeIcu.h */; settings = {ATTRIBUTES = (Private, ); }; }; 205 205 E195679809E7CF1200B89D13 /* Unicode.h in Headers */ = {isa = PBXBuildFile; fileRef = E195679409E7CF1200B89D13 /* Unicode.h */; settings = {ATTRIBUTES = (Private, ); }; }; 206 E1A596380DE3E1C300C17E37 /* AVLTree.h in Headers */ = {isa = PBXBuildFile; fileRef = E1A596370DE3E1C300C17E37 /* AVLTree.h */; }; 206 207 E1A862A90D7EBB76001EC6AA /* CollatorICU.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1A862A80D7EBB76001EC6AA /* CollatorICU.cpp */; settings = {COMPILER_FLAGS = "-fno-strict-aliasing"; }; }; 207 208 E1A862AB0D7EBB7D001EC6AA /* Collator.h in Headers */ = {isa = PBXBuildFile; fileRef = E1A862AA0D7EBB7D001EC6AA /* Collator.h */; settings = {ATTRIBUTES = (Private, ); }; }; … … 517 518 E195678F09E7CF1200B89D13 /* UnicodeIcu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnicodeIcu.h; sourceTree = "<group>"; }; 518 519 E195679409E7CF1200B89D13 /* Unicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Unicode.h; sourceTree = "<group>"; }; 520 E1A596370DE3E1C300C17E37 /* AVLTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVLTree.h; sourceTree = "<group>"; }; 519 521 E1A862A80D7EBB76001EC6AA /* CollatorICU.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CollatorICU.cpp; sourceTree = "<group>"; }; 520 522 E1A862AA0D7EBB7D001EC6AA /* Collator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Collator.h; sourceTree = "<group>"; }; … … 786 788 65E217B808E7EECC0023E5F6 /* Assertions.cpp */, 787 789 65E217B708E7EECC0023E5F6 /* Assertions.h */, 790 E1A596370DE3E1C300C17E37 /* AVLTree.h */, 788 791 5186111D0CC824830081412B /* Deque.h */, 789 792 938C4F6B0CA06BCE00D9310A /* DisallowCType.h */, … … 1156 1159 95742F660DD11F5A000917FB /* Profile.h in Headers */, 1157 1160 5DE3D0F50DD8DDFB00468714 /* WebKitAvailability.h in Headers */, 1161 E1A596380DE3E1C300C17E37 /* AVLTree.h in Headers */, 1158 1162 ); 1159 1163 runOnlyForDeploymentPostprocessing = 0; -
trunk/JavaScriptCore/kjs/array_instance.cpp
r33038 r33967 26 26 #include "PropertyNameArray.h" 27 27 #include <wtf/Assertions.h> 28 #include <wtf/AVLTree.h> 28 29 29 30 using std::min; … … 48 49 static const unsigned sparseArrayCutoff = 10000; 49 50 static const unsigned minDensityMultiplier = 8; 50 51 static const unsigned copyingSortCutoff = 50000;52 51 53 52 const ClassInfo ArrayInstance::info = {"Array", 0, 0, 0}; … … 356 355 } 357 356 358 voidArrayInstance::increaseVectorLength(unsigned newLength)357 bool ArrayInstance::increaseVectorLength(unsigned newLength) 359 358 { 360 359 ArrayStorage* storage = m_storage; … … 365 364 366 365 storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength))); 366 if (!storage) 367 return false; 368 367 369 m_vectorLength = newVectorLength; 368 370 … … 371 373 372 374 m_storage = storage; 375 return true; 373 376 } 374 377 … … 435 438 } 436 439 437 class ArraySortComparator { 438 public: 439 ArraySortComparator(ExecState* exec) : m_exec(exec) {} 440 441 bool operator()(JSValue* va, JSValue* vb) 440 void ArrayInstance::sort(ExecState* exec) 441 { 442 unsigned lengthNotIncludingUndefined = compactForSorting(); 443 if (m_storage->m_sparseValueMap) { 444 exec->setException(Error::create(exec, GeneralError, "Out of memory")); 445 return; 446 } 447 448 if (!lengthNotIncludingUndefined) 449 return; 450 451 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that. 452 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary 453 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return 454 // random or otherwise changing results, effectively making compare function inconsistent. 455 456 Vector<std::pair<JSValue*, UString> > values(lengthNotIncludingUndefined); 457 if (!values.begin()) { 458 exec->setException(Error::create(exec, GeneralError, "Out of memory")); 459 return; 460 } 461 462 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) { 463 JSValue* value = m_storage->m_vector[i]; 464 ASSERT(!value->isUndefined()); 465 values[i].first = value; 466 values[i].second = value->toString(exec); 467 } 468 469 if (exec->hadException()) 470 return; 471 472 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather 473 // than O(N log N). 474 475 #if HAVE(MERGESORT) 476 mergesort(values.begin(), values.size(), sizeof(std::pair<JSValue*, UString>), compareByStringPairForQSort); 477 #else 478 // FIXME: QSort may not be stable in some implementations. ECMAScript-262 does not require this, but in practice, browsers perform stable sort. 479 qsort(values.begin(), values.size(), sizeof(std::pair<JSValue*, UString>), compareByStringPairForQSort); 480 #endif 481 482 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) 483 m_storage->m_vector[i] = values[i].first; 484 } 485 486 struct AVLTreeNodeForArrayCompare { 487 JSValue* value; 488 489 // Child pointers. The high bit of gt is robbed and used as the 490 // balance factor sign. The high bit of lt is robbed and used as 491 // the magnitude of the balance factor. 492 int32_t gt; 493 int32_t lt; 494 }; 495 496 struct AVLTreeAbstractorForArrayCompare { 497 typedef int32_t handle; // Handle is an index into m_nodes vector. 498 typedef JSValue* key; 499 typedef int32_t size; 500 501 Vector<AVLTreeNodeForArrayCompare> m_nodes; 502 ExecState* m_exec; 503 JSObject* m_compareFunction; 504 JSObject* m_globalThisValue; 505 506 handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; } 507 void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; } 508 handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; } 509 void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; } 510 511 int get_balance_factor(handle h) 512 { 513 if (m_nodes[h].gt & 0x80000000) 514 return -1; 515 return static_cast<unsigned>(m_nodes[h].lt) >> 31; 516 } 517 518 void set_balance_factor(handle h, int bf) 519 { 520 if (bf == 0) { 521 m_nodes[h].lt &= 0x7FFFFFFF; 522 m_nodes[h].gt &= 0x7FFFFFFF; 523 } else { 524 m_nodes[h].lt |= 0x80000000; 525 if (bf < 0) 526 m_nodes[h].gt |= 0x80000000; 527 } 528 } 529 530 int compare_key_key(key va, key vb) 442 531 { 443 532 ASSERT(!va->isUndefined()); 444 533 ASSERT(!vb->isUndefined()); 445 return compare(va->toString(m_exec), vb->toString(m_exec)) < 0;446 }447 448 private:449 ExecState* m_exec;450 };451 452 void ArrayInstance::sort(ExecState* exec)453 {454 unsigned lengthNotIncludingUndefined = compactForSorting();455 456 if (lengthNotIncludingUndefined < copyingSortCutoff) {457 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.458 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary459 // buffer. For large arrays, we fall back to a qsort on the JavaScriptValues to avoid creating copies.460 461 Vector<std::pair<JSValue*, UString> > values(lengthNotIncludingUndefined);462 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {463 JSValue* value = m_storage->m_vector[i];464 ASSERT(!value->isUndefined());465 values[i].first = value;466 values[i].second = value->toString(exec);467 }468 469 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather470 // than O(N log N).471 472 #if HAVE(MERGESORT)473 mergesort(values.begin(), values.size(), sizeof(std::pair<JSValue*, UString>), compareByStringPairForQSort);474 #else475 qsort(values.begin(), values.size(), sizeof(std::pair<JSValue*, UString>), compareByStringPairForQSort);476 #endif477 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)478 m_storage->m_vector[i] = values[i].first;479 return;480 }481 482 std::sort(m_storage->m_vector, m_storage->m_vector + lengthNotIncludingUndefined, ArraySortComparator(exec));483 }484 485 struct CompareWithCompareFunctionArguments {486 CompareWithCompareFunctionArguments(ExecState* exec, JSObject* cf)487 : exec(exec)488 , compareFunction(cf)489 , globalThisValue(exec->globalThisValue())490 {491 }492 493 bool operator()(JSValue* va, JSValue* vb)494 {495 ASSERT(!va->isUndefined());496 ASSERT(!vb->isUndefined());497 534 498 535 List arguments; 499 536 arguments.append(va); 500 537 arguments.append(vb); 501 double compareResult = compareFunction->call(exec, globalThisValue, arguments)->toNumber(exec); 502 return compareResult < 0; 503 } 504 505 ExecState* exec; 506 JSObject* compareFunction; 507 JSObject* globalThisValue; 538 double compareResult = m_compareFunction->call(m_exec, m_globalThisValue, arguments)->toNumber(m_exec); 539 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent. 540 } 541 542 int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); } 543 int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); } 544 545 static handle null() { return 0x7FFFFFFF; } 508 546 }; 509 547 510 548 void ArrayInstance::sort(ExecState* exec, JSObject* compareFunction) 511 549 { 512 size_t lengthNotIncludingUndefined = compactForSorting(); 513 514 // FIXME: A tree sort using a perfectly balanced tree (e.g. an AVL tree) could do an even 515 // better job of minimizing compares. 516 517 std::sort(m_storage->m_vector, m_storage->m_vector + lengthNotIncludingUndefined, CompareWithCompareFunctionArguments(exec, compareFunction)); 550 // The maximum tree depth is compiled in - but the caller is clearly up to no good 551 // if a larger array is passed. 552 ASSERT(m_length <= static_cast<unsigned>(std::numeric_limits<int>::max())); 553 if (m_length > static_cast<unsigned>(std::numeric_limits<int>::max())) 554 return; 555 556 if (!m_length) 557 return; 558 559 unsigned usedVectorLength = min(m_length, m_vectorLength); 560 561 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items 562 tree.abstractor().m_exec = exec; 563 tree.abstractor().m_compareFunction = compareFunction; 564 tree.abstractor().m_globalThisValue = exec->globalThisValue(); 565 tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0)); 566 567 if (!tree.abstractor().m_nodes.begin()) { 568 exec->setException(Error::create(exec, GeneralError, "Out of memory")); 569 return; 570 } 571 572 unsigned numDefined = 0; 573 unsigned numUndefined = 0; 574 575 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. 576 for (; numDefined < usedVectorLength; ++numDefined) { 577 JSValue* v = m_storage->m_vector[numDefined]; 578 if (!v || v->isUndefined()) 579 break; 580 tree.abstractor().m_nodes[numDefined].value = v; 581 tree.insert(numDefined); 582 } 583 for (unsigned i = numDefined; i < usedVectorLength; ++i) { 584 if (JSValue* v = m_storage->m_vector[i]) { 585 if (v->isUndefined()) 586 ++numUndefined; 587 else { 588 tree.abstractor().m_nodes[numDefined].value = v; 589 tree.insert(numDefined); 590 ++numDefined; 591 } 592 } 593 } 594 595 unsigned newUsedVectorLength = numDefined + numUndefined; 596 597 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) { 598 newUsedVectorLength += map->size(); 599 if (newUsedVectorLength > m_vectorLength) { 600 if (!increaseVectorLength(newUsedVectorLength)) { 601 exec->setException(Error::create(exec, GeneralError, "Out of memory")); 602 return; 603 } 604 } 605 606 SparseArrayValueMap::iterator end = map->end(); 607 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) { 608 tree.abstractor().m_nodes[numDefined].value = it->second; 609 tree.insert(numDefined); 610 ++numDefined; 611 } 612 613 delete map; 614 m_storage->m_sparseValueMap = 0; 615 } 616 617 ASSERT(tree.abstractor().m_nodes.size() >= numDefined); 618 619 // Copy the values back into m_storage. 620 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; 621 iter.start_iter_least(tree); 622 for (unsigned i = 0; i < numDefined; ++i) { 623 m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value; 624 ++iter; 625 } 626 627 // Put undefined values back in. 628 for (unsigned i = numDefined; i < newUsedVectorLength; ++i) 629 m_storage->m_vector[i] = jsUndefined(); 630 631 // Ensure that unused values in the vector are zeroed out. 632 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i) 633 m_storage->m_vector[i] = 0; 518 634 } 519 635 … … 546 662 newUsedVectorLength += map->size(); 547 663 if (newUsedVectorLength > m_vectorLength) { 548 increaseVectorLength(newUsedVectorLength); 664 if (!increaseVectorLength(newUsedVectorLength)) 665 return 0; 549 666 storage = m_storage; 550 667 } -
trunk/JavaScriptCore/kjs/array_instance.h
r30534 r33967 59 59 60 60 void setLength(unsigned); 61 voidincreaseVectorLength(unsigned newLength);61 bool increaseVectorLength(unsigned newLength); 62 62 63 63 unsigned compactForSorting(); -
trunk/JavaScriptCore/tests/mozilla/ecma/Array/15.4.4.5-3.js
r11995 r33967 148 148 } 149 149 function comparefn3( x, y ) { 150 return ( x == y ? 0 : ( x > y ? 1: -1 ) );150 return ( +x == +y ? 0 : ( x > y ? 1 : -1 ) ); 151 151 } 152 152 function clone( source, target ) { -
trunk/JavaScriptCore/wtf/Vector.h
r31807 r33967 409 409 , m_buffer(size) 410 410 { 411 TypeOperations::initialize(begin(), end()); 411 if (begin()) 412 TypeOperations::initialize(begin(), end()); 412 413 } 413 414 … … 490 491 , m_buffer(size) 491 492 { 492 TypeOperations::uninitializedFill(begin(), end(), val); 493 if (begin()) 494 TypeOperations::uninitializedFill(begin(), end(), val); 493 495 } 494 496 … … 520 522 , m_buffer(other.capacity()) 521 523 { 522 TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); 524 if (begin()) 525 TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); 523 526 } 524 527 … … 529 532 , m_buffer(other.capacity()) 530 533 { 531 TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); 534 if (begin()) 535 TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); 532 536 } 533 537 … … 543 547 clear(); 544 548 reserveCapacity(other.size()); 549 if (!begin()) 550 return *this; 545 551 } 546 552 … … 564 570 clear(); 565 571 reserveCapacity(other.size()); 572 if (!begin()) 573 return *this; 566 574 } 567 575 … … 581 589 clear(); 582 590 reserveCapacity(newSize); 591 if (!begin()) 592 return; 583 593 } 584 594 -
trunk/LayoutTests/ChangeLog
r33966 r33967 1 2008-05-21 Alexey Proskuryakov <ap@webkit.org> 2 3 Reviewed by Darin. 4 5 <rdar://problem/5908520> REGRESSION (3.1.1-r33033): Crash in WebKit when opening or refreshing page on people.com 6 7 https://bugs.webkit.org/show_bug.cgi?id=18687 8 REGRESSION(r32220): ecma/Array/15.4.4.5-3.js test now fails in GMT(BST) 9 10 * fast/js/comparefn-sort-stability-expected.txt: Added. 11 * fast/js/comparefn-sort-stability.html: Added. 12 * fast/js/resources/comparefn-sort-stability.js: Added. 13 * fast/js/resources/sort-randomly.js: Added. 14 * fast/js/resources/sort-stability.js: Added. 15 * fast/js/sort-randomly-expected.txt: Added. 16 * fast/js/sort-randomly.html: Added. 17 * fast/js/sort-stability-expected.txt: Added. 18 * fast/js/sort-stability.html: Added. 19 1 20 2008-05-21 Alexey Proskuryakov <ap@webkit.org> 2 21
Note: See TracChangeset
for help on using the changeset viewer.