diff --git a/src/builtins/BuiltinArray.cpp b/src/builtins/BuiltinArray.cpp index da692c5a2..b1fda7a40 100644 --- a/src/builtins/BuiltinArray.cpp +++ b/src/builtins/BuiltinArray.cpp @@ -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()) @@ -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()) @@ -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 newTarget) diff --git a/src/builtins/BuiltinTypedArray.cpp b/src/builtins/BuiltinTypedArray.cpp index fb7b28612..c008edb95 100644 --- a/src/builtins/BuiltinTypedArray.cpp +++ b/src/builtins/BuiltinTypedArray.cpp @@ -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 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 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; } @@ -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 @@ -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), diff --git a/src/runtime/ArrayObject.cpp b/src/runtime/ArrayObject.cpp index 38c548488..1cf52159f 100644 --- a/src/runtime/ArrayObject.cpp +++ b/src/runtime/ArrayObject.cpp @@ -291,7 +291,7 @@ 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& comp) +void ArrayObject::sort(ExecutionState& state, uint64_t length, const std::function& comp) { if (length) { if (isFastModeArray()) { @@ -299,7 +299,7 @@ void ArrayObject::sort(ExecutionState& state, int64_t length, const std::functio bool canUseStack = byteLength <= 1024; Value* tempBuffer = canUseStack ? (Value*)alloca(byteLength) : CustomAllocator().allocate(length); - for (int64_t i = 0; i < length; i++) { + for (uint64_t i = 0; i < length; i++) { tempBuffer[i] = m_fastModeData[i]; } @@ -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) { @@ -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& comp) +void ArrayObject::toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function& 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().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; @@ -345,24 +360,26 @@ ArrayObject* ArrayObject::toSorted(ExecutionState& state, int64_t length, const return true; }); - ArrayObject* arr = new ArrayObject(state, static_cast(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) diff --git a/src/runtime/ArrayObject.h b/src/runtime/ArrayObject.h index eebfd08d9..806472694 100644 --- a/src/runtime/ArrayObject.h +++ b/src/runtime/ArrayObject.h @@ -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& comp) override; - virtual ArrayObject* toSorted(ExecutionState& state, int64_t length, const std::function& comp) override; + virtual void sort(ExecutionState& state, uint64_t length, const std::function& comp) override; + virtual void toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function& 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; diff --git a/src/runtime/Object.cpp b/src/runtime/Object.cpp index a427b1f05..a223354ab 100644 --- a/src/runtime/Object.cpp +++ b/src/runtime/Object.cpp @@ -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& comp) +void Object::sort(ExecutionState& state, uint64_t length, const std::function& 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) { @@ -1797,7 +1797,7 @@ void Object::sort(ExecutionState& state, int64_t length, const std::function& comp) +void Object::toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function& comp) { - ArrayObject* arr = new ArrayObject(state, static_cast(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)); } @@ -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) diff --git a/src/runtime/Object.h b/src/runtime/Object.h index 05fe96db1..5e513458c 100644 --- a/src/runtime/Object.h +++ b/src/runtime/Object.h @@ -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& comp); - virtual ArrayObject* toSorted(ExecutionState& state, int64_t length, const std::function& comp); + virtual void sort(ExecutionState& state, uint64_t length, const std::function& comp); + virtual void toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function& comp); virtual bool isInlineCacheable() { diff --git a/src/runtime/TypedArrayObject.cpp b/src/runtime/TypedArrayObject.cpp index 0073e21ff..0778bca32 100644 --- a/src/runtime/TypedArrayObject.cpp +++ b/src/runtime/TypedArrayObject.cpp @@ -121,13 +121,13 @@ void TypedArrayObject::enumeration(ExecutionState& state, bool (*callback)(Execu Object::enumeration(state, callback, data, shouldSkipSymbolKey); } -void TypedArrayObject::sort(ExecutionState& state, int64_t length, const std::function& comp) +void TypedArrayObject::sort(ExecutionState& state, uint64_t length, const std::function& comp) { if (length) { Value* tempBuffer = (Value*)GC_MALLOC(sizeof(Value) * length); - for (int64_t i = 0; i < length; i++) { - tempBuffer[i] = integerIndexedElementGet(state, i).value(state, this); + for (uint64_t i = 0; i < length; i++) { + tempBuffer[i] = getIndexedProperty(state, Value(i)).value(state, this); } TightVector> tempSpace; @@ -137,18 +137,41 @@ void TypedArrayObject::sort(ExecutionState& state, int64_t length, const std::fu return true; }); - for (int64_t i = 0; i < length; i++) { - integerIndexedElementSet(state, i, tempBuffer[i]); + for (uint64_t i = 0; i < length; i++) { + setIndexedProperty(state, Value(i), tempBuffer[i], this); } GC_FREE(tempBuffer); + } +} + +void TypedArrayObject::toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function& comp) +{ + ASSERT(target && (target->isArrayObject() || target->isTypedArrayObject())); + + if (length) { + Value* tempBuffer = (Value*)GC_MALLOC(sizeof(Value) * length); - return; + for (uint64_t i = 0; i < length; i++) { + tempBuffer[i] = getIndexedProperty(state, Value(i)).value(state, this); + } + + TightVector> tempSpace; + tempSpace.resizeWithUninitializedValues(length); + mergeSort(tempBuffer, length, tempSpace.data(), [&](const Value& a, const Value& b, bool* lessOrEqualp) -> bool { + *lessOrEqualp = comp(a, b); + return true; + }); + + for (uint64_t i = 0; i < length; i++) { + target->setIndexedProperty(state, Value(i), tempBuffer[i], target); + } + GC_FREE(tempBuffer); } } ArrayBuffer* TypedArrayObject::validateTypedArray(ExecutionState& state, const Value& O) { - if (!O.isObject() || !O.asObject()->isTypedArrayObject()) { + if (UNLIKELY(!O.isObject() || !O.asObject()->isTypedArrayObject())) { ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_ThisNotTypedArrayObject); } @@ -160,7 +183,7 @@ ArrayBuffer* TypedArrayObject::validateTypedArray(ExecutionState& state, const V // https://www.ecma-international.org/ecma-262/10.0/#sec-integerindexedelementget ObjectGetResult TypedArrayObject::integerIndexedElementGet(ExecutionState& state, double index) { - if (buffer()->isDetachedBuffer() || !Value(Value::DoubleToIntConvertibleTestNeeds, index).isInteger(state) || index == Value::MinusZeroIndex || index < 0 || index >= arrayLength()) { + if (UNLIKELY(buffer()->isDetachedBuffer() || !Value(Value::DoubleToIntConvertibleTestNeeds, index).isInteger(state) || index == Value::MinusZeroIndex || index < 0 || index >= arrayLength())) { return ObjectGetResult(); } diff --git a/src/runtime/TypedArrayObject.h b/src/runtime/TypedArrayObject.h index 94c92c57e..c2f12ebb6 100644 --- a/src/runtime/TypedArrayObject.h +++ b/src/runtime/TypedArrayObject.h @@ -84,7 +84,8 @@ class TypedArrayObject : public ArrayBufferView { virtual ObjectGetResult get(ExecutionState& state, const ObjectPropertyName& P, const Value& receiver) override; virtual bool set(ExecutionState& state, const ObjectPropertyName& P, const Value& v, const Value& receiver) override; virtual void enumeration(ExecutionState& state, bool (*callback)(ExecutionState& state, Object* self, const ObjectPropertyName&, const ObjectStructurePropertyDescriptor& desc, void* data), void* data, bool shouldSkipSymbolKey) override; - virtual void sort(ExecutionState& state, int64_t length, const std::function& comp) override; + virtual void sort(ExecutionState& state, uint64_t length, const std::function& comp) override; + virtual void toSorted(ExecutionState& state, Object* target, uint64_t length, const std::function& comp) override; static ArrayBuffer* validateTypedArray(ExecutionState& state, const Value& O); diff --git a/tools/test/test262/excludelist.orig.xml b/tools/test/test262/excludelist.orig.xml index f185390e6..5998db8b6 100644 --- a/tools/test/test262/excludelist.orig.xml +++ b/tools/test/test262/excludelist.orig.xml @@ -6387,7 +6387,6 @@ TODO TODO TODO - TODO TODO TODO TODO @@ -6397,13 +6396,6 @@ TODO TODO TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO TODO TODO TODO