/* * Copyright (c) 2022 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/containers/containers_queue.h" #include "ecmascript/containers/containers_errors.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/js_api/js_api_queue.h" #include "ecmascript/js_function.h" namespace panda::ecmascript::containers { JSTaggedValue ContainersQueue::QueueConstructor(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Queue, Constructor); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle newTarget = GetNewTarget(argv); if (newTarget->IsUndefined()) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR, "The Queue's constructor cannot be directly invoked"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle constructor = GetConstructor(argv); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle newTaggedArray = factory->NewTaggedArray(JSAPIQueue::DEFAULT_CAPACITY_LENGTH); obj->SetElements(thread, newTaggedArray); return obj.GetTaggedValue(); } JSTaggedValue ContainersQueue::Add(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIQueue()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIQueue()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The add method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle value = GetCallArg(argv, 0); JSAPIQueue::Add(thread, JSHandle::Cast(self), value); return JSTaggedValue::True(); } JSTaggedValue ContainersQueue::GetFirst(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIQueue()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIQueue()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The getFirst method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSTaggedValue value = JSAPIQueue::GetFirst(thread, JSHandle::Cast(self)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); return value; } JSTaggedValue ContainersQueue::Pop(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Queue, Add); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIQueue()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIQueue()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The pop method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSTaggedValue value = JSAPIQueue::Pop(thread, JSHandle::Cast(self)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); return value; } JSTaggedValue ContainersQueue::ForEach(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Queue, ForEach); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); // Let O be ToObject(this value). JSHandle thisHandle = GetThis(argv); // JSAPIQueue if (!thisHandle->IsJSAPIQueue()) { if (thisHandle->IsJSProxy() && JSHandle::Cast(thisHandle)->GetTarget().IsJSAPIQueue()) { thisHandle = JSHandle(thread, JSHandle::Cast(thisHandle)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The forEach method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle queue = JSHandle::Cast(thisHandle); // Let len be ToLength(Get(O, "length")). uint32_t len = JSAPIQueue::GetArrayLength(thread, queue); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle callbackFnHandle = GetCallArg(argv, 0); // If IsCallable(callbackfn) is false, throw a TypeError exception. if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } // If thisArg was supplied, let T be thisArg; else let T be undefined. JSHandle thisArgHandle = GetCallArg(argv, 1); JSMutableHandle key(thread, JSTaggedValue::Undefined()); uint32_t index = 0; uint32_t k = 0; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); while (k < len) { JSHandle kValue = JSHandle(thread, queue->Get(thread, index)); index = queue->GetNextPosition(index); key.Update(JSTaggedValue(k)); const uint32_t argsLength = 3; EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisHandle.GetTaggedValue()); JSTaggedValue funcResult = JSFunction::Call(info); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); k++; } return JSTaggedValue::Undefined(); } JSTaggedValue ContainersQueue::GetIteratorObj(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Queue, GetIteratorObj); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle self = GetThis(argv); if (!self->IsJSAPIQueue()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIQueue()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The Symbol.iterator method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } JSHandle iter(factory->NewJSAPIQueueIterator(JSHandle::Cast(self))); return iter.GetTaggedValue(); } JSTaggedValue ContainersQueue::GetSize(EcmaRuntimeCallInfo *argv) { ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Queue, GetSize); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); if (!self->IsJSAPIQueue()) { if (self->IsJSProxy() && JSHandle::Cast(self)->GetTarget().IsJSAPIQueue()) { self = JSHandle(thread, JSHandle::Cast(self)->GetTarget()); } else { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, "The getLength method cannot be bound"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } uint32_t length = JSHandle::Cast(self)->GetSize(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue(length); } } // namespace panda::ecmascript::containers