Skip to content

Commit

Permalink
Stable {Array,TypedArray}.prototype.sort
Browse files Browse the repository at this point in the history
Summary: Make {Array,TypedArray}.prototype.sort stable, by keeping a parallel array of the original indices to break ties. This is a repeat of D23088088, but with proper bounds checking of the index array and added unit tests from D30662659.

Reviewed By: tmikov

Differential Revision: D31058489

fbshipit-source-id: 990c5ec232bd96d793ed490f5f1e0f25dd07dbc8
  • Loading branch information
kodafb authored and facebook-github-bot committed Oct 5, 2021
1 parent c35a40e commit ec424a1
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 54 deletions.
3 changes: 2 additions & 1 deletion lib/VM/JSLib/Sorting.h → include/hermes/VM/JSLib/Sorting.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ class SortModel {
virtual ExecutionStatus swap(uint32_t a, uint32_t b) = 0;

// Compare elements at index a and at index b.
virtual CallResult<bool> less(uint32_t a, uint32_t b) = 0;
// Return negative if [a] < [b], positive if [a] > [b], 0 if [a] = [b]
virtual CallResult<int> compare(uint32_t a, uint32_t b) = 0;

virtual ~SortModel() = 0;
};
Expand Down
25 changes: 14 additions & 11 deletions lib/VM/JSLib/Array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
/// ES5.1 15.4 Initialize the Array constructor.
//===----------------------------------------------------------------------===//
#include "JSLibInternal.h"
#include "Sorting.h"

#include "hermes/VM/HandleRootOwner-inline.h"
#include "hermes/VM/JSLib/Sorting.h"
#include "hermes/VM/Operations.h"
#include "hermes/VM/StringBuilder.h"
#include "hermes/VM/StringRefUtils.h"
Expand Down Expand Up @@ -1112,9 +1112,10 @@ class StandardSortModel : public SortModel {
return ExecutionStatus::RETURNED;
}

/// If compareFn isn't null, return compareFn(obj[a], obj[b]) < 0.
/// If compareFn is null, return obj[a] < obj[b].
CallResult<bool> less(uint32_t a, uint32_t b) override {
/// If compareFn isn't null, return compareFn(obj[a], obj[b])
/// If compareFn is null, return -1 if obj[a] < obj[b], 1 if obj[a] > obj[b],
/// 0 otherwise
CallResult<int> compare(uint32_t a, uint32_t b) override {
// Ensure that we don't leave here with any new handles.
GCScopeMarkerRAII gcMarker{gcScope_, gcMarker_};

Expand All @@ -1131,7 +1132,7 @@ class StandardSortModel : public SortModel {
}
if ((*propRes)->isEmpty()) {
// Spec defines empty as greater than everything.
return false;
return 1;
}
aValue_ = std::move(*propRes);
assert(!aValue_->isEmpty());
Expand All @@ -1150,18 +1151,18 @@ class StandardSortModel : public SortModel {
}
if ((*propRes)->isEmpty()) {
// Spec defines empty as greater than everything.
return true;
return -1;
}
bValue_ = std::move(*propRes);
assert(!bValue_->isEmpty());

if (aValue_->isUndefined()) {
// Spec defines undefined as greater than everything.
return false;
return 1;
}
if (bValue_->isUndefined()) {
// Spec defines undefined as greater than everything.
return true;
return -1;
}

if (compareFn_) {
Expand All @@ -1180,9 +1181,11 @@ class StandardSortModel : public SortModel {
if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return intRes->getNumber() < 0;
// Cannot return intRes's value directly because it can be NaN
auto res = intRes->getNumber();
return (res < 0) ? -1 : (res > 0 ? 1 : 0);
} else {
// Convert both arguments to strings and use the lessOp on them.
// Convert both arguments to strings and compare
auto aValueRes = toString_RJS(runtime_, aValue_);
if (LLVM_UNLIKELY(aValueRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
Expand All @@ -1195,7 +1198,7 @@ class StandardSortModel : public SortModel {
}
bValue_ = bValueRes->getHermesValue();

return lessOp_RJS(runtime_, aValue_, bValue_).getValue();
return aValue_->getString()->compare(bValue_->getString());
}
}
};
Expand Down
Loading

0 comments on commit ec424a1

Please sign in to comment.