Skip to content

Commit 6b8e34a

Browse files
szledandbatyai
authored andcommitted
Implemented Array.prototype.sort()
JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai.u-szeged@partner.samsung.com JerryScript-DCO-1.0-Signed-off-by: Szilard Ledan szledan.u-szeged@partner.samsung.com
1 parent 8b28cac commit 6b8e34a

File tree

3 files changed

+430
-0
lines changed

3 files changed

+430
-0
lines changed

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

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,352 @@ ecma_builtin_array_prototype_object_last_index_of (ecma_value_t this_arg, /**< t
11091109
return ret_value;
11101110
} /* ecma_builtin_array_prototype_object_last_index_of */
11111111

1112+
/**
1113+
* SortCompare abstract method
1114+
*
1115+
* See also:
1116+
* ECMA-262 v5, 15.4.4.11
1117+
*
1118+
* @return completion value
1119+
* Returned value must be freed with ecma_free_completion_value.
1120+
*/
1121+
static ecma_completion_value_t
1122+
ecma_builtin_array_prototype_object_sort_compare_helper (ecma_value_t j, /**< left value */
1123+
ecma_value_t k, /**< right value */
1124+
ecma_value_t comparefn) /**< compare function */
1125+
{
1126+
/*
1127+
* ECMA-262 v5, 15.4.4.11 NOTE1: Because non-existent property values always
1128+
* compare greater than undefined property values, and undefined always
1129+
* compares greater than any other value, undefined property values always
1130+
* sort to the end of the result, followed by non-existent property values.
1131+
*/
1132+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1133+
ecma_number_t *result_p = ecma_alloc_number ();
1134+
1135+
bool j_is_undef = ecma_is_value_undefined (j);
1136+
bool k_is_undef = ecma_is_value_undefined (k);
1137+
1138+
if (j_is_undef)
1139+
{
1140+
if (k_is_undef)
1141+
{
1142+
*result_p = ecma_int32_to_number (0);
1143+
}
1144+
else
1145+
{
1146+
*result_p = ecma_int32_to_number (1);
1147+
}
1148+
}
1149+
else
1150+
{
1151+
if (k_is_undef)
1152+
{
1153+
*result_p = ecma_int32_to_number (-1);
1154+
}
1155+
else
1156+
{
1157+
if (ecma_is_value_undefined (comparefn))
1158+
{
1159+
/* Default comparison when no comparefn is passed. */
1160+
ECMA_TRY_CATCH (j_value, ecma_op_to_string (j), ret_value);
1161+
ECMA_TRY_CATCH (k_value, ecma_op_to_string (k), ret_value);
1162+
ecma_string_t *j_str_p = ecma_get_string_from_value (j_value);
1163+
ecma_string_t *k_str_p = ecma_get_string_from_value (k_value);
1164+
1165+
if (ecma_compare_ecma_strings_relational (j_str_p, k_str_p))
1166+
{
1167+
*result_p = ecma_int32_to_number (-1);
1168+
}
1169+
else if (!ecma_compare_ecma_strings (j_str_p, k_str_p))
1170+
{
1171+
*result_p = ecma_int32_to_number (1);
1172+
}
1173+
else
1174+
{
1175+
*result_p = ecma_int32_to_number (0);
1176+
}
1177+
1178+
ECMA_FINALIZE (k_value);
1179+
ECMA_FINALIZE (j_value);
1180+
}
1181+
else
1182+
{
1183+
/*
1184+
* comparefn, if not undefined, will always contain a callable function object.
1185+
* We checked this previously, before this function was called.
1186+
*/
1187+
JERRY_ASSERT (ecma_op_is_callable (comparefn));
1188+
ecma_object_t *comparefn_obj_p = ecma_get_object_from_value (comparefn);
1189+
1190+
ecma_value_t compare_args[] = {j, k};
1191+
1192+
ECMA_TRY_CATCH (call_value,
1193+
ecma_op_function_call (comparefn_obj_p,
1194+
ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED),
1195+
compare_args,
1196+
2),
1197+
ret_value);
1198+
1199+
if (!ecma_is_value_number (call_value))
1200+
{
1201+
ECMA_OP_TO_NUMBER_TRY_CATCH (ret_num, call_value, ret_value);
1202+
*result_p = ret_num;
1203+
ECMA_OP_TO_NUMBER_FINALIZE (ret_num);
1204+
}
1205+
else
1206+
{
1207+
*result_p = *ecma_get_number_from_value (call_value);
1208+
}
1209+
1210+
ECMA_FINALIZE (call_value);
1211+
}
1212+
}
1213+
}
1214+
1215+
if (ecma_is_completion_value_empty (ret_value))
1216+
{
1217+
ret_value = ecma_make_normal_completion_value (ecma_make_number_value (result_p));
1218+
}
1219+
else
1220+
{
1221+
ecma_dealloc_number (result_p);
1222+
}
1223+
1224+
return ret_value;
1225+
} /* ecma_builtin_array_prototype_object_sort_compare_helper */
1226+
1227+
/**
1228+
* Function used to reconstruct the ordered binary tree.
1229+
* Shifts 'index' down in the tree until it is in the correct position.
1230+
*
1231+
* @return completion value
1232+
* Returned value must be freed with ecma_free_completion_value.
1233+
*/
1234+
static ecma_completion_value_t
1235+
ecma_builtin_array_prototype_object_array_to_heap_helper (ecma_value_t array[], /**< heap data array */
1236+
int index, /**< current item index */
1237+
int right, /**< right index is a maximum index */
1238+
ecma_value_t comparefn) /**< compare function */
1239+
{
1240+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1241+
1242+
/* Left child of the current index. */
1243+
int child = index * 2 + 1;
1244+
ecma_value_t swap = array[index];
1245+
bool should_break = false;
1246+
1247+
while (child <= right && ecma_is_completion_value_empty (ret_value) && !should_break)
1248+
{
1249+
if (child < right)
1250+
{
1251+
/* Compare the two child nodes. */
1252+
ECMA_TRY_CATCH (child_compare_value,
1253+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
1254+
array[child + 1],
1255+
comparefn),
1256+
ret_value);
1257+
1258+
JERRY_ASSERT (ecma_is_value_number (child_compare_value));
1259+
1260+
/* Use the child that is greater. */
1261+
if (*ecma_get_number_from_value (child_compare_value) < ECMA_NUMBER_ZERO)
1262+
{
1263+
child++;
1264+
}
1265+
1266+
ECMA_FINALIZE (child_compare_value);
1267+
}
1268+
1269+
if (ecma_is_completion_value_empty (ret_value))
1270+
{
1271+
JERRY_ASSERT (child <= right);
1272+
1273+
/* Compare current child node with the swap (tree top). */
1274+
ECMA_TRY_CATCH (swap_compare_value,
1275+
ecma_builtin_array_prototype_object_sort_compare_helper (array[child],
1276+
swap,
1277+
comparefn),
1278+
ret_value);
1279+
JERRY_ASSERT (ecma_is_value_number (swap_compare_value));
1280+
1281+
if (*ecma_get_number_from_value (swap_compare_value) <= ECMA_NUMBER_ZERO)
1282+
{
1283+
/* Break from loop if current child is less than swap (tree top) */
1284+
should_break = true;
1285+
}
1286+
else
1287+
{
1288+
/* We have to move 'swap' lower in the tree, so shift current child up in the hierarchy. */
1289+
int parent = (child - 1) / 2;
1290+
JERRY_ASSERT (parent >= 0 && parent <= right);
1291+
array[parent] = array[child];
1292+
1293+
/* Update child to be the left child of the current node. */
1294+
child = child * 2 + 1;
1295+
}
1296+
1297+
ECMA_FINALIZE (swap_compare_value);
1298+
}
1299+
}
1300+
1301+
if (ecma_is_completion_value_empty (ret_value))
1302+
{
1303+
/*
1304+
* Loop ended, either current child does not exist, or is less than swap.
1305+
* This means that 'swap' should be placed in the parent node.
1306+
*/
1307+
int parent = (child - 1) / 2;
1308+
JERRY_ASSERT (parent >= 0 && parent <= right);
1309+
array[parent] = swap;
1310+
1311+
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED);
1312+
}
1313+
1314+
return ret_value;
1315+
} /* ecma_builtin_array_prototype_object_array_to_heap_helper */
1316+
1317+
/**
1318+
* Heapsort function
1319+
*
1320+
* @return completion value
1321+
* Returned value must be freed with ecma_free_completion_value.
1322+
*/
1323+
static ecma_completion_value_t
1324+
ecma_builtin_array_prototype_object_array_heap_sort_helper (ecma_value_t array[], /**< array to sort */
1325+
int right, /**< right index */
1326+
ecma_value_t comparefn) /**< compare function */
1327+
{
1328+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1329+
1330+
/* First, construct the ordered binary tree from the array. */
1331+
for (int i = right / 2; i >= 0 && ecma_is_completion_value_empty (ret_value); i--)
1332+
{
1333+
ECMA_TRY_CATCH (value,
1334+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
1335+
i,
1336+
right,
1337+
comparefn),
1338+
ret_value);
1339+
ECMA_FINALIZE (value);
1340+
}
1341+
1342+
/* Sorting elements. */
1343+
for (int i = right; i > 0 && ecma_is_completion_value_empty (ret_value); i--)
1344+
{
1345+
/*
1346+
* The top element will always contain the largest value.
1347+
* Move top to the end, and remove it from the tree.
1348+
*/
1349+
ecma_value_t swap = array[0];
1350+
array[0] = array[i];
1351+
array[i] = swap;
1352+
1353+
/* Rebuild binary tree from the remaining elements. */
1354+
ECMA_TRY_CATCH (value,
1355+
ecma_builtin_array_prototype_object_array_to_heap_helper (array,
1356+
0,
1357+
i - 1,
1358+
comparefn),
1359+
ret_value);
1360+
ECMA_FINALIZE (value);
1361+
}
1362+
1363+
return ret_value;
1364+
} /* ecma_builtin_array_prototype_object_array_heap_sort_helper */
1365+
1366+
/**
1367+
* The Array.prototype object's 'sort' routine
1368+
*
1369+
* See also:
1370+
* ECMA-262 v5, 15.4.4.11
1371+
*
1372+
* @return completion value
1373+
* Returned value must be freed with ecma_free_completion_value.
1374+
*/
1375+
static ecma_completion_value_t
1376+
ecma_builtin_array_prototype_object_sort (ecma_value_t this_arg, /**< this argument */
1377+
ecma_value_t arg1) /**< comparefn */
1378+
{
1379+
/* Check if the provided compare function is callable. */
1380+
if (!ecma_is_value_undefined (arg1) && !ecma_op_is_callable (arg1))
1381+
{
1382+
return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
1383+
}
1384+
1385+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
1386+
1387+
ECMA_TRY_CATCH (obj_this,
1388+
ecma_op_to_object (this_arg),
1389+
ret_value);
1390+
1391+
ecma_object_t *obj_p = ecma_get_object_from_value (obj_this);
1392+
ecma_string_t *magic_string_length_p = ecma_get_magic_string (ECMA_MAGIC_STRING_LENGTH);
1393+
1394+
ECMA_TRY_CATCH (len_value,
1395+
ecma_op_object_get (obj_p, magic_string_length_p),
1396+
ret_value);
1397+
1398+
ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value);
1399+
1400+
uint32_t len = ecma_number_to_uint32 (len_number);
1401+
1402+
MEM_DEFINE_LOCAL_ARRAY (values_buffer, len, ecma_value_t);
1403+
uint32_t copied_num = 0;
1404+
1405+
/* Copy unsorted array into a native c array. */
1406+
for (uint32_t index = 0; index < len && ecma_is_completion_value_empty (ret_value); index++)
1407+
{
1408+
ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index);
1409+
ECMA_TRY_CATCH (index_value, ecma_op_object_get (obj_p, index_string_p), ret_value);
1410+
1411+
values_buffer[index] = ecma_copy_value (index_value, true);
1412+
copied_num++;
1413+
1414+
ECMA_FINALIZE (index_value);
1415+
ecma_deref_ecma_string (index_string_p);
1416+
}
1417+
1418+
JERRY_ASSERT (copied_num == len || !ecma_is_completion_value_empty (ret_value));
1419+
1420+
/* Sorting. */
1421+
if (len > 1 && ecma_is_completion_value_empty (ret_value))
1422+
{
1423+
ECMA_TRY_CATCH (sort_value,
1424+
ecma_builtin_array_prototype_object_array_heap_sort_helper (values_buffer,
1425+
(int)(len - 1),
1426+
arg1),
1427+
ret_value);
1428+
ECMA_FINALIZE (sort_value);
1429+
}
1430+
1431+
if (ecma_is_completion_value_empty (ret_value))
1432+
{
1433+
/*
1434+
* FIXME: Casting len to ecma_length_t may overflow, but since ecma_length_t is still at least
1435+
* 16 bits long, with an array of that size, we would run out of memory way before this happens.
1436+
*/
1437+
JERRY_ASSERT ((ecma_length_t) len == len);
1438+
/* Copy the sorted array into a new array. */
1439+
ret_value = ecma_op_create_array_object (values_buffer, (ecma_length_t) len, false);
1440+
}
1441+
1442+
/* Free values that were copied to the local array. */
1443+
for (uint32_t index = 0; index < copied_num; index++)
1444+
{
1445+
ecma_free_value (values_buffer[index], true);
1446+
}
1447+
1448+
MEM_FINALIZE_LOCAL_ARRAY (values_buffer);
1449+
1450+
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
1451+
ECMA_FINALIZE (len_value);
1452+
ecma_deref_ecma_string (magic_string_length_p);
1453+
ECMA_FINALIZE (obj_this);
1454+
1455+
return ret_value;
1456+
} /* ecma_builtin_array_prototype_object_sort */
1457+
11121458
/**
11131459
* The Array.prototype object's 'shift' routine
11141460
*

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
@@ -68,6 +68,7 @@ ROUTINE (ECMA_MAGIC_STRING_PUSH, ecma_builtin_array_prototype_object_push, NON_F
6868
ROUTINE (ECMA_MAGIC_STRING_REVERSE, ecma_builtin_array_prototype_object_reverse, 0, 0)
6969
ROUTINE (ECMA_MAGIC_STRING_INDEX_OF_UL, ecma_builtin_array_prototype_object_index_of, 2, 1)
7070
ROUTINE (ECMA_MAGIC_STRING_LAST_INDEX_OF_UL, ecma_builtin_array_prototype_object_last_index_of, 2, 1)
71+
ROUTINE (ECMA_MAGIC_STRING_SORT, ecma_builtin_array_prototype_object_sort, 1, 1)
7172
ROUTINE (ECMA_MAGIC_STRING_SHIFT, ecma_builtin_array_prototype_object_shift, 0, 0)
7273
ROUTINE (ECMA_MAGIC_STRING_UNSHIFT, ecma_builtin_array_prototype_object_unshift, NON_FIXED, 1)
7374
ROUTINE (ECMA_MAGIC_STRING_EVERY, ecma_builtin_array_prototype_object_every, 2, 1)

0 commit comments

Comments
 (0)