1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/containers/containers_stack.h" 17 18#include "ecmascript/containers/containers_errors.h" 19#include "ecmascript/interpreter/interpreter.h" 20#include "ecmascript/js_api/js_api_stack_iterator.h" 21#include "ecmascript/js_function.h" 22 23namespace panda::ecmascript::containers { 24JSTaggedValue ContainersStack::StackConstructor(EcmaRuntimeCallInfo *argv) 25{ 26 ASSERT(argv != nullptr); 27 BUILTINS_API_TRACE(argv->GetThread(), Stack, Constructor); 28 JSThread *thread = argv->GetThread(); 29 [[maybe_unused]] EcmaHandleScope handleScope(thread); 30 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 31 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 32 if (newTarget->IsUndefined()) { 33 JSTaggedValue error = 34 ContainerError::BusinessError(thread, ErrorFlag::IS_NULL_ERROR, 35 "The List's constructor cannot be directly invoked"); 36 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 37 } 38 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 39 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 40 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 41 42 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(obj); 43 stack->SetTop(-1); 44 45 return obj.GetTaggedValue(); 46} 47 48JSTaggedValue ContainersStack::IsEmpty(EcmaRuntimeCallInfo *argv) 49{ 50 ASSERT(argv != nullptr); 51 BUILTINS_API_TRACE(argv->GetThread(), Stack, IsEmpty); 52 JSThread *thread = argv->GetThread(); 53 [[maybe_unused]] EcmaHandleScope handleScope(thread); 54 JSHandle<JSTaggedValue> self = GetThis(argv); 55 56 if (!self->IsJSAPIStack()) { 57 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) { 58 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 59 } else { 60 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 61 "The isEmpty method cannot be bound"); 62 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 63 } 64 } 65 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self); 66 bool judge = stack->Empty(); 67 68 return GetTaggedBoolean(judge); 69} 70 71JSTaggedValue ContainersStack::Push(EcmaRuntimeCallInfo *argv) 72{ 73 ASSERT(argv != nullptr); 74 BUILTINS_API_TRACE(argv->GetThread(), Stack, Push); 75 JSThread *thread = argv->GetThread(); 76 [[maybe_unused]] EcmaHandleScope handleScope(thread); 77 JSHandle<JSTaggedValue> self = GetThis(argv); 78 79 if (!self->IsJSAPIStack()) { 80 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) { 81 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 82 } else { 83 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 84 "The push method cannot be bound"); 85 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 86 } 87 } 88 89 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 90 JSTaggedValue jsValue = JSAPIStack::Push(thread, JSHandle<JSAPIStack>::Cast(self), value); 91 return jsValue; 92} 93 94JSTaggedValue ContainersStack::Peek(EcmaRuntimeCallInfo *argv) 95{ 96 ASSERT(argv != nullptr); 97 BUILTINS_API_TRACE(argv->GetThread(), Stack, Peek); 98 JSThread *thread = argv->GetThread(); 99 [[maybe_unused]] EcmaHandleScope handleScope(thread); 100 JSHandle<JSTaggedValue> self = GetThis(argv); 101 102 if (!self->IsJSAPIStack()) { 103 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) { 104 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 105 } else { 106 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 107 "The peek method cannot be bound"); 108 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 109 } 110 } 111 112 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self); 113 JSTaggedValue jsValue = stack->Peek(); 114 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 115 return jsValue; 116} 117 118JSTaggedValue ContainersStack::Locate(EcmaRuntimeCallInfo *argv) 119{ 120 ASSERT(argv != nullptr); 121 BUILTINS_API_TRACE(argv->GetThread(), Stack, Locate); 122 JSThread *thread = argv->GetThread(); 123 [[maybe_unused]] EcmaHandleScope handleScope(thread); 124 JSHandle<JSTaggedValue> self = GetThis(argv); 125 126 if (!self->IsJSAPIStack()) { 127 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) { 128 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 129 } else { 130 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 131 "The locate method cannot be bound"); 132 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 133 } 134 } 135 136 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 137 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self); 138 int num = stack->Search(value); 139 return JSTaggedValue(num); 140} 141 142JSTaggedValue ContainersStack::Pop(EcmaRuntimeCallInfo *argv) 143{ 144 ASSERT(argv != nullptr); 145 BUILTINS_API_TRACE(argv->GetThread(), Stack, Pop); 146 JSThread *thread = argv->GetThread(); 147 [[maybe_unused]] EcmaHandleScope handleScope(thread); 148 JSHandle<JSTaggedValue> self = GetThis(argv); 149 150 if (!self->IsJSAPIStack()) { 151 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) { 152 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 153 } else { 154 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 155 "The pop method cannot be bound"); 156 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 157 } 158 } 159 160 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(self); 161 JSTaggedValue jsValue = stack->Pop(); 162 return jsValue; 163} 164 165JSTaggedValue ContainersStack::ForEach(EcmaRuntimeCallInfo *argv) 166{ 167 ASSERT(argv != nullptr); 168 BUILTINS_API_TRACE(argv->GetThread(), Stack, ForEach); 169 JSThread *thread = argv->GetThread(); 170 [[maybe_unused]] EcmaHandleScope handleScope(thread); 171 172 JSHandle<JSTaggedValue> thisHandle = GetThis(argv); 173 if (!thisHandle->IsJSAPIStack()) { 174 if (thisHandle->IsJSProxy() && JSHandle<JSProxy>::Cast(thisHandle)->GetTarget().IsJSAPIStack()) { 175 thisHandle = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(thisHandle)->GetTarget()); 176 } else { 177 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 178 "The forEach method cannot be bound"); 179 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 180 } 181 } 182 183 JSHandle<JSAPIStack> stack = JSHandle<JSAPIStack>::Cast(thisHandle); 184 int32_t len = stack->GetSize(); 185 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 186 187 JSHandle<JSTaggedValue> callbackFnHandle = GetCallArg(argv, 0); 188 if (!callbackFnHandle->IsCallable()) { 189 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); 190 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 191 CString errorMsg = 192 "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); 193 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); 194 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 195 } 196 197 JSHandle<JSTaggedValue> thisArgHandle = GetCallArg(argv, 1); 198 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 199 uint32_t k = 0; 200 uint32_t actLen = static_cast<uint32_t>(len + 1); 201 while (k < actLen) { 202 JSTaggedValue kValue = stack->Get(k); 203 EcmaRuntimeCallInfo *info = 204 EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, 3); // 3:three args 205 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 206 info->SetCallArg(kValue, JSTaggedValue(k), thisHandle.GetTaggedValue()); 207 JSTaggedValue funcResult = JSFunction::Call(info); 208 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); 209 k++; 210 } 211 return JSTaggedValue::Undefined(); 212} 213 214JSTaggedValue ContainersStack::Iterator(EcmaRuntimeCallInfo *argv) 215{ 216 ASSERT(argv != nullptr); 217 BUILTINS_API_TRACE(argv->GetThread(), Stack, Iterator); 218 JSThread *thread = argv->GetThread(); 219 [[maybe_unused]] EcmaHandleScope handleScope(thread); 220 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 221 JSHandle<JSTaggedValue> self = GetThis(argv); 222 if (!self->IsJSAPIStack()) { 223 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) { 224 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 225 } else { 226 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 227 "The Symbol.iterator method cannot be bound"); 228 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 229 } 230 } 231 JSHandle<JSAPIStackIterator> iter(factory->NewJSAPIStackIterator(JSHandle<JSAPIStack>::Cast(self))); 232 return iter.GetTaggedValue(); 233} 234 235JSTaggedValue ContainersStack::GetLength(EcmaRuntimeCallInfo *argv) 236{ 237 ASSERT(argv != nullptr); 238 BUILTINS_API_TRACE(argv->GetThread(), Stack, GetLength); 239 JSThread *thread = argv->GetThread(); 240 [[maybe_unused]] EcmaHandleScope handleScope(thread); 241 JSHandle<JSTaggedValue> self = GetThis(argv); 242 243 if (!self->IsJSAPIStack()) { 244 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget().IsJSAPIStack()) { 245 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget()); 246 } else { 247 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::BIND_ERROR, 248 "The getLength method cannot be bound"); 249 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 250 } 251 } 252 253 int32_t len = (JSHandle<JSAPIStack>::Cast(self))->GetSize(); 254 return JSTaggedValue(len + 1); 255} 256} // namespace panda::ecmascript::containers 257