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 <csetjmp>
17#include <csignal>
18
19#include "ecmascript/ecma_handle_scope.h"
20#include "ecmascript/global_env.h"
21#include "ecmascript/js_tagged_value.h"
22#include "ecmascript/jspandafile/program_object.h"
23#include "ecmascript/mem/barriers.h"
24#include "ecmascript/mem/heap-inl.h"
25#include "ecmascript/mem/verification.h"
26#include "ecmascript/napi/include/jsnapi.h"
27#include "ecmascript/tagged_array.h"
28#include "ecmascript/tests/test_helper.h"
29#include "gtest/gtest.h"
30
31using namespace panda;
32
33using namespace panda::ecmascript;
34
35namespace panda::test {
36class HandleLeakTest : public BaseTestWithScope<false> {
37public:
38    void SetUp() override
39    {
40        JSRuntimeOptions options;
41        options.SetEnableForceGC(false);
42        options.SetLogLevel("info");
43        instance = JSNApi::CreateEcmaVM(options);
44        ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
45        thread = instance->GetJSThread();
46        thread->ManagedCodeBegin();
47        scope = new EcmaHandleScope(thread);
48    }
49};
50
51static sigjmp_buf env;
52static bool segmentFaultFlag = false;
53class HandleLeakTestManager {
54public:
55    static void ProcessHandleLeakSegmentFault(int sig)
56    {
57        segmentFaultFlag = true;
58        siglongjmp(env, sig);
59    }
60
61    static int RegisterSignal()
62    {
63        segmentFaultFlag = false;
64        struct sigaction act;
65        act.sa_handler = ProcessHandleLeakSegmentFault;
66        sigemptyset(&act.sa_mask);
67        sigaddset(&act.sa_mask, SIGQUIT);
68        act.sa_flags = SA_RESETHAND;
69        return sigaction(SIGSEGV, &act, nullptr);
70    }
71};
72
73HWTEST_F_L0(HandleLeakTest, HandleLeakCheck)
74{
75    EcmaHandleScope scope(thread);
76    std::vector<Global<ArrayRef>> result;
77    for (int i = 0; i < 75000; i++) {
78        result.emplace_back(Global<ArrayRef>(instance, ArrayRef::New(instance, 100)));
79    }
80}
81
82HWTEST_F_L0(HandleLeakTest, InitializeCheckOneProperty)
83{
84    EcmaHandleScope scope(thread);
85    JSHandle<Program> newProgram(thread, const_cast<Heap *>(instance->GetHeap())->AllocateYoungOrHugeObject(
86        JSHClass::Cast(thread->GlobalConstants()->GetProgramClass().GetTaggedObject())));
87
88    newProgram->SetMainFunction(thread, JSTaggedValue::Undefined());
89
90    size_t failCount = 0;
91    VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
92    verifier(*newProgram);
93    ASSERT_TRUE(newProgram.GetTaggedValue().IsProgram());
94    ASSERT_TRUE(failCount == 0);
95}
96
97static void HeandleLeakTestCommon(const EcmaVM *instance, JSHandle<TaggedArray>& newArray)
98{
99    size_t failCount = 0;
100    auto ret = sigsetjmp(env, 1);
101    if (ret != SIGSEGV) {
102        VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
103        verifier(*newArray);
104        ASSERT_TRUE(false);
105    } else {
106        // catch signal SIGSEGV caused by uninitialize
107        EXPECT_TRUE(segmentFaultFlag);
108        ASSERT_TRUE(failCount == 0);
109    }
110}
111
112HWTEST_F_L0(HandleLeakTest, PartInitializeCheckMoreProperty)
113{
114    EcmaHandleScope scope(thread);
115    JSHandle<JSHClass> arrayClass(thread->GlobalConstants()->GetHandledArrayClass());
116    static constexpr int SIZE = 100;
117    JSHandle<TaggedArray> newArray(thread, const_cast<Heap *>(instance->GetHeap())->AllocateNonMovableOrHugeObject(
118        *arrayClass, TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), SIZE)));
119    newArray->SetLength(SIZE);
120    for (uint32_t i = 0; i < SIZE / 2; i++) {
121        size_t offset = JSTaggedValue::TaggedTypeSize() * i;
122        ecmascript::Barriers::SetPrimitive(newArray->GetData(), offset, JSTaggedValue::Undefined());
123    }
124
125    if (HandleLeakTestManager::RegisterSignal() == -1) {
126        perror("sigaction error");
127        exit(1);
128    }
129    HeandleLeakTestCommon(instance, newArray);
130}
131
132HWTEST_F_L0(HandleLeakTest, InitializeCheckMoreProperty)
133{
134    EcmaHandleScope scope(thread);
135    JSHandle<JSHClass> arrayClass(thread->GlobalConstants()->GetHandledArrayClass());
136    static constexpr int SIZE = 100;
137    JSHandle<TaggedArray> newArray(thread, const_cast<Heap *>(instance->GetHeap())->AllocateNonMovableOrHugeObject(
138        *arrayClass, TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), SIZE)));
139
140    newArray->InitializeWithSpecialValue(JSTaggedValue::Hole(), SIZE);
141    size_t failCount = 0;
142    VerifyObjectVisitor verifier(instance->GetHeap(), &failCount);
143    verifier(*newArray);
144    ASSERT_TRUE(newArray.GetTaggedValue().IsTaggedArray());
145    ASSERT_TRUE(failCount == 0);
146}
147}  // namespace panda::test
148