Skip to content

Commit f939751

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 f939751

File tree

3 files changed

+409
-0
lines changed

3 files changed

+409
-0
lines changed

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

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,331 @@ 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+
* Shifting an item in the heap data structure
904+
*
905+
* @return completion value
906+
* Returned value must be freed with ecma_free_completion_value.
907+
*/
908+
static ecma_completion_value_t
909+
ecma_builtin_array_prototype_object_array_to_heap_helper (ecma_value_t array[], /**< heap data array */
910+
int index, /**< current item index */
911+
int right, /**< right index is a maximum index */
912+
ecma_value_t comparefn) /**< compare function */
913+
{
914+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
915+
int child = 2 * index;
916+
ecma_value_t swap = array[index];
917+
918+
while (child < right && ecma_is_completion_value_empty (ret_value))
919+
{
920+
ECMA_TRY_CATCH (compare_value,
921+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
922+
array[child + 1],
923+
comparefn),
924+
ret_value);
925+
JERRY_ASSERT (ecma_is_value_number (compare_value));
926+
927+
if ((*ecma_get_number_from_value (compare_value) < ECMA_NUMBER_ZERO))
928+
{
929+
child++;
930+
}
931+
932+
ECMA_TRY_CATCH (inner_compare_value,
933+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
934+
swap,
935+
comparefn),
936+
ret_value);
937+
JERRY_ASSERT (ecma_is_value_number (inner_compare_value));
938+
939+
if (*ecma_get_number_from_value (inner_compare_value) <= ECMA_NUMBER_ZERO)
940+
{
941+
/* Break from loop */
942+
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED);
943+
}
944+
else
945+
{
946+
array[child / 2] = array[child];
947+
child = (child == 0) ? 1 : child * 2;
948+
}
949+
950+
ECMA_FINALIZE (inner_compare_value);
951+
ECMA_FINALIZE (compare_value);
952+
}
953+
954+
if (ecma_is_completion_value_empty (ret_value) || ecma_is_completion_value_normal (ret_value))
955+
{
956+
array[child / 2] = swap;
957+
}
958+
959+
if (ecma_is_completion_value_empty (ret_value))
960+
{
961+
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED);
962+
}
963+
964+
return ret_value;
965+
} /* ecma_builtin_array_prototype_object_array_to_heap_helper */
966+
967+
/**
968+
* Heapsort function
969+
*
970+
* @return completion value
971+
* Returned value must be freed with ecma_free_completion_value.
972+
*/
973+
static ecma_completion_value_t
974+
ecma_builtin_array_prototype_object_array_heap_sort_helper (ecma_value_t array[], /**< array to sort */
975+
int right, /**< right index */
976+
ecma_value_t comparefn) /**< compare function */
977+
{
978+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
979+
980+
for (int i = right / 2; i >= 0 && ecma_is_completion_value_empty (ret_value); i--)
981+
{
982+
ECMA_TRY_CATCH (value,
983+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
984+
i,
985+
right,
986+
comparefn),
987+
ret_value);
988+
ECMA_FINALIZE (value);
989+
}
990+
991+
for (int i = right; i > 0 && ecma_is_completion_value_empty (ret_value); i--)
992+
{
993+
ecma_value_t swap = array[0];
994+
array[0] = array[i];
995+
array[i] = swap;
996+
ECMA_TRY_CATCH (value,
997+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
998+
0,
999+
i - 1,
1000+
comparefn),
1001+
ret_value);
1002+
ECMA_FINALIZE (value);
1003+
}
1004+
1005+
return ret_value;
1006+
} /* ecma_builtin_array_prototype_object_array_heap_sort_helper */
1007+
1008+
/**
1009+
* The Array.prototype object's 'sort' routine
1010+
*
1011+
* See also:
1012+
* ECMA-262 v5, 15.4.4.11
1013+
*
1014+
* @return completion value
1015+
* Returned value must be freed with ecma_free_completion_value.
1016+
*/
1017+
static ecma_completion_value_t
1018+
ecma_builtin_array_prototype_object_sort (ecma_value_t this_arg, /**< this argument */
1019+
ecma_value_t arg1) /**< comparefn */
1020+
{
1021+
/* Check if the provided compare function is callable. */
1022+
if (!ecma_is_value_undefined (arg1) && !ecma_op_is_callable (arg1))
1023+
{
1024+
return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
1025+
}
1026+
1027+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1028+
1029+
ECMA_TRY_CATCH (obj_this,
1030+
ecma_op_to_object (this_arg),
1031+
ret_value);
1032+
1033+
ecma_object_t *obj_p = ecma_get_object_from_value (obj_this);
1034+
ecma_string_t *magic_string_length_p = ecma_get_magic_string (ECMA_MAGIC_STRING_LENGTH);
1035+
1036+
ECMA_TRY_CATCH (len_value,
1037+
ecma_op_object_get (obj_p, magic_string_length_p),
1038+
ret_value);
1039+
1040+
ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value);
1041+
1042+
uint32_t len = ecma_number_to_uint32 (len_number);
1043+
1044+
MEM_DEFINE_LOCAL_ARRAY (values_buffer, len, ecma_value_t);
1045+
uint32_t copied_num = 0;
1046+
1047+
/* Copy unsorted array into a native c array. */
1048+
for (uint32_t index = 0; index < len && ecma_is_completion_value_empty (ret_value); index++)
1049+
{
1050+
ecma_string_t* index_string_p = ecma_new_ecma_string_from_uint32 (index);
1051+
ECMA_TRY_CATCH (index_value, ecma_op_object_get (obj_p, index_string_p), ret_value);
1052+
1053+
values_buffer[index] = ecma_copy_value (index_value, true);
1054+
copied_num++;
1055+
1056+
ECMA_FINALIZE (index_value);
1057+
ecma_deref_ecma_string (index_string_p);
1058+
}
1059+
1060+
JERRY_ASSERT (copied_num == len || !ecma_is_completion_value_empty (ret_value));
1061+
1062+
/* Sorting. */
1063+
if (len > 1 && ecma_is_completion_value_empty (ret_value))
1064+
{
1065+
ECMA_TRY_CATCH (sort_value,
1066+
ecma_builtin_array_prototype_object_array_heap_sort_helper (values_buffer,
1067+
(int)(len - 1),
1068+
arg1),
1069+
ret_value);
1070+
ECMA_FINALIZE (sort_value);
1071+
}
1072+
1073+
if (ecma_is_completion_value_empty (ret_value))
1074+
{
1075+
/* Casting len to ecma_length_t may overflow, but since ecma_length_t is still at lest 16 bits long,
1076+
with an array of that size, we would run out of memory way before this happens. */
1077+
JERRY_ASSERT ((ecma_length_t) len == len);
1078+
/* Copy the sorted array into a new array. */
1079+
ret_value = ecma_op_create_array_object (values_buffer, (ecma_length_t) len, false);
1080+
}
1081+
1082+
/* Free values that were copied to the local array. */
1083+
for (uint32_t index = 0; index < copied_num; index++)
1084+
{
1085+
ecma_free_value (values_buffer[index], true);
1086+
}
1087+
1088+
MEM_FINALIZE_LOCAL_ARRAY (values_buffer);
1089+
1090+
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
1091+
ECMA_FINALIZE (len_value);
1092+
ecma_deref_ecma_string (magic_string_length_p);
1093+
ECMA_FINALIZE (obj_this);
1094+
1095+
return ret_value;
1096+
} /* ecma_builtin_array_prototype_object_sort */
1097+
7731098
/**
7741099
* The Array.prototype object's 'shift' routine
7751100
*

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)