• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/js_api/js_api_lightweightset.h"
17 
18 #include "ecmascript/containers/containers_errors.h"
19 #include "ecmascript/interpreter/interpreter.h"
20 #include "ecmascript/js_api/js_api_lightweightset_iterator.h"
21 #include "ecmascript/js_array.h"
22 #include "ecmascript/js_function.h"
23 #include "ecmascript/js_tagged_value.h"
24 #include "ecmascript/object_factory.h"
25 
26 namespace panda::ecmascript {
27 using ContainerError = containers::ContainerError;
28 using ErrorFlag = containers::ErrorFlag;
Add(JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj,const JSHandle<JSTaggedValue> & value)29 bool JSAPILightWeightSet::Add(JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj,
30                               const JSHandle<JSTaggedValue> &value)
31 {
32     uint32_t hashCode = obj->Hash(value.GetTaggedValue());
33     JSHandle<TaggedArray> hashArray(thread, obj->GetHashes());
34     JSHandle<TaggedArray> valueArray(thread, obj->GetValues());
35     int32_t size = static_cast<int32_t>(obj->GetLength());
36     int32_t index = obj->GetHashIndex(value, size);
37     if (index >= 0) {
38         return false;
39     }
40     index ^= JSAPILightWeightSet::HASH_REBELLION;
41     if (index < size) {
42         obj->AdjustArray(thread, hashArray, index, size, true);
43         obj->AdjustArray(thread, valueArray, index, size, true);
44     }
45     uint32_t capacity = hashArray->GetLength();
46     if (size + 1 >= static_cast<int32_t>(capacity)) {
47         // need expanding
48         uint32_t newCapacity = capacity << 1U;
49         hashArray = thread->GetEcmaVM()->GetFactory()->CopyArray(hashArray, capacity, newCapacity);
50         valueArray = thread->GetEcmaVM()->GetFactory()->CopyArray(valueArray, capacity, newCapacity);
51         obj->SetHashes(thread, hashArray);
52         obj->SetValues(thread, valueArray);
53     }
54     hashArray->Set(thread, index, JSTaggedValue(hashCode));
55     valueArray->Set(thread, index, value.GetTaggedValue());
56     size++;
57     obj->SetLength(size);
58     return true;
59 }
60 
Get(const uint32_t index)61 JSTaggedValue JSAPILightWeightSet::Get(const uint32_t index)
62 {
63     TaggedArray *valueArray = TaggedArray::Cast(GetValues().GetTaggedObject());
64     return valueArray->Get(index);
65 }
66 
CreateSlot(const JSThread * thread,const uint32_t capacity)67 JSHandle<TaggedArray> JSAPILightWeightSet::CreateSlot(const JSThread *thread, const uint32_t capacity)
68 {
69     ASSERT_PRINT(capacity > 0, "size must be a non-negative integer");
70     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
71     JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(capacity);
72     for (uint32_t i = 0; i < capacity; i++) {
73         taggedArray->Set(thread, i, JSTaggedValue::Hole());
74     }
75     return taggedArray;
76 }
77 
GetHashIndex(const JSHandle<JSTaggedValue> & value,int32_t size)78 int32_t JSAPILightWeightSet::GetHashIndex(const JSHandle<JSTaggedValue> &value, int32_t size)
79 {
80     uint32_t hashCode = Hash(value.GetTaggedValue());
81     int32_t index = BinarySearchHashes(hashCode, size);
82     if (index < 0) {
83         return index;
84     }
85     TaggedArray *valueArray = TaggedArray::Cast(GetValues().GetTaggedObject());
86     if (index < size && (JSTaggedValue::SameValue(valueArray->Get(index), value.GetTaggedValue()))) {
87         return index;
88     }
89     TaggedArray *hashArray = TaggedArray::Cast(GetHashes().GetTaggedObject());
90     int32_t right = index;
91     while (right < size && (hashArray->Get(right).GetNumber() == hashCode)) {
92         if (JSTaggedValue::SameValue(valueArray->Get(right), value.GetTaggedValue())) {
93             return right;
94         }
95         right++;
96     }
97     int32_t left = index - 1;
98     while (left >= 0 && ((hashArray->Get(left).GetNumber() == hashCode))) {
99         if (JSTaggedValue::SameValue(valueArray->Get(left), value.GetTaggedValue())) {
100             return left;
101         }
102         left--;
103     }
104     return -right;
105 }
106 
BinarySearchHashes(uint32_t hash,int32_t size)107 int32_t JSAPILightWeightSet::BinarySearchHashes(uint32_t hash, int32_t size)
108 {
109     int32_t low = 0;
110     int32_t high = size - 1;
111     TaggedArray *hashArray = TaggedArray::Cast(GetHashes().GetTaggedObject());
112     while (low <= high) {
113         uint32_t mid = static_cast<uint32_t>(low + high) >> 1U;
114         uint32_t midVal = (uint32_t)(hashArray->Get(mid).GetNumber());
115         if (midVal < hash) {
116             low = mid + 1;
117         } else {
118             if (midVal <= hash) {
119                 return mid;
120             }
121             high = mid - 1;
122         }
123     }
124     return -(low + 1);
125 }
126 
AddAll(JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj,const JSHandle<JSTaggedValue> & value)127 bool JSAPILightWeightSet::AddAll(JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj,
128                                  const JSHandle<JSTaggedValue> &value)
129 {
130     bool changed = false;
131     JSHandle<JSAPILightWeightSet> srcLightWeightSet = JSHandle<JSAPILightWeightSet>::Cast(value);
132     uint32_t srcSize = srcLightWeightSet->GetSize();
133     uint32_t size = obj->GetSize();
134     obj->EnsureCapacity(thread, obj, size + srcSize);
135     JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined());
136     for (uint32_t i = 0; i < srcSize; i++) {
137         element.Update(srcLightWeightSet->GetValueAt(i));
138         changed |= JSAPILightWeightSet::Add(thread, obj, element);
139     }
140     return changed;
141 }
142 
EnsureCapacity(const JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj,uint32_t minimumCapacity)143 void JSAPILightWeightSet::EnsureCapacity(const JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj,
144                                          uint32_t minimumCapacity)
145 {
146     TaggedArray *hashes = TaggedArray::Cast(obj->GetValues().GetTaggedObject());
147     uint32_t capacity = hashes->GetLength();
148     uint32_t newCapacity = capacity;
149     if (capacity > minimumCapacity) {
150         return;
151     }
152     // adjust
153     while (newCapacity <= minimumCapacity) {
154         newCapacity = newCapacity << 1U;
155     }
156     obj->SizeCopy(thread, obj, capacity, newCapacity);
157 }
158 
SizeCopy(const JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj,uint32_t capacity,uint32_t newCapacity)159 void JSAPILightWeightSet::SizeCopy(const JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj,
160                                    uint32_t capacity, uint32_t newCapacity)
161 {
162     JSHandle<TaggedArray> hashArray(thread, obj->GetHashes());
163     JSHandle<TaggedArray> valueArray(thread, obj->GetValues());
164     hashArray = thread->GetEcmaVM()->GetFactory()->CopyArray(hashArray, capacity, newCapacity);
165     valueArray = thread->GetEcmaVM()->GetFactory()->CopyArray(valueArray, capacity, newCapacity);
166 
167     obj->SetValues(thread, hashArray);
168     obj->SetHashes(thread, valueArray);
169 }
170 
IsEmpty()171 bool JSAPILightWeightSet::IsEmpty()
172 {
173     return GetLength() == 0;
174 }
175 
GetValueAt(int32_t index)176 JSTaggedValue JSAPILightWeightSet::GetValueAt(int32_t index)
177 {
178     int32_t size = static_cast<int32_t>(GetLength());
179     if (index < 0 || index >= size) {
180         return JSTaggedValue::Undefined();
181     }
182     TaggedArray *values = TaggedArray::Cast(GetValues().GetTaggedObject());
183     return values->Get(index);
184 }
185 
GetHashAt(int32_t index)186 JSTaggedValue JSAPILightWeightSet::GetHashAt(int32_t index)
187 {
188     int32_t size = static_cast<int32_t>(GetLength());
189     if (index < 0 || index >= size) {
190         return JSTaggedValue::Undefined();
191     }
192     TaggedArray *values = TaggedArray::Cast(GetHashes().GetTaggedObject());
193     return values->Get(index);
194 }
195 
HasAll(const JSHandle<JSTaggedValue> & value)196 bool JSAPILightWeightSet::HasAll(const JSHandle<JSTaggedValue> &value)
197 {
198     bool result = false;
199     uint32_t relocate = 0;
200     JSAPILightWeightSet *lightweightSet = JSAPILightWeightSet::Cast(value.GetTaggedValue().GetTaggedObject());
201     uint32_t size = GetLength();
202     uint32_t destSize = lightweightSet->GetLength();
203     TaggedArray *hashes = TaggedArray::Cast(GetHashes().GetTaggedObject());
204     TaggedArray *destHashes = TaggedArray::Cast(lightweightSet->GetHashes().GetTaggedObject());
205     if (destSize > size) {
206         return result;
207     }
208     for (uint32_t i = 0; i < destSize; i++) {
209         uint32_t destHashCode = destHashes->Get(i).GetNumber();
210         result = false;
211         for (uint32_t j = relocate; j < size; j++) {
212             uint32_t hashCode = hashes->Get(j).GetNumber();
213             if (destHashCode == hashCode) {
214                 result = true;
215                 relocate = j + 1;
216                 break;
217             }
218         }
219         if (!result) {
220             break;
221         }
222     }
223     return result;
224 }
225 
Has(const JSHandle<JSTaggedValue> & value)226 bool JSAPILightWeightSet::Has(const JSHandle<JSTaggedValue> &value)
227 {
228     uint32_t size = GetLength();
229     int32_t index = GetHashIndex(value, size);
230     if (index < 0) {
231         return false;
232     }
233     return true;
234 }
235 
HasHash(const JSHandle<JSTaggedValue> & hashCode)236 bool JSAPILightWeightSet::HasHash(const JSHandle<JSTaggedValue> &hashCode)
237 {
238     uint32_t size = GetLength();
239     int32_t index = BinarySearchHashes(hashCode.GetTaggedValue().GetNumber(), size);
240     if (index < 0) {
241         return false;
242     }
243     return true;
244 }
245 
Equal(JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj,const JSHandle<JSTaggedValue> & value)246 bool JSAPILightWeightSet::Equal(JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj,
247                                 const JSHandle<JSTaggedValue> &value)
248 {
249     bool result = false;
250     JSHandle<TaggedArray> destHashes(thread, obj->GetValues());
251     uint32_t destSize = obj->GetLength();
252     uint32_t srcSize = 0;
253     JSMutableHandle<TaggedArray> srcHashes(thread, obj->GetHashes());
254     if (value.GetTaggedValue().IsJSAPILightWeightSet()) {
255         JSAPILightWeightSet *srcLightWeightSet = JSAPILightWeightSet::Cast(value.GetTaggedValue().GetTaggedObject());
256         srcSize = srcLightWeightSet->GetLength();
257         if (srcSize == 0 || destSize == 0) {
258             return false;
259         }
260         srcHashes.Update(srcLightWeightSet->GetHashes());
261     }
262     if (value.GetTaggedValue().IsJSArray()) {
263         srcHashes.Update(JSArray::ToTaggedArray(thread, value));
264         srcSize = srcHashes->GetLength();
265         if (srcSize == 0 || destSize == 0) {
266             return false;
267         }
268     }
269     if (srcSize != destSize) {
270         return false;
271     }
272     for (uint32_t i = 0; i < destSize; i++) {
273         JSTaggedValue compareValue = destHashes->Get(i);
274         JSTaggedValue values = srcHashes->Get(i);
275         if (compareValue.IsNumber() && values.IsNumber()) {
276             result = JSTaggedValue::SameValueNumberic(compareValue, values);
277         }
278         if (compareValue.IsString() && values.IsString()) {
279             result =
280                 JSTaggedValue::StringCompare(EcmaString::Cast(compareValue.GetTaggedObject()),
281                                              EcmaString::Cast(values.GetTaggedObject()));
282         }
283         if (!result) {
284             return result;
285         }
286     }
287     return result;
288 }
289 
IncreaseCapacityTo(JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj,int32_t minCapacity)290 void JSAPILightWeightSet::IncreaseCapacityTo(JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj,
291                                              int32_t minCapacity)
292 {
293     uint32_t capacity = TaggedArray::Cast(obj->GetValues().GetTaggedObject())->GetLength();
294     int32_t intCapacity = static_cast<int32_t>(capacity);
295     if (minCapacity <= 0 || intCapacity >= minCapacity) {
296         std::ostringstream oss;
297         oss << "The value of \"minimumCapacity\" is out of range. It must be > " << intCapacity
298             << ". Received value is: " << minCapacity;
299         JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str());
300         THROW_NEW_ERROR_AND_RETURN(thread, error);
301     }
302     JSHandle<TaggedArray> hashArray(thread, obj->GetHashes());
303     JSHandle<TaggedArray> newElements =
304         thread->GetEcmaVM()->GetFactory()->NewAndCopyTaggedArray(hashArray,
305                                                                  static_cast<uint32_t>(minCapacity), capacity);
306     obj->SetHashes(thread, newElements);
307 }
308 
GetIteratorObj(JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj,IterationKind kind)309 JSHandle<JSTaggedValue> JSAPILightWeightSet::GetIteratorObj(JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj,
310                                                             IterationKind kind)
311 {
312     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
313     JSHandle<JSTaggedValue> iter =
314         JSHandle<JSTaggedValue>::Cast(factory->NewJSAPILightWeightSetIterator(obj, kind));
315     return iter;
316 }
317 
ForEach(JSThread * thread,const JSHandle<JSTaggedValue> & thisHandle,const JSHandle<JSTaggedValue> & callbackFn,const JSHandle<JSTaggedValue> & thisArg)318 JSTaggedValue JSAPILightWeightSet::ForEach(JSThread *thread, const JSHandle<JSTaggedValue> &thisHandle,
319                                            const JSHandle<JSTaggedValue> &callbackFn,
320                                            const JSHandle<JSTaggedValue> &thisArg)
321 {
322     JSHandle<JSAPILightWeightSet> lightweightset = JSHandle<JSAPILightWeightSet>::Cast(thisHandle);
323     uint32_t length = lightweightset->GetSize();
324     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
325     for (uint32_t k = 0; k < length; k++) {
326         JSTaggedValue kValue = lightweightset->GetValueAt(k);
327         EcmaRuntimeCallInfo *info =
328             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFn, thisArg, undefined, 3); // 3:three args
329         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
330         info->SetCallArg(kValue, kValue, thisHandle.GetTaggedValue());
331         JSTaggedValue funcResult = JSFunction::Call(info);
332         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult);
333         if (lightweightset->GetSize() != length) {  // prevent length change
334             length = lightweightset->GetSize();
335         }
336     }
337     return JSTaggedValue::Undefined();
338 }
339 
GetIndexOf(JSHandle<JSTaggedValue> & value)340 int32_t JSAPILightWeightSet::GetIndexOf(JSHandle<JSTaggedValue> &value)
341 {
342     uint32_t size = GetLength();
343     int32_t index = GetHashIndex(value, size);
344     return index;
345 }
346 
Remove(JSThread * thread,JSHandle<JSTaggedValue> & value)347 JSTaggedValue JSAPILightWeightSet::Remove(JSThread *thread, JSHandle<JSTaggedValue> &value)
348 {
349     uint32_t size = GetLength();
350     TaggedArray *valueArray = TaggedArray::Cast(GetValues().GetTaggedObject());
351     int32_t index = GetHashIndex(value, size);
352     if (index < 0) {
353         return JSTaggedValue::Undefined();
354     }
355     JSTaggedValue result = valueArray->Get(index);
356     RemoveAt(thread, index);
357     return result;
358 }
359 
RemoveAt(JSThread * thread,int32_t index)360 bool JSAPILightWeightSet::RemoveAt(JSThread *thread, int32_t index)
361 {
362     uint32_t size = GetLength();
363     if (index < 0 || index >= static_cast<int32_t>(size)) {
364         return false;
365     }
366     JSHandle<TaggedArray> valueArray(thread, GetValues());
367     JSHandle<TaggedArray> hashArray(thread, GetHashes());
368     RemoveValue(thread, hashArray, static_cast<uint32_t>(index));
369     RemoveValue(thread, valueArray, static_cast<uint32_t>(index));
370     SetLength(size - 1);
371     return true;
372 }
373 
RemoveValue(const JSThread * thread,JSHandle<TaggedArray> & taggedArray,uint32_t index)374 void JSAPILightWeightSet::RemoveValue(const JSThread *thread, JSHandle<TaggedArray> &taggedArray,
375                                       uint32_t index)
376 {
377     uint32_t len = GetLength();
378     ASSERT(index < len);
379     TaggedArray::RemoveElementByIndex(thread, taggedArray, index, len);
380 }
381 
AdjustArray(JSThread * thread,JSHandle<TaggedArray> srcArray,uint32_t fromIndex,uint32_t toIndex,bool direction)382 void JSAPILightWeightSet::AdjustArray(JSThread *thread, JSHandle<TaggedArray> srcArray, uint32_t fromIndex,
383                                       uint32_t toIndex, bool direction)
384 {
385     uint32_t size = GetLength();
386     uint32_t idx = size - 1;
387     if (direction) {
388         while (fromIndex < toIndex) {
389             JSTaggedValue value = srcArray->Get(idx);
390             srcArray->Set(thread, idx + 1, value);
391             idx--;
392             fromIndex++;
393         }
394     } else {
395         uint32_t moveSize = size - fromIndex;
396         for (uint32_t i = 0; i < moveSize; i++) {
397             if ((fromIndex + i) < size) {
398                 JSTaggedValue value = srcArray->Get(fromIndex + i);
399                 srcArray->Set(thread, toIndex + i, value);
400             } else {
401                 srcArray->Set(thread, toIndex + i, JSTaggedValue::Hole());
402             }
403         }
404     }
405 }
406 
ToString(JSThread * thread,const JSHandle<JSAPILightWeightSet> & obj)407 JSTaggedValue JSAPILightWeightSet::ToString(JSThread *thread, const JSHandle<JSAPILightWeightSet> &obj)
408 {
409     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
410     std::u16string sepStr = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.from_bytes(",");
411 
412     uint32_t length = obj->GetSize();
413     JSHandle<TaggedArray> valueArray(thread, obj->GetValues());
414     std::u16string concatStr;
415     JSMutableHandle<JSTaggedValue> values(thread, JSTaggedValue::Undefined());
416     for (uint32_t k = 0; k < length; k++) {
417         std::u16string nextStr;
418         values.Update(valueArray->Get(k));
419         if (!values->IsUndefined() && !values->IsNull()) {
420             JSHandle<EcmaString> nextStringHandle = JSTaggedValue::ToString(thread, values);
421             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
422             nextStr = EcmaStringAccessor(nextStringHandle).ToU16String();
423         }
424         if (k > 0) {
425             concatStr.append(sepStr);
426             concatStr.append(nextStr);
427             continue;
428         }
429         concatStr.append(nextStr);
430     }
431     char16_t *char16tData = concatStr.data();
432     auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
433     int32_t u16strSize = concatStr.size();
434     return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
435 }
436 
Clear(JSThread * thread)437 void JSAPILightWeightSet::Clear(JSThread *thread)
438 {
439     TaggedArray *hashArray = TaggedArray::Cast(GetHashes().GetTaggedObject());
440     TaggedArray *valueArray = TaggedArray::Cast(GetValues().GetTaggedObject());
441     uint32_t size = GetLength();
442     for (uint32_t index = 0; index < size; index++) {
443         hashArray->Set(thread, index, JSTaggedValue::Hole());
444         valueArray->Set(thread, index, JSTaggedValue::Hole());
445     }
446     SetLength(0);
447 }
448 
Hash(JSTaggedValue key)449 uint32_t JSAPILightWeightSet::Hash(JSTaggedValue key)
450 {
451     if (key.IsDouble() && key.GetDouble() == 0.0) {
452         key = JSTaggedValue(0);
453     }
454     if (key.IsSymbol()) {
455         auto symbolString = JSSymbol::Cast(key.GetTaggedObject());
456         return symbolString->GetHashField();
457     }
458     if (key.IsString()) {
459         auto keyString = EcmaString::Cast(key.GetTaggedObject());
460         return EcmaStringAccessor(keyString).GetHashcode();
461     }
462     if (key.IsECMAObject()) {
463         uint32_t hash = ECMAObject::Cast(key.GetTaggedObject())->GetHash();
464         if (hash == 0) {
465             hash = base::RandomGenerator::GenerateIdentityHash();
466             ECMAObject::Cast(key.GetTaggedObject())->SetHash(hash);
467         }
468         return hash;
469     }
470     if (key.IsInt()) {
471         uint32_t hash = key.GetInt();
472         return hash;
473     }
474     uint64_t keyValue = key.GetRawData();
475     return GetHash32(reinterpret_cast<uint8_t *>(&keyValue), sizeof(keyValue) / sizeof(uint8_t));
476 }
477 } // namespace panda::ecmascript
478