diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index 4501c2ea29..feb62e245a 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -2217,6 +2217,8 @@ void Builtins::Initialize##Type(const JSHandle &env, const JSHandleSetProtoOrHClass(thread_, arrFuncInstanceHClass.GetTaggedValue()); \ SetConstant(arrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); \ SetConstant(JSHandle(arrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); \ + /* %TypedArray%.protoofprototype (where %TypedArray% is one of Int8Array, Uint8Array, etc.) */ \ + JSTaggedValue protoOfPrototypeValue = arrFuncPrototype->GetJSHClass()->GetPrototype(); \ env->Set##Type##Function(thread_, arrayFunction); \ env->Set##Type##FunctionPrototype(thread_, arrFuncPrototypeValue); \ env->Set##Type##RootHclass(thread_, arrFuncInstanceHClass); \ @@ -2225,7 +2227,9 @@ void Builtins::Initialize##Type(const JSHandle &env, const JSHandleSetInitialBuiltinHClass(BuiltinTypeId::TYPE, \ arrayFunction->GetJSHClass(), \ *arrFuncInstanceHClass, \ - arrFuncPrototype->GetJSHClass()); \ + arrFuncPrototype->GetJSHClass(), \ + protoOfPrototypeValue.IsHeapObject() ? protoOfPrototypeValue.GetTaggedObject()->GetClass() : nullptr, \ + *arrFuncInstanceHClassOnHeap); \ } BUILTIN_TYPED_ARRAY_TYPES(BUILTIN_TYPED_ARRAY_DEFINE_INITIALIZE) diff --git a/ecmascript/js_thread.cpp b/ecmascript/js_thread.cpp index e3ec6b87a4..3020017f34 100644 --- a/ecmascript/js_thread.cpp +++ b/ecmascript/js_thread.cpp @@ -500,7 +500,7 @@ void JSThread::ResetGuardians() void JSThread::SetInitialBuiltinHClass( BuiltinTypeId type, JSHClass *builtinHClass, JSHClass *instanceHClass, - JSHClass *prototypeHClass, JSHClass *prototypeOfPrototypeHClass) + JSHClass *prototypeHClass, JSHClass *prototypeOfPrototypeHClass, JSHClass *extraHClass) { size_t index = BuiltinHClassEntries::GetEntryIndex(type); auto &entry = glueData_.builtinHClassEntries_.entries[index]; @@ -509,11 +509,13 @@ void JSThread::SetInitialBuiltinHClass( << ", builtinHClass = " << builtinHClass << ", instanceHClass = " << instanceHClass << ", prototypeHClass = " << prototypeHClass - << ", prototypeOfPrototypeHClass = " << prototypeOfPrototypeHClass; + << ", prototypeOfPrototypeHClass = " << prototypeOfPrototypeHClass + << ", extraHClass = " << extraHClass; entry.builtinHClass = builtinHClass; entry.instanceHClass = instanceHClass; entry.prototypeHClass = prototypeHClass; entry.prototypeOfPrototypeHClass = prototypeOfPrototypeHClass; + entry.extraHClass = extraHClass; } JSHClass *JSThread::GetBuiltinHClass(BuiltinTypeId type) const @@ -528,6 +530,12 @@ JSHClass *JSThread::GetBuiltinInstanceHClass(BuiltinTypeId type) const return glueData_.builtinHClassEntries_.entries[index].instanceHClass; } +JSHClass *JSThread::GetBuiltinExtraHClass(BuiltinTypeId type) const +{ + size_t index = BuiltinHClassEntries::GetEntryIndex(type); + return glueData_.builtinHClassEntries_.entries[index].extraHClass; +} + JSHClass *JSThread::GetArrayInstanceHClass(ElementsKind kind) const { auto iter = GetArrayHClassIndexMap().find(kind); diff --git a/ecmascript/js_thread.h b/ecmascript/js_thread.h index 4057264ec1..1cfbd9efe2 100644 --- a/ecmascript/js_thread.h +++ b/ecmascript/js_thread.h @@ -280,11 +280,13 @@ public: void SetInitialBuiltinHClass( BuiltinTypeId type, JSHClass *builtinHClass, JSHClass *instanceHClass, - JSHClass *prototypeHClass, JSHClass *prototypeOfPrototypeHClass = nullptr); + JSHClass *prototypeHClass, JSHClass *prototypeOfPrototypeHClass = nullptr, + JSHClass *extraHClass = nullptr); JSHClass *GetBuiltinHClass(BuiltinTypeId type) const; JSHClass *GetBuiltinInstanceHClass(BuiltinTypeId type) const; + JSHClass *GetBuiltinExtraHClass(BuiltinTypeId type) const; JSHClass *GetArrayInstanceHClass(ElementsKind kind) const; PUBLIC_API JSHClass *GetBuiltinPrototypeHClass(BuiltinTypeId type) const; diff --git a/ecmascript/js_thread_hclass_entries.h b/ecmascript/js_thread_hclass_entries.h index 74822c7220..399edea855 100644 --- a/ecmascript/js_thread_hclass_entries.h +++ b/ecmascript/js_thread_hclass_entries.h @@ -47,6 +47,8 @@ struct BuiltinHClassEntries { JSHClass *prototypeHClass = nullptr; // prototypeOfPrototypeHClass = HClass of X.prototype.prototype JSHClass *prototypeOfPrototypeHClass = nullptr; + //extraHClass . In typedArray means instanceHClassOnHeap + JSHClass *extraHClass = nullptr; }; Entry entries[N_ENTRIES]; @@ -75,6 +77,12 @@ struct BuiltinHClassEntries { return sizeof(Entry) * index + MEMBER_OFFSET(Entry, instanceHClass); } + static size_t GetExtraHClassOffset(BuiltinTypeId type) + { + size_t index = GetEntryIndex(type); + return sizeof(Entry) * index + MEMBER_OFFSET(Entry, extraHClass); + } + static size_t GetPrototypeHClassOffset(BuiltinTypeId type) { size_t index = GetEntryIndex(type); diff --git a/ecmascript/pgo_profiler/pgo_profiler.cpp b/ecmascript/pgo_profiler/pgo_profiler.cpp index ae8320f529..3bda7a5efb 100644 --- a/ecmascript/pgo_profiler/pgo_profiler.cpp +++ b/ecmascript/pgo_profiler/pgo_profiler.cpp @@ -1450,21 +1450,39 @@ void PGOProfiler::AddBuiltinsInfoByNameInProt(ApEntityId abcId, const CString &r } auto exceptHoldHClass = thread->GetBuiltinPrototypeHClass(builtinsId.value()); + auto exceptPrototypeOfPrototypeHClass = + thread->GetBuiltinPrototypeOfPrototypeHClass(builtinsId.value()); // iterator needs to find two layers of prototype if (builtinsId == BuiltinTypeId::ARRAY_ITERATOR) { - auto exceptPrototypeOfPrototypeHClass = - thread->GetBuiltinPrototypeOfPrototypeHClass(builtinsId.value()); if ((exceptRecvHClass != receiver) || (exceptHoldHClass != hold && exceptPrototypeOfPrototypeHClass != hold)) { return; } + } else if (IsTypedArrayType(builtinsId.value())) { + auto exceptRecvHClassOnHeap = thread->GetBuiltinExtraHClass(builtinsId.value()); + ASSERT_PRINT(exceptRecvHClassOnHeap == nullptr || exceptRecvHClassOnHeap->IsOnHeapFromBitField(), + "must be on heap"); + if (IsJSHClassNotEqual(receiver, hold, exceptRecvHClass, exceptRecvHClassOnHeap, + exceptHoldHClass, exceptPrototypeOfPrototypeHClass)) { + return; + } } else if (exceptRecvHClass != receiver || exceptHoldHClass != hold) { - return ; + return; } AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, receiver, receiver); } +bool PGOProfiler::IsJSHClassNotEqual(JSHClass *receiver, JSHClass *hold, JSHClass *exceptRecvHClass, + JSHClass *exceptRecvHClassOnHeap, JSHClass *exceptHoldHClass, + JSHClass *exceptPrototypeOfPrototypeHClass) +{ + //exceptRecvHClass = IHC, exceptRecvHClassOnHeap = IHC OnHeap + //exceptHoldHClass = PHC, exceptPrototypeOfPrototypeHClass = HClass of X.prototype.prototype + return ((exceptRecvHClass != receiver && exceptRecvHClassOnHeap != receiver) || + (exceptHoldHClass != hold && exceptPrototypeOfPrototypeHClass != hold)); +} + void PGOProfiler::AddBuiltinsGlobalInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, ConstantIndex globalId) { diff --git a/ecmascript/pgo_profiler/pgo_profiler.h b/ecmascript/pgo_profiler/pgo_profiler.h index 72f20283b3..81a0530db2 100644 --- a/ecmascript/pgo_profiler/pgo_profiler.h +++ b/ecmascript/pgo_profiler/pgo_profiler.h @@ -173,6 +173,9 @@ private: void UpdateExtraProfileTypeInfo(ApEntityId abcId, const CString& recordName, EntityId methodId, WorkNode* current); WorkNode* PopFromProfileQueue(); void SaveProfiler(bool force); + bool IsJSHClassNotEqual(JSHClass *receiver, JSHClass *hold, JSHClass *exceptRecvHClass, + JSHClass *exceptRecvHClassOnHeap, JSHClass *exceptHoldHClass, + JSHClass *exceptPrototypeOfPrototypeHClass); class PGOProfilerTask : public Task { public: diff --git a/ecmascript/pgo_profiler/tests/BUILD.gn b/ecmascript/pgo_profiler/tests/BUILD.gn index bce8e747ec..9e2dbc147a 100644 --- a/ecmascript/pgo_profiler/tests/BUILD.gn +++ b/ecmascript/pgo_profiler/tests/BUILD.gn @@ -30,6 +30,7 @@ test_js_files = [ "array_size_test", "truck", "vehicle", + "typedarray_length", ] foreach(file, test_js_files) { diff --git a/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp b/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp index 081b003bb4..ab38990478 100644 --- a/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp +++ b/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp @@ -1218,4 +1218,34 @@ HWTEST_F_L0(PGOProfilerTest, ApVersionMatchCheck) unlink("ark-ApVersionMatchCheck/modules.ap"); unlink("ark-ApVersionMatchCheck/"); } + +HWTEST_F_L0(PGOProfilerTest, TypedArrayOnHeap) +{ + mkdir("ark-profiler24/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "typedarray_length"; + ExecuteAndLoadJSPandaFile("ark-profiler24/", targetRecordName); + ASSERT_NE(pf_, nullptr); + uint32_t checksum = pf_->GetChecksum(); + + // Loader + PGOProfilerDecoder decoder("ark-profiler24/modules.ap", 1); + ASSERT_TRUE(decoder.LoadAndVerify(checksum)); + auto methodLiterals = pf_->GetMethodLiteralMap(); + for (auto iter : methodLiterals) { + auto methodLiteral = iter.second; + auto methodId = methodLiteral->GetMethodId(); + auto methodName = methodLiteral->GetMethodName(pf_.get(), methodId); + auto callback = [methodName](uint32_t offset, const PGOType *type) { + ASSERT_NE(offset, 0); + if (type->IsRwOpType() && std::string(methodName) == "test") { + auto pgoRWOpType = *reinterpret_cast(type); + ASSERT_TRUE(pgoRWOpType.GetCount() == 1); + } + }; + decoder.GetTypeInfo(pf_.get(), targetRecordName, methodLiteral, + callback); + } + unlink("ark-profiler24/modules.ap"); + rmdir("ark-profiler24/"); +} } // namespace panda::test diff --git a/ecmascript/pgo_profiler/tests/pgo_test_case/typedarray_length.js b/ecmascript/pgo_profiler/tests/pgo_test_case/typedarray_length.js new file mode 100644 index 0000000000..5e940d3bf9 --- /dev/null +++ b/ecmascript/pgo_profiler/tests/pgo_test_case/typedarray_length.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Items { + public static test(itemMap){ + itemMap.forEach((value) => { // OneItem + value.length; // OneItem.value.length + }); + } +} +let itemMap = new Map(); +for (let i = 1; i < 10; i++) { + itemMap.set(i, new Int8Array(i * 6)); +} +Items.test(itemMap) diff --git a/test/resource/js_runtime/ohos_test.xml b/test/resource/js_runtime/ohos_test.xml index 7a9a4db677..f36177ee28 100755 --- a/test/resource/js_runtime/ohos_test.xml +++ b/test/resource/js_runtime/ohos_test.xml @@ -730,6 +730,7 @@