/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/base/array_helper.h" #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/internal_call_params.h" #include "ecmascript/js_array.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_tagged_number.h" #include "ecmascript/js_tagged_value-inl.h" namespace panda::ecmascript::base { bool ArrayHelper::IsConcatSpreadable(JSThread *thread, const JSHandle &obj) { // 1. If Type(O) is not Object, return false. if (!obj->IsECMAObject()) { return false; } // 2. Let spreadable be Get(O, @@isConcatSpreadable). auto ecmaVm = thread->GetEcmaVM(); JSHandle env = ecmaVm->GetGlobalEnv(); JSHandle isConcatsprKey = env->GetIsConcatSpreadableSymbol(); JSHandle spreadable = JSTaggedValue::GetProperty(thread, obj, isConcatsprKey).GetValue(); // 3. ReturnIfAbrupt(spreadable). RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); // 4. If spreadable is not undefined, return ToBoolean(spreadable). if (!spreadable->IsUndefined()) { return spreadable->ToBoolean(); } // 5. Return IsArray(O). return obj->IsArray(thread); } int32_t ArrayHelper::SortCompare(JSThread *thread, const JSHandle &callbackfnHandle, const JSHandle &valueX, const JSHandle &valueY) { // 1. If x and y are both undefined, return +0. if (valueX->IsHole()) { if (valueY->IsHole()) { return 0; } return 1; } if (valueY->IsHole()) { return -1; } if (valueX->IsUndefined()) { if (valueY->IsUndefined()) { return 0; } // 2. If x is undefined, return 1. return 1; } // 3. If y is undefined, return -1. if (valueY->IsUndefined()) { return -1; } // 4. If the argument comparefn is not undefined, then // a. Let v be ToNumber(Call(comparefn, undefined, «x, y»)). // b. ReturnIfAbrupt(v). // c. If v is NaN, return +0. // d. Return v. if (!callbackfnHandle->IsUndefined()) { JSHandle thisArgHandle(thread, JSTaggedValue::Undefined()); InternalCallParams *arguments = thread->GetInternalCallParams(); arguments->MakeArgv(valueX, valueY); JSTaggedValue callResult = JSFunction::Call(thread, callbackfnHandle, thisArgHandle, 2, arguments->GetArgv()); // 2: two args if (callResult.IsInt()) { return callResult.GetInt(); } RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); JSHandle testResult(thread, callResult); JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); double value = v.GetNumber(); if (std::isnan(value)) { return +0; } return static_cast(value); } // 5. Let xString be ToString(x). // 6. ReturnIfAbrupt(xString). // 7. Let yString be ToString(y). // 8. ReturnIfAbrupt(yString). // 9. If xString < yString, return -1. // 10. If xString > yString, return 1. // 11. Return +0. JSHandle xValueHandle(JSTaggedValue::ToString(thread, valueX)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); JSHandle yValueHandle(JSTaggedValue::ToString(thread, valueY)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); ComparisonResult compareResult = JSTaggedValue::Compare(thread, xValueHandle, yValueHandle); return compareResult == ComparisonResult::GREAT ? 1 : 0; } double ArrayHelper::GetLength(JSThread *thread, const JSHandle &thisHandle) { if (thisHandle->IsJSArray()) { return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength(); } if (thisHandle->IsTypedArray()) { return TypedArrayHelper::GetArrayLength(thread, JSHandle::Cast(thisHandle)); } JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); JSHandle lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue(); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); return len.GetNumber(); } double ArrayHelper::GetArrayLength(JSThread *thread, const JSHandle &thisHandle) { if (thisHandle->IsJSArray()) { return JSArray::Cast(thisHandle->GetTaggedObject())->GetArrayLength(); } JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); JSHandle lenResult = JSTaggedValue::GetProperty(thread, thisHandle, lengthKey).GetValue(); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); JSTaggedNumber len = JSTaggedValue::ToLength(thread, lenResult); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0); return len.GetNumber(); } } // namespace panda::ecmascript::base