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