Skip to content

Commit 761c079

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 761c079

File tree

3 files changed

+447
-0
lines changed

3 files changed

+447
-0
lines changed

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

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,369 @@ 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 = 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 = ecma_int32_to_number (0);
804+
}
805+
else
806+
{
807+
*result = ecma_int32_to_number (1);
808+
}
809+
}
810+
else
811+
{
812+
if (k_is_undef)
813+
{
814+
*result = 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_completion_value (j_value);
824+
ecma_string_t *k_str_p = ecma_get_string_from_completion_value (k_value);
825+
826+
if (ecma_compare_ecma_strings_relational (j_str_p, k_str_p))
827+
{
828+
*result = ecma_int32_to_number (-1);
829+
}
830+
else if (!ecma_compare_ecma_strings (j_str_p, k_str_p))
831+
{
832+
*result = ecma_int32_to_number (1);
833+
}
834+
else
835+
{
836+
*result = 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+
ecma_number_t *num_p = ecma_alloc_number ();
864+
*num_p = ret_num;
865+
ret_value = ecma_make_number_value (num_p);
866+
ECMA_OP_TO_NUMBER_FINALIZE (ret_num);
867+
}
868+
else
869+
{
870+
ecma_number_t *number = ecma_get_number_from_value (call_value);
871+
if (*number < ECMA_NUMBER_ZERO)
872+
{
873+
*result = ecma_int32_to_number (-1);
874+
}
875+
else if (*number > ECMA_NUMBER_ZERO)
876+
{
877+
*result = ecma_int32_to_number (1);
878+
}
879+
else
880+
{
881+
*result = ecma_int32_to_number (0);
882+
}
883+
}
884+
885+
ECMA_FINALIZE (call_value);
886+
}
887+
}
888+
}
889+
890+
if (ecma_is_completion_value_empty (ret_value))
891+
{
892+
ret_value = ecma_make_normal_completion_value (ecma_make_number_value (result));
893+
}
894+
else
895+
{
896+
ecma_dealloc_number (result);
897+
}
898+
899+
return ret_value;
900+
} /* ecma_builtin_array_prototype_object_sort_compare_helper */
901+
902+
/**
903+
* Function used to reconstruct the ordered binary tree.
904+
* Shifts 'index' down in the tree until it is in the correct position.
905+
*
906+
* @return completion value
907+
* Returned value must be freed with ecma_free_completion_value.
908+
*/
909+
static ecma_completion_value_t
910+
ecma_builtin_array_prototype_object_array_to_heap_helper (ecma_value_t array[], /**< heap data array */
911+
int index, /**< current item index */
912+
int right, /**< right index is a maximum index */
913+
ecma_value_t comparefn) /**< compare function */
914+
{
915+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
916+
917+
/* Left child of the current index. */
918+
int child = index * 2 + 1;
919+
ecma_value_t swap = array[index];
920+
921+
while (child <= right && ecma_is_completion_value_empty (ret_value))
922+
{
923+
if (child < right)
924+
{
925+
/* Compare the two child nodes. */
926+
ECMA_TRY_CATCH (child_compare_value,
927+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
928+
array[child + 1],
929+
comparefn),
930+
ret_value);
931+
932+
JERRY_ASSERT (ecma_is_value_number (child_compare_value));
933+
934+
/* Use the child that is greater. */
935+
if ((*ecma_get_number_from_value (child_compare_value) < ECMA_NUMBER_ZERO))
936+
{
937+
child++;
938+
}
939+
940+
ECMA_FINALIZE (child_compare_value);
941+
}
942+
943+
if (!ecma_is_completion_value_empty (ret_value))
944+
{
945+
return ret_value;
946+
}
947+
948+
JERRY_ASSERT (child <= right);
949+
950+
/* Compare current child node with the swap (tree top). */
951+
ECMA_TRY_CATCH (swap_compare_value,
952+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
953+
swap,
954+
comparefn),
955+
ret_value);
956+
JERRY_ASSERT (ecma_is_value_number (swap_compare_value));
957+
958+
if (*ecma_get_number_from_value (swap_compare_value) <= ECMA_NUMBER_ZERO)
959+
{
960+
/* Break from loop if current child is less than swap (tree top) */
961+
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED);
962+
}
963+
else
964+
{
965+
/* We have to move 'swap' lower in the tree, so shift current child up in the hierarchy. */
966+
int parent = (child - 1) / 2;
967+
JERRY_ASSERT (parent >= 0 && parent <= right);
968+
array[parent] = array[child];
969+
970+
/* Update child to be the left child of the current node. */
971+
child = child * 2 + 1;
972+
}
973+
974+
ECMA_FINALIZE (swap_compare_value);
975+
}
976+
977+
978+
if (ecma_is_completion_value_empty (ret_value) || ecma_is_completion_value_normal (ret_value))
979+
{
980+
/*
981+
* Loop ended, either current child does not exist, or is less than swap.
982+
* This means that 'swap' should be placed in the parent node.
983+
*/
984+
int parent = (child - 1) / 2;
985+
JERRY_ASSERT (parent >= 0 && parent <= right);
986+
array[parent] = swap;
987+
}
988+
989+
if (ecma_is_completion_value_empty (ret_value))
990+
{
991+
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED);
992+
}
993+
994+
return ret_value;
995+
} /* ecma_builtin_array_prototype_object_array_to_heap_helper */
996+
997+
/**
998+
* Heapsort function
999+
*
1000+
* @return completion value
1001+
* Returned value must be freed with ecma_free_completion_value.
1002+
*/
1003+
static ecma_completion_value_t
1004+
ecma_builtin_array_prototype_object_array_heap_sort_helper (ecma_value_t array[], /**< array to sort */
1005+
int right, /**< right index */
1006+
ecma_value_t comparefn) /**< compare function */
1007+
{
1008+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1009+
1010+
/* First, construct the ordered binary tree from the array. */
1011+
for (int i = right / 2; i >= 0 && ecma_is_completion_value_empty (ret_value); i--)
1012+
{
1013+
ECMA_TRY_CATCH (value,
1014+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
1015+
i,
1016+
right,
1017+
comparefn),
1018+
ret_value);
1019+
ECMA_FINALIZE (value);
1020+
}
1021+
1022+
/* Sorting elements. */
1023+
for (int i = right; i > 0 && ecma_is_completion_value_empty (ret_value); i--)
1024+
{
1025+
/*
1026+
* The top element will always contain the largest value.
1027+
* Move top to the end, and remove it from the tree.
1028+
*/
1029+
ecma_value_t swap = array[0];
1030+
array[0] = array[i];
1031+
array[i] = swap;
1032+
1033+
/* Rebuild binary tree from the remaining elements. */
1034+
ECMA_TRY_CATCH (value,
1035+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
1036+
0,
1037+
i - 1,
1038+
comparefn),
1039+
ret_value);
1040+
ECMA_FINALIZE (value);
1041+
}
1042+
1043+
return ret_value;
1044+
} /* ecma_builtin_array_prototype_object_array_heap_sort_helper */
1045+
1046+
/**
1047+
* The Array.prototype object's 'sort' routine
1048+
*
1049+
* See also:
1050+
* ECMA-262 v5, 15.4.4.11
1051+
*
1052+
* @return completion value
1053+
* Returned value must be freed with ecma_free_completion_value.
1054+
*/
1055+
static ecma_completion_value_t
1056+
ecma_builtin_array_prototype_object_sort (ecma_value_t this_arg, /**< this argument */
1057+
ecma_value_t arg1) /**< comparefn */
1058+
{
1059+
/* Check if the provided compare function is callable. */
1060+
if (!ecma_is_value_undefined (arg1) && !ecma_op_is_callable (arg1))
1061+
{
1062+
return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
1063+
}
1064+
1065+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1066+
1067+
ECMA_TRY_CATCH (obj_this,
1068+
ecma_op_to_object (this_arg),
1069+
ret_value);
1070+
1071+
ecma_object_t *obj_p = ecma_get_object_from_value (obj_this);
1072+
ecma_string_t *magic_string_length_p = ecma_get_magic_string (ECMA_MAGIC_STRING_LENGTH);
1073+
1074+
ECMA_TRY_CATCH (len_value,
1075+
ecma_op_object_get (obj_p, magic_string_length_p),
1076+
ret_value);
1077+
1078+
ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value);
1079+
1080+
uint32_t len = ecma_number_to_uint32 (len_number);
1081+
1082+
MEM_DEFINE_LOCAL_ARRAY (values_buffer, len, ecma_value_t);
1083+
uint32_t copied_num = 0;
1084+
1085+
/* Copy unsorted array into a native c array. */
1086+
for (uint32_t index = 0; index < len && ecma_is_completion_value_empty (ret_value); index++)
1087+
{
1088+
ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index);
1089+
ECMA_TRY_CATCH (index_value, ecma_op_object_get (obj_p, index_string_p), ret_value);
1090+
1091+
values_buffer[index] = ecma_copy_value (index_value, true);
1092+
copied_num++;
1093+
1094+
ECMA_FINALIZE (index_value);
1095+
ecma_deref_ecma_string (index_string_p);
1096+
}
1097+
1098+
JERRY_ASSERT (copied_num == len || !ecma_is_completion_value_empty (ret_value));
1099+
1100+
/* Sorting. */
1101+
if (len > 1 && ecma_is_completion_value_empty (ret_value))
1102+
{
1103+
ECMA_TRY_CATCH (sort_value,
1104+
ecma_builtin_array_prototype_object_array_heap_sort_helper (values_buffer,
1105+
(int)(len - 1),
1106+
arg1),
1107+
ret_value);
1108+
ECMA_FINALIZE (sort_value);
1109+
}
1110+
1111+
if (ecma_is_completion_value_empty (ret_value))
1112+
{
1113+
/* Casting len to ecma_length_t may overflow, but since ecma_length_t is still at lest 16 bits long,
1114+
with an array of that size, we would run out of memory way before this happens. */
1115+
JERRY_ASSERT ((ecma_length_t) len == len);
1116+
/* Copy the sorted array into a new array. */
1117+
ret_value = ecma_op_create_array_object (values_buffer, (ecma_length_t) len, false);
1118+
}
1119+
1120+
/* Free values that were copied to the local array. */
1121+
for (uint32_t index = 0; index < copied_num; index++)
1122+
{
1123+
ecma_free_value (values_buffer[index], true);
1124+
}
1125+
1126+
MEM_FINALIZE_LOCAL_ARRAY (values_buffer);
1127+
1128+
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
1129+
ECMA_FINALIZE (len_value);
1130+
ecma_deref_ecma_string (magic_string_length_p);
1131+
ECMA_FINALIZE (obj_this);
1132+
1133+
return ret_value;
1134+
} /* ecma_builtin_array_prototype_object_sort */
1135+
7731136
/**
7741137
* The Array.prototype object's 'shift' routine
7751138
*

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)