-
Notifications
You must be signed in to change notification settings - Fork 688
Add %TypedArray%.prototype.sort([ compareFunction ]) support. #2437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add %TypedArray%.prototype.sort([ compareFunction ]) support. #2437
Conversation
|
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,0When 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. |
dad959b to
6e08652
Compare
| } | ||
|
|
||
| return ret_value; | ||
| } /* ecma_helpers_array_heap_sort_helper */ |
There was a problem hiding this comment.
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 */ |
There was a problem hiding this comment.
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.
6e08652 to
5aa0470
Compare
| * 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 */ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 */ |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
5aa0470 to
b3edf88
Compare
|
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; |
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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.
b3edf88 to
e8b4f7b
Compare
| * 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 */ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 */ |
There was a problem hiding this comment.
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 */ |
There was a problem hiding this comment.
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 */ |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
e8b4f7b to
3a01f25
Compare
zherczeg
left a comment
There was a problem hiding this 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 */ |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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}; |
There was a problem hiding this comment.
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.
adead3d to
5a834dc
Compare
| 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--) |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
zherczeg
left a comment
There was a problem hiding this 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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
| /** | ||
| * Comparison callback function header for sorting helper routines. | ||
| */ | ||
| typedef ecma_value_t (*sort_compare_t)(ecma_value_t lhs, /**< left value */ |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LaszloLango Done.
5a834dc to
9f0f09c
Compare
JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra anthony@anthony-calandra.com
9f0f09c to
35afa52
Compare
LaszloLango
left a comment
There was a problem hiding this 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
Add %TypedArray%.prototype.sort([ compareFunction ]) support.
JerryScript-DCO-1.0-Signed-off-by: Anthony Calandra anthony@anthony-calandra.com