Skip to content

Commit

Permalink
Update TypedArray.prototype.toSorted builtin method
Browse files Browse the repository at this point in the history
* fix some errors in sort method too
* refactor other sort and toSorted methods

Signed-off-by: HyukWoo Park <[email protected]>
  • Loading branch information
clover2123 authored and ksh8281 committed May 23, 2024
1 parent 696cff8 commit 47cc02e
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 56 deletions.
9 changes: 6 additions & 3 deletions src/builtins/BuiltinArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ static Value builtinArraySort(ExecutionState& state, Value thisValue, size_t arg
bool defaultSort = (argc == 0) || cmpfn.isUndefined();

Object* thisObject = thisValue.toObject(state);
int64_t len = thisObject->length(state);
uint64_t len = thisObject->length(state);

thisObject->sort(state, len, [defaultSort, &cmpfn, &state](const Value& a, const Value& b) -> bool {
if (a.isEmpty() && b.isUndefined())
Expand Down Expand Up @@ -790,9 +790,10 @@ static Value builtinArrayToSorted(ExecutionState& state, Value thisValue, size_t
bool defaultSort = (argc == 0) || cmpfn.isUndefined();

Object* thisObject = thisValue.toObject(state);
int64_t len = thisObject->length(state);
uint64_t len = thisObject->length(state);

return thisObject->toSorted(state, len, [defaultSort, &cmpfn, &state](const Value& a, const Value& b) -> bool {
ArrayObject* arr = new ArrayObject(state, len);
thisObject->toSorted(state, arr, len, [defaultSort, &cmpfn, &state](const Value& a, const Value& b) -> bool {
if (a.isEmpty() && b.isUndefined())
return false;
if (a.isUndefined() && b.isEmpty())
Expand All @@ -810,6 +811,8 @@ static Value builtinArrayToSorted(ExecutionState& state, Value thisValue, size_t
Value ret = Object::call(state, cmpfn, Value(), 2, arg);
return (ret.toNumber(state) < 0);
} });

return arr;
}

static Value builtinArrayForEach(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
Expand Down
66 changes: 61 additions & 5 deletions src/builtins/BuiltinTypedArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,26 +883,79 @@ static Value builtinTypedArraySome(ExecutionState& state, Value thisValue, size_

static Value builtinTypedArraySort(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
Value cmpfn = argv[0];
if (!cmpfn.isUndefined() && !cmpfn.isCallable()) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().Array.string(), true, state.context()->staticStrings().sort.string(), ErrorObject::Messages::GlobalObject_FirstArgumentNotCallable);
}

// Let buffer be TypedArrayObject::validateTypedArray(obj).
ArrayBuffer* buffer = TypedArrayObject::validateTypedArray(state, thisValue);
TypedArrayObject* O = thisValue.asObject()->asTypedArrayObject();

// Let len be the value of O’s [[ArrayLength]] internal slot.
int64_t len = O->arrayLength();
uint64_t len = O->arrayLength();
bool defaultSort = (argc == 0) || cmpfn.isUndefined();
// [defaultSort, &cmpfn, &state, &buffer]
O->sort(state, len, [&](const Value& x, const Value& y) -> bool {
ASSERT((x.isNumber() || x.isBigInt()) && (y.isNumber() || y.isBigInt()));
if (!defaultSort) {
Value args[] = { x, y };
double v = Object::call(state, cmpfn, Value(), 2, args).toNumber(state);
if (std::isnan(v)) {
return false;
}
return (v < 0);
} else {
if (LIKELY(x.isNumber())) {
double xNum = x.asNumber();
double yNum = y.asNumber();

// 22.2.3.25.3-10
if (std::isnan(xNum)) {
return false;
}

if (std::isnan(yNum)) {
return true;
}

if (xNum == 0.0 && xNum == yNum) {
return std::signbit(xNum);
}

return xNum <= yNum;
} else {
return x.asBigInt()->lessThanEqual(y.asBigInt());
}
} });

return O;
}

static Value builtinTypedArrayToSorted(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
Value cmpfn = argv[0];
if (!cmpfn.isUndefined() && !cmpfn.isCallable()) {
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().Array.string(), true, state.context()->staticStrings().sort.string(), ErrorObject::Messages::GlobalObject_FirstArgumentNotCallable);
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().Array.string(), true, state.context()->staticStrings().toSorted.string(), ErrorObject::Messages::GlobalObject_FirstArgumentNotCallable);
}

// Let buffer be TypedArrayObject::validateTypedArray(obj).
ArrayBuffer* buffer = TypedArrayObject::validateTypedArray(state, thisValue);
TypedArrayObject* O = thisValue.asObject()->asTypedArrayObject();

// Let len be the value of O’s [[ArrayLength]] internal slot.
uint64_t len = O->arrayLength();
bool defaultSort = (argc == 0) || cmpfn.isUndefined();

Value arg[1] = { Value(len) };
TypedArrayObject* A = TypedArrayCreateSameType(state, O, 1, arg).asObject()->asTypedArrayObject();

// [defaultSort, &cmpfn, &state, &buffer]
O->sort(state, len, [&](const Value& x, const Value& y) -> bool {
O->toSorted(state, A, len, [&](const Value& x, const Value& y) -> bool {
ASSERT((x.isNumber() || x.isBigInt()) && (y.isNumber() || y.isBigInt()));
if (!defaultSort) {
Value args[] = { x, y };
double v = Object::call(state, cmpfn, Value(), 2, args).toNumber(state);
buffer->throwTypeErrorIfDetached(state);
if (std::isnan(v)) {
return false;
}
Expand Down Expand Up @@ -930,7 +983,8 @@ static Value builtinTypedArraySort(ExecutionState& state, Value thisValue, size_
return x.asBigInt()->lessThanEqual(y.asBigInt());
}
} });
return O;

return A;
}

// https://www.ecma-international.org/ecma-262/10.0/#sec-%typedarray%.prototype.subarray
Expand Down Expand Up @@ -1811,6 +1865,8 @@ void GlobalObject::installTypedArray(ExecutionState& state)
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->some, builtinTypedArraySome, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
typedArrayPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->sort),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->sort, builtinTypedArraySort, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
typedArrayPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toSorted),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toSorted, builtinTypedArrayToSorted, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
typedArrayPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->copyWithin),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->copyWithin, builtinTypedArrayCopyWithin, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
typedArrayPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->every),
Expand Down
47 changes: 32 additions & 15 deletions src/runtime/ArrayObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,15 +291,15 @@ void ArrayObject::enumeration(ExecutionState& state, bool (*callback)(ExecutionS
Object::enumeration(state, callback, data, shouldSkipSymbolKey);
}

void ArrayObject::sort(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
void ArrayObject::sort(ExecutionState& state, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
{
if (length) {
if (isFastModeArray()) {
size_t byteLength = sizeof(Value) * length;
bool canUseStack = byteLength <= 1024;
Value* tempBuffer = canUseStack ? (Value*)alloca(byteLength) : CustomAllocator<Value>().allocate(length);

for (int64_t i = 0; i < length; i++) {
for (uint64_t i = 0; i < length; i++) {
tempBuffer[i] = m_fastModeData[i];
}

Expand All @@ -310,8 +310,20 @@ void ArrayObject::sort(ExecutionState& state, int64_t length, const std::functio
return true;
});

for (int64_t i = 0; i < length; i++) {
m_fastModeData[i] = tempBuffer[i];
if (UNLIKELY(arrayLength(state) != length)) {
// array length could be changed due to the compare function executed in the previous merge sort
setArrayLength(state, length);
}

if (LIKELY(isFastModeArray())) {
for (uint64_t i = 0; i < length; i++) {
m_fastModeData[i] = tempBuffer[i];
}
} else {
// fast-mode could be changed due to the compare function executed in the previous merge sort
for (uint64_t i = 0; i < length; i++) {
setIndexedPropertyThrowsException(state, Value(i), tempBuffer[i]);
}
}

if (!canUseStack) {
Expand All @@ -324,15 +336,18 @@ void ArrayObject::sort(ExecutionState& state, int64_t length, const std::functio
}
}

ArrayObject* ArrayObject::toSorted(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
void ArrayObject::toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
{
ASSERT(target && target->isArrayObject() && target->length(state) == length);
ArrayObject* arr = target->asArrayObject();

if (length) {
if (isFastModeArray()) {
size_t byteLength = sizeof(Value) * length;
bool canUseStack = byteLength <= 1024;
Value* tempBuffer = canUseStack ? (Value*)alloca(byteLength) : CustomAllocator<Value>().allocate(length);

for (int64_t i = 0; i < length; i++) {
for (uint64_t i = 0; i < length; i++) {
// toSorted handles all hole elements as undefined values
Value v = m_fastModeData[i];
tempBuffer[i] = v.isEmpty() ? Value() : v;
Expand All @@ -345,24 +360,26 @@ ArrayObject* ArrayObject::toSorted(ExecutionState& state, int64_t length, const
return true;
});

ArrayObject* arr = new ArrayObject(state, static_cast<uint64_t>(length));
ASSERT(arr->isFastModeArray());
for (int64_t i = 0; i < length; i++) {
arr->m_fastModeData[i] = tempBuffer[i];
ASSERT(arr->arrayLength(state) == length);
if (LIKELY(arr->isFastModeArray())) {
for (uint64_t i = 0; i < length; i++) {
arr->m_fastModeData[i] = tempBuffer[i];
}
} else {
// fast-mode could be changed due to the compare function executed in the previous merge sort
for (uint64_t i = 0; i < length; i++) {
arr->setIndexedPropertyThrowsException(state, Value(i), tempBuffer[i]);
}
}

if (!canUseStack) {
GC_FREE(tempSpace);
GC_FREE(tempBuffer);
}

return arr;
} else {
return Object::toSorted(state, length, comp);
Object::toSorted(state, arr, length, comp);
}
}

return new ArrayObject(state);
}

void* ArrayObject::operator new(size_t size)
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/ArrayObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ class ArrayObject : public DerivedObject {
virtual bool defineOwnProperty(ExecutionState& state, const ObjectPropertyName& P, const ObjectPropertyDescriptor& desc) override;
virtual bool deleteOwnProperty(ExecutionState& state, const ObjectPropertyName& P) override;
virtual void enumeration(ExecutionState& state, bool (*callback)(ExecutionState& state, Object* self, const ObjectPropertyName&, const ObjectStructurePropertyDescriptor& desc, void* data), void* data, bool shouldSkipSymbolKey = true) override;
virtual void sort(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp) override;
virtual ArrayObject* toSorted(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp) override;
virtual void sort(ExecutionState& state, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp) override;
virtual void toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp) override;
virtual ObjectGetResult getIndexedProperty(ExecutionState& state, const Value& property, const Value& receiver) override;
virtual ObjectHasPropertyResult hasIndexedProperty(ExecutionState& state, const Value& propertyName) override;
virtual bool setIndexedProperty(ExecutionState& state, const Value& property, const Value& value, const Value& receiver) override;
Expand Down
23 changes: 11 additions & 12 deletions src/runtime/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1781,14 +1781,14 @@ void Object::nextIndexBackward(ExecutionState& state, Object* obj, const int64_t
nextIndex = std::min(ret, cur - 1);
}

void Object::sort(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
void Object::sort(ExecutionState& state, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
{
ValueVectorWithInlineStorage64 selected;

int64_t n = 0;
int64_t k = 0;

while (k < length) {
while (k < (int64_t)length) {
Value idx = Value(k);
auto hasResult = hasProperty(state, ObjectPropertyName(state, idx));
if (hasResult) {
Expand All @@ -1797,7 +1797,7 @@ void Object::sort(ExecutionState& state, int64_t length, const std::function<boo
k++;
} else {
int64_t result;
nextIndexForward(state, this, k, length, result);
nextIndexForward(state, this, k, (int64_t)length, result);
k = result;
}
}
Expand All @@ -1815,28 +1815,29 @@ void Object::sort(ExecutionState& state, int64_t length, const std::function<boo

int64_t i;
for (i = 0; i < n; i++) {
setThrowsException(state, ObjectPropertyName(state, Value(i)), selected[i], this);
setIndexedPropertyThrowsException(state, Value(i), selected[i]);
}

while (i < length) {
while (i < (int64_t)length) {
Value idx = Value(i);
if (hasOwnProperty(state, ObjectPropertyName(state, idx))) {
deleteOwnProperty(state, ObjectPropertyName(state, Value(i)));
i++;
} else {
int64_t result;
nextIndexForward(state, this, i, length, result);
nextIndexForward(state, this, i, (int64_t)length, result);
i = result;
}
}
}

ArrayObject* Object::toSorted(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
void Object::toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp)
{
ArrayObject* arr = new ArrayObject(state, static_cast<uint64_t>(length));
ASSERT(target && target->isArrayObject() && target->length(state) == length);
ArrayObject* arr = target->asArrayObject();

ValueVectorWithInlineStorage64 selected;
for (int64_t i = 0; i < length; i++) {
for (uint64_t i = 0; i < length; i++) {
// toSorted should be read-through-holes
selected.push_back(get(state, ObjectPropertyName(state, Value(i)), this).value(state, this));
}
Expand All @@ -1851,11 +1852,9 @@ ArrayObject* Object::toSorted(ExecutionState& state, int64_t length, const std::
});
}

for (int64_t i = 0; i < length; i++) {
for (uint64_t i = 0; i < length; i++) {
arr->setIndexedPropertyThrowsException(state, Value(i), selected[i]);
}

return arr;
}

Value Object::getOwnPropertyUtilForObjectAccCase(ExecutionState& state, size_t idx, const Value& receiver)
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -964,8 +964,8 @@ class Object : public PointerValue {
static void nextIndexForward(ExecutionState& state, Object* obj, const int64_t cur, const int64_t len, int64_t& nextIndex);
static void nextIndexBackward(ExecutionState& state, Object* obj, const int64_t cur, const int64_t end, int64_t& nextIndex);

virtual void sort(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp);
virtual ArrayObject* toSorted(ExecutionState& state, int64_t length, const std::function<bool(const Value& a, const Value& b)>& comp);
virtual void sort(ExecutionState& state, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp);
virtual void toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function<bool(const Value& a, const Value& b)>& comp);

virtual bool isInlineCacheable()
{
Expand Down
Loading

0 comments on commit 47cc02e

Please sign in to comment.