Change heap sort to merge sort for Array.prototype (#4026)

ES11 requires Array.prototype.sort to be stable

JerryScript-DCO-1.0-Signed-off-by: Rafal Walczyna r.walczyna@samsung.com
This commit is contained in:
Rafal Walczyna
2020-07-22 13:33:07 +02:00
committed by GitHub
parent 0124368ae7
commit a4952da831
4 changed files with 103 additions and 100 deletions
@@ -1178,10 +1178,10 @@ ecma_builtin_array_prototype_object_sort (ecma_value_t this_arg, /**< this argum
if (copied_num > 1) if (copied_num > 1)
{ {
const ecma_builtin_helper_sort_compare_fn_t sort_cb = &ecma_builtin_array_prototype_object_sort_compare_helper; const ecma_builtin_helper_sort_compare_fn_t sort_cb = &ecma_builtin_array_prototype_object_sort_compare_helper;
ecma_value_t sort_value = ecma_builtin_helper_array_heap_sort_helper (values_buffer, ecma_value_t sort_value = ecma_builtin_helper_array_merge_sort_helper (values_buffer,
(uint32_t) (copied_num - 1), (uint32_t) (copied_num),
arg1, arg1,
sort_cb); sort_cb);
if (ECMA_IS_VALUE_ERROR (sort_value)) if (ECMA_IS_VALUE_ERROR (sort_value))
{ {
goto clean_up; goto clean_up;
@@ -17,133 +17,136 @@
#include "ecma-globals.h" #include "ecma-globals.h"
/** /**
* Function used to reconstruct the ordered binary tree. * Function used to merge two arrays for merge sort.
* Shifts 'index' down in the tree until it is in the correct position. * Arrays are stored as below:
* First -> source_array_p [left_idx : right_idx - 1]
* Second -> source_array_p [right_idx : end_idx - 1]
* Output -> output_array_p
* *
* @return ecma value * @return ecma value
* Returned value must be freed with ecma_free_value. * Returned value must be freed with ecma_free_value.
*/ */
static ecma_value_t static ecma_value_t
ecma_builtin_helper_array_to_heap (ecma_value_t *array_p, /**< heap data array */ ecma_builtin_helper_array_merge_sort_bottom_up (ecma_value_t *source_array_p, /**< arrays to merge */
uint32_t index, /**< current item index */ uint32_t left_idx, /**< first array begin */
uint32_t right, /**< right index is a maximum index */ uint32_t right_idx, /**< first array end */
ecma_value_t compare_func, /**< compare function */ uint32_t end_idx, /**< second array end */
const ecma_builtin_helper_sort_compare_fn_t sort_cb) /**< sorting cb */ ecma_value_t *output_array_p, /**< output array */
ecma_value_t compare_func, /**< compare function */
const ecma_builtin_helper_sort_compare_fn_t sort_cb) /**< sorting cb */
{ {
/* Left child of the current index. */
uint32_t child = index * 2 + 1;
ecma_value_t swap = array_p[index];
ecma_value_t ret_value = ECMA_VALUE_EMPTY; ecma_value_t ret_value = ECMA_VALUE_EMPTY;
uint32_t i = left_idx, j = right_idx;
while (child <= right) for (uint32_t k = left_idx; k < end_idx; k++)
{ {
if (child < right) ecma_value_t compare_value = ecma_make_number_value (ECMA_NUMBER_ZERO);
{
/* Compare the two child nodes. */
ecma_value_t child_compare_value = sort_cb (array_p[child], array_p[child + 1], compare_func);
if (ECMA_IS_VALUE_ERROR (child_compare_value)) if (i < right_idx && j < end_idx)
{
compare_value = sort_cb (source_array_p[i], source_array_p[j], compare_func);
if (ECMA_IS_VALUE_ERROR (compare_value))
{ {
ret_value = ECMA_VALUE_ERROR; ret_value = ECMA_VALUE_ERROR;
break; break;
} }
JERRY_ASSERT (ecma_is_value_number (child_compare_value));
/* Use the child that is greater. */
if (ecma_get_number_from_value (child_compare_value) < ECMA_NUMBER_ZERO)
{
child++;
}
ecma_free_value (child_compare_value);
} }
JERRY_ASSERT (child <= right); if (i < right_idx && ecma_get_number_from_value (compare_value) <= ECMA_NUMBER_ZERO)
/* Compare current child node with the swap (tree top). */
ecma_value_t swap_compare_value = sort_cb (array_p[child], swap, compare_func);
if (ECMA_IS_VALUE_ERROR (swap_compare_value))
{ {
ret_value = ECMA_VALUE_ERROR; output_array_p[k] = source_array_p[i];
break; i++;
} }
else
JERRY_ASSERT (ecma_is_value_number (swap_compare_value));
if (ecma_get_number_from_value (swap_compare_value) <= ECMA_NUMBER_ZERO)
{ {
/* Break from loop if current child is less than swap (tree top) */ output_array_p[k] = source_array_p[j];
ecma_free_value (swap_compare_value); j++;
break;
} }
ecma_free_value (compare_value);
/* We have to move 'swap' lower in the tree, so shift current child up in the hierarchy. */
uint32_t parent = (child - 1) / 2;
JERRY_ASSERT (parent <= right);
array_p[parent] = array_p[child];
/* Update child to be the left child of the current node. */
child = child * 2 + 1;
ecma_free_value (swap_compare_value);
} }
/*
* Loop ended, either current child does not exist, or is less than swap.
* This means that 'swap' should be placed in the parent node.
*/
uint32_t parent = (child - 1) / 2;
JERRY_ASSERT (parent <= right);
array_p[parent] = swap;
return ret_value; return ret_value;
} /* ecma_builtin_helper_array_to_heap */ } /* ecma_builtin_helper_array_merge_sort_bottom_up */
/** /**
* Heapsort function * Mergesort function
* *
* @return ecma value * @return ecma value
* Returned value must be freed with ecma_free_value. * Returned value must be freed with ecma_free_value.
*/ */
ecma_value_t ecma_value_t
ecma_builtin_helper_array_heap_sort_helper (ecma_value_t *array_p, /**< array to sort */ ecma_builtin_helper_array_merge_sort_helper (ecma_value_t *array_p, /**< array to sort */
uint32_t right, /**< right index */ uint32_t length, /**< length */
ecma_value_t compare_func, /**< compare function */ ecma_value_t compare_func, /**< compare function */
const ecma_builtin_helper_sort_compare_fn_t sort_cb) /**< sorting cb */ const ecma_builtin_helper_sort_compare_fn_t sort_cb) /**< sorting cb */
{ {
/* First, construct the ordered binary tree from the array. */ ecma_value_t ret_value = ECMA_VALUE_EMPTY;
for (uint32_t i = (right / 2) + 1; i > 0; i--) JMEM_DEFINE_LOCAL_ARRAY (dest_array_p, length, ecma_value_t);
{
ecma_value_t value = ecma_builtin_helper_array_to_heap (array_p, i - 1, right, compare_func, sort_cb);
if (ECMA_IS_VALUE_ERROR (value)) ecma_value_t *temp_p;
ecma_value_t *base_array_p = array_p;
uint32_t r, e;
for (uint32_t w = 1; w < length; w = 2 * w)
{
for (uint32_t i = 0; i < length; i = i + 2 * w)
{ {
return value; // End of first array
r = i + w;
if (r > length)
{
r = length;
}
// End of second array
e = i + 2 * w;
if (e > length)
{
e = length;
}
// Merge two arrays
ret_value = ecma_builtin_helper_array_merge_sort_bottom_up (array_p,
i,
r,
e,
dest_array_p,
compare_func,
sort_cb);
if (ECMA_IS_VALUE_ERROR (ret_value))
{
break;
}
} }
if (ECMA_IS_VALUE_ERROR (ret_value))
{
break;
}
// Swap arrays
temp_p = dest_array_p;
dest_array_p = array_p;
array_p = temp_p;
} }
/* Sorting elements. */ // Sorted array is in dest_array_p - there was uneven number of arrays swaps
for (uint32_t i = right; i > 0; i--) if (dest_array_p == base_array_p)
{ {
/* uint32_t index = 0;
* The top element will always contain the largest value. temp_p = dest_array_p;
* Move top to the end, and remove it from the tree. dest_array_p = array_p;
*/ array_p = temp_p;
ecma_value_t swap = array_p[0];
array_p[0] = array_p[i];
array_p[i] = swap;
/* Rebuild binary tree from the remaining elements. */ while (index < length)
ecma_value_t value = ecma_builtin_helper_array_to_heap (array_p, 0, i - 1, compare_func, sort_cb);
if (ECMA_IS_VALUE_ERROR (value))
{ {
return value; array_p[index] = dest_array_p[index];
index++;
} }
JERRY_ASSERT (index == length);
} }
return ECMA_VALUE_EMPTY; JMEM_FINALIZE_LOCAL_ARRAY (dest_array_p);
} /* ecma_builtin_helper_array_heap_sort_helper */
return ret_value;
} /* ecma_builtin_helper_array_merge_sort_helper */
@@ -238,10 +238,10 @@ typedef ecma_value_t (*ecma_builtin_helper_sort_compare_fn_t) (ecma_value_t lhs,
ecma_value_t rhs, /**< right value */ ecma_value_t rhs, /**< right value */
ecma_value_t compare_func); /**< compare function */ ecma_value_t compare_func); /**< compare function */
ecma_value_t ecma_builtin_helper_array_heap_sort_helper (ecma_value_t *array_p, ecma_value_t ecma_builtin_helper_array_merge_sort_helper (ecma_value_t *array_p,
uint32_t right, uint32_t length,
ecma_value_t compare_func, ecma_value_t compare_func,
const ecma_builtin_helper_sort_compare_fn_t sort_cb); const ecma_builtin_helper_sort_compare_fn_t sort_cb);
/** /**
* @} * @}
@@ -1527,10 +1527,10 @@ ecma_builtin_typedarray_prototype_sort (ecma_value_t this_arg, /**< this argumen
const ecma_builtin_helper_sort_compare_fn_t sort_cb = &ecma_builtin_typedarray_prototype_sort_compare_helper; const ecma_builtin_helper_sort_compare_fn_t sort_cb = &ecma_builtin_typedarray_prototype_sort_compare_helper;
ecma_value_t sort_value = ecma_builtin_helper_array_heap_sort_helper (values_buffer, ecma_value_t sort_value = ecma_builtin_helper_array_merge_sort_helper (values_buffer,
(uint32_t) (info.length - 1), (uint32_t) (info.length),
compare_func, compare_func,
sort_cb); sort_cb);
if (ECMA_IS_VALUE_ERROR (sort_value)) if (ECMA_IS_VALUE_ERROR (sort_value))
{ {