Skip to content

Commit 9a88ef0

Browse files
szledandbatyai
authored andcommitted
Implemented Array.prototype.sort()
JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai [email protected] JerryScript-DCO-1.0-Signed-off-by: Szilard Ledan [email protected]
1 parent b614c0b commit 9a88ef0

File tree

3 files changed

+428
-0
lines changed

3 files changed

+428
-0
lines changed

jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,350 @@ ecma_builtin_array_prototype_object_last_index_of (ecma_value_t this_arg, /**< t
770770
return ret_value;
771771
} /* ecma_builtin_array_prototype_object_last_index_of */
772772

773+
/**
774+
* SortCompare abstract method
775+
*
776+
* See also:
777+
* ECMA-262 v5, 15.4.4.11
778+
*
779+
* @return completion value
780+
* Returned value must be freed with ecma_free_completion_value.
781+
*/
782+
static ecma_completion_value_t
783+
ecma_builtin_array_prototype_object_sort_compare_helper (ecma_value_t j, /**< left value */
784+
ecma_value_t k, /**< right value */
785+
ecma_value_t comparefn) /**< compare function */
786+
{
787+
/*
788+
* ECMA-262 v5, 15.4.4.11 NOTE1: Because non-existent property values always
789+
* compare greater than undefined property values, and undefined always
790+
* compares greater than any other value, undefined property values always
791+
* sort to the end of the result, followed by non-existent property values.
792+
*/
793+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
794+
ecma_number_t *result_p = ecma_alloc_number ();
795+
796+
bool j_is_undef = ecma_is_value_undefined (j);
797+
bool k_is_undef = ecma_is_value_undefined (k);
798+
799+
if (j_is_undef)
800+
{
801+
if (k_is_undef)
802+
{
803+
*result_p = ecma_int32_to_number (0);
804+
}
805+
else
806+
{
807+
*result_p = ecma_int32_to_number (1);
808+
}
809+
}
810+
else
811+
{
812+
if (k_is_undef)
813+
{
814+
*result_p = ecma_int32_to_number (-1);
815+
}
816+
else
817+
{
818+
if (ecma_is_value_undefined (comparefn))
819+
{
820+
/* Default comparison when no comparefn is passed. */
821+
ECMA_TRY_CATCH (j_value, ecma_op_to_string (j), ret_value);
822+
ECMA_TRY_CATCH (k_value, ecma_op_to_string (k), ret_value);
823+
ecma_string_t *j_str_p = ecma_get_string_from_value (j_value);
824+
ecma_string_t *k_str_p = ecma_get_string_from_value (k_value);
825+
826+
if (ecma_compare_ecma_strings_relational (j_str_p, k_str_p))
827+
{
828+
*result_p = ecma_int32_to_number (-1);
829+
}
830+
else if (!ecma_compare_ecma_strings (j_str_p, k_str_p))
831+
{
832+
*result_p = ecma_int32_to_number (1);
833+
}
834+
else
835+
{
836+
*result_p = ecma_int32_to_number (0);
837+
}
838+
839+
ECMA_FINALIZE (k_value);
840+
ECMA_FINALIZE (j_value);
841+
}
842+
else
843+
{
844+
/*
845+
* comparefn, if not undefined, will always contain a callable function object.
846+
* We checked this previously, before this function was called.
847+
*/
848+
JERRY_ASSERT (ecma_op_is_callable (comparefn));
849+
ecma_object_t *comparefn_obj_p = ecma_get_object_from_value (comparefn);
850+
851+
ecma_value_t compare_args[] = {j, k};
852+
853+
ECMA_TRY_CATCH (call_value,
854+
ecma_op_function_call (comparefn_obj_p,
855+
ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED),
856+
compare_args,
857+
2),
858+
ret_value);
859+
860+
if (!ecma_is_value_number (call_value))
861+
{
862+
ECMA_OP_TO_NUMBER_TRY_CATCH (ret_num, call_value, ret_value);
863+
*result_p = ret_num;
864+
ECMA_OP_TO_NUMBER_FINALIZE (ret_num);
865+
}
866+
else
867+
{
868+
*result_p = *ecma_get_number_from_value (call_value);
869+
}
870+
871+
ECMA_FINALIZE (call_value);
872+
}
873+
}
874+
}
875+
876+
if (ecma_is_completion_value_empty (ret_value))
877+
{
878+
ret_value = ecma_make_normal_completion_value (ecma_make_number_value (result_p));
879+
}
880+
else
881+
{
882+
ecma_dealloc_number (result_p);
883+
}
884+
885+
return ret_value;
886+
} /* ecma_builtin_array_prototype_object_sort_compare_helper */
887+
888+
/**
889+
* Function used to reconstruct the ordered binary tree.
890+
* Shifts 'index' down in the tree until it is in the correct position.
891+
*
892+
* @return completion value
893+
* Returned value must be freed with ecma_free_completion_value.
894+
*/
895+
static ecma_completion_value_t
896+
ecma_builtin_array_prototype_object_array_to_heap_helper (ecma_value_t array[], /**< heap data array */
897+
int index, /**< current item index */
898+
int right, /**< right index is a maximum index */
899+
ecma_value_t comparefn) /**< compare function */
900+
{
901+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
902+
903+
/* Left child of the current index. */
904+
int child = index * 2 + 1;
905+
ecma_value_t swap = array[index];
906+
bool should_break = false;
907+
908+
while (child <= right && ecma_is_completion_value_empty (ret_value) && !should_break)
909+
{
910+
if (child < right)
911+
{
912+
/* Compare the two child nodes. */
913+
ECMA_TRY_CATCH (child_compare_value,
914+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
915+
array[child + 1],
916+
comparefn),
917+
ret_value);
918+
919+
JERRY_ASSERT (ecma_is_value_number (child_compare_value));
920+
921+
/* Use the child that is greater. */
922+
if (*ecma_get_number_from_value (child_compare_value) < ECMA_NUMBER_ZERO)
923+
{
924+
child++;
925+
}
926+
927+
ECMA_FINALIZE (child_compare_value);
928+
}
929+
930+
if (ecma_is_completion_value_empty (ret_value))
931+
{
932+
JERRY_ASSERT (child <= right);
933+
934+
/* Compare current child node with the swap (tree top). */
935+
ECMA_TRY_CATCH (swap_compare_value,
936+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
937+
swap,
938+
comparefn),
939+
ret_value);
940+
JERRY_ASSERT (ecma_is_value_number (swap_compare_value));
941+
942+
if (*ecma_get_number_from_value (swap_compare_value) <= ECMA_NUMBER_ZERO)
943+
{
944+
/* Break from loop if current child is less than swap (tree top) */
945+
should_break = true;
946+
}
947+
else
948+
{
949+
/* We have to move 'swap' lower in the tree, so shift current child up in the hierarchy. */
950+
int parent = (child - 1) / 2;
951+
JERRY_ASSERT (parent >= 0 && parent <= right);
952+
array[parent] = array[child];
953+
954+
/* Update child to be the left child of the current node. */
955+
child = child * 2 + 1;
956+
}
957+
958+
ECMA_FINALIZE (swap_compare_value);
959+
}
960+
}
961+
962+
if (ecma_is_completion_value_empty (ret_value))
963+
{
964+
/*
965+
* Loop ended, either current child does not exist, or is less than swap.
966+
* This means that 'swap' should be placed in the parent node.
967+
*/
968+
int parent = (child - 1) / 2;
969+
JERRY_ASSERT (parent >= 0 && parent <= right);
970+
array[parent] = swap;
971+
972+
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED);
973+
}
974+
975+
return ret_value;
976+
} /* ecma_builtin_array_prototype_object_array_to_heap_helper */
977+
978+
/**
979+
* Heapsort function
980+
*
981+
* @return completion value
982+
* Returned value must be freed with ecma_free_completion_value.
983+
*/
984+
static ecma_completion_value_t
985+
ecma_builtin_array_prototype_object_array_heap_sort_helper (ecma_value_t array[], /**< array to sort */
986+
int right, /**< right index */
987+
ecma_value_t comparefn) /**< compare function */
988+
{
989+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
990+
991+
/* First, construct the ordered binary tree from the array. */
992+
for (int i = right / 2; i >= 0 && ecma_is_completion_value_empty (ret_value); i--)
993+
{
994+
ECMA_TRY_CATCH (value,
995+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
996+
i,
997+
right,
998+
comparefn),
999+
ret_value);
1000+
ECMA_FINALIZE (value);
1001+
}
1002+
1003+
/* Sorting elements. */
1004+
for (int i = right; i > 0 && ecma_is_completion_value_empty (ret_value); i--)
1005+
{
1006+
/*
1007+
* The top element will always contain the largest value.
1008+
* Move top to the end, and remove it from the tree.
1009+
*/
1010+
ecma_value_t swap = array[0];
1011+
array[0] = array[i];
1012+
array[i] = swap;
1013+
1014+
/* Rebuild binary tree from the remaining elements. */
1015+
ECMA_TRY_CATCH (value,
1016+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
1017+
0,
1018+
i - 1,
1019+
comparefn),
1020+
ret_value);
1021+
ECMA_FINALIZE (value);
1022+
}
1023+
1024+
return ret_value;
1025+
} /* ecma_builtin_array_prototype_object_array_heap_sort_helper */
1026+
1027+
/**
1028+
* The Array.prototype object's 'sort' routine
1029+
*
1030+
* See also:
1031+
* ECMA-262 v5, 15.4.4.11
1032+
*
1033+
* @return completion value
1034+
* Returned value must be freed with ecma_free_completion_value.
1035+
*/
1036+
static ecma_completion_value_t
1037+
ecma_builtin_array_prototype_object_sort (ecma_value_t this_arg, /**< this argument */
1038+
ecma_value_t arg1) /**< comparefn */
1039+
{
1040+
/* Check if the provided compare function is callable. */
1041+
if (!ecma_is_value_undefined (arg1) && !ecma_op_is_callable (arg1))
1042+
{
1043+
return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
1044+
}
1045+
1046+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1047+
1048+
ECMA_TRY_CATCH (obj_this,
1049+
ecma_op_to_object (this_arg),
1050+
ret_value);
1051+
1052+
ecma_object_t *obj_p = ecma_get_object_from_value (obj_this);
1053+
ecma_string_t *magic_string_length_p = ecma_get_magic_string (ECMA_MAGIC_STRING_LENGTH);
1054+
1055+
ECMA_TRY_CATCH (len_value,
1056+
ecma_op_object_get (obj_p, magic_string_length_p),
1057+
ret_value);
1058+
1059+
ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value);
1060+
1061+
uint32_t len = ecma_number_to_uint32 (len_number);
1062+
1063+
MEM_DEFINE_LOCAL_ARRAY (values_buffer, len, ecma_value_t);
1064+
uint32_t copied_num = 0;
1065+
1066+
/* Copy unsorted array into a native c array. */
1067+
for (uint32_t index = 0; index < len && ecma_is_completion_value_empty (ret_value); index++)
1068+
{
1069+
ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index);
1070+
ECMA_TRY_CATCH (index_value, ecma_op_object_get (obj_p, index_string_p), ret_value);
1071+
1072+
values_buffer[index] = ecma_copy_value (index_value, true);
1073+
copied_num++;
1074+
1075+
ECMA_FINALIZE (index_value);
1076+
ecma_deref_ecma_string (index_string_p);
1077+
}
1078+
1079+
JERRY_ASSERT (copied_num == len || !ecma_is_completion_value_empty (ret_value));
1080+
1081+
/* Sorting. */
1082+
if (len > 1 && ecma_is_completion_value_empty (ret_value))
1083+
{
1084+
ECMA_TRY_CATCH (sort_value,
1085+
ecma_builtin_array_prototype_object_array_heap_sort_helper (values_buffer,
1086+
(int)(len - 1),
1087+
arg1),
1088+
ret_value);
1089+
ECMA_FINALIZE (sort_value);
1090+
}
1091+
1092+
if (ecma_is_completion_value_empty (ret_value))
1093+
{
1094+
/* Casting len to ecma_length_t may overflow, but since ecma_length_t is still at lest 16 bits long,
1095+
with an array of that size, we would run out of memory way before this happens. */
1096+
JERRY_ASSERT ((ecma_length_t) len == len);
1097+
/* Copy the sorted array into a new array. */
1098+
ret_value = ecma_op_create_array_object (values_buffer, (ecma_length_t) len, false);
1099+
}
1100+
1101+
/* Free values that were copied to the local array. */
1102+
for (uint32_t index = 0; index < copied_num; index++)
1103+
{
1104+
ecma_free_value (values_buffer[index], true);
1105+
}
1106+
1107+
MEM_FINALIZE_LOCAL_ARRAY (values_buffer);
1108+
1109+
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
1110+
ECMA_FINALIZE (len_value);
1111+
ecma_deref_ecma_string (magic_string_length_p);
1112+
ECMA_FINALIZE (obj_this);
1113+
1114+
return ret_value;
1115+
} /* ecma_builtin_array_prototype_object_sort */
1116+
7731117
/**
7741118
* The Array.prototype object's 'shift' routine
7751119
*

jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.inc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ ROUTINE (ECMA_MAGIC_STRING_POP, ecma_builtin_array_prototype_object_pop, 0, 0)
6565
ROUTINE (ECMA_MAGIC_STRING_PUSH, ecma_builtin_array_prototype_object_push, NON_FIXED, 1)
6666
ROUTINE (ECMA_MAGIC_STRING_INDEX_OF_UL, ecma_builtin_array_prototype_object_index_of, 2, 1)
6767
ROUTINE (ECMA_MAGIC_STRING_LAST_INDEX_OF_UL, ecma_builtin_array_prototype_object_last_index_of, 2, 1)
68+
ROUTINE (ECMA_MAGIC_STRING_SORT, ecma_builtin_array_prototype_object_sort, 1, 1)
6869
ROUTINE (ECMA_MAGIC_STRING_SHIFT, ecma_builtin_array_prototype_object_shift, 0, 0)
6970
ROUTINE (ECMA_MAGIC_STRING_UNSHIFT, ecma_builtin_array_prototype_object_unshift, NON_FIXED, 1)
7071
ROUTINE (ECMA_MAGIC_STRING_SLICE, ecma_builtin_array_prototype_object_slice, 2, 2)

0 commit comments

Comments
 (0)