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 "builtins_set.h" 17#include "ecmascript/interpreter/interpreter.h" 18#include "ecmascript/js_function.h" 19#include "ecmascript/js_set.h" 20#include "ecmascript/js_set_iterator.h" 21#include "ecmascript/linked_hash_table.h" 22namespace panda::ecmascript::builtins { 23JSTaggedValue BuiltinsSet::SetConstructor(EcmaRuntimeCallInfo *argv) 24{ 25 ASSERT(argv); 26 BUILTINS_API_TRACE(argv->GetThread(), Set, 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 set be OrdinaryCreateFromConstructor(NewTarget, "%SetPrototype%", «[[SetData]]» ). 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<JSSet> set = JSHandle<JSSet>::Cast(obj); 42 // 3.ReturnIfAbrupt(set). 43 // 4.Set set’s [[SetData]] internal slot to a new empty List. 44 JSHandle<LinkedHashSet> linkedSet = LinkedHashSet::Create(thread); 45 set->SetLinkedSet(thread, linkedSet); 46 47 // add data into set from iterable 48 // 5.If iterable is not present, let iterable be undefined. 49 // 6.If iterable is either undefined or null, let iter be undefined. 50 JSHandle<JSTaggedValue> iterable(GetCallArg(argv, 0)); 51 // 8.If iter is undefined, return set 52 if (iterable->IsUndefined() || iterable->IsNull()) { 53 return set.GetTaggedValue(); 54 } 55 // Let adder be Get(set, "add"). 56 JSHandle<JSTaggedValue> adderKey(thread->GlobalConstants()->GetHandledAddString()); 57 JSHandle<JSTaggedValue> setHandle(set); 58 JSHandle<JSTaggedValue> adder = JSObject::GetProperty(thread, setHandle, adderKey).GetValue(); 59 // ReturnIfAbrupt(adder). 60 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, adder.GetTaggedValue()); 61 // If IsCallable(adder) is false, throw a TypeError exception 62 if (!adder->IsCallable()) { 63 THROW_TYPE_ERROR_AND_RETURN(thread, "adder is not callable", adder.GetTaggedValue()); 64 } 65 // Let iter be GetIterator(iterable). 66 JSHandle<JSTaggedValue> iter(JSIterator::GetIterator(thread, iterable)); 67 // ReturnIfAbrupt(iter). 68 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, iter.GetTaggedValue()); 69 // values in iterator_result may be a JSArray, values[0] = key values[1]=value, used valueIndex to get value from 70 // jsarray 71 JSHandle<JSTaggedValue> valueIndex(thread, JSTaggedValue(1)); 72 JSHandle<JSTaggedValue> next = JSIterator::IteratorStep(thread, iter); 73 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); 74 while (!next->IsFalse()) { 75 // Let nextValue be IteratorValue(next). 76 JSHandle<JSTaggedValue> nextValue(JSIterator::IteratorValue(thread, next)); 77 // ReturnIfAbrupt(nextValue). 78 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); 79 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 80 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, adder, setHandle, undefined, 1); 81 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, nextValue.GetTaggedValue()); 82 info->SetCallArg(nextValue.GetTaggedValue()); 83 if (nextValue->IsArray(thread)) { 84 auto prop = JSTaggedValue::GetProperty(thread, nextValue, valueIndex).GetValue(); 85 info->SetCallArg(prop.GetTaggedValue()); 86 } 87 JSFunction::Call(info); 88 // Let status be Call(adder, set, «nextValue.[[value]]»). 89 if (thread->HasPendingException()) { 90 return JSIterator::IteratorCloseAndReturn(thread, iter); 91 } 92 // Let next be IteratorStep(iter). 93 next = JSIterator::IteratorStep(thread, iter); 94 // ReturnIfAbrupt(next). 95 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, next.GetTaggedValue()); 96 } 97 return set.GetTaggedValue(); 98} 99 100JSTaggedValue BuiltinsSet::Add(EcmaRuntimeCallInfo *argv) 101{ 102 ASSERT(argv); 103 BUILTINS_API_TRACE(argv->GetThread(), Set, Add); 104 JSThread *thread = argv->GetThread(); 105 [[maybe_unused]] EcmaHandleScope handleScope(thread); 106 JSHandle<JSTaggedValue> self = GetThis(argv); 107 108 // 2.If Type(S) is not Object, throw a TypeError exception. 109 // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. 110 if (!self->IsJSSet()) { 111 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); 112 } 113 114 JSHandle<JSTaggedValue> value(GetCallArg(argv, 0)); 115 JSHandle<JSSet> set(self); 116 JSSet::Add(thread, set, value); 117 return set.GetTaggedValue(); 118} 119 120JSTaggedValue BuiltinsSet::Clear(EcmaRuntimeCallInfo *argv) 121{ 122 ASSERT(argv); 123 BUILTINS_API_TRACE(argv->GetThread(), Set, Clear); 124 JSThread *thread = argv->GetThread(); 125 [[maybe_unused]] EcmaHandleScope handleScope(thread); 126 JSHandle<JSTaggedValue> self = GetThis(argv); 127 128 // 2.If Type(S) is not Object, throw a TypeError exception. 129 // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. 130 if (!self->IsJSSet()) { 131 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); 132 } 133 JSHandle<JSSet> set(self); 134 JSSet::Clear(thread, set); 135 return JSTaggedValue::Undefined(); 136} 137 138JSTaggedValue BuiltinsSet::Delete(EcmaRuntimeCallInfo *argv) 139{ 140 ASSERT(argv); 141 BUILTINS_API_TRACE(argv->GetThread(), Set, Delete); 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 [[SetData]] internal slot, throw a TypeError exception. 147 if (!self->IsJSSet()) { 148 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); 149 } 150 151 JSHandle<JSSet> set(self); 152 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 153 bool flag = JSSet::Delete(thread, set, value); 154 return GetTaggedBoolean(flag); 155} 156 157JSTaggedValue BuiltinsSet::Has(EcmaRuntimeCallInfo *argv) 158{ 159 ASSERT(argv); 160 BUILTINS_API_TRACE(argv->GetThread(), Set, Has); 161 JSThread *thread = argv->GetThread(); 162 [[maybe_unused]] EcmaHandleScope handleScope(thread); 163 JSHandle<JSTaggedValue> self = GetThis(argv); 164 // 2.If Type(S) is not Object, throw a TypeError exception. 165 // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. 166 if (!self->IsJSSet()) { 167 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); 168 } 169 JSSet* jsSet = JSSet::Cast(self.GetTaggedValue().GetTaggedObject()); 170 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); 171 bool flag = jsSet->Has(thread, value.GetTaggedValue()); 172 return GetTaggedBoolean(flag); 173} 174 175JSTaggedValue BuiltinsSet::ForEach(EcmaRuntimeCallInfo *argv) 176{ 177 JSThread *thread = argv->GetThread(); 178 BUILTINS_API_TRACE(thread, Set, ForEach); 179 [[maybe_unused]] EcmaHandleScope handleScope(thread); 180 JSHandle<JSTaggedValue> self = GetThis(argv); 181 // 2.If Type(S) is not Object, throw a TypeError exception. 182 // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. 183 if (!self->IsJSSet()) { 184 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); 185 } 186 JSHandle<JSSet> set(self); 187 188 // 4.If IsCallable(callbackfn) is false, throw a TypeError exception. 189 JSHandle<JSTaggedValue> func(GetCallArg(argv, 0)); 190 if (!func->IsCallable()) { 191 THROW_TYPE_ERROR_AND_RETURN(thread, "callbackfn is not callable", JSTaggedValue::Exception()); 192 } 193 194 // 5.If thisArg was supplied, let T be thisArg; else let T be undefined. 195 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 1); 196 197 // 6.Let entries be the List that is the value of S’s [[SetData]] internal slot. 198 JSMutableHandle<LinkedHashSet> hashSet(thread, set->GetLinkedSet()); 199 const uint32_t argsLength = 3; 200 int index = 0; 201 int totalElements = hashSet->NumberOfElements() + hashSet->NumberOfDeletedElements(); 202 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 203 // 7.Repeat for each e that is an element of entries, in original insertion order 204 while (index < totalElements) { 205 JSHandle<JSTaggedValue> key(thread, hashSet->GetKey(index++)); 206 // a. If e is not empty, then 207 if (!key->IsHole()) { 208 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo( 209 thread, func, thisArg, undefined, argsLength); 210 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 211 info->SetCallArg(key.GetTaggedValue(), key.GetTaggedValue(), set.GetTaggedValue()); 212 // i. Let funcResult be Call(callbackfn, T, «e, e, S»). 213 JSTaggedValue ret = JSFunction::Call(info); // 3: three args 214 // ii. ReturnIfAbrupt(funcResult). 215 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ret); 216 // Maybe add or delete 217 JSTaggedValue nextTable = hashSet->GetNextTable(); 218 while (!nextTable.IsHole()) { 219 index -= hashSet->GetDeletedElementsAt(index); 220 hashSet.Update(nextTable); 221 nextTable = hashSet->GetNextTable(); 222 } 223 totalElements = hashSet->NumberOfElements() + hashSet->NumberOfDeletedElements(); 224 } 225 } 226 return JSTaggedValue::Undefined(); 227} 228 229JSTaggedValue BuiltinsSet::Species(EcmaRuntimeCallInfo *argv) 230{ 231 return GetThis(argv).GetTaggedValue(); 232} 233 234JSTaggedValue BuiltinsSet::GetSize(EcmaRuntimeCallInfo *argv) 235{ 236 ASSERT(argv); 237 BUILTINS_API_TRACE(argv->GetThread(), Set, GetSize); 238 JSThread *thread = argv->GetThread(); 239 [[maybe_unused]] EcmaHandleScope handleScope(thread); 240 JSHandle<JSTaggedValue> self(GetThis(argv)); 241 // 2.If Type(S) is not Object, throw a TypeError exception. 242 // 3.If S does not have a [[SetData]] internal slot, throw a TypeError exception. 243 if (!self->IsJSSet()) { 244 THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); 245 } 246 JSSet* jsSet = JSSet::Cast(self.GetTaggedValue().GetTaggedObject()); 247 uint32_t count = jsSet->GetSize(); 248 return JSTaggedValue(count); 249} 250 251JSTaggedValue BuiltinsSet::Entries(EcmaRuntimeCallInfo *argv) 252{ 253 ASSERT(argv); 254 BUILTINS_API_TRACE(argv->GetThread(), Set, Entries); 255 JSThread *thread = argv->GetThread(); 256 [[maybe_unused]] EcmaHandleScope handleScope(thread); 257 JSHandle<JSTaggedValue> self = GetThis(argv); 258 JSHandle<JSTaggedValue> iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::KEY_AND_VALUE); 259 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 260 return iter.GetTaggedValue(); 261} 262 263JSTaggedValue BuiltinsSet::Values(EcmaRuntimeCallInfo *argv) 264{ 265 ASSERT(argv); 266 BUILTINS_API_TRACE(argv->GetThread(), Set, Values); 267 JSThread *thread = argv->GetThread(); 268 [[maybe_unused]] EcmaHandleScope handleScope(thread); 269 JSHandle<JSTaggedValue> self = GetThis(argv); 270 JSHandle<JSTaggedValue> iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::VALUE); 271 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 272 return iter.GetTaggedValue(); 273} 274} // namespace panda::ecmascript::builtins 275