diff --git a/ecmascript/builtins/builtins_array.cpp b/ecmascript/builtins/builtins_array.cpp index 157796c1966c925a82c61980ba49c1184b80a215..2849a7bc0504f6642f082db323140f578e4bf412 100644 --- a/ecmascript/builtins/builtins_array.cpp +++ b/ecmascript/builtins/builtins_array.cpp @@ -19,6 +19,7 @@ #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/interpreter/interpreter.h" +#include "ecmascript/js_array.h" #include "ecmascript/js_map_iterator.h" #include "ecmascript/js_stable_array.h" #include "ecmascript/object_fast_operator-inl.h" @@ -517,6 +518,7 @@ JSTaggedValue BuiltinsArray::Concat(EcmaRuntimeCallInfo *argv) JSMutableHandle ele(thread, JSTaggedValue::Undefined()); JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle valHandle(thread, JSTaggedValue::Undefined()); // 4. Prepend O to items. // 5. For each element E of items, do for (int i = -1; i < argc; i++) { @@ -544,6 +546,14 @@ JSTaggedValue BuiltinsArray::Concat(EcmaRuntimeCallInfo *argv) JSStableArray::Concat(thread, newArrayHandle, JSHandle::Cast(ele), k, n); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } + #if ENABLE_NEXT_OPTIMIZATION + else if (JSArray::IsProtoNotModifiedDictionaryJSArray(thread, JSHandle::Cast(ele))) { + JSArray::FastConcatDictionaryArray(thread, JSHandle::Cast(ele), newArrayHandle, + valHandle, toKey, n); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + continue; + } + #endif // iv. Repeat, while k < len, while (k < len) { // 1. Let P be ToString(k). diff --git a/ecmascript/builtins/builtins_array.h b/ecmascript/builtins/builtins_array.h index 42bc99362b99a947da5af3e9354e3bd112ad34c5..ef58d18d6f7904583d9c112b16dcc861727377d7 100644 --- a/ecmascript/builtins/builtins_array.h +++ b/ecmascript/builtins/builtins_array.h @@ -253,7 +253,6 @@ private: BUILTIN_ARRAY_PROTOTYPE_FUNCTIONS(BUILTIN_ARRAY_FUNCTION_ENTRY) }; #undef BUILTIN_ARRAY_FUNCTION_ENTRY - static JSTaggedValue IndexOfStable( EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle); static JSTaggedValue IndexOfSlowPath( diff --git a/ecmascript/jit/tests/BUILD.gn b/ecmascript/jit/tests/BUILD.gn index e314fa4951b9beaba86e4b23b7e153e59153c6a6..af4507853a5b625f69c2b7e53f7e616c4b1fbf28 100644 --- a/ecmascript/jit/tests/BUILD.gn +++ b/ecmascript/jit/tests/BUILD.gn @@ -59,6 +59,6 @@ group("host_unittest") { } if (is_mac) { - deps -= [ ":JIT_001_TestAction" ] + deps = [ ":JIT_001_TestAction" ] } } diff --git a/ecmascript/js_array.cpp b/ecmascript/js_array.cpp index 5185eaf7f15cbe8d184469626860c3ff9ae5186f..d6c83c0da2ba88190e20b2890eb373ec5be4dcea 100644 --- a/ecmascript/js_array.cpp +++ b/ecmascript/js_array.cpp @@ -401,6 +401,19 @@ bool JSArray::IsLengthString(JSThread *thread, const JSHandle &ke return key.GetTaggedValue() == thread->GlobalConstants()->GetLengthString(); } +// static +// Check whether the element of the array is dictionary element, +// proto of the array has not been modified +// the element of the array prototype has not been modified +// the attribute of the array has not been modified +bool JSArray::IsProtoNotModifiedDictionaryJSArray(JSThread *thread, const JSHandle &obj) +{ + return obj->GetJSHClass()->IsDictionaryElement() && + !thread->IsArrayPrototypeChangedGuardiansInvalid() && + !obj->GetClass()->IsJSArrayPrototypeModifiedFromBitField() && + JSObject::AttributesUnchanged(thread, obj); +} + // ecma6 7.3 Operations on Objects JSHandle JSArray::CreateArrayFromList(JSThread *thread, const JSHandle &elements) { @@ -595,6 +608,45 @@ void JSArray::SortElementsByMergeSort(JSThread *thread, const JSHandle obj, + JSHandle &newArrayHandle, JSMutableHandle &fromValHandle, + JSMutableHandle &toKey, int64_t &n) +{ + bool isArrayHandleDictionary = newArrayHandle->GetJSHClass()->IsDictionaryElement(); + if (!isArrayHandleDictionary) { + JSObject::ElementsToDictionary(thread, newArrayHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + ASSERT(newArrayHandle->GetJSHClass()->IsDictionaryElement()); + JSHandle objVal(obj); + int64_t len = base::ArrayHelper::GetArrayLength(thread, objVal); + JSHandle elements(thread, obj->GetElements()); + uint32_t size = static_cast(elements->Size()); + JSMutableHandle dict(thread, newArrayHandle->GetElements()); + auto attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes()); + for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) { + JSTaggedValue key = elements->GetKey(hashIndex); + if (key.IsUndefined() || key.IsHole()) { + continue; + } + ASSERT(key.IsInt()); + uint32_t uintKey = static_cast(key.GetInt()); + if (uintKey < len) { + JSTaggedValue value = elements->GetValue(hashIndex); + toKey.Update(JSTaggedValue(n + uintKey)); + fromValHandle.Update(value); + JSHandle newDict = \ + NumberDictionary::PutIfAbsent(thread, dict, toKey, fromValHandle, attr); + dict.Update(newDict); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + newArrayHandle->SetElements(thread, dict); + n += len; + return JSTaggedValue(true); +} + void JSArray::MergeSortedElements(JSThread *thread, const JSHandle &elements, const JSHandle &fn, int64_t startIdx, int64_t middleIdx, int64_t endIdx) @@ -775,14 +827,14 @@ void JSArray::CheckAndCopyArray(const JSThread *thread, JSHandle obj) } } -//static +// static bool JSArray::IsProtoNotChangeJSArray(JSThread *thread, const JSHandle &obj) { if (obj->IsJSArray()) { if (obj->GetJSHClass()->GetElementsKind() != ElementsKind::GENERIC) { return true; } - JSTaggedValue arrayProtoValue = obj->GetJSHClass()->GetProto(); + JSTaggedValue arrayProtoValue = JSObject::GetPrototype(obj); JSTaggedValue genericArrayHClass = thread->GlobalConstants()->GetElementHoleTaggedClass(); JSTaggedValue genericArrayProtoValue = \ JSHClass::Cast(genericArrayHClass.GetTaggedObject())->GetProto(); diff --git a/ecmascript/js_array.h b/ecmascript/js_array.h index 679595bf113107bcefbcb7449df3ba587936cac2..e41768ff6466ee599ccbadf145331b3280c54342 100644 --- a/ecmascript/js_array.h +++ b/ecmascript/js_array.h @@ -46,10 +46,15 @@ public: static bool IsLengthString(JSThread *thread, const JSHandle &key); static bool IsProtoNotChangeJSArray(JSThread *thread, const JSHandle &obj); + static bool IsProtoNotModifiedDictionaryJSArray(JSThread *thread, const JSHandle &obj); // ecma6 7.3 Operations on Objects static JSHandle CreateArrayFromList(JSThread *thread, const JSHandle &elements); static JSHandle CreateArrayFromList(JSThread *thread, const JSHandle &newtarget, const JSHandle &elements); + static JSTaggedValue FastConcatDictionaryArray(JSThread *thread, JSHandle obj, + JSHandle &newArrayHandle, JSMutableHandle &fromValHandle, + JSMutableHandle &toKey, int64_t &n); + // use first inlined property slot for array length inline uint32_t GetArrayLength() const { diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp index b26a45935a4c2a19141b06973518116d0112b11e..906b3a754076cdee22bd5d2aee00e02aeff3eb04 100644 --- a/ecmascript/js_object.cpp +++ b/ecmascript/js_object.cpp @@ -248,7 +248,8 @@ void JSObject::ElementsToDictionary(const JSThread *thread, JSHandle o TryMigrateToGenericKindForJSObject(thread, obj, oldKind); } -inline bool JSObject::ShouldOptimizeAsFastElements(const JSThread *thread, JSHandle obj) +bool JSObject::AttributesUnchanged(const JSThread *thread, + const JSHandle &obj) { JSHandle elements(thread, obj->GetElements()); uint32_t size = static_cast(elements->Size()); @@ -268,7 +269,7 @@ inline bool JSObject::ShouldOptimizeAsFastElements(const JSThread *thread, JSHan void JSObject::TryOptimizeAsFastElements(const JSThread *thread, JSHandle obj) { ASSERT(obj->GetJSHClass()->IsDictionaryElement() && obj->IsJSArray()); - if (ShouldOptimizeAsFastElements(thread, obj)) { + if (AttributesUnchanged(thread, obj)) { uint32_t length = JSArray::Cast(*obj)->GetLength(); JSHandle elements(thread, obj->GetElements()); uint32_t size = static_cast(elements->Size()); diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h index 44fcab44a96b0d03ad92ccd93aa4476cd5ea3b35..4e6048de6df9aa326f414c0073ebc0ba248788a0 100644 --- a/ecmascript/js_object.h +++ b/ecmascript/js_object.h @@ -746,7 +746,7 @@ public: bool UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value); static bool ShouldTransToDict(uint32_t capacity, uint32_t index); static bool ShouldTransToFastElements(JSThread *thread, TaggedArray *elements, uint32_t capacity, uint32_t index); - static bool ShouldOptimizeAsFastElements(const JSThread *thread, JSHandle obj); + static bool AttributesUnchanged(const JSThread *thread, const JSHandle &obj); static JSHandle GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, uint32_t capacity, bool highGrowth = false, bool isNew = false); @@ -769,7 +769,6 @@ public: static void PUBLIC_API TryMigrateToGenericKindForJSObject(const JSThread *thread, const JSHandle &obj, const ElementsKind oldKind); -protected: static void ElementsToDictionary(const JSThread *thread, JSHandle obj); private: diff --git a/test/moduletest/BUILD.gn b/test/moduletest/BUILD.gn index 8b37707e7bf873ea72341aa348c2f8ea0a662584..b7ed79a3281c273fbdddd7dece40068f10700a59 100644 --- a/test/moduletest/BUILD.gn +++ b/test/moduletest/BUILD.gn @@ -266,6 +266,7 @@ group("ark_asm_test") { "arrayspliceproto", "arrayunshiftproto", "arrayfillproto", + "arrayconcat", "arrayfindlast", "arrayfill", "arrayflat", diff --git a/test/moduletest/arrayConcat/BUILD.gn b/test/moduletest/arrayconcat/BUILD.gn similarity index 94% rename from test/moduletest/arrayConcat/BUILD.gn rename to test/moduletest/arrayconcat/BUILD.gn index 6534cb435800be36b71616c71922a2a94c3b3f37..32dda9fb22784fc839763569b93b9ae22f4951c6 100644 --- a/test/moduletest/arrayConcat/BUILD.gn +++ b/test/moduletest/arrayconcat/BUILD.gn @@ -13,6 +13,6 @@ import("//arkcompiler/ets_runtime/test/test_helper.gni") -host_moduletest_action("arrayConcat") { +host_moduletest_action("arrayconcat") { deps = [] } diff --git a/test/moduletest/arrayConcat/arrayConcat.js b/test/moduletest/arrayconcat/arrayconcat.js similarity index 30% rename from test/moduletest/arrayConcat/arrayConcat.js rename to test/moduletest/arrayconcat/arrayconcat.js index 0a4d9b92728cfb563bdc3cfeecb66e86a405a652..e9ab1c22d2c439e0698431fbe3b1411faeb37db6 100644 --- a/test/moduletest/arrayConcat/arrayConcat.js +++ b/test/moduletest/arrayconcat/arrayconcat.js @@ -50,32 +50,151 @@ num11[0].push(4); print(numbers2); -var target = {0:"a",1:"b",[Symbol.isConcatSpreadable]:"truish"}; -var obj=new Proxy(target,{ - get(a,p){ - print(p.toString()); - return a[p]; - } -}) -[].concat(obj); print([1, , 3].concat([4, 5])); // [1, empty, 3, 4, 5] print([1, 2].concat([3, , 5])); // [1, 2, 3, empty, 5] const emptyArr = []; print(emptyArr.concat([]).length); -let px = new Proxy([], {}); - -let idx = 0; -class A extends Array { - constructor() { - super(2); - idx++; - if (idx == 1) { - this.concat(A); - } - return px; - } +/* + * @tc.name:Array concat1 + * @tc.desc:test long Array Concat long Array and change proto + * @tc.type: FUNC + */ +{ + let arr1 = new Array(2000); + arr1[0] = 1; + arr1[1] = 1; + let arr2 = new Array(2000); + arr2.__proto__ = arr1; + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(2000); + let arr4 = arr3.concat(arr2); + print(arr4[2000], arr4[2001], arr4[2002], arr4[2003], arr4[2004]); +} + +/* + * @tc.name:Array concat2 + * @tc.desc:test long Array Concat short Array and change proto + * @tc.type: FUNC + */ +{ + let arr1 = new Array(10); + arr1[0] = 1; + arr1[1] = 1; + let arr2 = new Array(10); + arr2.__proto__ = arr1; + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(2000); + let arr4 = arr3.concat(arr2); + print(arr4[2000], arr4[2001], arr4[2002], arr4[2003], arr4[2004]); +} + +/* + * @tc.name:Array concat3 + * @tc.desc:test short Array Concat long Array and change proto + * @tc.type: FUNC + */ +{ + let arr1 = new Array(2000); + arr1[0] = 1; + arr1[1] = 1; + let arr2 = new Array(2000); + arr2.__proto__ = arr1; + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(10); + let arr4 = arr3.concat(arr2); + print(arr4[10], arr4[11], arr4[12], arr4[13], arr4[14]); +} + +/* + * @tc.name:Array concat4 + * @tc.desc:test long Array Concat long Array + * @tc.type: FUNC + */ +{ + let arr2 = new Array(2000); + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(2000); + let arr4 = arr3.concat(arr2); + print(arr4[2000], arr4[2001], arr4[2002], arr4[2003], arr4[2004]); +} + +/* + * @tc.name:Array concat5 + * @tc.desc:test long Array Concat short Array + * @tc.type: FUNC + */ +{ + let arr2 = new Array(10); + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(2000); + let arr4 = arr3.concat(arr2); + print(arr4[2000], arr4[2001], arr4[2002], arr4[2003], arr4[2004]); +} + +/* + * @tc.name:Array concat6 + * @tc.desc:test short Array Concat long Array + * @tc.type: FUNC + */ +{ + let arr2 = new Array(2000); + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(10); + let arr4 = arr3.concat(arr2); + print(arr4[10], arr4[11], arr4[12], arr4[13], arr4[14]); +} + +/* + * @tc.name:Array concat7 + * @tc.desc:test long Array Concat long Array change origin prototype + * @tc.type: FUNC + */ +{ + let arr2 = new Array(2000); + arr2.__proto__[0] = "??"; + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(2000); + let arr4 = arr3.concat(arr2); + print(arr4[0], arr4[2000], arr4[2001], arr4[2002], arr4[2003], arr4[2004]); +} + +/* + * @tc.name:Array concat8 + * @tc.desc:test long Array Concat long Array change origin prototype + * @tc.type: FUNC + */ +{ + let arr2 = new Array(2000); + arr2.__proto__[0] = "??"; + arr2[1] = 3; + arr2[2] = 2; + arr2[3] = "ab"; + arr2[4] = 1; + let arr3 = new Array(2000); + let arr4 = arr3.concat(arr2); + print(arr4[0], arr4[2000], arr4[2001], arr4[2002], arr4[2003], arr4[2004]); } -let a = new A(); -print("proxy defineproperty success!"); \ No newline at end of file +print("test arrayconcat success!"); \ No newline at end of file diff --git a/test/moduletest/arrayConcat/expect_output.txt b/test/moduletest/arrayconcat/expect_output.txt similarity index 80% rename from test/moduletest/arrayConcat/expect_output.txt rename to test/moduletest/arrayconcat/expect_output.txt index ede8f6d4ad65c2d952c68230ee12e800be333394..14590f14c115f2dedf10016f693c1779c0498fbb 100644 --- a/test/moduletest/arrayConcat/expect_output.txt +++ b/test/moduletest/arrayconcat/expect_output.txt @@ -17,9 +17,15 @@ a,b,c,1,2,3 a,b,c,1,2,3 1,2,3,2,3 1,2,3,2,3 -Symbol(Symbol.isConcatSpreadable) -length -1, undefined, 3, 4, 5 -1, 2, 3, undefined, 5 +1,,3,4,5 +1,2,3,,5 0 -proxy defineproperty success! \ No newline at end of file +1 3 2 ab 1 +1 3 2 ab 1 +1 3 2 ab 1 +undefined 3 2 ab 1 +undefined 3 2 ab 1 +undefined 3 2 ab 1 +?? ?? 3 2 ab 1 +?? ?? 3 2 ab 1 +test arrayconcat success! \ No newline at end of file