14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#ifndef ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H
174514f5e3Sopenharmony_ci#define ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H
184514f5e3Sopenharmony_ci
194514f5e3Sopenharmony_ci#include "ecmascript/debugger/debugger_api.h"
204514f5e3Sopenharmony_ci#include "ecmascript/debugger/js_debugger_manager.h"
214514f5e3Sopenharmony_ci#include "ecmascript/debugger/js_pt_method.h"
224514f5e3Sopenharmony_ci#include "ecmascript/ecma_vm.h"
234514f5e3Sopenharmony_ci#include "ecmascript/jspandafile/method_literal.h"
244514f5e3Sopenharmony_ci
254514f5e3Sopenharmony_cinamespace panda::ecmascript::tooling {
264514f5e3Sopenharmony_ciclass JSBreakpoint {
274514f5e3Sopenharmony_cipublic:
284514f5e3Sopenharmony_ci    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
294514f5e3Sopenharmony_ci    JSBreakpoint(const std::string &sourceFile, PtMethod *ptMethod, uint32_t bcOffset,
304514f5e3Sopenharmony_ci        const Global<FunctionRef> &condFuncRef) : sourceFile_(sourceFile), ptMethod_(ptMethod),
314514f5e3Sopenharmony_ci        bcOffset_(bcOffset), condFuncRef_(condFuncRef) {}
324514f5e3Sopenharmony_ci    ~JSBreakpoint() = default;
334514f5e3Sopenharmony_ci
344514f5e3Sopenharmony_ci    const std::string &GetSourceFile() const
354514f5e3Sopenharmony_ci    {
364514f5e3Sopenharmony_ci        return sourceFile_;
374514f5e3Sopenharmony_ci    }
384514f5e3Sopenharmony_ci
394514f5e3Sopenharmony_ci    PtMethod *GetPtMethod() const
404514f5e3Sopenharmony_ci    {
414514f5e3Sopenharmony_ci        return ptMethod_;
424514f5e3Sopenharmony_ci    }
434514f5e3Sopenharmony_ci
444514f5e3Sopenharmony_ci    uint32_t GetBytecodeOffset() const
454514f5e3Sopenharmony_ci    {
464514f5e3Sopenharmony_ci        return bcOffset_;
474514f5e3Sopenharmony_ci    }
484514f5e3Sopenharmony_ci
494514f5e3Sopenharmony_ci    bool operator==(const JSBreakpoint &bpoint) const
504514f5e3Sopenharmony_ci    {
514514f5e3Sopenharmony_ci        return bcOffset_ == bpoint.GetBytecodeOffset() &&
524514f5e3Sopenharmony_ci            ptMethod_->GetMethodId() == bpoint.GetPtMethod()->GetMethodId() &&
534514f5e3Sopenharmony_ci            sourceFile_ == bpoint.GetSourceFile() &&
544514f5e3Sopenharmony_ci            ptMethod_->GetJSPandaFile() == bpoint.GetPtMethod()->GetJSPandaFile();
554514f5e3Sopenharmony_ci    }
564514f5e3Sopenharmony_ci
574514f5e3Sopenharmony_ci    std::string ToString() const
584514f5e3Sopenharmony_ci    {
594514f5e3Sopenharmony_ci        std::stringstream breakpoint;
604514f5e3Sopenharmony_ci        breakpoint << "[";
614514f5e3Sopenharmony_ci        breakpoint << "methodId:" << ptMethod_->GetMethodId()  << ", ";
624514f5e3Sopenharmony_ci        breakpoint << "bytecodeOffset:" << bcOffset_ << ", ";
634514f5e3Sopenharmony_ci        breakpoint << "sourceFile:" << "\""<< sourceFile_ << "\""<< ", ";
644514f5e3Sopenharmony_ci        breakpoint << "jsPandaFile:" << "\"" << ptMethod_->GetJSPandaFile()->GetJSPandaFileDesc() << "\"";
654514f5e3Sopenharmony_ci        breakpoint << "]";
664514f5e3Sopenharmony_ci        return breakpoint.str();
674514f5e3Sopenharmony_ci    }
684514f5e3Sopenharmony_ci
694514f5e3Sopenharmony_ci    const Global<FunctionRef> &GetConditionFunction()
704514f5e3Sopenharmony_ci    {
714514f5e3Sopenharmony_ci        return condFuncRef_;
724514f5e3Sopenharmony_ci    }
734514f5e3Sopenharmony_ci
744514f5e3Sopenharmony_ci    DEFAULT_COPY_SEMANTIC(JSBreakpoint);
754514f5e3Sopenharmony_ci    DEFAULT_MOVE_SEMANTIC(JSBreakpoint);
764514f5e3Sopenharmony_ci
774514f5e3Sopenharmony_ciprivate:
784514f5e3Sopenharmony_ci    std::string sourceFile_;
794514f5e3Sopenharmony_ci    PtMethod *ptMethod_ {nullptr};
804514f5e3Sopenharmony_ci    uint32_t bcOffset_;
814514f5e3Sopenharmony_ci    Global<FunctionRef> condFuncRef_;
824514f5e3Sopenharmony_ci};
834514f5e3Sopenharmony_ci
844514f5e3Sopenharmony_ciclass HashJSBreakpoint {
854514f5e3Sopenharmony_cipublic:
864514f5e3Sopenharmony_ci    size_t operator()(const JSBreakpoint &bpoint) const
874514f5e3Sopenharmony_ci    {
884514f5e3Sopenharmony_ci        return (std::hash<std::string>()(bpoint.GetSourceFile())) ^
894514f5e3Sopenharmony_ci            (std::hash<uint32_t>()(bpoint.GetPtMethod()->GetMethodId().GetOffset())) ^
904514f5e3Sopenharmony_ci            (std::hash<uint32_t>()(bpoint.GetBytecodeOffset()));
914514f5e3Sopenharmony_ci    }
924514f5e3Sopenharmony_ci};
934514f5e3Sopenharmony_ci
944514f5e3Sopenharmony_ciclass JSDebugger : public JSDebugInterface, RuntimeListener {
954514f5e3Sopenharmony_cipublic:
964514f5e3Sopenharmony_ci    explicit JSDebugger(const EcmaVM *vm) : ecmaVm_(vm)
974514f5e3Sopenharmony_ci    {
984514f5e3Sopenharmony_ci        notificationMgr_ = ecmaVm_->GetJsDebuggerManager()->GetNotificationManager();
994514f5e3Sopenharmony_ci        notificationMgr_->AddListener(this);
1004514f5e3Sopenharmony_ci    }
1014514f5e3Sopenharmony_ci    ~JSDebugger() override
1024514f5e3Sopenharmony_ci    {
1034514f5e3Sopenharmony_ci        notificationMgr_->RemoveListener(this);
1044514f5e3Sopenharmony_ci    }
1054514f5e3Sopenharmony_ci
1064514f5e3Sopenharmony_ci    void RegisterHooks(PtHooks *hooks) override
1074514f5e3Sopenharmony_ci    {
1084514f5e3Sopenharmony_ci        hooks_ = hooks;
1094514f5e3Sopenharmony_ci        // send vm start event after add hooks
1104514f5e3Sopenharmony_ci        notificationMgr_->VmStartEvent();
1114514f5e3Sopenharmony_ci    }
1124514f5e3Sopenharmony_ci    void UnregisterHooks() override
1134514f5e3Sopenharmony_ci    {
1144514f5e3Sopenharmony_ci        // send vm death event before delete hooks
1154514f5e3Sopenharmony_ci        notificationMgr_->VmDeathEvent();
1164514f5e3Sopenharmony_ci        hooks_ = nullptr;
1174514f5e3Sopenharmony_ci    }
1184514f5e3Sopenharmony_ci    bool HandleDebuggerStmt(JSHandle<Method> method, uint32_t bcOffset) override;
1194514f5e3Sopenharmony_ci    bool SetBreakpoint(const JSPtLocation &location, Local<FunctionRef> condFuncRef) override;
1204514f5e3Sopenharmony_ci    bool SetSmartBreakpoint(const JSPtLocation &location);
1214514f5e3Sopenharmony_ci    bool RemoveBreakpoint(const JSPtLocation &location) override;
1224514f5e3Sopenharmony_ci    void RemoveAllBreakpoints() override;
1234514f5e3Sopenharmony_ci    bool RemoveBreakpointsByUrl(const std::string &url) override;
1244514f5e3Sopenharmony_ci    void BytecodePcChanged(JSThread *thread, JSHandle<Method> method, uint32_t bcOffset) override;
1254514f5e3Sopenharmony_ci    void LoadModule(std::string_view filename, std::string_view entryPoint) override
1264514f5e3Sopenharmony_ci    {
1274514f5e3Sopenharmony_ci        if (hooks_ == nullptr) {
1284514f5e3Sopenharmony_ci            return;
1294514f5e3Sopenharmony_ci        }
1304514f5e3Sopenharmony_ci        hooks_->LoadModule(filename, entryPoint);
1314514f5e3Sopenharmony_ci    }
1324514f5e3Sopenharmony_ci    void VmStart() override
1334514f5e3Sopenharmony_ci    {
1344514f5e3Sopenharmony_ci        if (hooks_ == nullptr) {
1354514f5e3Sopenharmony_ci            return;
1364514f5e3Sopenharmony_ci        }
1374514f5e3Sopenharmony_ci        hooks_->VmStart();
1384514f5e3Sopenharmony_ci    }
1394514f5e3Sopenharmony_ci    void VmDeath() override
1404514f5e3Sopenharmony_ci    {
1414514f5e3Sopenharmony_ci        if (hooks_ == nullptr) {
1424514f5e3Sopenharmony_ci            return;
1434514f5e3Sopenharmony_ci        }
1444514f5e3Sopenharmony_ci        hooks_->VmDeath();
1454514f5e3Sopenharmony_ci    }
1464514f5e3Sopenharmony_ci    void NativeCalling(const void *nativeAddress) override
1474514f5e3Sopenharmony_ci    {
1484514f5e3Sopenharmony_ci        if (hooks_ == nullptr) {
1494514f5e3Sopenharmony_ci            return;
1504514f5e3Sopenharmony_ci        }
1514514f5e3Sopenharmony_ci        hooks_->NativeCalling(nativeAddress);
1524514f5e3Sopenharmony_ci    }
1534514f5e3Sopenharmony_ci    void NativeReturn(const void *nativeAddress) override
1544514f5e3Sopenharmony_ci    {
1554514f5e3Sopenharmony_ci        if (hooks_ == nullptr) {
1564514f5e3Sopenharmony_ci            return;
1574514f5e3Sopenharmony_ci        }
1584514f5e3Sopenharmony_ci        hooks_->NativeReturn(nativeAddress);
1594514f5e3Sopenharmony_ci    }
1604514f5e3Sopenharmony_ci    void MethodEntry(JSHandle<Method> method, JSHandle<JSTaggedValue> envHandle) override;
1614514f5e3Sopenharmony_ci    void MethodExit(JSHandle<Method> method) override;
1624514f5e3Sopenharmony_ci    // used by debugger statement
1634514f5e3Sopenharmony_ci    bool GetSingleStepStatus() const
1644514f5e3Sopenharmony_ci    {
1654514f5e3Sopenharmony_ci        return singleStepOnDebuggerStmt_;
1664514f5e3Sopenharmony_ci    }
1674514f5e3Sopenharmony_ci    void SetSingleStepStatus(bool status)
1684514f5e3Sopenharmony_ci    {
1694514f5e3Sopenharmony_ci        singleStepOnDebuggerStmt_ = status;
1704514f5e3Sopenharmony_ci    }
1714514f5e3Sopenharmony_ciprivate:
1724514f5e3Sopenharmony_ci    std::unique_ptr<PtMethod> FindMethod(const JSPtLocation &location) const;
1734514f5e3Sopenharmony_ci    std::optional<JSBreakpoint> FindBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const;
1744514f5e3Sopenharmony_ci    std::optional<JSBreakpoint> FindSmartBreakpoint(JSHandle<Method> method, uint32_t bcOffset) const;
1754514f5e3Sopenharmony_ci    bool RemoveBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset);
1764514f5e3Sopenharmony_ci    bool RemoveSmartBreakpoint(const std::unique_ptr<PtMethod> &ptMethod, uint32_t bcOffset);
1774514f5e3Sopenharmony_ci    void HandleExceptionThrowEvent(const JSThread *thread, JSHandle<Method> method, uint32_t bcOffset);
1784514f5e3Sopenharmony_ci    bool HandleStep(JSHandle<Method> method, uint32_t bcOffset);
1794514f5e3Sopenharmony_ci    bool HandleNativeOut();
1804514f5e3Sopenharmony_ci    bool HandleBreakpoint(JSHandle<Method> method, uint32_t bcOffset);
1814514f5e3Sopenharmony_ci    void DumpBreakpoints();
1824514f5e3Sopenharmony_ci    bool IsBreakpointCondSatisfied(std::optional<JSBreakpoint> breakpoint) const;
1834514f5e3Sopenharmony_ci
1844514f5e3Sopenharmony_ci    const EcmaVM *ecmaVm_;
1854514f5e3Sopenharmony_ci    PtHooks *hooks_ {nullptr};
1864514f5e3Sopenharmony_ci    NotificationManager *notificationMgr_ {nullptr};
1874514f5e3Sopenharmony_ci    bool singleStepOnDebuggerStmt_ {false};
1884514f5e3Sopenharmony_ci
1894514f5e3Sopenharmony_ci    CUnorderedSet<JSBreakpoint, HashJSBreakpoint> breakpoints_ {};
1904514f5e3Sopenharmony_ci    CUnorderedSet<JSBreakpoint, HashJSBreakpoint> smartBreakpoints_ {};
1914514f5e3Sopenharmony_ci
1924514f5e3Sopenharmony_ci    friend class JsDebuggerFriendTest;
1934514f5e3Sopenharmony_ci};
1944514f5e3Sopenharmony_ci}  // namespace panda::ecmascript::tooling
1954514f5e3Sopenharmony_ci
1964514f5e3Sopenharmony_ci#endif  // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H
197