Skip to content

Conversation

@AnthonyCalandra
Copy link
Contributor

Add %TypedArray%.prototype.sort([ compareFunction ]) support.

JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra anthony@anthony-calandra.com

@AnthonyCalandra
Copy link
Contributor Author

AnthonyCalandra commented Jul 25, 2018

While testing the sorting functionality, I've noticed that if I do something like the following, I get unexpected output:

jerry> [0, 1, 0].sort(function() { return NaN; })
0,0,1
jerry> [0, 1, 0].sort(function() { return 0; })
1,0,0

When I execute the above in V8, the arrays remain as they were originally, but JerryScript seems to incorrectly "sort" them. Since this PR reuses the heap sort implementation, this behavior is also seen when sorting typedarrays.

@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch from dad959b to 6e08652 Compare July 25, 2018 17:41
}

return ret_value;
} /* ecma_helpers_array_heap_sort_helper */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to mention, really this code was just moved (almost) verbatim from ecma-builtin-array-prototype.c (see below). I made one slight modification, and that is a ecma_sort_compare_helper_t which is a function pointer that points to either the typedarray sort routine or the array routine. The reason for choosing not to reuse the array implementation for typedarrays is because there is unnecessary overhead (such as converting to a string, comparing, etc.) while a typedarray comparison is strictly Number based.

* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_helpers_array_to_heap (ecma_value_t array[], /**< heap data array */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a builtin specific function, keep it in ecma/builtins. You can create a new file for them if needed though.

@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch from 6e08652 to 5aa0470 Compare July 31, 2018 19:57
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_helper_array_to_heap (ecma_value_t array[], /**< heap data array */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ecma_value_t *array_p style.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copied this over, but sure, I'll make the change.

int index, /**< current item index */
int right, /**< right index is a maximum index */
ecma_value_t comparefn, /**< compare function */
const ecma_sort_compare_helper_t sortfn) /**< sorting function */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use sort_compare_cb.


ecma_value_t ret_value = ECMA_VALUE_EMPTY;

JMEM_DEFINE_LOCAL_ARRAY (values_buffer, typedarray_length, ecma_value_t);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need allocating memory? I think the heap-sort is capable of using the arraybuffer buffer for sorting (in-place sorting). This cannot be done for normal arrays since they don't have a buffer.

Copy link
Contributor Author

@AnthonyCalandra AnthonyCalandra Aug 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it would work if I just gave the heap-sort helper an underlying buffer without having to change the implementation of the helper -- then that means my helper isn't generic anymore. The heap-sort seems to work on ecma_value_ts and I don't think I can just map variable-sized (ie. 8-bit integers, 32-bit floats, etc.) arraybuffer elements to it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, at least this is good for binary size. We can optimize the code later if needed, since heapsort is not exactly a complicated algorithm.

@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch from 5aa0470 to b3edf88 Compare August 7, 2018 14:19
@AnthonyCalandra
Copy link
Contributor Author

I've addressed your remaining comments.

/* Sorting. */
if (copied_num > 1 && ecma_is_value_empty (ret_value))
{
const sort_compare_cb sortfn = &ecma_builtin_array_prototype_object_sort_compare_helper;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might wasn't clear. Ok, here is an example from jerry-core.h:

/**
 * Function type for allocating buffer for JerryScript instance.
 */
typedef void *(*jerry_instance_alloc_t) (size_t size, void *cb_data_p);

/**
 * Type information of a native pointer.
 */
typedef struct
{
  jerry_object_native_free_callback_t free_cb; /**< the free callback of the native pointer */
} jerry_object_native_info_t;

So we put _t after types, including callbacks. However, for the names of the variables we put _cb for callbacks, _p for pointers. This was the rule way before I joined to the project. So the correct form looks like this:

const sort_compare_t sort_cb = &ecma_builtin_array_prototype_object_sort_compare_helper;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand now. Made the change.

@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch from b3edf88 to e8b4f7b Compare August 8, 2018 17:53
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_helper_array_to_heap (ecma_value_t *array, /**< heap data array */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the _p postfix is still missing. All pointers require a _p in JerryScript. I know the original style is wrong, but lets fix it if we change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, not used to this style.

ecma_builtin_helper_array_to_heap (ecma_value_t *array, /**< heap data array */
int index, /**< current item index */
int right, /**< right index is a maximum index */
ecma_value_t comparefn, /**< compare function */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compare_func

* Returned value must be freed with ecma_free_value.
*/
ecma_value_t
ecma_builtin_helper_array_heap_sort_helper (ecma_value_t *array, /**< array to sort */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two needs to be fixed as well.

static ecma_value_t
ecma_builtin_typedarray_prototype_sort_compare_helper (ecma_value_t j, /**< left value */
ecma_value_t k, /**< right value */
ecma_value_t comparefn) /**< compare function */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix all instances :)

ecma_value_t ret_value = ECMA_VALUE_EMPTY;
ecma_number_t result = ECMA_NUMBER_ZERO;
bool j_is_undef = ecma_is_value_undefined (j);
bool k_is_undef = ecma_is_value_undefined (k);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can this be undefined for a typed array?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know either... I thought I had left this in for a reason but I guess I just forgot to remove it.

{
if (k_is_undef)
{
result = ECMA_NUMBER_ZERO;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use return ecma_make_integer_value (0);

If you use return, less else blocks and indentation is needed, which makes the code more readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've cleaned up the code a little bit since I don't need to check if j and k are undefined, see if that helps.

@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch from e8b4f7b to 3a01f25 Compare August 10, 2018 18:27
Copy link
Member

@zherczeg zherczeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch gradually becomes better.

static ecma_value_t
ecma_builtin_typedarray_prototype_sort_compare_helper (ecma_value_t lhs, /**< left value */
ecma_value_t rhs, /**< right value */
ecma_value_t comparefn) /**< compare function */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still there are some comparefn in the code. Please search trough the patch.

(int)(copied_num - 1),
arg1),
ecma_builtin_helper_array_heap_sort_helper (values_buffer,
(int)(copied_num - 1),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a space after casting. It is strange that style checkers don't catch it. See your own (double) castings below. There are multiple instances of this style issue, please fix all.

I am thinking about the int type here. ES allows the full uint32 range for array indicies. However we cannot allocate a local buffer that big, so it might be not possible to use that big numbers anyway. What is your opinion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't hurt to be consistent with ES and "allow" the full range even if we have hardware limitations, IMHO.

JERRY_ASSERT (ecma_op_is_callable (comparefn));
ecma_object_t *comparefn_obj_p = ecma_get_object_from_value (comparefn);

ecma_value_t compare_args[] = {lhs, rhs};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space before lfs, and after rhs.

@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch 3 times, most recently from adead3d to 5a834dc Compare August 14, 2018 19:44
ecma_value_t ret_value = ECMA_VALUE_EMPTY;

/* First, construct the ordered binary tree from the array. */
for (uint32_t i = (right / 2) + 1; i > 0 && ecma_is_value_empty (ret_value); i--)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To allow having the type of the index a uint32_t I made a small change here where I added one more to i and changed the condition from i >= 0 to i > 0 because before you would decrement when i = 0 (which would previously terminate the loop) but after making the value unsigned this would wrap to the max unsigned value and so the loop would never terminate.

for (uint32_t i = (right / 2) + 1; i > 0 && ecma_is_value_empty (ret_value); i--)
{
ECMA_TRY_CATCH (value,
ecma_builtin_helper_array_to_heap (array_p, i - 1, right, compare_func, sort_cb),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also changed i to i - 1; see reason above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Copy link
Member

@zherczeg zherczeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

for (uint32_t i = (right / 2) + 1; i > 0 && ecma_is_value_empty (ret_value); i--)
{
ECMA_TRY_CATCH (value,
ecma_builtin_helper_array_to_heap (array_p, i - 1, right, compare_func, sort_cb),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@akosthekiss akosthekiss added ecma builtins Related to ECMA built-in routines ES2015 Related to ES2015 features labels Aug 17, 2018
/**
* Comparison callback function header for sorting helper routines.
*/
typedef ecma_value_t (*sort_compare_t)(ecma_value_t lhs, /**< left value */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the ecma_builtin_helper_ prefix: ecma_builtin_helper_sort_compare_fn_t

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LaszloLango Done.

@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch from 5a834dc to 9f0f09c Compare August 21, 2018 20:05
JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra anthony@anthony-calandra.com
@AnthonyCalandra AnthonyCalandra force-pushed the typedarray-sort-support branch from 9f0f09c to 35afa52 Compare August 21, 2018 20:59
Copy link
Contributor

@LaszloLango LaszloLango left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. LGTM

@LaszloLango LaszloLango merged commit b2cf7eb into jerryscript-project:master Aug 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecma builtins Related to ECMA built-in routines ES2015 Related to ES2015 features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants