1/* 2 * Copyright (c) 2024 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/builtins/builtins_shared_map.h" 17 18#include "ecmascript/interpreter/interpreter.h" 19#include "ecmascript/js_function.h" 20#include "ecmascript/linked_hash_table.h" 21#include "ecmascript/shared_objects/concurrent_api_scope.h" 22#include "ecmascript/shared_objects/js_shared_map.h" 23#include "ecmascript/shared_objects/js_shared_map_iterator.h" 24 25namespace panda::ecmascript::builtins { 26JSTaggedValue BuiltinsSharedMap::Constructor(EcmaRuntimeCallInfo *argv) 27{ 28 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Constructor); 29 JSThread *thread = argv->GetThread(); 30 [[maybe_unused]] EcmaHandleScope handleScope(thread); 31 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 32 // 1. If NewTarget is undefined, throw exception 33 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 34 if (newTarget->IsUndefined()) { 35 JSTaggedValue error = containers::ContainerError::BusinessError( 36 thread, containers::ErrorFlag::IS_NULL_ERROR, "The ArkTS Map's constructor cannot be directly invoked."); 37 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 38 } 39 // 2.Let Map be OrdinaryCreateFromConstructor(NewTarget, "%MapPrototype%", «[[MapData]]» ). 40 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 41 ASSERT(constructor->IsJSSharedFunction() && constructor.GetTaggedValue().IsInSharedHeap()); 42 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 43 // 3.returnIfAbrupt() 44 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 45 ASSERT(obj.GetTaggedValue().IsInSharedHeap()); 46 JSHandle<JSSharedMap> map = JSHandle<JSSharedMap>::Cast(obj); 47 48 // 4.Set map’s [[MapData]] internal slot to a new empty List. 49 JSHandle<LinkedHashMap> linkedMap = LinkedHashMap::Create(thread, 50 LinkedHashMap::MIN_CAPACITY, MemSpaceKind::SHARED); 51 map->SetLinkedMap(thread, linkedMap); 52 // add data into set from iterable 53 // 5.If iterable is not present, let iterable be undefined. 54 // 6.If iterable is either undefined or null, let iter be undefined. 55 JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0); 56 if (iterable->IsUndefined() || iterable->IsNull()) { 57 return map.GetTaggedValue(); 58 } 59 if (!iterable->IsECMAObject()) { 60 THROW_TYPE_ERROR_AND_RETURN(thread, "iterable is not object", JSTaggedValue::Exception()); 61 } 62 // Let adder be Get(map, "set"). 63 JSHandle<JSTaggedValue> adderKey = thread->GlobalConstants()->GetHandledSetString(); 64 JSHandle<JSTaggedValue> adder = JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(map), adderKey).GetValue(); 65 // ReturnIfAbrupt(adder). 66 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); 67 return AddEntriesFromIterable(thread, obj, iterable, adder, factory); 68} 69 70JSTaggedValue BuiltinsSharedMap::Set(EcmaRuntimeCallInfo *argv) 71{ 72 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Set); 73 JSThread *thread = argv->GetThread(); 74 [[maybe_unused]] EcmaHandleScope handleScope(thread); 75 JSHandle<JSTaggedValue> self = GetThis(argv); 76 if (!self->IsJSSharedMap()) { 77 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 78 "The set method cannot be bound."); 79 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 80 } 81 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 82 JSHandle<JSTaggedValue> value = GetCallArg(argv, 1); 83 JSHandle<JSSharedMap> map(self); 84 JSSharedMap::Set(thread, map, key, value); 85 return map.GetTaggedValue(); 86} 87 88JSTaggedValue BuiltinsSharedMap::Clear(EcmaRuntimeCallInfo *argv) 89{ 90 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Clear); 91 JSThread *thread = argv->GetThread(); 92 [[maybe_unused]] EcmaHandleScope handleScope(thread); 93 JSHandle<JSTaggedValue> self = GetThis(argv); 94 if (!self->IsJSSharedMap()) { 95 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 96 "The clear method cannot be bound."); 97 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 98 } 99 JSHandle<JSSharedMap> map(self); 100 JSSharedMap::Clear(thread, map); 101 return JSTaggedValue::Undefined(); 102} 103 104JSTaggedValue BuiltinsSharedMap::Delete(EcmaRuntimeCallInfo *argv) 105{ 106 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Delete); 107 JSThread *thread = argv->GetThread(); 108 [[maybe_unused]] EcmaHandleScope handleScope(thread); 109 JSHandle<JSTaggedValue> self = GetThis(argv); 110 if (!self->IsJSSharedMap()) { 111 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 112 "The delete method cannot be bound."); 113 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 114 } 115 JSHandle<JSSharedMap> map(self); 116 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 117 bool flag = JSSharedMap::Delete(thread, map, key); 118 return GetTaggedBoolean(flag); 119} 120 121JSTaggedValue BuiltinsSharedMap::Has(EcmaRuntimeCallInfo *argv) 122{ 123 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Has); 124 JSThread *thread = argv->GetThread(); 125 [[maybe_unused]] EcmaHandleScope handleScope(thread); 126 JSHandle<JSTaggedValue> self(GetThis(argv)); 127 if (!self->IsJSSharedMap()) { 128 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 129 "The has method cannot be bound."); 130 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 131 } 132 JSHandle<JSSharedMap> map(self); 133 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 134 bool flag = JSSharedMap::Has(thread, map, key.GetTaggedValue()); 135 return GetTaggedBoolean(flag); 136} 137 138JSTaggedValue BuiltinsSharedMap::Get(EcmaRuntimeCallInfo *argv) 139{ 140 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Get); 141 JSThread *thread = argv->GetThread(); 142 [[maybe_unused]] EcmaHandleScope handleScope(thread); 143 JSHandle<JSTaggedValue> self(GetThis(argv)); 144 if (!self->IsJSSharedMap()) { 145 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 146 "The get method cannot be bound."); 147 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 148 } 149 JSHandle<JSSharedMap> map(self); 150 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 151 JSTaggedValue value = JSSharedMap::Get(thread, map, key.GetTaggedValue()); 152 return value; 153} 154 155JSTaggedValue BuiltinsSharedMap::ForEach(EcmaRuntimeCallInfo *argv) 156{ 157 JSThread *thread = argv->GetThread(); 158 BUILTINS_API_TRACE(thread, SharedMap, ForEach); 159 [[maybe_unused]] EcmaHandleScope handleScope(thread); 160 JSHandle<JSTaggedValue> self = GetThis(argv); 161 if (!self->IsJSSharedMap()) { 162 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 163 "The forEach method cannot be bound."); 164 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 165 } 166 [[maybe_unused]] ConcurrentApiScope<JSSharedMap> scope(thread, self); 167 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 168 JSHandle<JSSharedMap> map(self); 169 JSHandle<JSTaggedValue> func(GetCallArg(argv, 0)); 170 if (!func->IsCallable()) { 171 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not Callable", JSTaggedValue::Exception()); 172 } 173 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 1); 174 JSMutableHandle<LinkedHashMap> hashMap(thread, map->GetLinkedMap()); 175 const uint32_t argsLength = 3; 176 int index = 0; 177 int totalElements = hashMap->NumberOfElements() + hashMap->NumberOfDeletedElements(); 178 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 179 // Repeat for each e that is an element of entries, in original insertion order 180 while (index < totalElements) { 181 JSHandle<JSTaggedValue> key(thread, hashMap->GetKey(index++)); 182 if (!key->IsHole()) { 183 JSHandle<JSTaggedValue> value(thread, hashMap->GetValue(index - 1)); 184 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo( 185 thread, func, thisArg, undefined, argsLength); 186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 187 info->SetCallArg(value.GetTaggedValue(), key.GetTaggedValue(), map.GetTaggedValue()); 188 JSTaggedValue ret = JSFunction::Call(info); 189 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); 190 } 191 } 192 193 return JSTaggedValue::Undefined(); 194} 195 196JSTaggedValue BuiltinsSharedMap::Species(EcmaRuntimeCallInfo *argv) 197{ 198 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Species); 199 return GetThis(argv).GetTaggedValue(); 200} 201 202JSTaggedValue BuiltinsSharedMap::GetSize(EcmaRuntimeCallInfo *argv) 203{ 204 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, GetSize); 205 JSThread *thread = argv->GetThread(); 206 [[maybe_unused]] EcmaHandleScope handleScope(thread); 207 JSHandle<JSTaggedValue> self(GetThis(argv)); 208 if (!self->IsJSSharedMap()) { 209 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not SharedMap", JSTaggedValue::Exception()); 210 } 211 JSHandle<JSSharedMap> map(self); 212 uint32_t size = JSSharedMap::GetSize(thread, map); 213 return JSTaggedValue(size); 214} 215 216JSTaggedValue BuiltinsSharedMap::Entries(EcmaRuntimeCallInfo *argv) 217{ 218 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Entries); 219 JSThread *thread = argv->GetThread(); 220 [[maybe_unused]] EcmaHandleScope handleScope(thread); 221 JSHandle<JSTaggedValue> self = GetThis(argv); 222 if (!self->IsJSSharedMap()) { 223 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 224 "The entries method cannot be bound."); 225 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 226 } 227 JSHandle<JSTaggedValue> iter = JSSharedMapIterator::CreateMapIterator(thread, self, IterationKind::KEY_AND_VALUE); 228 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 229 return iter.GetTaggedValue(); 230} 231 232JSTaggedValue BuiltinsSharedMap::Keys(EcmaRuntimeCallInfo *argv) 233{ 234 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Keys); 235 JSThread *thread = argv->GetThread(); 236 [[maybe_unused]] EcmaHandleScope handleScope(thread); 237 JSHandle<JSTaggedValue> self = GetThis(argv); 238 if (!self->IsJSSharedMap()) { 239 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 240 "The keys method cannot be bound."); 241 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 242 } 243 JSHandle<JSTaggedValue> iter = JSSharedMapIterator::CreateMapIterator(thread, self, IterationKind::KEY); 244 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 245 return iter.GetTaggedValue(); 246} 247 248JSTaggedValue BuiltinsSharedMap::Values(EcmaRuntimeCallInfo *argv) 249{ 250 BUILTINS_API_TRACE(argv->GetThread(), SharedMap, Values); 251 JSThread *thread = argv->GetThread(); 252 [[maybe_unused]] EcmaHandleScope handleScope(thread); 253 JSHandle<JSTaggedValue> self = GetThis(argv); 254 if (!self->IsJSSharedMap()) { 255 auto error = containers::ContainerError::BusinessError(thread, containers::ErrorFlag::BIND_ERROR, 256 "The values method cannot be bound."); 257 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); 258 } 259 JSHandle<JSTaggedValue> iter = JSSharedMapIterator::CreateMapIterator(thread, self, IterationKind::VALUE); 260 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 261 return iter.GetTaggedValue(); 262} 263 264JSTaggedValue BuiltinsSharedMap::AddEntriesFromIterable(JSThread *thread, const JSHandle<JSObject> &target, 265 const JSHandle<JSTaggedValue> &iterable, const JSHandle<JSTaggedValue> &adder, ObjectFactory *factory) 266{ 267 BUILTINS_API_TRACE(thread, SharedMap, AddEntriesFromIterable); 268 // If IsCallable(adder) is false, throw a TypeError exception 269 if (!adder->IsCallable()) { 270 THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); 271 } 272 // Let iter be GetIterator(iterable). 273 JSHandle<JSTaggedValue> iter(JSIterator::GetIterator(thread, iterable)); 274 // ReturnIfAbrupt(iter). 275 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); 276 JSHandle<JSTaggedValue> keyIndex(thread, JSTaggedValue(0)); 277 JSHandle<JSTaggedValue> valueIndex(thread, JSTaggedValue(1)); 278 JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iter); 279 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); 280 while (!next->IsFalse()) { 281 // Let nextValue be IteratorValue(next). 282 JSHandle<JSTaggedValue> nextValue(JSIterator::IteratorValue(thread, next)); 283 // ReturnIfAbrupt(nextValue). 284 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); 285 286 // If Type(nextItem) is not Object 287 if (!nextValue->IsECMAObject()) { 288 JSHandle<JSObject> typeError = factory->GetJSError(ErrorType::TYPE_ERROR, 289 "nextItem is not Object", StackCheck::NO); 290 JSHandle<JSTaggedValue> record( 291 factory->NewCompletionRecord(CompletionRecordType::THROW, JSHandle<JSTaggedValue>(typeError))); 292 JSTaggedValue ret = JSIterator::IteratorClose(thread, iter, record).GetTaggedValue(); 293 if (!thread->HasPendingException()) { 294 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, typeError.GetTaggedValue(), ret); 295 } 296 return ret; 297 } 298 // Let k be Get(nextItem, "0"). 299 JSHandle<JSTaggedValue> key = JSTaggedValue::GetProperty(thread, nextValue, keyIndex).GetValue(); 300 // If k is an abrupt completion, return IteratorClose(iter, k). 301 if (thread->HasPendingException()) { 302 return JSIterator::IteratorCloseAndReturn(thread, iter); 303 } 304 // Let v be Get(nextItem, "1"). 305 JSHandle<JSTaggedValue> value = JSTaggedValue::GetProperty(thread, nextValue, valueIndex).GetValue(); 306 // If v is an abrupt completion, return IteratorClose(iter, v). 307 if (thread->HasPendingException()) { 308 return JSIterator::IteratorCloseAndReturn(thread, iter); 309 } 310 const uint32_t argsLength = 2; // 2: key and value pair 311 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 312 EcmaRuntimeCallInfo *info = 313 EcmaInterpreter::NewRuntimeCallInfo(thread, adder, JSHandle<JSTaggedValue>(target), undefined, argsLength); 314 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); 315 info->SetCallArg(key.GetTaggedValue(), value.GetTaggedValue()); 316 JSFunction::Call(info); 317 // If status is an abrupt completion, return IteratorClose(iter, status). 318 if (thread->HasPendingException()) { 319 return JSIterator::IteratorCloseAndReturn(thread, iter); 320 } 321 // Let next be IteratorStep(iter). 322 next = JSIterator::IteratorStep(thread, iter); 323 // ReturnIfAbrupt(next). 324 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); 325 } 326 return target.GetTaggedValue(); 327} 328} // namespace panda::ecmascript::builtins 329