1/*
2 * Copyright (c) 2023 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#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_DROP_FRAME_TEST_H
17#define ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_DROP_FRAME_TEST_H
18
19#include "test/utils/test_util.h"
20
21namespace panda::ecmascript::tooling::test {
22class JsDropFrameTest : public TestEvents {
23public:
24    JsDropFrameTest()
25    {
26        channel_ = new JsDropFrameTestChannel();
27
28        breakpoint = [this](const JSPtLocation &location) {
29            ASSERT_TRUE(location.GetMethodId().IsValid());
30            ++breakpointCounter_;
31            debugger_->NotifyPaused(location, PauseReason::INSTRUMENTATION);
32            static_cast<JsDropFrameTestChannel *>(channel_)->IncreaseCheckVariableCounter();
33            DropFrameIfChecked();
34            TestUtil::SuspendUntilContinue(DebugEvent::BREAKPOINT, location);
35            return true;
36        };
37
38        singleStep = [this](const JSPtLocation &location) {
39            if (debugger_->NotifySingleStep(location)) {
40                debugger_->NotifyPaused({}, PauseReason::INSTRUMENTATION);
41                static_cast<JsDropFrameTestChannel *>(channel_)->IncreaseCheckVariableCounter();
42                TestUtil::SuspendUntilContinue(DebugEvent::CHECK_COMPLETE);
43                DropFrameIfChecked();
44                TestUtil::SuspendUntilContinue(DebugEvent::DROPFRAME);
45                return true;
46            }
47            return false;
48        };
49
50        loadModule = [this](std::string_view moduleName) {
51            static_cast<JsDropFrameTestChannel *>(channel_)->Initial(vm_, runtime_);
52            runtime_->Enable();
53            size_t breakpoint[POINTER_SIZE][LINE_COLUMN] =
54                {{26, 0}, {42, 0}, {57, 0}, {69, 0}, {87, 0}, {100, 0}, {117, 0},
55                 {143, 0}, {151, 0}, {164, 0}, {172, 0}, {182, 0}, {192, 0}, {206, 0},
56                 {217, 0}, {224, 0}, {230, 0}}; // 0-based
57            SetJSPtLocation(breakpoint[0], POINTER_SIZE, pointerLocations_);
58            InitBreakpointOpQueue();
59            TestUtil::SuspendUntilContinue(DebugEvent::LOAD_MODULE);
60            ASSERT_EQ(moduleName, pandaFile_);
61            ASSERT_TRUE(debugger_->NotifyScriptParsed(0, pandaFile_));
62            auto condFuncRef = FunctionRef::Undefined(vm_);
63            for (auto &iter : pointerLocations_) {
64                auto ret = debugInterface_->SetBreakpoint(iter, condFuncRef);
65                ASSERT_TRUE(ret);
66            }
67            return true;
68        };
69
70        scenario = [this]() {
71            TestUtil::WaitForLoadModule();
72            TestUtil::Continue();
73            for (size_t index = 0; index < POINTER_SIZE; index++) {
74                for (size_t hitCount = 0; hitCount < breakpointHitTimes[index]; hitCount++) {
75                    TestUtil::WaitForBreakpoint(pointerLocations_.at(index));
76                    if (hitCount == breakpointHitTimes[index] - 1) {
77                        auto ret = debugInterface_->RemoveBreakpoint(pointerLocations_.at(index));
78                        ASSERT_TRUE(ret);
79                    }
80                    TestUtil::Continue();
81                    while (dropFrameChecked_) {
82                        dropFrameChecked_ = false;
83                        TestUtil::WaitForCheckComplete();
84                        TestUtil::Continue();
85                        TestUtil::WaitForDropframe();
86                        dropframeCounter_++;
87                        TestUtil::Continue();
88                    }
89                }
90            }
91            ASSERT_EXITED();
92            return true;
93        };
94
95        vmDeath = [this]() {
96            ASSERT_EQ(breakpointCounter_, 59U);  // a total of 59 times hitting breakpoints
97            ASSERT_EQ(dropframeCounter_, 47U); // a total of 47 times dropping frames
98            return true;
99        };
100    }
101
102    std::pair<std::string, std::string> GetEntryPoint() override
103    {
104        return {pandaFile_, entryPoint_};
105    }
106    ~JsDropFrameTest()
107    {
108        delete channel_;
109        channel_ = nullptr;
110    }
111
112private:
113    class JsDropFrameTestChannel : public TestChannel {
114    public:
115        JsDropFrameTestChannel() = default;
116        ~JsDropFrameTestChannel() = default;
117        void Initial(const EcmaVM *vm, RuntimeImpl *runtime)
118        {
119            vm_ = vm;
120            runtime_ = runtime;
121        }
122
123        void SendNotification(const PtBaseEvents &events) override
124        {
125            const static std::vector<std::function<bool(const PtBaseEvents &events)>> eventList = {
126                [](const PtBaseEvents &events) -> bool {
127                    std::string sourceFile = DEBUGGER_JS_DIR "dropframe.js";
128                    auto parsed = static_cast<const ScriptParsed *>(&events);
129                    ASSERT_EQ(parsed->GetName(), "Debugger.scriptParsed");
130                    ASSERT_EQ(parsed->GetUrl(), sourceFile);
131                    return true;
132                },
133                [this](const PtBaseEvents &events) -> bool {
134                    std::cout << checkVariableCounter_ << std::endl;
135                    auto paused = static_cast<const Paused *>(&events);
136                    ASSERT_EQ(paused->GetName(), "Debugger.paused");
137                    auto allFrames = paused->GetCallFrames();
138                    std::map<std::string, std::string> truthVariableMap = variableMap_.at(checkVariableCounter_);
139                    for (size_t index = 0; index < allFrames->size(); index++) {
140                        auto frame = paused->GetCallFrames()->at(index).get();
141                        auto scopes = frame->GetScopeChain();
142                        for (uint32_t i = 0; i < scopes->size(); i++) {
143                            auto scope = scopes->at(i).get();
144                            std::string scopeType = scope->GetType();
145                            if (scopeType != Scope::Type::Local() && scopeType != Scope::Type::Closure()) {
146                                continue;
147                            }
148                            auto localId = scope->GetObject()->GetObjectId();
149                            GetPropertiesParams params;
150                            params.SetObjectId(localId).SetOwnProperties(true);
151                            std::vector<std::unique_ptr<PropertyDescriptor>> outPropertyDesc;
152                            runtime_->GetProperties(params, &outPropertyDesc, {}, {}, {});
153                            for (const auto &property : outPropertyDesc) {
154                                std::cout << "=====================================" << std::endl;
155                                std::string name = property->GetName();
156                                std::cout << name << std::endl;
157                                if (truthVariableMap.count(name)) {
158                                    auto value = property->GetValue();
159                                    if (value->HasValue()) {
160                                        std::string valueStr = value->GetValue()->ToString(vm_)->ToString(vm_);
161                                        std::cout << valueStr << std::endl;
162                                        ASSERT_EQ(valueStr, truthVariableMap[name]);
163                                    }
164                                }
165                            }
166                        }
167                    }
168                    return true;
169                }
170            };
171
172            ASSERT_TRUE(eventList[index_](events));
173            index_ |= 1;
174        }
175
176        void IncreaseCheckVariableCounter()
177        {
178            checkVariableCounter_++;
179        }
180
181    private:
182        NO_COPY_SEMANTIC(JsDropFrameTestChannel);
183        NO_MOVE_SEMANTIC(JsDropFrameTestChannel);
184
185        const std::vector<std::map<std::string, std::string>> variableMap_ = {
186            { { "a", "2" }, { "b", "3" }, { "c", "4" }, {"d", "2"} },
187            { { "a", "2" }, { "b", "3" }, { "c", "3" }, {"d", "1"} },
188            { { "a", "2" }, { "b", "2" }, { "c", "3" }, {"d", "0"} },
189            { { "a", "1" }, { "b", "2" }, { "c", "3" } },
190            { { "a", "2" }, { "b", "3" }, { "c", "4" }, {"d", "2"} },
191            { { "a", "4" }, { "b", "5" }, { "c", "6" }, {"d", "2"} },
192            { { "a", "6" }, { "b", "4" }, { "c", "5" }, {"d", "2"} },
193            { { "a", "6" }, { "b", "4" }, { "c", "5" }, {"d", "1"} },
194            { { "a", "2" }, { "b", "3" }, { "c", "4" } },
195            { { "a", "4" }, { "b", "5" }, { "c", "6" }, {"d", "2"} },
196            { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "1"} },
197            { { "a", "4" }, { "b", "5" }, { "c", "6" }, {"d", "1"} },
198            { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "1"} },
199            { { "a", "16" }, { "b", "29" }, { "c", "2262" }, {"d", "182"} },
200            { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "182"} },
201            { { "a", "4" }, { "b", "5" }, { "c", "6" } },
202            { { "a", "8" }, { "b", "13" }, { "c", "78" }, {"d", "1"} },
203            { { "a", "32" }, { "b", "61" }, { "c", "4214" }, {"d", "1"} },
204            { { "a", "16" }, { "b", "29" }, { "c", "2262" }, {"d", "1"} },
205            { { "a", "32" }, { "b", "61" }, { "c", "4214" }, {"d", "1"} },
206            { { "a", "64" }, { "b", "125" }, { "c", "12214" }, {"d", "29"} },
207            { { "a", "128" }, { "b", "253" }, { "c", "44598" }, {"d", "1769"} },
208            { { "a", "256" }, { "b", "509" }, { "c", "174902" }, {"d", "221125"} },
209            { { "a", "128" }, { "b", "253" }, { "c", "44598" }, {"d", "221125"} },
210            { { "a", "16" }, { "b", "29" }, { "c", "2262" } },
211            { { "a", "32" }, { "b", "61" }, { "c", "4214" }, {"d", "1"} },
212            { { "a", "256" }, { "b", "1277" }, { "c", "176435" } },
213            { { "a", "256" }, { "b", "509" }, { "c", "174902" } },
214            { { "a", "256" }, { "b", "509" }, { "c", "174902" } },
215            { { "a", "256" }, { "b", "1277" }, { "c", "176435" } },
216            { { "a", "256" }, { "b", "2301" }, { "c", "178992" } },
217            { { "a", "512" }, { "b", "3837" }, { "c", "183341" } },
218            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } },
219            { { "a", "512" }, { "b", "3837" }, { "c", "183341" } },
220            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } },
221            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "2"} },
222            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "2"} },
223            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } },
224            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } },
225            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } },
226            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "2"} },
227            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "3"} },
228            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" } },
229            { { "a", "1024" }, { "b", "7933" }, { "c", "192298" }, {"s", "3"} },
230            { { "a", "10" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
231            { { "a", "9" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
232            { { "a", "8" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
233            { { "a", "10" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
234            { { "a", "11" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
235            { { "a", "12" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
236            { { "a", "7" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
237            { { "a", "10" }, { "b", "7933" }, { "c", "192298" }, {"x", "7"} },
238            { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "1"} },
239            { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "1"} },
240            { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "1"} },
241            { { "a", "20" }, { "b", "3" }, { "c", "4" }, {"d", "3"} },
242            { { "a", "19" }, { "b", "2" }, { "c", "3" }, {"d", "2"} },
243            { { "a", "20" }, { "b", "3" }, { "c", "4" }, {"d", "3"} },
244            { { "a", "23" }, { "b", "6" }, { "c", "7" }, {"d", "7"} },
245            { { "a", "20" }, { "b", "3" }, { "c", "4" }, {"d", "4"} },
246            { { "a", "23" }, { "b", "6" }, { "c", "7" }, {"d", "7"} },
247            { { "a", "29" }, { "b", "12" }, { "c", "13" }, {"d", "14"} },
248            { { "a", "39" }, { "b", "22" }, { "c", "23" }, {"d", "25"} },
249            { { "a", "29" }, { "b", "12" }, { "c", "13" }, {"d", "15"} },
250            { { "a", "39" }, { "b", "22" }, { "c", "23" }, {"d", "25"} },
251            { { "a", "54" }, { "b", "37" }, { "c", "38" }, {"d", "41"} },
252            { { "a", "54" }, { "b", "37" }, { "c", "38" }, {"d", "42"} },
253            { { "a", "19" }, { "b", "2" }, { "c", "3" } },
254            { { "a", "54" }, { "b", "37" }, { "c", "38" }, {"d", "42"} },
255            { { "a", "76" }, { "b", "60" }, { "c", "62" }, {"d", "1"}, {"e", "77"} },
256            { { "a", "75" }, { "b", "58" }, { "c", "59" }, {"d", "1"}, {"e", "1"} },
257            { { "a", "75" }, { "b", "58" }, { "c", "59" }, {"d", "1"} },
258            { { "a", "75" }, { "b", "58" }, { "c", "59" } },
259            { { "a", "76" }, { "b", "60" }, { "c", "62" }, {"d", "1"}, {"e", "77"} },
260            { { "a", "81" }, { "b", "65" }, { "c", "67" }, {"d", "5"} },
261            { { "a", "76" }, { "b", "60" }, { "c", "62" }, {"d", "5"} },
262            { { "a", "75" }, { "b", "58" }, { "c", "59" } },
263            { { "a", "81" }, { "b", "65" }, { "c", "67" }, {"d", "5"} },
264            { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "6"} },
265            { { "a", "81" }, { "b", "65" }, { "c", "67" }, {"d", "6"} },
266            { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "6"} },
267            { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "7"}, {"e", "84"} },
268            { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "7"}, {"e", "1"} },
269            { { "a", "87" }, { "b", "71" }, { "c", "73" }, {"d", "7"} },
270            { { "a", "75" }, { "b", "58" }, { "c", "59" } },
271            { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "7"}, {"e", "84"} },
272            { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "11"} },
273            { { "a", "75" }, { "b", "58" }, { "c", "59" } },
274            { { "a", "88" }, { "b", "73" }, { "c", "76" }, {"d", "11"} },
275            { { "a", "6" }, { "b", "13" }, { "c", "3" }, {"d", "11"}, {"e", "11"} },
276            { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"}, {"e", "1"} },
277            { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"} },
278            { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"} },
279            { { "a", "1" }, { "b", "2" }, { "c", "3" } },
280            { { "a", "6" }, { "b", "13" }, { "c", "3" }, {"d", "11"}, {"e", "11"} },
281            { { "a", "7" }, { "b", "14" }, { "c", "14" }, {"d", "11"} },
282            { { "a", "1" }, { "b", "2" }, { "c", "3" }, {"d", "10"} },
283            { { "a", "1" }, { "b", "2" }, { "c", "3" } },
284            { { "a", "7" }, { "b", "14" }, { "c", "14" }, {"d", "11"} },
285            { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "11"} },
286            { { "a", "7" }, { "b", "14" }, { "c", "15" }, {"d", "11"} },
287            { { "a", "1" }, { "b", "2" }, { "c", "3" } },
288            { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "11"} },
289            { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "19"} },
290            { { "a", "1" }, { "b", "2" }, { "c", "3" } },
291            { { "a", "8" }, { "b", "16" }, { "c", "18" }, {"d", "19"} }
292        };
293
294        int32_t index_ {0};
295        const EcmaVM *vm_ {nullptr};
296        RuntimeImpl *runtime_ {nullptr};
297        size_t checkVariableCounter_ {0};
298    };
299
300    enum breakpointOpType {
301        RESUME = 1,
302        DROPLASTFRAME = 2
303    };
304
305    static constexpr size_t LINE_COLUMN = 2;
306    static constexpr size_t POINTER_SIZE = 17;
307
308    std::string pandaFile_ = DEBUGGER_ABC_DIR "dropframe.abc";
309    std::string sourceFile_ = DEBUGGER_JS_DIR "dropframe.js";
310    std::string entryPoint_ = "_GLOBAL::func_main_0";
311    size_t breakpointCounter_ = 0;
312    size_t dropframeCounter_ = 0;
313    bool dropFrameChecked_ = false;
314    std::vector<JSPtLocation> pointerLocations_;
315    size_t breakpointHitTimes[POINTER_SIZE] = {2, 2, 4, 6, 6, 4, 5, 10, 2, 2, 4, 2, 2, 2, 2, 2, 2};
316    std::queue<std::pair<uint8_t, uint8_t>> breakpointOp;
317
318    void InitBreakpointOpQueue()
319    {
320        breakpointOp.push({1, breakpointOpType::DROPLASTFRAME});
321        breakpointOp.push({1, breakpointOpType::DROPLASTFRAME});
322        breakpointOp.push({1, breakpointOpType::DROPLASTFRAME});
323        breakpointOp.push({3, breakpointOpType::DROPLASTFRAME});
324        breakpointOp.push({3, breakpointOpType::DROPLASTFRAME});
325        breakpointOp.push({3, breakpointOpType::DROPLASTFRAME});
326        breakpointOp.push({5, breakpointOpType::DROPLASTFRAME});
327        breakpointOp.push({7, breakpointOpType::DROPLASTFRAME});
328        breakpointOp.push({7, breakpointOpType::DROPLASTFRAME});
329        breakpointOp.push({9, breakpointOpType::DROPLASTFRAME});
330        breakpointOp.push({13, breakpointOpType::DROPLASTFRAME});
331        breakpointOp.push({13, breakpointOpType::DROPLASTFRAME});
332        breakpointOp.push({15, breakpointOpType::DROPLASTFRAME});
333        breakpointOp.push({15, breakpointOpType::DROPLASTFRAME});
334        breakpointOp.push({19, breakpointOpType::DROPLASTFRAME});
335        breakpointOp.push({21, breakpointOpType::DROPLASTFRAME});
336        breakpointOp.push({21, breakpointOpType::DROPLASTFRAME});
337        breakpointOp.push({21, breakpointOpType::DROPLASTFRAME});
338        breakpointOp.push({21, breakpointOpType::DROPLASTFRAME});
339        breakpointOp.push({23, breakpointOpType::DROPLASTFRAME});
340        breakpointOp.push({25, breakpointOpType::DROPLASTFRAME});
341        breakpointOp.push({25, breakpointOpType::DROPLASTFRAME});
342        breakpointOp.push({28, breakpointOpType::DROPLASTFRAME});
343        breakpointOp.push({30, breakpointOpType::DROPLASTFRAME});
344        breakpointOp.push({32, breakpointOpType::DROPLASTFRAME});
345        breakpointOp.push({34, breakpointOpType::DROPLASTFRAME});
346        breakpointOp.push({37, breakpointOpType::DROPLASTFRAME});
347        breakpointOp.push({40, breakpointOpType::DROPLASTFRAME});
348        breakpointOp.push({42, breakpointOpType::DROPLASTFRAME});
349        breakpointOp.push({42, breakpointOpType::DROPLASTFRAME});
350        breakpointOp.push({42, breakpointOpType::DROPLASTFRAME});
351        breakpointOp.push({44, breakpointOpType::DROPLASTFRAME});
352        breakpointOp.push({44, breakpointOpType::DROPLASTFRAME});
353        breakpointOp.push({46, breakpointOpType::DROPLASTFRAME});
354        breakpointOp.push({48, breakpointOpType::DROPLASTFRAME});
355        breakpointOp.push({48, breakpointOpType::DROPLASTFRAME});
356        breakpointOp.push({48, breakpointOpType::DROPLASTFRAME});
357        breakpointOp.push({50, breakpointOpType::DROPLASTFRAME});
358        breakpointOp.push({52, breakpointOpType::DROPLASTFRAME});
359        breakpointOp.push({52, breakpointOpType::DROPLASTFRAME});
360        breakpointOp.push({52, breakpointOpType::DROPLASTFRAME});
361        breakpointOp.push({52, breakpointOpType::DROPLASTFRAME});
362        breakpointOp.push({54, breakpointOpType::DROPLASTFRAME});
363        breakpointOp.push({54, breakpointOpType::DROPLASTFRAME});
364        breakpointOp.push({56, breakpointOpType::DROPLASTFRAME});
365        breakpointOp.push({56, breakpointOpType::DROPLASTFRAME});
366        breakpointOp.push({58, breakpointOpType::DROPLASTFRAME});
367    }
368
369    void DropFrameIfChecked()
370    {
371        while (!dropFrameChecked_ && !breakpointOp.empty() && breakpointOp.front().first == breakpointCounter_) {
372            if (breakpointOp.front().second == breakpointOpType::DROPLASTFRAME) {
373                PtJson paramsJson;
374                paramsJson.Add("droppedDepth", 1U);
375                std::unique_ptr<DropFrameParams> params = DropFrameParams::Create(paramsJson);
376                debugger_->DropFrame(*params);
377                dropFrameChecked_ = true;
378            }
379            breakpointOp.pop();
380        }
381    }
382
383    void SetJSPtLocation(size_t *arr, size_t number, std::vector<JSPtLocation> &locations)
384    {
385        for (size_t i = 0; i < number; i++) {
386            JSPtLocation location = TestUtil::GetLocation(sourceFile_.c_str(), arr[i * LINE_COLUMN],
387                                                          arr[i * LINE_COLUMN + 1], pandaFile_.c_str());
388            locations.push_back(location);
389        }
390    };
391};
392
393std::unique_ptr<TestEvents> GetJsDropFrameTest()
394{
395    return std::make_unique<JsDropFrameTest>();
396}
397}  // namespace panda::ecmascript::tooling::test
398
399#endif  // ECMASCRIPT_TOOLING_TEST_UTILS_TESTCASES_JS_DROP_FRAME_TEST_H
400