Diff
Modified: trunk/LayoutTests/ChangeLog (183307 => 183308)
--- trunk/LayoutTests/ChangeLog 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/ChangeLog 2015-04-25 06:57:12 UTC (rev 183308)
@@ -1,3 +1,18 @@
+2015-04-24 Commit Queue <[email protected]>
+
+ Unreviewed, rolling out r183288.
+ https://bugs.webkit.org/show_bug.cgi?id=144189
+
+ Made js/sort-with-side-effecting-comparisons.html time out in
+ debug builds (Requested by ap on #webkit).
+
+ Reverted changeset:
+
+ "It shouldn't take 1846 lines of code and 5 FIXMEs to sort an
+ array."
+ https://bugs.webkit.org/show_bug.cgi?id=144013
+ http://trac.webkit.org/changeset/183288
+
2015-04-24 Myles C. Maxfield <[email protected]>
Implement parsing support for font-synthesis CSS property
Modified: trunk/LayoutTests/js/array-holes-expected.txt (183307 => 183308)
--- trunk/LayoutTests/js/array-holes-expected.txt 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/js/array-holes-expected.txt 2015-04-25 06:57:12 UTC (rev 183308)
@@ -36,7 +36,7 @@
PASS showHoles([0, , 2, 3].reverse()) is '[3, 2, peekaboo, 0]'
PASS a = [0, , 2, 3]; a.shift(); showHoles(a) is '[peekaboo, 2, 3]'
PASS showHoles([0, , 2, 3].slice(0, 3)) is '[0, peekaboo, 2]'
-PASS showHoles([0, , 2, 3].sort()) is '[0, 2, 3, peekaboo]'
+PASS showHoles([0, , 2, 3].sort()) is '[0, 2, 3, hole]'
PASS showHoles([0, undefined, 2, 3].sort()) is '[0, 2, 3, undefined]'
PASS a = [0, , 2, 3]; a.splice(2, 3, 5, 6); showHoles(a) is '[0, hole, 5, 6]'
PASS a = [0, , 2, 3]; a.unshift(4); showHoles(a) is '[4, 0, peekaboo, 2, 3]'
Modified: trunk/LayoutTests/js/array-sort-sparse-expected.txt (183307 => 183308)
--- trunk/LayoutTests/js/array-sort-sparse-expected.txt 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/js/array-sort-sparse-expected.txt 2015-04-25 06:57:12 UTC (rev 183308)
@@ -5,11 +5,6 @@
PASS testSort([,undefined,0,1]) is true
PASS testSort({length:4,1:undefined,2:0,3:1}) is true
-PASS 0 in array is true
-PASS 1 in array is false
-PASS 0 in array is true
-PASS 1 in array is false
-PASS 2 in array is false
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/js/comparefn-sort-stability-expected.txt (183307 => 183308)
--- trunk/LayoutTests/js/comparefn-sort-stability-expected.txt 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/js/comparefn-sort-stability-expected.txt 2015-04-25 06:57:12 UTC (rev 183308)
@@ -3,206 +3,14 @@
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[i] is ones[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
-PASS array[count + i] is twos[i]
+PASS arr[0] is sortArr[0]
+PASS arr[1] is sortArr[2]
+PASS arr[2] is sortArr[1]
+PASS arr[3] is sortArr[3]
+PASS arr[0] is sortArr[0]
+PASS arr[1] is sortArr[2]
+PASS arr[2] is sortArr[1]
+PASS arr[3] is sortArr[3]
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/js/dom/array-prototype-properties-expected.txt (183307 => 183308)
--- trunk/LayoutTests/js/dom/array-prototype-properties-expected.txt 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/js/dom/array-prototype-properties-expected.txt 2015-04-25 06:57:12 UTC (rev 183308)
@@ -12,7 +12,7 @@
PASS Array.prototype.reverse.call(undefined) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.reverse.call(undefined)').
PASS Array.prototype.shift.call(undefined) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.shift.call(undefined)').
PASS Array.prototype.slice.call(undefined, 0, 1) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.slice.call(undefined, 0, 1)').
-PASS Array.prototype.sort.call(undefined) threw exception TypeError: Array.prototype.sort requires that |this| not be undefined.
+PASS Array.prototype.sort.call(undefined) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.sort.call(undefined)').
PASS Array.prototype.splice.call(undefined, 0, 1) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.splice.call(undefined, 0, 1)').
PASS Array.prototype.unshift.call(undefined, {}) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.unshift.call(undefined, {})').
PASS Array.prototype.every.call(undefined, toString) threw exception TypeError: Array.prototype.every requires that |this| not be undefined.
Modified: trunk/LayoutTests/js/script-tests/array-holes.js (183307 => 183308)
--- trunk/LayoutTests/js/script-tests/array-holes.js 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/js/script-tests/array-holes.js 2015-04-25 06:57:12 UTC (rev 183308)
@@ -87,7 +87,7 @@
shouldBe("showHoles([0, , 2, 3].reverse())", "'[3, 2, peekaboo, 0]'");
shouldBe("a = [0, , 2, 3]; a.shift(); showHoles(a)", "'[peekaboo, 2, 3]'");
shouldBe("showHoles([0, , 2, 3].slice(0, 3))", "'[0, peekaboo, 2]'");
-shouldBe("showHoles([0, , 2, 3].sort())", "'[0, 2, 3, peekaboo]'");
+shouldBe("showHoles([0, , 2, 3].sort())", "'[0, 2, 3, hole]'");
shouldBe("showHoles([0, undefined, 2, 3].sort())", "'[0, 2, 3, undefined]'");
shouldBe("a = [0, , 2, 3]; a.splice(2, 3, 5, 6); showHoles(a)", "'[0, hole, 5, 6]'");
shouldBe("a = [0, , 2, 3]; a.unshift(4); showHoles(a)", "'[4, 0, peekaboo, 2, 3]'");
Modified: trunk/LayoutTests/js/script-tests/array-sort-sparse.js (183307 => 183308)
--- trunk/LayoutTests/js/script-tests/array-sort-sparse.js 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/js/script-tests/array-sort-sparse.js 2015-04-25 06:57:12 UTC (rev 183308)
@@ -10,14 +10,3 @@
shouldBeTrue("testSort([,undefined,0,1])");
shouldBeTrue("testSort({length:4,1:undefined,2:0,3:1})");
-
-var array = [ , undefined ];
-array.sort();
-shouldBeTrue("0 in array");
-shouldBeFalse("1 in array");
-
-var array = [ , 1, , ];
-array.sort();
-shouldBeTrue("0 in array");
-shouldBeFalse("1 in array");
-shouldBeFalse("2 in array");
Modified: trunk/LayoutTests/js/script-tests/comparefn-sort-stability.js (183307 => 183308)
--- trunk/LayoutTests/js/script-tests/comparefn-sort-stability.js 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/LayoutTests/js/script-tests/comparefn-sort-stability.js 2015-04-25 06:57:12 UTC (rev 183308)
@@ -2,25 +2,30 @@
"This tests that sort(compareFn) is a stable sort."
);
-var count = 100;
+function clone(source, target) {
+ for (i = 0; i < source.length; i++) {
+ target[i] = source[i];
+ }
+}
-var _ones_ = [];
-for (var i = 0; i < count; ++i)
- ones.push(new Number(1));
+var arr = [];
+arr[0] = new Number(1);
+arr[1] = new Number(2);
+arr[2] = new Number(1);
+arr[3] = new Number(2);
-var twos = [];
-for (var i = 0; i < count; ++i)
- twos.push(new Number(2));
+var sortArr = [];
+clone(arr, sortArr);
+sortArr.sort(function(a,b) { return a - b; });
-var array = [];
-for (var i = 0; i < count; ++i) {
- array.push(ones[i]);
- array.push(twos[i]);
-}
+shouldBe('arr[0]', 'sortArr[0]');
+shouldBe('arr[1]', 'sortArr[2]');
+shouldBe('arr[2]', 'sortArr[1]');
+shouldBe('arr[3]', 'sortArr[3]');
-array.sort(function(a,b) { return a - b; });
-for (var i = 0; i < count; ++i)
- shouldBe('array[i]', 'ones[i]');
-
-for (var i = 0; i < count; ++i)
- shouldBe('array[count + i]', 'twos[i]');
+// Just try again...
+sortArr.sort(function(a,b) { return a - b; });
+shouldBe('arr[0]', 'sortArr[0]');
+shouldBe('arr[1]', 'sortArr[2]');
+shouldBe('arr[2]', 'sortArr[1]');
+shouldBe('arr[3]', 'sortArr[3]');
Modified: trunk/Source/_javascript_Core/ChangeLog (183307 => 183308)
--- trunk/Source/_javascript_Core/ChangeLog 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-04-25 06:57:12 UTC (rev 183308)
@@ -1,3 +1,18 @@
+2015-04-24 Commit Queue <[email protected]>
+
+ Unreviewed, rolling out r183288.
+ https://bugs.webkit.org/show_bug.cgi?id=144189
+
+ Made js/sort-with-side-effecting-comparisons.html time out in
+ debug builds (Requested by ap on #webkit).
+
+ Reverted changeset:
+
+ "It shouldn't take 1846 lines of code and 5 FIXMEs to sort an
+ array."
+ https://bugs.webkit.org/show_bug.cgi?id=144013
+ http://trac.webkit.org/changeset/183288
+
2015-04-24 Filip Pizlo <[email protected]>
CRASH in operationCreateDirectArgumentsDuringExit()
Modified: trunk/Source/_javascript_Core/builtins/Array.prototype.js (183307 => 183308)
--- trunk/Source/_javascript_Core/builtins/Array.prototype.js 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/builtins/Array.prototype.js 2015-04-25 06:57:12 UTC (rev 183308)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -277,185 +277,3 @@
}
return false;
}
-
-function sort(comparator)
-{
- "use strict";
-
- function min(a, b)
- {
- return a < b ? a : b;
- }
-
- function stringComparator(a, b)
- {
- var aString = @String(a);
- var bString = @String(b);
-
- var aLength = aString.length;
- var bLength = bString.length;
- var length = min(aLength, bLength);
-
- for (var i = 0; i < length; ++i) {
- var aCharCode = aString.@charCodeAt(i);
- var bCharCode = bString.@charCodeAt(i);
-
- if (aCharCode == bCharCode)
- continue;
-
- if (aCharCode < bCharCode)
- return -1;
-
- return 1;
- }
-
- if (aLength == bLength)
- return 0;
-
- if (aLength < bLength)
- return -1;
-
- return 1;
- }
-
- // Move undefineds and holes to the end of a sparse array. Result is [values..., undefineds..., holes...].
- function compactSparse(array, dst, src, length)
- {
- var values = [ ];
- var seen = { };
- var valueCount = 0;
- var undefinedCount = 0;
-
- // Clean up after the in-progress non-sparse compaction that failed.
- for (var i = dst; i < src; ++i)
- delete array[i];
-
- for (var object = array; object; object = @Object.@getPrototypeOf(object)) {
- var propertyNames = @Object.@getOwnPropertyNames(object);
- for (var i = 0; i < propertyNames.length; ++i) {
- var index = propertyNames[i];
- if (index < length) { // Exclude non-numeric properties and properties past length.
- if (seen[index]) // Exclude duplicates.
- continue;
- seen[index] = 1;
-
- var value = array[index];
- delete array[index];
-
- if (value === undefined) {
- ++undefinedCount;
- continue;
- }
-
- array[valueCount++] = value;
- }
- }
- }
-
- for (var i = valueCount; i < valueCount + undefinedCount; ++i)
- array[i] = undefined;
-
- return valueCount;
- }
-
- // Move undefineds and holes to the end of an array. Result is [values..., undefineds..., holes...].
- function compact(array, length)
- {
- var holeCount = 0;
-
- for (var dst = 0, src = "" src < length; ++src) {
- if (!(src in array)) {
- ++holeCount;
- if (holeCount < 256)
- continue;
- return compactSparse(array, dst, src, length);
- }
-
- var value = array[src];
- if (value === undefined)
- continue;
- array[dst++] = value;
- }
-
- var valueCount = dst;
- var undefinedCount = length - valueCount - holeCount;
-
- for (var i = valueCount; i < valueCount + undefinedCount; ++i)
- array[i] = undefined;
-
- for (var i = valueCount + undefinedCount; i < length; ++i)
- delete array[i];
-
- return valueCount;
- }
-
- function merge(dst, src, srcIndex, srcEnd, width, comparator)
- {
- var left = srcIndex;
- var leftEnd = min(left + width, srcEnd);
- var right = leftEnd;
- var rightEnd = min(right + width, srcEnd);
-
- for (var dstIndex = left; dstIndex < rightEnd; ++dstIndex) {
- if (right < rightEnd) {
- if (left >= leftEnd || comparator(src[left], src[right]) > 0) {
- dst[dstIndex] = src[right++];
- continue;
- }
- }
-
- dst[dstIndex] = src[left++];
- }
- }
-
- function mergeSort(array, valueCount, comparator)
- {
- var buffer = [ ];
- buffer.length = valueCount;
-
- var dst = buffer;
- var src = ""
- for (var width = 1; width < valueCount; width *= 2) {
- for (var srcIndex = 0; srcIndex < valueCount; srcIndex += 2 * width)
- merge(dst, src, srcIndex, valueCount, width, comparator);
-
- var tmp = src;
- src = ""
- dst = tmp;
- }
-
- if (src != array) {
- for(var i = 0; i < valueCount; i++)
- array[i] = src[i];
- }
- }
-
- function comparatorSort(array, comparator)
- {
- var length = array.length >>> 0;
-
- // For compatibility with Firefox and Chrome, do nothing observable
- // to the target array if it has 0 or 1 sortable properties.
- if (length < 2)
- return;
-
- var valueCount = compact(array, length);
- mergeSort(array, valueCount, comparator);
- }
-
- if (this === null)
- throw new @TypeError("Array.prototype.sort requires that |this| not be null");
-
- if (this === undefined)
- throw new @TypeError("Array.prototype.sort requires that |this| not be undefined");
-
- if (typeof this == "string")
- throw new @TypeError("Attempted to assign to readonly property.");
-
- if (typeof comparator !== "function")
- comparator = stringComparator;
-
- var array = @Object(this);
- comparatorSort(array, comparator);
- return array;
-}
Modified: trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -101,6 +101,7 @@
if (closedVariable == m_vm.propertyNames->undefinedKeyword.impl())
continue;
+ RELEASE_ASSERT(m_vm.propertyNames->isPrivateName(closedVariable.get()));
}
body->overrideName(name);
UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&m_vm, source, body, kind, WTF::move(sourceOverride));
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.h (183307 => 183308)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.h 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.h 2015-04-25 06:57:12 UTC (rev 183308)
@@ -249,6 +249,8 @@
return static_cast<Instruction*>(returnAddress) - instructions().begin();
}
+ bool isNumericCompareFunction() { return m_unlinkedCode->isNumericCompareFunction(); }
+
unsigned numberOfInstructions() const { return m_instructions.size(); }
RefCountedArray<Instruction>& instructions() { return m_instructions; }
const RefCountedArray<Instruction>& instructions() const { return m_instructions; }
Modified: trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -241,6 +241,7 @@
, m_globalObjectRegister(VirtualRegister())
, m_needsFullScopeChain(info.needsActivation())
, m_usesEval(info.usesEval())
+ , m_isNumericCompareFunction(false)
, m_isStrictMode(info.isStrictMode())
, m_isConstructor(info.isConstructor())
, m_hasCapturedVariables(false)
Modified: trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.h (183307 => 183308)
--- trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.h 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.h 2015-04-25 06:57:12 UTC (rev 183308)
@@ -344,6 +344,9 @@
unsigned jumpTarget(int index) const { return m_jumpTargets[index]; }
unsigned lastJumpTarget() const { return m_jumpTargets.last(); }
+ void setIsNumericCompareFunction(bool isNumericCompareFunction) { m_isNumericCompareFunction = isNumericCompareFunction; }
+ bool isNumericCompareFunction() const { return m_isNumericCompareFunction; }
+
bool isBuiltinFunction() const { return m_isBuiltinFunction; }
ConstructorKind constructorKind() const { return static_cast<ConstructorKind>(m_constructorKind); }
@@ -533,6 +536,7 @@
unsigned m_needsFullScopeChain : 1;
unsigned m_usesEval : 1;
+ unsigned m_isNumericCompareFunction : 1;
unsigned m_isStrictMode : 1;
unsigned m_isConstructor : 1;
unsigned m_hasCapturedVariables : 1;
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -2603,6 +2603,11 @@
return newTemporary();
}
+void BytecodeGenerator::setIsNumericCompareFunction(bool isNumericCompareFunction)
+{
+ m_codeBlock->setIsNumericCompareFunction(isNumericCompareFunction);
+}
+
bool BytecodeGenerator::isArgumentNumber(const Identifier& ident, int argumentNumber)
{
RegisterID* registerID = variable(ident).local();
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (183307 => 183308)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2015-04-25 06:57:12 UTC (rev 183308)
@@ -276,6 +276,8 @@
bool isArgumentNumber(const Identifier&, int);
+ void setIsNumericCompareFunction(bool isNumericCompareFunction);
+
Variable variable(const Identifier&);
// Ignores the possibility of intervening scopes.
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -2821,6 +2821,22 @@
generator.emitReturn(r0);
return;
}
+
+ // If there is a return statment, and it is the only statement in the function, check if this is a numeric compare.
+ if (static_cast<BlockNode*>(singleStatement)->singleStatement()) {
+ ExpressionNode* returnValueExpression = returnNode->value();
+ if (returnValueExpression && returnValueExpression->isSubtract()) {
+ ExpressionNode* lhsExpression = static_cast<SubNode*>(returnValueExpression)->lhs();
+ ExpressionNode* rhsExpression = static_cast<SubNode*>(returnValueExpression)->rhs();
+ if (lhsExpression->isResolveNode()
+ && rhsExpression->isResolveNode()
+ && generator.isArgumentNumber(static_cast<ResolveNode*>(lhsExpression)->identifier(), 0)
+ && generator.isArgumentNumber(static_cast<ResolveNode*>(rhsExpression)->identifier(), 1)) {
+
+ generator.setIsNumericCompareFunction(true);
+ }
+ }
+ }
}
// ------------------------------ FuncDeclNode ---------------------------------
Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/heap/Heap.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -481,6 +481,17 @@
}
}
+void Heap::pushTempSortVector(Vector<ValueStringPair, 0, UnsafeVectorOverflow>* tempVector)
+{
+ m_tempSortingVectors.append(tempVector);
+}
+
+void Heap::popTempSortVector(Vector<ValueStringPair, 0, UnsafeVectorOverflow>* tempVector)
+{
+ ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last());
+ m_tempSortingVectors.removeLast();
+}
+
void Heap::harvestWeakReferences()
{
m_slotVisitor.harvestWeakReferences();
@@ -560,6 +571,7 @@
visitSmallStrings();
visitConservativeRoots(conservativeRoots);
visitProtectedObjects(heapRootVisitor);
+ visitTempSortVectors(heapRootVisitor);
visitArgumentBuffers(heapRootVisitor);
visitException(heapRootVisitor);
visitStrongHandles(heapRootVisitor);
@@ -698,6 +710,23 @@
m_slotVisitor.donateAndDrain();
}
+void Heap::visitTempSortVectors(HeapRootVisitor& heapRootVisitor)
+{
+ GCPHASE(VisitTempSortVectors);
+
+ for (auto* vector : m_tempSortingVectors) {
+ for (auto& valueStringPair : *vector) {
+ if (valueStringPair.first)
+ heapRootVisitor.visit(&valueStringPair.first);
+ }
+ }
+
+ if (Options::logGC() == GCLogging::Verbose)
+ dataLog("Temp Sort Vectors:\n", m_slotVisitor);
+
+ m_slotVisitor.donateAndDrain();
+}
+
void Heap::visitArgumentBuffers(HeapRootVisitor& visitor)
{
GCPHASE(MarkingArgumentBuffers);
Modified: trunk/Source/_javascript_Core/heap/Heap.h (183307 => 183308)
--- trunk/Source/_javascript_Core/heap/Heap.h 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/heap/Heap.h 2015-04-25 06:57:12 UTC (rev 183308)
@@ -75,6 +75,7 @@
static void* const zombifiedBits = reinterpret_cast<void*>(0xdeadbeef);
+typedef std::pair<JSValue, WTF::String> ValueStringPair;
typedef HashCountedSet<JSCell*> ProtectCountSet;
typedef HashCountedSet<const char*> TypeCountSet;
@@ -185,6 +186,9 @@
JS_EXPORT_PRIVATE std::unique_ptr<TypeCountSet> objectTypeCounts();
void showStatistics();
+ void pushTempSortVector(Vector<ValueStringPair, 0, UnsafeVectorOverflow>*);
+ void popTempSortVector(Vector<ValueStringPair, 0, UnsafeVectorOverflow>*);
+
HashSet<MarkedArgumentBuffer*>& markListSet();
template<typename Functor> typename Functor::ReturnType forEachProtectedCell(Functor&);
@@ -296,6 +300,7 @@
void visitCompilerWorklistWeakReferences();
void removeDeadCompilerWorklistEntries();
void visitProtectedObjects(HeapRootVisitor&);
+ void visitTempSortVectors(HeapRootVisitor&);
void visitArgumentBuffers(HeapRootVisitor&);
void visitException(HeapRootVisitor&);
void visitStrongHandles(HeapRootVisitor&);
@@ -366,6 +371,7 @@
HashSet<const JSCell*> m_copyingRememberedSet;
ProtectCountSet m_protectedValues;
+ Vector<Vector<ValueStringPair, 0, UnsafeVectorOverflow>*> m_tempSortingVectors;
std::unique_ptr<HashSet<MarkedArgumentBuffer*>> m_markListSet;
MachineThreads m_machineThreads;
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -288,6 +288,7 @@
features |= ModifiedArgumentsFeature;
Vector<RefPtr<StringImpl>> closedVariables;
if (m_parsingBuiltin) {
+ RELEASE_ASSERT(!capturedVariables.size());
IdentifierSet usedVariables;
scope->getUsedVariables(usedVariables);
for (const auto& variable : usedVariables) {
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -54,6 +54,7 @@
EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState*);
EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState*);
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState*);
+EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState*);
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState*);
EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState*);
EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState*);
@@ -69,6 +70,21 @@
namespace JSC {
+static inline bool isNumericCompareFunction(ExecState* exec, JSValue function, CallType callType, const CallData& callData)
+{
+ if (callType != CallTypeJS)
+ return false;
+
+ FunctionExecutable* executable = callData.js.functionExecutable;
+ JSScope* scope = callData.js.scope;
+
+ JSObject* error = executable->prepareForExecution(exec, jsCast<JSFunction*>(function), scope, CodeForCall);
+ if (error)
+ return false;
+
+ return executable->codeBlockForCall()->isNumericCompareFunction();
+}
+
// ------------------------------ ArrayPrototype ----------------------------
const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, &arrayPrototypeTable, CREATE_METHOD_TABLE(ArrayPrototype)};
@@ -638,6 +654,155 @@
return JSValue();
}
+static bool attemptFastSort(ExecState* exec, JSObject* thisObj, JSValue function, CallData& callData, CallType& callType)
+{
+ if (thisObj->classInfo() != JSArray::info()
+ || asArray(thisObj)->hasSparseMap()
+ || shouldUseSlowPut(thisObj->indexingType()))
+ return false;
+
+ if (isNumericCompareFunction(exec, function, callType, callData))
+ asArray(thisObj)->sortNumeric(exec, function, callType, callData);
+ else if (callType != CallTypeNone)
+ asArray(thisObj)->sort(exec, function, callType, callData);
+ else
+ asArray(thisObj)->sort(exec);
+ return true;
+}
+
+static bool performSlowSort(ExecState* exec, JSObject* thisObj, unsigned length, JSValue function, CallData& callData, CallType& callType)
+{
+ // "Min" sort. Not the fastest, but definitely less code than heapsort
+ // or quicksort, and much less swapping than bubblesort/insertionsort.
+ for (unsigned i = 0; i < length - 1; ++i) {
+ JSValue iObj = getOrHole(thisObj, exec, i);
+ if (exec->hadException())
+ return false;
+ unsigned themin = i;
+ JSValue minObj = iObj;
+ for (unsigned j = i + 1; j < length; ++j) {
+ JSValue jObj = getOrHole(thisObj, exec, j);
+ if (exec->hadException())
+ return false;
+ double compareResult;
+ if (!jObj)
+ compareResult = 1;
+ else if (!minObj)
+ compareResult = -1;
+ else if (jObj.isUndefined())
+ compareResult = 1; // don't check minObj because there's no need to differentiate == (0) from > (1)
+ else if (minObj.isUndefined())
+ compareResult = -1;
+ else if (callType != CallTypeNone) {
+ MarkedArgumentBuffer l;
+ l.append(jObj);
+ l.append(minObj);
+ compareResult = call(exec, function, callType, callData, jsUndefined(), l).toNumber(exec);
+ } else
+ compareResult = codePointCompareLessThan(jObj.toWTFStringInline(exec), minObj.toWTFStringInline(exec)) ? -1 : 1;
+
+ if (compareResult < 0) {
+ themin = j;
+ minObj = jObj;
+ }
+ }
+ // Swap themin and i
+ if (themin > i) {
+ if (minObj) {
+ thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, i, minObj, true);
+ if (exec->hadException())
+ return false;
+ } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, i)) {
+ throwTypeError(exec, ASCIILiteral("Unable to delete property."));
+ return false;
+ }
+ if (iObj) {
+ thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, themin, iObj, true);
+ if (exec->hadException())
+ return false;
+ } else if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, themin)) {
+ throwTypeError(exec, ASCIILiteral("Unable to delete property."));
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+EncodedJSValue JSC_HOST_CALL arrayProtoFuncSort(ExecState* exec)
+{
+ JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+ unsigned length = getLength(exec, thisObj);
+ if (!length || exec->hadException())
+ return JSValue::encode(thisObj);
+
+ JSValue function = exec->argument(0);
+ CallData callData;
+ CallType callType = getCallData(function, callData);
+
+ if (attemptFastSort(exec, thisObj, function, callData, callType))
+ return JSValue::encode(thisObj);
+
+ // Assume that for small-ish arrays, doing the slow sort directly is better.
+ if (length < 1000)
+ return performSlowSort(exec, thisObj, length, function, callData, callType) ? JSValue::encode(thisObj) : JSValue::encode(jsUndefined());
+
+ JSGlobalObject* globalObject = JSGlobalObject::create(
+ exec->vm(), JSGlobalObject::createStructure(exec->vm(), jsNull()));
+ JSArray* flatArray = constructEmptyArray(globalObject->globalExec(), nullptr);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ PropertyNameArray nameArray(exec);
+ thisObj->methodTable(exec->vm())->getPropertyNames(thisObj, exec, nameArray, EnumerationMode(DontEnumPropertiesMode::Include));
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ Vector<uint32_t, 0, UnsafeVectorOverflow> keys;
+ for (size_t i = 0; i < nameArray.size(); ++i) {
+ PropertyName name = nameArray[i];
+ Optional<uint32_t> optionalIndex = parseIndex(name);
+ if (!optionalIndex)
+ continue;
+
+ uint32_t index = optionalIndex.value();
+ JSValue value = getOrHole(thisObj, exec, index);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ if (!value)
+ continue;
+ keys.append(index);
+ flatArray->push(exec, value);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
+
+ if (!attemptFastSort(exec, flatArray, function, callData, callType)
+ && !performSlowSort(exec, flatArray, flatArray->length(), function, callData, callType))
+ return JSValue::encode(jsUndefined());
+
+ for (size_t i = 0; i < keys.size(); ++i) {
+ size_t index = keys[i];
+ if (index < flatArray->length())
+ continue;
+
+ if (!thisObj->methodTable(exec->vm())->deletePropertyByIndex(thisObj, exec, index)) {
+ throwTypeError(exec, ASCIILiteral("Unable to delete property."));
+ return JSValue::encode(jsUndefined());
+ }
+ }
+
+ for (size_t i = flatArray->length(); i--;) {
+ JSValue value = getOrHole(flatArray, exec, i);
+ RELEASE_ASSERT(value);
+ thisObj->methodTable(exec->vm())->putByIndex(thisObj, exec, i, value, true);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
+
+ return JSValue::encode(thisObj);
+}
+
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
{
// 15.4.4.12
Modified: trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h (183307 => 183308)
--- trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h 2015-04-25 06:57:12 UTC (rev 183308)
@@ -269,12 +269,9 @@
macro(objectGetOwnPropertySymbols) \
macro(Number) \
macro(Array) \
- macro(String) \
macro(abs) \
macro(floor) \
macro(isFinite) \
- macro(getPrototypeOf) \
- macro(getOwnPropertyNames) \
macro(TypeError) \
macro(undefined) \
macro(BuiltinLog) \
Modified: trunk/Source/_javascript_Core/runtime/JSArray.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/runtime/JSArray.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/runtime/JSArray.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -34,6 +34,7 @@
#include "JSCInlines.h"
#include "PropertyNameArray.h"
#include "Reject.h"
+#include <wtf/AVLTree.h>
#include <wtf/Assertions.h>
using namespace std;
@@ -993,6 +994,521 @@
}
}
+static int compareNumbersForQSortWithInt32(const void* a, const void* b)
+{
+ int32_t ia = static_cast<const JSValue*>(a)->asInt32();
+ int32_t ib = static_cast<const JSValue*>(b)->asInt32();
+ return ia - ib;
+}
+
+static int compareNumbersForQSortWithDouble(const void* a, const void* b)
+{
+ double da = *static_cast<const double*>(a);
+ double db = *static_cast<const double*>(b);
+ return (da > db) - (da < db);
+}
+
+static int compareNumbersForQSort(const void* a, const void* b)
+{
+ double da = static_cast<const JSValue*>(a)->asNumber();
+ double db = static_cast<const JSValue*>(b)->asNumber();
+ return (da > db) - (da < db);
+}
+
+static int compareByStringPairForQSort(const void* a, const void* b)
+{
+ const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
+ const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
+ return codePointCompare(va->second, vb->second);
+}
+
+template<IndexingType arrayIndexingType>
+void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
+{
+ ASSERT(arrayIndexingType == ArrayWithInt32 || arrayIndexingType == ArrayWithDouble || arrayIndexingType == ArrayWithContiguous || arrayIndexingType == ArrayWithArrayStorage);
+
+ unsigned lengthNotIncludingUndefined;
+ unsigned newRelevantLength;
+ compactForSorting<arrayIndexingType>(
+ lengthNotIncludingUndefined,
+ newRelevantLength);
+
+ ContiguousJSValues data = ""
+
+ if (arrayIndexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) {
+ throwOutOfMemoryError(exec);
+ return;
+ }
+
+ if (!lengthNotIncludingUndefined)
+ return;
+
+ bool allValuesAreNumbers = true;
+ switch (arrayIndexingType) {
+ case ArrayWithInt32:
+ case ArrayWithDouble:
+ break;
+
+ default:
+ for (size_t i = 0; i < newRelevantLength; ++i) {
+ if (!data[i].isNumber()) {
+ allValuesAreNumbers = false;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (!allValuesAreNumbers)
+ return sort(exec, compareFunction, callType, callData);
+
+ // For numeric comparison, which is fast, qsort is faster than mergesort. We
+ // also don't require mergesort's stability, since there's no user visible
+ // side-effect from swapping the order of equal primitive values.
+ int (*compare)(const void*, const void*);
+ switch (arrayIndexingType) {
+ case ArrayWithInt32:
+ compare = compareNumbersForQSortWithInt32;
+ break;
+
+ case ArrayWithDouble:
+ compare = compareNumbersForQSortWithDouble;
+ ASSERT(sizeof(WriteBarrier<Unknown>) == sizeof(double));
+ break;
+
+ default:
+ compare = compareNumbersForQSort;
+ break;
+ }
+ ASSERT(data.length() >= newRelevantLength);
+ qsort(data.data(), newRelevantLength, sizeof(WriteBarrier<Unknown>), compare);
+ return;
+}
+
+void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
+{
+ ASSERT(!inSparseIndexingMode());
+
+ switch (indexingType()) {
+ case ArrayClass:
+ case ArrayWithUndecided:
+ return;
+
+ case ArrayWithInt32:
+ sortNumericVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
+ return;
+
+ case ArrayWithDouble:
+ sortNumericVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
+ return;
+
+ case ArrayWithContiguous:
+ sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
+ return;
+
+ case ArrayWithArrayStorage:
+ sortNumericVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
+ return;
+
+ default:
+ dataLog("Indexing type: ", indexingType(), "\n");
+ RELEASE_ASSERT_NOT_REACHED();
+ return;
+ }
+}
+
+template <IndexingType> struct ContiguousTypeAccessor {
+ typedef WriteBarrier<Unknown> Type;
+ static JSValue getAsValue(ContiguousData<Type> data, size_t i) { return data[i].get(); }
+ static void setWithValue(VM& vm, JSArray* thisValue, ContiguousData<Type> data, size_t i, JSValue value)
+ {
+ data[i].set(vm, thisValue, value);
+ }
+ static void replaceDataReference(ContiguousData<Type>* outData, ContiguousJSValues inData)
+ {
+ *outData = inData;
+ }
+};
+
+template <> struct ContiguousTypeAccessor<ArrayWithDouble> {
+ typedef double Type;
+ static JSValue getAsValue(ContiguousData<Type> data, size_t i) { ASSERT(data[i] == data[i]); return JSValue(JSValue::EncodeAsDouble, data[i]); }
+ static void setWithValue(VM&, JSArray*, ContiguousData<Type> data, size_t i, JSValue value)
+ {
+ data[i] = value.asNumber();
+ }
+ static NO_RETURN_DUE_TO_CRASH void replaceDataReference(ContiguousData<Type>*, ContiguousJSValues)
+ {
+ RELEASE_ASSERT_WITH_MESSAGE(0, "Inconsistent indexing types during compact array sort.");
+ }
+};
+
+
+template<IndexingType arrayIndexingType, typename StorageType>
+void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> data, unsigned relevantLength)
+{
+ if (!relevantLength)
+ return;
+
+ VM& vm = exec->vm();
+
+ // Converting _javascript_ values to strings can be expensive, so we do it once up front and sort based on that.
+ // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
+ // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
+ // random or otherwise changing results, effectively making compare function inconsistent.
+
+ Vector<ValueStringPair, 0, UnsafeVectorOverflow> values(relevantLength);
+ if (!values.begin()) {
+ throwOutOfMemoryError(exec);
+ return;
+ }
+
+ Heap::heap(this)->pushTempSortVector(&values);
+
+ bool isSortingPrimitiveValues = true;
+
+ for (size_t i = 0; i < relevantLength; i++) {
+ JSValue value = ContiguousTypeAccessor<arrayIndexingType>::getAsValue(data, i);
+ ASSERT(arrayIndexingType != ArrayWithInt32 || value.isInt32());
+ ASSERT(!value.isUndefined());
+ values[i].first = value;
+ if (arrayIndexingType != ArrayWithDouble && arrayIndexingType != ArrayWithInt32)
+ isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
+ }
+
+ // FIXME: The following loop continues to call toString on subsequent values even after
+ // a toString call raises an exception.
+
+ for (size_t i = 0; i < relevantLength; i++)
+ values[i].second = values[i].first.toWTFStringInline(exec);
+
+ if (exec->hadException()) {
+ Heap::heap(this)->popTempSortVector(&values);
+ return;
+ }
+
+ // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
+ // than O(N log N).
+
+#if HAVE(MERGESORT)
+ if (isSortingPrimitiveValues)
+ qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
+ else
+ mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
+#else
+ // FIXME: The qsort library function is likely to not be a stable sort.
+ // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
+ qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
+#endif
+
+ // If the toString function changed the length of the array or vector storage,
+ // increase the length to handle the orignal number of actual values.
+ switch (arrayIndexingType) {
+ case ArrayWithInt32:
+ case ArrayWithDouble:
+ case ArrayWithContiguous:
+ ensureLength(vm, relevantLength);
+ break;
+
+ case ArrayWithArrayStorage:
+ if (arrayStorage()->vectorLength() < relevantLength) {
+ increaseVectorLength(exec->vm(), relevantLength);
+ ContiguousTypeAccessor<arrayIndexingType>::replaceDataReference(&data, arrayStorage()->vector());
+ }
+ if (arrayStorage()->length() < relevantLength)
+ arrayStorage()->setLength(relevantLength);
+ break;
+
+ default:
+ CRASH();
+ }
+
+ for (size_t i = 0; i < relevantLength; i++)
+ ContiguousTypeAccessor<arrayIndexingType>::setWithValue(vm, this, data, i, values[i].first);
+
+ Heap::heap(this)->popTempSortVector(&values);
+}
+
+void JSArray::sort(ExecState* exec)
+{
+ ASSERT(!inSparseIndexingMode());
+
+ switch (indexingType()) {
+ case ArrayClass:
+ case ArrayWithUndecided:
+ return;
+
+ case ArrayWithInt32: {
+ unsigned lengthNotIncludingUndefined;
+ unsigned newRelevantLength;
+ compactForSorting<ArrayWithInt32>(
+ lengthNotIncludingUndefined, newRelevantLength);
+
+ sortCompactedVector<ArrayWithInt32>(
+ exec, m_butterfly->contiguousInt32(), lengthNotIncludingUndefined);
+ return;
+ }
+
+ case ArrayWithDouble: {
+ unsigned lengthNotIncludingUndefined;
+ unsigned newRelevantLength;
+ compactForSorting<ArrayWithDouble>(
+ lengthNotIncludingUndefined, newRelevantLength);
+
+ sortCompactedVector<ArrayWithDouble>(
+ exec, m_butterfly->contiguousDouble(), lengthNotIncludingUndefined);
+ return;
+ }
+
+ case ArrayWithContiguous: {
+ unsigned lengthNotIncludingUndefined;
+ unsigned newRelevantLength;
+ compactForSorting<ArrayWithContiguous>(
+ lengthNotIncludingUndefined, newRelevantLength);
+
+ sortCompactedVector<ArrayWithContiguous>(
+ exec, m_butterfly->contiguous(), lengthNotIncludingUndefined);
+ return;
+ }
+
+ case ArrayWithArrayStorage: {
+ unsigned lengthNotIncludingUndefined;
+ unsigned newRelevantLength;
+ compactForSorting<ArrayWithArrayStorage>(
+ lengthNotIncludingUndefined, newRelevantLength);
+ ArrayStorage* storage = m_butterfly->arrayStorage();
+ ASSERT(!storage->m_sparseMap);
+
+ sortCompactedVector<ArrayWithArrayStorage>(exec, storage->vector(), lengthNotIncludingUndefined);
+ return;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+struct AVLTreeNodeForArrayCompare {
+ JSValue value;
+
+ // Child pointers. The high bit of gt is robbed and used as the
+ // balance factor sign. The high bit of lt is robbed and used as
+ // the magnitude of the balance factor.
+ int32_t gt;
+ int32_t lt;
+};
+
+struct AVLTreeAbstractorForArrayCompare {
+ typedef int32_t handle; // Handle is an index into m_nodes vector.
+ typedef JSValue key;
+ typedef int32_t size;
+
+ Vector<AVLTreeNodeForArrayCompare, 0, UnsafeVectorOverflow> m_nodes;
+ ExecState* m_exec;
+ JSValue m_compareFunction;
+ CallType m_compareCallType;
+ const CallData* m_compareCallData;
+ std::unique_ptr<CachedCall> m_cachedCall;
+
+ handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
+ void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
+ handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
+ void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
+
+ int get_balance_factor(handle h)
+ {
+ if (m_nodes[h].gt & 0x80000000)
+ return -1;
+ return static_cast<unsigned>(m_nodes[h].lt) >> 31;
+ }
+
+ void set_balance_factor(handle h, int bf)
+ {
+ if (bf == 0) {
+ m_nodes[h].lt &= 0x7FFFFFFF;
+ m_nodes[h].gt &= 0x7FFFFFFF;
+ } else {
+ m_nodes[h].lt |= 0x80000000;
+ if (bf < 0)
+ m_nodes[h].gt |= 0x80000000;
+ else
+ m_nodes[h].gt &= 0x7FFFFFFF;
+ }
+ }
+
+ int compare_key_key(key va, key vb)
+ {
+ ASSERT(!va.isUndefined());
+ ASSERT(!vb.isUndefined());
+
+ if (m_exec->hadException())
+ return 1;
+
+ double compareResult;
+ if (m_cachedCall) {
+ m_cachedCall->setThis(jsUndefined());
+ m_cachedCall->setArgument(0, va);
+ m_cachedCall->setArgument(1, vb);
+ compareResult = m_cachedCall->call().toNumber(m_exec);
+ } else {
+ MarkedArgumentBuffer arguments;
+ arguments.append(va);
+ arguments.append(vb);
+ compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, jsUndefined(), arguments).toNumber(m_exec);
+ }
+ return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
+ }
+
+ int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
+ int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
+
+ static handle null() { return 0x7FFFFFFF; }
+};
+
+template<IndexingType arrayIndexingType>
+void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
+{
+ ASSERT(!inSparseIndexingMode());
+ ASSERT(arrayIndexingType == indexingType());
+
+ // FIXME: This ignores exceptions raised in the compare function or in toNumber.
+
+ // The maximum tree depth is compiled in - but the caller is clearly up to no good
+ // if a larger array is passed.
+ ASSERT(m_butterfly->publicLength() <= static_cast<unsigned>(std::numeric_limits<int>::max()));
+ if (m_butterfly->publicLength() > static_cast<unsigned>(std::numeric_limits<int>::max()))
+ return;
+
+ unsigned usedVectorLength = relevantLength<arrayIndexingType>();
+ unsigned nodeCount = usedVectorLength;
+
+ if (!nodeCount)
+ return;
+
+ AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
+ tree.abstractor().m_exec = exec;
+ tree.abstractor().m_compareFunction = compareFunction;
+ tree.abstractor().m_compareCallType = callType;
+ tree.abstractor().m_compareCallData = &callData;
+ tree.abstractor().m_nodes.grow(nodeCount);
+
+ if (callType == CallTypeJS)
+ tree.abstractor().m_cachedCall = std::make_unique<CachedCall>(exec, jsCast<JSFunction*>(compareFunction), 2);
+
+ if (!tree.abstractor().m_nodes.begin()) {
+ throwOutOfMemoryError(exec);
+ return;
+ }
+
+ // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
+ // right out from under us while we're building the tree here.
+
+ unsigned numDefined = 0;
+ unsigned numUndefined = 0;
+
+ // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
+ for (; numDefined < usedVectorLength; ++numDefined) {
+ if (numDefined >= m_butterfly->vectorLength())
+ break;
+ JSValue v = getHolyIndexQuickly(numDefined);
+ if (!v || v.isUndefined())
+ break;
+ tree.abstractor().m_nodes[numDefined].value = v;
+ tree.insert(numDefined);
+ }
+ for (unsigned i = numDefined; i < usedVectorLength; ++i) {
+ if (i >= m_butterfly->vectorLength())
+ break;
+ JSValue v = getHolyIndexQuickly(i);
+ if (v) {
+ if (v.isUndefined())
+ ++numUndefined;
+ else {
+ tree.abstractor().m_nodes[numDefined].value = v;
+ tree.insert(numDefined);
+ ++numDefined;
+ }
+ }
+ }
+
+ unsigned newUsedVectorLength = numDefined + numUndefined;
+
+ // The array size may have changed. Figure out the new bounds.
+ unsigned newestUsedVectorLength = currentRelevantLength();
+
+ unsigned elementsToExtractThreshold = min(min(newestUsedVectorLength, numDefined), static_cast<unsigned>(tree.abstractor().m_nodes.size()));
+ unsigned undefinedElementsThreshold = min(newestUsedVectorLength, newUsedVectorLength);
+ unsigned clearElementsThreshold = min(newestUsedVectorLength, usedVectorLength);
+
+ // Copy the values back into m_storage.
+ AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
+ iter.start_iter_least(tree);
+ VM& vm = exec->vm();
+ for (unsigned i = 0; i < elementsToExtractThreshold; ++i) {
+ ASSERT(i < butterfly()->vectorLength());
+ if (indexingType() == ArrayWithDouble)
+ butterfly()->contiguousDouble()[i] = tree.abstractor().m_nodes[*iter].value.asNumber();
+ else
+ currentIndexingData()[i].set(vm, this, tree.abstractor().m_nodes[*iter].value);
+ ++iter;
+ }
+ // Put undefined values back in.
+ switch (indexingType()) {
+ case ArrayWithInt32:
+ case ArrayWithDouble:
+ ASSERT(elementsToExtractThreshold == undefinedElementsThreshold);
+ break;
+
+ default:
+ for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) {
+ ASSERT(i < butterfly()->vectorLength());
+ currentIndexingData()[i].setUndefined();
+ }
+ }
+
+ // Ensure that unused values in the vector are zeroed out.
+ for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) {
+ ASSERT(i < butterfly()->vectorLength());
+ if (indexingType() == ArrayWithDouble)
+ butterfly()->contiguousDouble()[i] = PNaN;
+ else
+ currentIndexingData()[i].clear();
+ }
+
+ if (hasAnyArrayStorage(indexingType()))
+ arrayStorage()->m_numValuesInVector = newUsedVectorLength;
+}
+
+void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
+{
+ ASSERT(!inSparseIndexingMode());
+
+ switch (indexingType()) {
+ case ArrayClass:
+ case ArrayWithUndecided:
+ return;
+
+ case ArrayWithInt32:
+ sortVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
+ return;
+
+ case ArrayWithDouble:
+ sortVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
+ return;
+
+ case ArrayWithContiguous:
+ sortVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
+ return;
+
+ case ArrayWithArrayStorage:
+ sortVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
+ return;
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
{
unsigned i = 0;
@@ -1123,4 +1639,95 @@
}
}
+template<IndexingType arrayIndexingType>
+void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLength)
+{
+ ASSERT(!inSparseIndexingMode());
+ ASSERT(arrayIndexingType == indexingType());
+
+ unsigned myRelevantLength = relevantLength<arrayIndexingType>();
+
+ numDefined = 0;
+ unsigned numUndefined = 0;
+
+ for (; numDefined < myRelevantLength; ++numDefined) {
+ ASSERT(numDefined < m_butterfly->vectorLength());
+ if (arrayIndexingType == ArrayWithInt32) {
+ JSValue v = m_butterfly->contiguousInt32()[numDefined].get();
+ if (!v)
+ break;
+ ASSERT(v.isInt32());
+ continue;
+ }
+ if (arrayIndexingType == ArrayWithDouble) {
+ double v = m_butterfly->contiguousDouble()[numDefined];
+ if (v != v)
+ break;
+ continue;
+ }
+ JSValue v = indexingData<arrayIndexingType>()[numDefined].get();
+ if (!v || v.isUndefined())
+ break;
+ }
+
+ for (unsigned i = numDefined; i < myRelevantLength; ++i) {
+ ASSERT(i < m_butterfly->vectorLength());
+ if (arrayIndexingType == ArrayWithInt32) {
+ JSValue v = m_butterfly->contiguousInt32()[i].get();
+ if (!v)
+ continue;
+ ASSERT(v.isInt32());
+ ASSERT(numDefined < m_butterfly->vectorLength());
+ m_butterfly->contiguousInt32()[numDefined++].setWithoutWriteBarrier(v);
+ continue;
+ }
+ if (arrayIndexingType == ArrayWithDouble) {
+ double v = m_butterfly->contiguousDouble()[i];
+ if (v != v)
+ continue;
+ ASSERT(numDefined < m_butterfly->vectorLength());
+ m_butterfly->contiguousDouble()[numDefined++] = v;
+ continue;
+ }
+ JSValue v = indexingData<arrayIndexingType>()[i].get();
+ if (v) {
+ if (v.isUndefined())
+ ++numUndefined;
+ else {
+ ASSERT(numDefined < m_butterfly->vectorLength());
+ indexingData<arrayIndexingType>()[numDefined++].setWithoutWriteBarrier(v);
+ }
+ }
+ }
+
+ newRelevantLength = numDefined + numUndefined;
+
+ if (hasAnyArrayStorage(arrayIndexingType))
+ RELEASE_ASSERT(!arrayStorage()->m_sparseMap);
+
+ switch (arrayIndexingType) {
+ case ArrayWithInt32:
+ case ArrayWithDouble:
+ RELEASE_ASSERT(numDefined == newRelevantLength);
+ break;
+
+ default:
+ for (unsigned i = numDefined; i < newRelevantLength; ++i) {
+ ASSERT(i < m_butterfly->vectorLength());
+ indexingData<arrayIndexingType>()[i].setUndefined();
+ }
+ break;
+ }
+ for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) {
+ ASSERT(i < m_butterfly->vectorLength());
+ if (arrayIndexingType == ArrayWithDouble)
+ m_butterfly->contiguousDouble()[i] = PNaN;
+ else
+ indexingData<arrayIndexingType>()[i].clear();
+ }
+
+ if (hasAnyArrayStorage(arrayIndexingType))
+ arrayStorage()->m_numValuesInVector = newRelevantLength;
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/JSArray.h (183307 => 183308)
--- trunk/Source/_javascript_Core/runtime/JSArray.h 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/runtime/JSArray.h 2015-04-25 06:57:12 UTC (rev 183308)
@@ -70,6 +70,10 @@
// OK to use on new arrays, but not if it might be a RegExpMatchArray.
JS_EXPORT_PRIVATE bool setLength(ExecState*, unsigned, bool throwException = false);
+ JS_EXPORT_PRIVATE void sort(ExecState*);
+ JS_EXPORT_PRIVATE void sort(ExecState*, JSValue compareFunction, CallType, const CallData&);
+ JS_EXPORT_PRIVATE void sortNumeric(ExecState*, JSValue compareFunction, CallType, const CallData&);
+
JS_EXPORT_PRIVATE void push(ExecState*, JSValue);
JS_EXPORT_PRIVATE JSValue pop(ExecState*);
@@ -159,8 +163,20 @@
bool unshiftCountWithArrayStorage(ExecState*, unsigned startIndex, unsigned count, ArrayStorage*);
bool unshiftCountSlowCase(VM&, bool, unsigned);
+ template<IndexingType indexingType>
+ void sortNumericVector(ExecState*, JSValue compareFunction, CallType, const CallData&);
+
+ template<IndexingType indexingType, typename StorageType>
+ void sortCompactedVector(ExecState*, ContiguousData<StorageType>, unsigned relevantLength);
+
+ template<IndexingType indexingType>
+ void sortVector(ExecState*, JSValue compareFunction, CallType, const CallData&);
+
bool setLengthWithArrayStorage(ExecState*, unsigned newLength, bool throwException, ArrayStorage*);
void setLengthWritable(ExecState*, bool writable);
+
+ template<IndexingType indexingType>
+ void compactForSorting(unsigned& numDefined, unsigned& newRelevantLength);
};
inline Butterfly* createContiguousArrayButterfly(VM& vm, JSCell* intendedOwner, unsigned length, unsigned& vectorLength)
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -445,11 +445,8 @@
GlobalPropertyInfo(vm.propertyNames->BuiltinLogPrivateName, builtinLog, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->ArrayPrivateName, arrayConstructor, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->NumberPrivateName, numberConstructor, DontEnum | DontDelete | ReadOnly),
- GlobalPropertyInfo(vm.propertyNames->StringPrivateName, stringConstructor, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->absPrivateName, privateFuncAbs, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->floorPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly),
- GlobalPropertyInfo(vm.propertyNames->getPrototypeOfPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly),
- GlobalPropertyInfo(vm.propertyNames->getOwnPropertyNamesPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->isFinitePrivateName, privateFuncIsFinite, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->arrayIterationKindKeyPrivateName, jsNumber(ArrayIterateKey), DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->arrayIterationKindValuePrivateName, jsNumber(ArrayIterateValue), DontEnum | DontDelete | ReadOnly),
Modified: trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp (183307 => 183308)
--- trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp 2015-04-25 06:57:12 UTC (rev 183308)
@@ -96,9 +96,6 @@
if (!globalObject->runtimeFlags().isSymbolDisabled())
JSC_NATIVE_FUNCTION("getOwnPropertySymbols", objectConstructorGetOwnPropertySymbols, DontEnum, 1);
-
- JSC_NATIVE_FUNCTION(vm.propertyNames->getPrototypeOfPrivateName, objectConstructorGetPrototypeOf, DontEnum, 1);
- JSC_NATIVE_FUNCTION(vm.propertyNames->getOwnPropertyNamesPrivateName, objectConstructorGetOwnPropertyNames, DontEnum, 1);
}
bool ObjectConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
Modified: trunk/Source/WTF/ChangeLog (183307 => 183308)
--- trunk/Source/WTF/ChangeLog 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/WTF/ChangeLog 2015-04-25 06:57:12 UTC (rev 183308)
@@ -1,3 +1,18 @@
+2015-04-24 Commit Queue <[email protected]>
+
+ Unreviewed, rolling out r183288.
+ https://bugs.webkit.org/show_bug.cgi?id=144189
+
+ Made js/sort-with-side-effecting-comparisons.html time out in
+ debug builds (Requested by ap on #webkit).
+
+ Reverted changeset:
+
+ "It shouldn't take 1846 lines of code and 5 FIXMEs to sort an
+ array."
+ https://bugs.webkit.org/show_bug.cgi?id=144013
+ http://trac.webkit.org/changeset/183288
+
2015-04-21 Geoffrey Garen <[email protected]>
It shouldn't take 1846 lines of code and 5 FIXMEs to sort an array.
Modified: trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj (183307 => 183308)
--- trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj 2015-04-25 06:57:12 UTC (rev 183308)
@@ -163,6 +163,7 @@
<ClInclude Include="..\wtf\Assertions.h" />
<ClInclude Include="..\wtf\Atomics.h" />
<ClInclude Include="..\wtf\AutodrainedPool.h" />
+ <ClInclude Include="..\wtf\AVLTree.h" />
<ClInclude Include="..\wtf\Bag.h" />
<ClInclude Include="..\wtf\BagToHashMap.h" />
<ClInclude Include="..\wtf\Bitmap.h" />
Modified: trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj.filters (183307 => 183308)
--- trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj.filters 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj.filters 2015-04-25 06:57:12 UTC (rev 183308)
@@ -381,6 +381,9 @@
<ClInclude Include="..\wtf\Atomics.h">
<Filter>wtf</Filter>
</ClInclude>
+ <ClInclude Include="..\wtf\AVLTree.h">
+ <Filter>wtf</Filter>
+ </ClInclude>
<ClInclude Include="..\wtf\Bitmap.h">
<Filter>wtf</Filter>
</ClInclude>
Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (183307 => 183308)
--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2015-04-25 06:57:12 UTC (rev 183308)
@@ -107,6 +107,7 @@
A8A47386151A825B004123FF /* Assertions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8A4725B151A825A004123FF /* Assertions.cpp */; };
A8A47387151A825B004123FF /* Assertions.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A4725C151A825A004123FF /* Assertions.h */; };
A8A47388151A825B004123FF /* Atomics.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A4725D151A825A004123FF /* Atomics.h */; };
+ A8A47389151A825B004123FF /* AVLTree.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A4725E151A825A004123FF /* AVLTree.h */; };
A8A4738A151A825B004123FF /* Bitmap.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A4725F151A825A004123FF /* Bitmap.h */; };
A8A4738B151A825B004123FF /* BitVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8A47260151A825A004123FF /* BitVector.cpp */; };
A8A4738C151A825B004123FF /* BitVector.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A47261151A825A004123FF /* BitVector.h */; };
@@ -391,6 +392,7 @@
A8A4725B151A825A004123FF /* Assertions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Assertions.cpp; sourceTree = "<group>"; };
A8A4725C151A825A004123FF /* Assertions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Assertions.h; sourceTree = "<group>"; };
A8A4725D151A825A004123FF /* Atomics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atomics.h; sourceTree = "<group>"; };
+ A8A4725E151A825A004123FF /* AVLTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVLTree.h; sourceTree = "<group>"; };
A8A4725F151A825A004123FF /* Bitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Bitmap.h; sourceTree = "<group>"; };
A8A47260151A825A004123FF /* BitVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BitVector.cpp; sourceTree = "<group>"; };
A8A47261151A825A004123FF /* BitVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BitVector.h; sourceTree = "<group>"; };
@@ -694,6 +696,7 @@
A8A4725D151A825A004123FF /* Atomics.h */,
1469419A16EAB10A0024E146 /* AutodrainedPool.h */,
1469419B16EAB10A0024E146 /* AutodrainedPoolMac.mm */,
+ A8A4725E151A825A004123FF /* AVLTree.h */,
0FB14E18180FA218009B6B4D /* Bag.h */,
0FB14E1A1810E1DA009B6B4D /* BagToHashMap.h */,
A8A4725F151A825A004123FF /* Bitmap.h */,
@@ -1036,6 +1039,7 @@
A8A47438151A825B004123FF /* AtomicStringImpl.h in Headers */,
9BD8F40B176C2B470002D865 /* AtomicStringTable.h in Headers */,
1469419C16EAB10A0024E146 /* AutodrainedPool.h in Headers */,
+ A8A47389151A825B004123FF /* AVLTree.h in Headers */,
8134013915B092FD001FF0B8 /* Base64.h in Headers */,
A8A473A9151A825B004123FF /* bignum-dtoa.h in Headers */,
A8A473AB151A825B004123FF /* bignum.h in Headers */,
Added: trunk/Source/WTF/wtf/AVLTree.h (0 => 183308)
--- trunk/Source/WTF/wtf/AVLTree.h (rev 0)
+++ trunk/Source/WTF/wtf/AVLTree.h 2015-04-25 06:57:12 UTC (rev 183308)
@@ -0,0 +1,960 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Based on Abstract AVL Tree Template v1.5 by Walt Karas
+ * <http://geocities.com/wkaras/gen_cpp/avl_tree.html>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AVL_TREE_H_
+#define AVL_TREE_H_
+
+#include <array>
+#include <wtf/Assertions.h>
+
+namespace WTF {
+
+// Here is the reference class for BSet.
+//
+// class BSet
+// {
+// public:
+//
+// class ANY_bitref
+// {
+// public:
+// operator bool ();
+// void operator = (bool b);
+// };
+//
+// // Does not have to initialize bits.
+// BSet();
+//
+// // Must return a valid value for index when 0 <= index < maxDepth
+// ANY_bitref operator [] (unsigned index);
+//
+// // Set all bits to 1.
+// void set();
+//
+// // Set all bits to 0.
+// void reset();
+// };
+
+template<unsigned maxDepth>
+class AVLTreeDefaultBSet {
+public:
+ bool& operator[](unsigned i) { ASSERT_WITH_SECURITY_IMPLICATION(i < maxDepth); return m_data[i]; }
+ void set() { for (unsigned i = 0; i < maxDepth; ++i) m_data[i] = true; }
+ void reset() { for (unsigned i = 0; i < maxDepth; ++i) m_data[i] = false; }
+
+private:
+ std::array<bool, maxDepth> m_data;
+};
+
+// How to determine maxDepth:
+// d Minimum number of nodes
+// 2 2
+// 3 4
+// 4 7
+// 5 12
+// 6 20
+// 7 33
+// 8 54
+// 9 88
+// 10 143
+// 11 232
+// 12 376
+// 13 609
+// 14 986
+// 15 1,596
+// 16 2,583
+// 17 4,180
+// 18 6,764
+// 19 10,945
+// 20 17,710
+// 21 28,656
+// 22 46,367
+// 23 75,024
+// 24 121,392
+// 25 196,417
+// 26 317,810
+// 27 514,228
+// 28 832,039
+// 29 1,346,268
+// 30 2,178,308
+// 31 3,524,577
+// 32 5,702,886
+// 33 9,227,464
+// 34 14,930,351
+// 35 24,157,816
+// 36 39,088,168
+// 37 63,245,985
+// 38 102,334,154
+// 39 165,580,140
+// 40 267,914,295
+// 41 433,494,436
+// 42 701,408,732
+// 43 1,134,903,169
+// 44 1,836,311,902
+// 45 2,971,215,072
+//
+// E.g., if, in a particular instantiation, the maximum number of nodes in a tree instance is 1,000,000, the maximum depth should be 28.
+// You pick 28 because MN(28) is 832,039, which is less than or equal to 1,000,000, and MN(29) is 1,346,268, which is strictly greater than 1,000,000.
+
+template <class Abstractor, unsigned maxDepth = 32, class BSet = AVLTreeDefaultBSet<maxDepth>>
+class AVLTree {
+public:
+
+ typedef typename Abstractor::key key;
+ typedef typename Abstractor::handle handle;
+ typedef typename Abstractor::size size;
+
+ enum SearchType {
+ EQUAL = 1,
+ LESS = 2,
+ GREATER = 4,
+ LESS_EQUAL = EQUAL | LESS,
+ GREATER_EQUAL = EQUAL | GREATER
+ };
+
+
+ Abstractor& abstractor() { return abs; }
+
+ inline handle insert(handle h);
+
+ inline handle search(key k, SearchType st = EQUAL);
+ inline handle search_least();
+ inline handle search_greatest();
+
+ inline handle remove(key k);
+
+ inline handle subst(handle new_node);
+
+ void purge() { abs.root = null(); }
+
+ bool is_empty() { return abs.root == null(); }
+
+ AVLTree() { abs.root = null(); }
+
+ class Iterator {
+ public:
+
+ // Initialize depth to invalid value, to indicate iterator is
+ // invalid. (Depth is zero-base.)
+ Iterator() { depth = ~0U; }
+
+ void start_iter(AVLTree &tree, key k, SearchType st = EQUAL)
+ {
+ // Mask of high bit in an int.
+ const int MASK_HIGH_BIT = (int) ~ ((~ (unsigned) 0) >> 1);
+
+ // Save the tree that we're going to iterate through in a
+ // member variable.
+ tree_ = &tree;
+
+ int cmp, target_cmp;
+ handle h = tree_->abs.root;
+ unsigned d = 0;
+
+ depth = ~0U;
+
+ if (h == null())
+ // Tree is empty.
+ return;
+
+ if (st & LESS)
+ // Key can be greater than key of starting node.
+ target_cmp = 1;
+ else if (st & GREATER)
+ // Key can be less than key of starting node.
+ target_cmp = -1;
+ else
+ // Key must be same as key of starting node.
+ target_cmp = 0;
+
+ for (;;) {
+ cmp = cmp_k_n(k, h);
+ if (cmp == 0) {
+ if (st & EQUAL) {
+ // Equal node was sought and found as starting node.
+ depth = d;
+ break;
+ }
+ cmp = -target_cmp;
+ } else if (target_cmp != 0) {
+ if (!((cmp ^ target_cmp) & MASK_HIGH_BIT)) {
+ // cmp and target_cmp are both negative or both positive.
+ depth = d;
+ }
+ }
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ if (h == null())
+ break;
+ branch[d] = cmp > 0;
+ path_h[d++] = h;
+ }
+ }
+
+ void start_iter_least(AVLTree &tree)
+ {
+ tree_ = &tree;
+
+ handle h = tree_->abs.root;
+
+ depth = ~0U;
+
+ branch.reset();
+
+ while (h != null()) {
+ if (depth != ~0U)
+ path_h[depth] = h;
+ depth++;
+ h = get_lt(h);
+ }
+ }
+
+ void start_iter_greatest(AVLTree &tree)
+ {
+ tree_ = &tree;
+
+ handle h = tree_->abs.root;
+
+ depth = ~0U;
+
+ branch.set();
+
+ while (h != null()) {
+ if (depth != ~0U)
+ path_h[depth] = h;
+ depth++;
+ h = get_gt(h);
+ }
+ }
+
+ handle operator*()
+ {
+ if (depth == ~0U)
+ return null();
+
+ return depth == 0 ? tree_->abs.root : path_h[depth - 1];
+ }
+
+ void operator++()
+ {
+ if (depth != ~0U) {
+ handle h = get_gt(**this);
+ if (h == null()) {
+ do {
+ if (depth == 0) {
+ depth = ~0U;
+ break;
+ }
+ depth--;
+ } while (branch[depth]);
+ } else {
+ branch[depth] = true;
+ path_h[depth++] = h;
+ for (;;) {
+ h = get_lt(h);
+ if (h == null())
+ break;
+ branch[depth] = false;
+ path_h[depth++] = h;
+ }
+ }
+ }
+ }
+
+ void operator--()
+ {
+ if (depth != ~0U) {
+ handle h = get_lt(**this);
+ if (h == null())
+ do {
+ if (depth == 0) {
+ depth = ~0U;
+ break;
+ }
+ depth--;
+ } while (!branch[depth]);
+ else {
+ branch[depth] = false;
+ path_h[depth++] = h;
+ for (;;) {
+ h = get_gt(h);
+ if (h == null())
+ break;
+ branch[depth] = true;
+ path_h[depth++] = h;
+ }
+ }
+ }
+ }
+
+ void operator++(int) { ++(*this); }
+ void operator--(int) { --(*this); }
+
+ protected:
+
+ // Tree being iterated over.
+ AVLTree *tree_;
+
+ // Records a path into the tree. If branch[n] is true, indicates
+ // take greater branch from the nth node in the path, otherwise
+ // take the less branch. branch[0] gives branch from root, and
+ // so on.
+ BSet branch;
+
+ // Zero-based depth of path into tree.
+ unsigned depth;
+
+ // Handles of nodes in path from root to current node (returned by *).
+ handle path_h[maxDepth - 1];
+
+ int cmp_k_n(key k, handle h) { return tree_->abs.compare_key_node(k, h); }
+ int cmp_n_n(handle h1, handle h2) { return tree_->abs.compare_node_node(h1, h2); }
+ handle get_lt(handle h) { return tree_->abs.get_less(h); }
+ handle get_gt(handle h) { return tree_->abs.get_greater(h); }
+ handle null() { return tree_->abs.null(); }
+ };
+
+ template<typename fwd_iter>
+ bool build(fwd_iter p, size num_nodes)
+ {
+ if (num_nodes == 0) {
+ abs.root = null();
+ return true;
+ }
+
+ // Gives path to subtree being built. If branch[N] is false, branch
+ // less from the node at depth N, if true branch greater.
+ BSet branch;
+
+ // If rem[N] is true, then for the current subtree at depth N, it's
+ // greater subtree has one more node than it's less subtree.
+ BSet rem;
+
+ // Depth of root node of current subtree.
+ unsigned depth = 0;
+
+ // Number of nodes in current subtree.
+ size num_sub = num_nodes;
+
+ // The algorithm relies on a stack of nodes whose less subtree has
+ // been built, but whose right subtree has not yet been built. The
+ // stack is implemented as linked list. The nodes are linked
+ // together by having the "greater" handle of a node set to the
+ // next node in the list. "less_parent" is the handle of the first
+ // node in the list.
+ handle less_parent = null();
+
+ // h is root of current subtree, child is one of its children.
+ handle h, child;
+
+ for (;;) {
+ while (num_sub > 2) {
+ // Subtract one for root of subtree.
+ num_sub--;
+ rem[depth] = !!(num_sub & 1);
+ branch[depth++] = false;
+ num_sub >>= 1;
+ }
+
+ if (num_sub == 2) {
+ // Build a subtree with two nodes, slanting to greater.
+ // I arbitrarily chose to always have the extra node in the
+ // greater subtree when there is an odd number of nodes to
+ // split between the two subtrees.
+
+ h = *p;
+ p++;
+ child = *p;
+ p++;
+ set_lt(child, null());
+ set_gt(child, null());
+ set_bf(child, 0);
+ set_gt(h, child);
+ set_lt(h, null());
+ set_bf(h, 1);
+ } else { // num_sub == 1
+ // Build a subtree with one node.
+
+ h = *p;
+ p++;
+ set_lt(h, null());
+ set_gt(h, null());
+ set_bf(h, 0);
+ }
+
+ while (depth) {
+ depth--;
+ if (!branch[depth])
+ // We've completed a less subtree.
+ break;
+
+ // We've completed a greater subtree, so attach it to
+ // its parent (that is less than it). We pop the parent
+ // off the stack of less parents.
+ child = h;
+ h = less_parent;
+ less_parent = get_gt(h);
+ set_gt(h, child);
+ // num_sub = 2 * (num_sub - rem[depth]) + rem[depth] + 1
+ num_sub <<= 1;
+ num_sub += 1 - rem[depth];
+ if (num_sub & (num_sub - 1))
+ // num_sub is not a power of 2
+ set_bf(h, 0);
+ else
+ // num_sub is a power of 2
+ set_bf(h, 1);
+ }
+
+ if (num_sub == num_nodes)
+ // We've completed the full tree.
+ break;
+
+ // The subtree we've completed is the less subtree of the
+ // next node in the sequence.
+
+ child = h;
+ h = *p;
+ p++;
+ set_lt(h, child);
+
+ // Put h into stack of less parents.
+ set_gt(h, less_parent);
+ less_parent = h;
+
+ // Proceed to creating greater than subtree of h.
+ branch[depth] = true;
+ num_sub += rem[depth++];
+
+ } // end for (;;)
+
+ abs.root = h;
+
+ return true;
+ }
+
+protected:
+
+ friend class Iterator;
+
+ // Create a class whose sole purpose is to take advantage of
+ // the "empty member" optimization.
+ struct abs_plus_root : public Abstractor {
+ // The handle of the root element in the AVL tree.
+ handle root;
+ };
+
+ abs_plus_root abs;
+
+
+ handle get_lt(handle h) { return abs.get_less(h); }
+ void set_lt(handle h, handle lh) { abs.set_less(h, lh); }
+
+ handle get_gt(handle h) { return abs.get_greater(h); }
+ void set_gt(handle h, handle gh) { abs.set_greater(h, gh); }
+
+ int get_bf(handle h) { return abs.get_balance_factor(h); }
+ void set_bf(handle h, int bf) { abs.set_balance_factor(h, bf); }
+
+ int cmp_k_n(key k, handle h) { return abs.compare_key_node(k, h); }
+ int cmp_n_n(handle h1, handle h2) { return abs.compare_node_node(h1, h2); }
+
+ handle null() { return abs.null(); }
+
+private:
+
+ // Balances subtree, returns handle of root node of subtree
+ // after balancing.
+ handle balance(handle bal_h)
+ {
+ handle deep_h;
+
+ // Either the "greater than" or the "less than" subtree of
+ // this node has to be 2 levels deeper (or else it wouldn't
+ // need balancing).
+
+ if (get_bf(bal_h) > 0) {
+ // "Greater than" subtree is deeper.
+
+ deep_h = get_gt(bal_h);
+
+ if (get_bf(deep_h) < 0) {
+ handle old_h = bal_h;
+ bal_h = get_lt(deep_h);
+
+ set_gt(old_h, get_lt(bal_h));
+ set_lt(deep_h, get_gt(bal_h));
+ set_lt(bal_h, old_h);
+ set_gt(bal_h, deep_h);
+
+ int bf = get_bf(bal_h);
+ if (bf != 0) {
+ if (bf > 0) {
+ set_bf(old_h, -1);
+ set_bf(deep_h, 0);
+ } else {
+ set_bf(deep_h, 1);
+ set_bf(old_h, 0);
+ }
+ set_bf(bal_h, 0);
+ } else {
+ set_bf(old_h, 0);
+ set_bf(deep_h, 0);
+ }
+ } else {
+ set_gt(bal_h, get_lt(deep_h));
+ set_lt(deep_h, bal_h);
+ if (get_bf(deep_h) == 0) {
+ set_bf(deep_h, -1);
+ set_bf(bal_h, 1);
+ } else {
+ set_bf(deep_h, 0);
+ set_bf(bal_h, 0);
+ }
+ bal_h = deep_h;
+ }
+ } else {
+ // "Less than" subtree is deeper.
+
+ deep_h = get_lt(bal_h);
+
+ if (get_bf(deep_h) > 0) {
+ handle old_h = bal_h;
+ bal_h = get_gt(deep_h);
+ set_lt(old_h, get_gt(bal_h));
+ set_gt(deep_h, get_lt(bal_h));
+ set_gt(bal_h, old_h);
+ set_lt(bal_h, deep_h);
+
+ int bf = get_bf(bal_h);
+ if (bf != 0) {
+ if (bf < 0) {
+ set_bf(old_h, 1);
+ set_bf(deep_h, 0);
+ } else {
+ set_bf(deep_h, -1);
+ set_bf(old_h, 0);
+ }
+ set_bf(bal_h, 0);
+ } else {
+ set_bf(old_h, 0);
+ set_bf(deep_h, 0);
+ }
+ } else {
+ set_lt(bal_h, get_gt(deep_h));
+ set_gt(deep_h, bal_h);
+ if (get_bf(deep_h) == 0) {
+ set_bf(deep_h, 1);
+ set_bf(bal_h, -1);
+ } else {
+ set_bf(deep_h, 0);
+ set_bf(bal_h, 0);
+ }
+ bal_h = deep_h;
+ }
+ }
+
+ return bal_h;
+ }
+
+};
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::insert(handle h)
+{
+ set_lt(h, null());
+ set_gt(h, null());
+ set_bf(h, 0);
+
+ if (abs.root == null())
+ abs.root = h;
+ else {
+ // Last unbalanced node encountered in search for insertion point.
+ handle unbal = null();
+ // Parent of last unbalanced node.
+ handle parent_unbal = null();
+ // Balance factor of last unbalanced node.
+ int unbal_bf;
+
+ // Zero-based depth in tree.
+ unsigned depth = 0, unbal_depth = 0;
+
+ // Records a path into the tree. If branch[n] is true, indicates
+ // take greater branch from the nth node in the path, otherwise
+ // take the less branch. branch[0] gives branch from root, and
+ // so on.
+ BSet branch;
+
+ handle hh = abs.root;
+ handle parent = null();
+ int cmp;
+
+ do {
+ if (get_bf(hh) != 0) {
+ unbal = hh;
+ parent_unbal = parent;
+ unbal_depth = depth;
+ }
+ cmp = cmp_n_n(h, hh);
+ if (cmp == 0)
+ // Duplicate key.
+ return hh;
+ parent = hh;
+ hh = cmp < 0 ? get_lt(hh) : get_gt(hh);
+ branch[depth++] = cmp > 0;
+ } while (hh != null());
+
+ // Add node to insert as leaf of tree.
+ if (cmp < 0)
+ set_lt(parent, h);
+ else
+ set_gt(parent, h);
+
+ depth = unbal_depth;
+
+ if (unbal == null())
+ hh = abs.root;
+ else {
+ cmp = branch[depth++] ? 1 : -1;
+ unbal_bf = get_bf(unbal);
+ if (cmp < 0)
+ unbal_bf--;
+ else // cmp > 0
+ unbal_bf++;
+ hh = cmp < 0 ? get_lt(unbal) : get_gt(unbal);
+ if ((unbal_bf != -2) && (unbal_bf != 2)) {
+ // No rebalancing of tree is necessary.
+ set_bf(unbal, unbal_bf);
+ unbal = null();
+ }
+ }
+
+ if (hh != null())
+ while (h != hh) {
+ cmp = branch[depth++] ? 1 : -1;
+ if (cmp < 0) {
+ set_bf(hh, -1);
+ hh = get_lt(hh);
+ } else { // cmp > 0
+ set_bf(hh, 1);
+ hh = get_gt(hh);
+ }
+ }
+
+ if (unbal != null()) {
+ unbal = balance(unbal);
+ if (parent_unbal == null())
+ abs.root = unbal;
+ else {
+ depth = unbal_depth - 1;
+ cmp = branch[depth] ? 1 : -1;
+ if (cmp < 0)
+ set_lt(parent_unbal, unbal);
+ else // cmp > 0
+ set_gt(parent_unbal, unbal);
+ }
+ }
+ }
+
+ return h;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::search(key k, typename AVLTree<Abstractor, maxDepth, BSet>::SearchType st)
+{
+ const int MASK_HIGH_BIT = (int) ~ ((~ (unsigned) 0) >> 1);
+
+ int cmp, target_cmp;
+ handle match_h = null();
+ handle h = abs.root;
+
+ if (st & LESS)
+ target_cmp = 1;
+ else if (st & GREATER)
+ target_cmp = -1;
+ else
+ target_cmp = 0;
+
+ while (h != null()) {
+ cmp = cmp_k_n(k, h);
+ if (cmp == 0) {
+ if (st & EQUAL) {
+ match_h = h;
+ break;
+ }
+ cmp = -target_cmp;
+ } else if (target_cmp != 0)
+ if (!((cmp ^ target_cmp) & MASK_HIGH_BIT))
+ // cmp and target_cmp are both positive or both negative.
+ match_h = h;
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ }
+
+ return match_h;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::search_least()
+{
+ handle h = abs.root, parent = null();
+
+ while (h != null()) {
+ parent = h;
+ h = get_lt(h);
+ }
+
+ return parent;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::search_greatest()
+{
+ handle h = abs.root, parent = null();
+
+ while (h != null()) {
+ parent = h;
+ h = get_gt(h);
+ }
+
+ return parent;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::remove(key k)
+{
+ // Zero-based depth in tree.
+ unsigned depth = 0, rm_depth;
+
+ // Records a path into the tree. If branch[n] is true, indicates
+ // take greater branch from the nth node in the path, otherwise
+ // take the less branch. branch[0] gives branch from root, and
+ // so on.
+ BSet branch;
+
+ handle h = abs.root;
+ handle parent = null(), child;
+ int cmp, cmp_shortened_sub_with_path = 0;
+
+ for (;;) {
+ if (h == null())
+ // No node in tree with given key.
+ return null();
+ cmp = cmp_k_n(k, h);
+ if (cmp == 0)
+ // Found node to remove.
+ break;
+ parent = h;
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ branch[depth++] = cmp > 0;
+ cmp_shortened_sub_with_path = cmp;
+ }
+ handle rm = h;
+ handle parent_rm = parent;
+ rm_depth = depth;
+
+ // If the node to remove is not a leaf node, we need to get a
+ // leaf node, or a node with a single leaf as its child, to put
+ // in the place of the node to remove. We will get the greatest
+ // node in the less subtree (of the node to remove), or the least
+ // node in the greater subtree. We take the leaf node from the
+ // deeper subtree, if there is one.
+
+ if (get_bf(h) < 0) {
+ child = get_lt(h);
+ branch[depth] = false;
+ cmp = -1;
+ } else {
+ child = get_gt(h);
+ branch[depth] = true;
+ cmp = 1;
+ }
+ depth++;
+
+ if (child != null()) {
+ cmp = -cmp;
+ do {
+ parent = h;
+ h = child;
+ if (cmp < 0) {
+ child = get_lt(h);
+ branch[depth] = false;
+ } else {
+ child = get_gt(h);
+ branch[depth] = true;
+ }
+ depth++;
+ } while (child != null());
+
+ if (parent == rm)
+ // Only went through do loop once. Deleted node will be replaced
+ // in the tree structure by one of its immediate children.
+ cmp_shortened_sub_with_path = -cmp;
+ else
+ cmp_shortened_sub_with_path = cmp;
+
+ // Get the handle of the opposite child, which may not be null.
+ child = cmp > 0 ? get_lt(h) : get_gt(h);
+ }
+
+ if (parent == null())
+ // There were only 1 or 2 nodes in this tree.
+ abs.root = child;
+ else if (cmp_shortened_sub_with_path < 0)
+ set_lt(parent, child);
+ else
+ set_gt(parent, child);
+
+ // "path" is the parent of the subtree being eliminated or reduced
+ // from a depth of 2 to 1. If "path" is the node to be removed, we
+ // set path to the node we're about to poke into the position of the
+ // node to be removed.
+ handle path = parent == rm ? h : parent;
+
+ if (h != rm) {
+ // Poke in the replacement for the node to be removed.
+ set_lt(h, get_lt(rm));
+ set_gt(h, get_gt(rm));
+ set_bf(h, get_bf(rm));
+ if (parent_rm == null())
+ abs.root = h;
+ else {
+ depth = rm_depth - 1;
+ if (branch[depth])
+ set_gt(parent_rm, h);
+ else
+ set_lt(parent_rm, h);
+ }
+ }
+
+ if (path != null()) {
+ // Create a temporary linked list from the parent of the path node
+ // to the root node.
+ h = abs.root;
+ parent = null();
+ depth = 0;
+ while (h != path) {
+ if (branch[depth++]) {
+ child = get_gt(h);
+ set_gt(h, parent);
+ } else {
+ child = get_lt(h);
+ set_lt(h, parent);
+ }
+ parent = h;
+ h = child;
+ }
+
+ // Climb from the path node to the root node using the linked
+ // list, restoring the tree structure and rebalancing as necessary.
+ bool reduced_depth = true;
+ int bf;
+ cmp = cmp_shortened_sub_with_path;
+ for (;;) {
+ if (reduced_depth) {
+ bf = get_bf(h);
+ if (cmp < 0)
+ bf++;
+ else // cmp > 0
+ bf--;
+ if ((bf == -2) || (bf == 2)) {
+ h = balance(h);
+ bf = get_bf(h);
+ } else
+ set_bf(h, bf);
+ reduced_depth = (bf == 0);
+ }
+ if (parent == null())
+ break;
+ child = h;
+ h = parent;
+ cmp = branch[--depth] ? 1 : -1;
+ if (cmp < 0) {
+ parent = get_lt(h);
+ set_lt(h, child);
+ } else {
+ parent = get_gt(h);
+ set_gt(h, child);
+ }
+ }
+ abs.root = h;
+ }
+
+ return rm;
+}
+
+template <class Abstractor, unsigned maxDepth, class BSet>
+inline typename AVLTree<Abstractor, maxDepth, BSet>::handle
+AVLTree<Abstractor, maxDepth, BSet>::subst(handle new_node)
+{
+ handle h = abs.root;
+ handle parent = null();
+ int cmp, last_cmp;
+
+ /* Search for node already in tree with same key. */
+ for (;;) {
+ if (h == null())
+ /* No node in tree with same key as new node. */
+ return null();
+ cmp = cmp_n_n(new_node, h);
+ if (cmp == 0)
+ /* Found the node to substitute new one for. */
+ break;
+ last_cmp = cmp;
+ parent = h;
+ h = cmp < 0 ? get_lt(h) : get_gt(h);
+ }
+
+ /* Copy tree housekeeping fields from node in tree to new node. */
+ set_lt(new_node, get_lt(h));
+ set_gt(new_node, get_gt(h));
+ set_bf(new_node, get_bf(h));
+
+ if (parent == null())
+ /* New node is also new root. */
+ abs.root = new_node;
+ else {
+ /* Make parent point to new node. */
+ if (last_cmp < 0)
+ set_lt(parent, new_node);
+ else
+ set_gt(parent, new_node);
+ }
+
+ return h;
+}
+
+}
+
+#endif
Modified: trunk/Source/WTF/wtf/CMakeLists.txt (183307 => 183308)
--- trunk/Source/WTF/wtf/CMakeLists.txt 2015-04-25 05:19:07 UTC (rev 183307)
+++ trunk/Source/WTF/wtf/CMakeLists.txt 2015-04-25 06:57:12 UTC (rev 183308)
@@ -1,5 +1,6 @@
set(WTF_HEADERS
ASCIICType.h
+ AVLTree.h
Assertions.h
Atomics.h
Bag.h