diff options
author | chai <chaifix@163.com> | 2021-10-19 02:21:47 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2021-10-19 02:21:47 +0800 |
commit | 8446078851f5430e8315d6618d8d5dd9d6e3d1ab (patch) | |
tree | ecbbd9ae71367eebf3a4e414373f92a3f0a2cd44 | |
parent | 45c05ac5610416e75a123995af649681d43adf7f (diff) |
+LuaPanda
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Editor/EditorMain.cpp | 42 | ||||
-rw-r--r-- | Editor/Path.cpp | 0 | ||||
-rw-r--r-- | Editor/Path.h | 0 | ||||
-rw-r--r-- | Editor/Win/Win.cpp | 18 | ||||
-rw-r--r-- | Editor/Win/Win.h | 14 | ||||
-rw-r--r-- | Projects/VisualStudio/Editor/Editor.vcxproj | 4 | ||||
-rw-r--r-- | Projects/VisualStudio/Editor/Editor.vcxproj.filters | 15 | ||||
-rw-r--r-- | Projects/VisualStudio/Editor/Editor.vcxproj.user | 2 | ||||
-rw-r--r-- | Projects/VisualStudio/GameLab.sln | 11 | ||||
-rw-r--r-- | Projects/VisualStudio/ImGUI/ImGUI.vcxproj | 122 | ||||
-rw-r--r-- | Projects/VisualStudio/ImGUI/ImGUI.vcxproj.filters | 2 | ||||
-rw-r--r-- | Projects/VisualStudio/ImGUI/ImGUI.vcxproj.user | 4 | ||||
-rw-r--r-- | Resources/Libraries/LuaPanda.lua | 3606 | ||||
-rw-r--r-- | Resources/Libraries/socket/core.dll | bin | 0 -> 104448 bytes | |||
-rw-r--r-- | Resources/Scripts/EditorApplication.lua | 6 | ||||
-rw-r--r-- | Resources/readme.txt | 8 |
17 files changed, 3684 insertions, 171 deletions
@@ -2,3 +2,4 @@ /Projects/VisualStudio/.vs /Projects/VisualStudio/x64 /Projects/VisualStudio/*/x64 +/Resources/.vscode
\ No newline at end of file diff --git a/Editor/EditorMain.cpp b/Editor/EditorMain.cpp index f8ee4ef..5e0cf99 100644 --- a/Editor/EditorMain.cpp +++ b/Editor/EditorMain.cpp @@ -1,40 +1,15 @@ #include <windows.h>
#include <vector>
+
#include "GUI/EditorWindows.h"
#include "Runtime/Lua/LuaBind/LuaBind.h"
#include "EditorManager.h"
#include "Runtime/Graphics/OpenGL.h"
#include "Editor/Scripting/EditorScripting.h"
+#include "Editor/Win/Win.h"
using namespace LuaBind;
-static int MainLoop() -{
- BOOL returnValue; - MSG msg, lastMsg; - msg.message = WM_NULL; - std::vector<MSG> messages; - PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE); - bool isQuitSignaled = msg.message == WM_QUIT; -
- while (!isQuitSignaled)
- {
- MSG msg; - while (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) != 0) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - - if (msg.message == WM_QUIT) - isQuitSignaled = true; - - } -
- }
-
- return (INT)msg.wParam; -}
-
void ErrorHandle(cc8* msg)
{
log_error(std::string("[Lua] ") + msg);
@@ -50,7 +25,18 @@ void InitLuaState() log_error("Can't setup scripting.");
}
LuaBind::State state = vm.GetMainState();
- state.DoFile("./Scripts/EditorApplication.lua", ErrorHandle);
+
+ // https://stackoverflow.com/questions/21495901/loadlibrarya-and-relative-path/21495971
+ // ll_load装载dll,路径需要设置搜索路径
+ //log_info(Win::GetCurrentWorkingDirectory());
+ std::string workingDir = Win::GetCurrentWorkingDirectory();
+ Win::SetDllSearchDirectory(workingDir);
+
+ // set cpath
+ state.DoString(R"(package.path=package.path .. ";" .. ".\\Libraries\\?.lua")");
+ state.DoString(R"(package.cpath=package.cpath .. ";" .. ".\\Libraries\\?.dll")");
+
+ state.DoFile(".\\Scripts\\EditorApplication.lua", ErrorHandle);
}
#ifdef GAMELAB_DEBUG
diff --git a/Editor/Path.cpp b/Editor/Path.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Editor/Path.cpp diff --git a/Editor/Path.h b/Editor/Path.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Editor/Path.h diff --git a/Editor/Win/Win.cpp b/Editor/Win/Win.cpp new file mode 100644 index 0000000..f6310e2 --- /dev/null +++ b/Editor/Win/Win.cpp @@ -0,0 +1,18 @@ +#include "win.h" + +namespace Win +{ + + std::string GetCurrentWorkingDirectory() + { + char path[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, path); + return path; + } + + void SetDllSearchDirectory(std::string path) + { + SetDllDirectory(path.c_str()); + } + +}
\ No newline at end of file diff --git a/Editor/Win/Win.h b/Editor/Win/Win.h new file mode 100644 index 0000000..cbbba4c --- /dev/null +++ b/Editor/Win/Win.h @@ -0,0 +1,14 @@ +#pragma once + +#include <windows.h> +#include <string> + +// windows 辅助函数 + +namespace Win +{ + + std::string GetCurrentWorkingDirectory(); + void SetDllSearchDirectory(std::string path); + +} diff --git a/Projects/VisualStudio/Editor/Editor.vcxproj b/Projects/VisualStudio/Editor/Editor.vcxproj index 8dfc5c6..ab1a930 100644 --- a/Projects/VisualStudio/Editor/Editor.vcxproj +++ b/Projects/VisualStudio/Editor/Editor.vcxproj @@ -156,6 +156,7 @@ <ClCompile Include="..\..\..\Editor\GUI\WindowUtil.cpp" />
<ClCompile Include="..\..\..\Editor\GUI\WinUtils.cpp" />
<ClCompile Include="..\..\..\Editor\IMGUI\GUIButton.cpp" />
+ <ClCompile Include="..\..\..\Editor\Path.cpp" />
<ClCompile Include="..\..\..\Editor\Resource\ResourceManager.cpp" />
<ClCompile Include="..\..\..\Editor\Scripting\EditorGUI\ContainerWindow.bind.cpp" />
<ClCompile Include="..\..\..\Editor\Scripting\EditorGUI\EditorGUI.bind.cpp" />
@@ -167,6 +168,7 @@ <ClCompile Include="..\..\..\Editor\Scripting\IMGUI\GUIButton.bind.cpp" />
<ClCompile Include="..\..\..\Editor\Shaders\BuiltinShaders.cpp" />
<ClCompile Include="..\..\..\Editor\Utils\HelperFuncs.cpp" />
+ <ClCompile Include="..\..\..\Editor\Win\Win.cpp" />
<ClCompile Include="..\..\..\Runtime\Debug\Log.cpp" />
<ClCompile Include="..\..\..\Runtime\Graphics\OpenGL.cpp" />
<ClCompile Include="..\..\..\Runtime\Lua\LuaBind\LuaBindCFunctions.cpp" />
@@ -195,10 +197,12 @@ <ClInclude Include="..\..\..\Editor\GUI\MenuManager.h" />
<ClInclude Include="..\..\..\Editor\GUI\Rect.h" />
<ClInclude Include="..\..\..\Editor\GUI\WinUtils.h" />
+ <ClInclude Include="..\..\..\Editor\Path.h" />
<ClInclude Include="..\..\..\Editor\Resource\ResourceManager.h" />
<ClInclude Include="..\..\..\Editor\Scripting\EditorScripting.h" />
<ClInclude Include="..\..\..\Editor\Shaders\BuiltinShaders.h" />
<ClInclude Include="..\..\..\Editor\Utils\HelperFuncs.h" />
+ <ClInclude Include="..\..\..\Editor\Win\Win.h" />
<ClInclude Include="..\..\..\Runtime\Debug\Log.h" />
<ClInclude Include="..\..\..\Runtime\Graphics\OpenGL.h" />
<ClInclude Include="..\..\..\Runtime\Lua\LuaBind\LuaBind.h" />
diff --git a/Projects/VisualStudio/Editor/Editor.vcxproj.filters b/Projects/VisualStudio/Editor/Editor.vcxproj.filters index 5062a9f..df356e4 100644 --- a/Projects/VisualStudio/Editor/Editor.vcxproj.filters +++ b/Projects/VisualStudio/Editor/Editor.vcxproj.filters @@ -73,6 +73,9 @@ <Filter Include="Runtime\Lua\LuaBind">
<UniqueIdentifier>{f9573ff2-4a53-4953-806e-f0ce0c586910}</UniqueIdentifier>
</Filter>
+ <Filter Include="Editor\Win">
+ <UniqueIdentifier>{c2d9fa5b-8087-48e3-90b7-b5e6d02be909}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\Editor\GUI\Dock.cpp">
@@ -204,6 +207,12 @@ <ClCompile Include="..\..\..\Runtime\Lua\LuaHelper.cpp">
<Filter>Runtime\Lua</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\Editor\Win\Win.cpp">
+ <Filter>Editor\Win</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Editor\Path.cpp">
+ <Filter>Editor</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\Editor\GUI\Dock.h">
@@ -335,6 +344,12 @@ <ClInclude Include="..\..\..\Runtime\Lua\LuaHelper.h">
<Filter>Runtime\Lua</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\Editor\Win\Win.h">
+ <Filter>Editor\Win</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Editor\Path.h">
+ <Filter>Editor</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\Runtime\Lua\LuaBind\LuaBindClass.inc">
diff --git a/Projects/VisualStudio/Editor/Editor.vcxproj.user b/Projects/VisualStudio/Editor/Editor.vcxproj.user index f86de5d..d353c78 100644 --- a/Projects/VisualStudio/Editor/Editor.vcxproj.user +++ b/Projects/VisualStudio/Editor/Editor.vcxproj.user @@ -5,6 +5,6 @@ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup>
- <ShowAllFiles>true</ShowAllFiles>
+ <ShowAllFiles>false</ShowAllFiles>
</PropertyGroup>
</Project>
\ No newline at end of file diff --git a/Projects/VisualStudio/GameLab.sln b/Projects/VisualStudio/GameLab.sln index aac6292..27e8383 100644 --- a/Projects/VisualStudio/GameLab.sln +++ b/Projects/VisualStudio/GameLab.sln @@ -23,8 +23,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stb", "stb\stb.vcxproj", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib\zlib.vcxproj", "{49F29C84-8A46-4421-9F93-CA96A9292716}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImGUI", "ImGUI\ImGUI.vcxproj", "{A93844EE-1BF4-42A9-B58C-27192721A063}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -105,14 +103,6 @@ Global {49F29C84-8A46-4421-9F93-CA96A9292716}.Release|x64.Build.0 = Release|x64 {49F29C84-8A46-4421-9F93-CA96A9292716}.Release|x86.ActiveCfg = Release|Win32 {49F29C84-8A46-4421-9F93-CA96A9292716}.Release|x86.Build.0 = Release|Win32 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Debug|x64.ActiveCfg = Debug|x64 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Debug|x64.Build.0 = Debug|x64 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Debug|x86.ActiveCfg = Debug|Win32 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Debug|x86.Build.0 = Debug|Win32 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Release|x64.ActiveCfg = Release|x64 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Release|x64.Build.0 = Release|x64 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Release|x86.ActiveCfg = Release|Win32 - {A93844EE-1BF4-42A9-B58C-27192721A063}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -125,7 +115,6 @@ Global {AD09415F-4BF9-4FCE-901F-7AB22D429CFC} = {0F6EE105-E1FF-4770-8314-06F9F98FB68F} {BFAA8A26-DE6F-4B71-8851-3FF3CF0C8B9F} = {0F6EE105-E1FF-4770-8314-06F9F98FB68F} {49F29C84-8A46-4421-9F93-CA96A9292716} = {0F6EE105-E1FF-4770-8314-06F9F98FB68F} - {A93844EE-1BF4-42A9-B58C-27192721A063} = {0F6EE105-E1FF-4770-8314-06F9F98FB68F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C78D376C-9B0B-4EF0-A7D1-0F612F43E793} diff --git a/Projects/VisualStudio/ImGUI/ImGUI.vcxproj b/Projects/VisualStudio/ImGUI/ImGUI.vcxproj deleted file mode 100644 index 615e945..0000000 --- a/Projects/VisualStudio/ImGUI/ImGUI.vcxproj +++ /dev/null @@ -1,122 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <VCProjectVersion>15.0</VCProjectVersion> - <ProjectGuid>{A93844EE-1BF4-42A9-B58C-27192721A063}</ProjectGuid> - <RootNamespace>ImGUI</RootNamespace> - <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>MultiByte</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup /> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - </ClCompile> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - </ClCompile> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - </ClCompile> - <Link> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - </ClCompile> - <Link> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project>
\ No newline at end of file diff --git a/Projects/VisualStudio/ImGUI/ImGUI.vcxproj.filters b/Projects/VisualStudio/ImGUI/ImGUI.vcxproj.filters deleted file mode 100644 index 9cd8510..0000000 --- a/Projects/VisualStudio/ImGUI/ImGUI.vcxproj.filters +++ /dev/null @@ -1,2 +0,0 @@ -锘<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
\ No newline at end of file diff --git a/Projects/VisualStudio/ImGUI/ImGUI.vcxproj.user b/Projects/VisualStudio/ImGUI/ImGUI.vcxproj.user deleted file mode 100644 index be25078..0000000 --- a/Projects/VisualStudio/ImGUI/ImGUI.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ -锘<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <PropertyGroup /> -</Project>
\ No newline at end of file diff --git a/Resources/Libraries/LuaPanda.lua b/Resources/Libraries/LuaPanda.lua new file mode 100644 index 0000000..c5c307c --- /dev/null +++ b/Resources/Libraries/LuaPanda.lua @@ -0,0 +1,3606 @@ +-- Tencent is pleased to support the open source community by making LuaPanda available. +-- Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. +-- Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at +-- https://opensource.org/licenses/BSD-3-Clause +-- 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. + +-- API: +-- LuaPanda.printToVSCode(logStr, printLevel, type) +-- 鎵撳嵃鏃ュ織鍒癡SCode Output涓 LuaPanda Debugger 涓 +-- @printLevel: debug(0)/info(1)/error(2) 杩欓噷鐨勬棩蹇楃瓑绾ч渶楂樹簬launch.json涓厤缃瓑绾ф棩蹇楁墠鑳借緭鍑 (鍙夊弬鏁帮紝榛樿0) +-- @type(鍙夊弬鏁帮紝榛樿0): 0:VSCode output console 1:VSCode tip 2:VSCode debug console + +-- LuaPanda.BP() +-- 寮哄埗鎵撴柇鐐癸紝鍙互鍦ㄥ崗绋嬩腑浣跨敤銆傚缓璁娇鐢ㄤ互涓嬪啓娉: +-- local ret = LuaPanda and LuaPanda.BP and LuaPanda.BP(); +-- 濡傛灉鎴愬姛鍔犲叆鏂偣ret杩斿洖true锛屽惁鍒欐槸nil + +-- LuaPanda.getInfo() +-- 杩斿洖鑾峰彇璋冭瘯鍣ㄤ俊鎭傚寘鎷増鏈彿锛屾槸鍚︿娇鐢╨ib搴擄紝绯荤粺鏄惁鏀寔loadstring(load鏂规硶)銆傝繑鍥炲肩被鍨媠tring, 鎺ㄨ崘鍦ㄨ皟璇曟帶鍒跺彴涓娇鐢ㄣ + +-- LuaPanda.testBreakpoint() +-- 娴嬭瘯鏂偣锛岀敤浜庡垎鏋愯矾寰勯敊璇鑷存柇鐐规棤娉曞仠姝㈢殑鎯呭喌銆傛祴璇曟柟娉曟槸 +-- 1. launch.json 涓紑鍚 stopOnEntry, 鎴栬呭湪浠g爜涓姞鍏uaPanda.BP()銆 +-- 2. 杩愯璋冭瘯鍣ㄥ拰 lua 杩涚▼锛屽綋鍋滄鍦 stopOnEntry 鎴栬 LuaPanda.BP() 鏃跺湪璋冭瘯鎺у埗鍙拌緭鍏 LuaPanda.testBreakpoint() +-- 3. 鏍规嵁鎻愮ず鏇存柊鏂偣鍚庡啀娆¤緭鍏 LuaPanda.testBreakpoint()銆傛鏃剁郴缁熶細杈撳嚭涓浜涙彁绀猴紝甯姪鐢ㄦ埛鍒嗘瀽鏂偣鍙兘鏃犳硶鍋滄鐨勫師鍥犮 + +-- LuaPanda.doctor() +-- 杩斿洖瀵瑰綋鍓嶇幆澧冪殑璇婃柇淇℃伅锛屾彁绀哄彲鑳藉瓨鍦ㄧ殑闂銆傝繑鍥炲肩被鍨媠tring, 鎺ㄨ崘鍦ㄨ皟璇曟帶鍒跺彴涓娇鐢ㄣ + +-- LuaPanda.getBreaks() +-- 鑾峰彇鏂偣淇℃伅锛屾帹鑽愬湪璋冭瘯鎺у埗鍙颁腑浣跨敤銆 + +-- LuaPanda.serializeTable(table) +-- 鎶妕able搴忓垪鍖栦负瀛楃涓诧紝杩斿洖鍊肩被鍨嬫槸string銆 + +-- LuaPanda.stopAttach() +-- 鏂紑杩炴帴锛屽仠姝ttach锛屾湰娆¤璋冭瘯绋嬪簭杩愯杩囩▼鏃犳硶鍐嶆杩涜attach杩炴帴銆 + +-- 鍏朵粬璇存槑锛 +-- 鍏充簬鐪熸満璋冭瘯锛岄娆′娇鐢ㄧ湡鏈鸿皟璇曟椂瑕佹敞鎰忎笅鏂"鐢ㄦ埛璁剧疆椤"涓殑閰嶇疆 +-- 1. 纭畾 attach 寮鍏虫墦寮: openAttachMode = true; 杩欐牱鍙互閬垮厤鍏堝惎鍔ㄦ墜鏈篴pp涔嬪悗鍚姩璋冭瘯鍣ㄦ棤娉曡繛鎺ャ +-- 2. 鎶婅繛鎺ユ椂闂存斁闀: connectTimeoutSec 璁剧疆涓 0.5 鎴栬 1銆傞娆″皾璇曠湡鏈鸿皟璇曟椂杩欎釜鍊煎彲浠ヨ缃ぇ涓鐐癸紝涔嬪悗鍐嶆牴鎹嚜宸辩殑缃戠粶鐘跺喌鍚戜笅璋冩暣銆 +-- 璋冭瘯鏂规硶鍙互鍙傝 github 鏂囨。 + +--鐢ㄦ埛璁剧疆椤 +local openAttachMode = true; --鏄惁寮鍚痑ttach妯″紡銆俛ttach妯″紡寮鍚悗鍙互鍦ㄤ换鎰忔椂鍒诲惎鍔╲scode杩炴帴璋冭瘯銆傜己鐐规槸娌℃湁杩炴帴璋冭瘯鏃朵篃浼氱暐闄嶄綆lua鎵ц鏁堢巼(浼氫笉鏂繘琛宎ttach璇锋眰) +local attachInterval = 1; --attach闂撮殧鏃堕棿(s) +local connectTimeoutSec = 0.005; --lua杩涚▼浣滀负Client鏃, 杩炴帴瓒呮椂鏃堕棿, 鍗曚綅s. 鏃堕棿杩囬暱绛夊緟attach鏃朵細閫犳垚鍗¢】锛屾椂闂磋繃鐭彲鑳芥棤娉曡繛鎺ャ傚缓璁0.005 - 0.05 +local listeningTimeoutSec = 0.5; -- lua杩涚▼浣滀负Server鏃,杩炴帴瓒呮椂鏃堕棿, 鍗曚綅s. 鏃堕棿杩囬暱绛夊緟attach鏃朵細閫犳垚鍗¢】锛屾椂闂磋繃鐭彲鑳芥棤娉曡繛鎺ャ傚缓璁0.1 - 1 +local userDotInRequire = true; --鍏煎require涓娇鐢 require(a.b) 鍜 require(a/b) 鐨勫舰寮忓紩鐢ㄦ枃浠跺す涓殑鏂囦欢锛岄粯璁ゆ棤闇淇敼 +local traversalUserData = false; --濡傛灉鍙互鐨勮瘽(鍙栧喅浜巙serdata鍘熻〃涓殑__pairs)锛屽睍绀簎serdata涓殑鍏冪礌銆 濡傛灉鍦ㄨ皟璇曞櫒涓睍寮userdata鏃舵湁閿欒锛岃鍏抽棴姝ら」. +local customGetSocketInstance = nil; --鏀寔鐢ㄦ埛瀹炵幇涓涓嚜瀹氫箟璋冪敤luasocket鐨勫嚱鏁帮紝鍑芥暟杩斿洖鍊煎繀椤绘槸涓涓猻ocket瀹炰緥銆備緥: function() return require("socket.core").tcp() end; +local consoleLogLevel = 2; --鎵撳嵃鍦ㄦ帶鍒跺彴(print)鐨勬棩蹇楃瓑绾 0 : all/ 1: info/ 2: error. +--鐢ㄦ埛璁剧疆椤笶ND + +local debuggerVer = "3.2.0"; --debugger鐗堟湰鍙 +LuaPanda = {}; +local this = LuaPanda; +local tools = {}; --寮曠敤鐨勫紑婧愬伐鍏凤紝鍖呮嫭json瑙f瀽鍜宼able灞曞紑宸ュ叿绛 +this.tools = tools; +this.curStackId = 0; +--json澶勭悊 +local json; +--hook鐘舵佸垪琛 +local hookState = { + DISCONNECT_HOOK = 0, --鏂紑杩炴帴 + LITE_HOOK = 1, --鍏ㄥ眬鏃犳柇鐐 + MID_HOOK = 2, --鍏ㄥ眬鏈夋柇鐐癸紝鏈枃浠舵棤鏂偣 + ALL_HOOK = 3, --鏈枃浠舵湁鏂偣 +}; +--杩愯鐘舵佸垪琛 +local runState = { + DISCONNECT = 0, --鏈繛鎺 + WAIT_CMD = 1, --宸茶繛鎺ワ紝绛夊緟鍛戒护 + STOP_ON_ENTRY = 2, --鍒濆鐘舵 + RUN = 3, + STEPOVER = 4, + STEPIN = 5, + STEPOUT = 6, + STEPOVER_STOP = 7, + STEPIN_STOP = 8, + STEPOUT_STOP = 9, + HIT_BREAKPOINT = 10 +}; + +local TCPSplitChar = "|*|"; --json鍗忚鍒嗛殧绗︼紝璇蜂笉瑕佷慨鏀 +local MAX_TIMEOUT_SEC = 3600 * 24; --缃戠粶鏈澶ц秴鏃剁瓑寰呮椂闂 +--褰撳墠杩愯鐘舵 +local currentRunState; +local currentHookState; +--鏂偣淇℃伅 +local breaks = {}; --淇濆瓨鏂偣鐨勬暟缁 +this.breaks = breaks; --渚沨ookLib璋冪敤 +local recCallbackId = ""; +--VSCode绔紶杩囨潵鐨勯厤缃紝鍦╒SCode绔殑launch閰嶇疆锛屼紶杩囨潵骞惰祴鍊 +local luaFileExtension = ""; --vscode浼犺繃鏉ョ殑鑴氭湰鍚庣紑 +local cwd = ""; --宸ヤ綔璺緞 +local DebuggerFileName = ""; --Debugger鏂囦欢鍚(鍘熷,鏈粡path澶勭悊), 鍑芥暟涓細鑷姩鑾峰彇 +local DebuggerToolsName = ""; +local lastRunFunction = {}; --涓婁竴涓墽琛岃繃鐨勫嚱鏁般傚湪鏈変簺澶嶆潅鍦烘櫙涓(find,getcomponent)涓琛屼細鎸轰袱娆 +local currentCallStack = {}; --鑾峰彇褰撳墠璋冪敤鍫嗘爤淇℃伅 +local hitBP = false; --BP()涓殑寮哄埗鏂偣鍛戒腑鏍囪 +local TempFilePath_luaString = ""; --VSCode绔厤缃殑涓存椂鏂囦欢瀛樻斁璺緞 +local recordHost; --璁板綍杩炴帴绔疘P +local recordPort; --璁板綍杩炴帴绔彛鍙 +local sock; --lua socket 鏂囦欢鎻忚堪绗 +local server; --server 鎻忚堪绗 +local OSType; --VSCode璇嗗埆鍑虹殑绯荤粺绫诲瀷锛屼篃鍙互鑷璁剧疆銆俉indows_NT | Linux | Darwin +local clibPath; --chook搴撳湪VScode绔殑璺緞锛屼篃鍙嚜琛岃缃 +local hookLib; --chook搴撶殑寮曠敤瀹炰緥 +local adapterVer; --VScode浼犳潵鐨刟dapter鐗堟湰鍙 +local truncatedOPath; --VScode涓敤鎴疯缃殑鐢ㄤ簬鎴柇opath璺緞鐨勬爣蹇楋紝娉ㄦ剰杩欓噷鍙互鎺ュ彈lua榄旀硶瀛楃 +local distinguishSameNameFile = false; --鏄惁鍖哄垎lua鍚屽悕鏂囦欢涓殑鏂偣锛屽湪VScode launch.json 涓 distinguishSameNameFile 鎺у埗 +--鏍囪浣 +local logLevel = 1; --鏃ュ織绛夌骇all/info/error. 姝よ缃搴旂殑鏄疺SCode绔缃殑鏃ュ織绛夌骇. +local variableRefIdx = 1; --鍙橀噺绱㈠紩 +local variableRefTab = {}; --鍙橀噺璁板綍table +local lastRunFilePath = ""; --鏈鍚庢墽琛岀殑鏂囦欢璺緞 +local pathCaseSensitivity = true; --璺緞鏄惁鍙戝ぇ灏忓啓鏁忔劅锛岃繖涓夐」鎺ユ敹VScode璁剧疆锛岃鍕垮湪姝ゅ鏇存敼 +local recvMsgQueue = {}; --鎺ユ敹鐨勬秷鎭槦鍒 +local coroutinePool = setmetatable({}, {__mode = "v"}); --淇濆瓨鐢ㄦ埛鍗忕▼鐨勯槦鍒 +local winDiskSymbolUpper = false;--璁剧疆win涓嬬洏绗︾殑澶у皬鍐欍備互姝ょ‘淇濅粠VSCode涓紶鍏ョ殑鏂偣璺緞,cwd鍜屼粠lua铏氭嫙鏈鸿幏寰楃殑鏂囦欢璺緞鐩樼澶у皬鍐欎竴鑷 +local isNeedB64EncodeStr = false;-- 璁板綍鏄惁浣跨敤base64缂栫爜瀛楃涓 +local loadclibErrReason = 'launch.json鏂囦欢鐨勯厤缃」useCHook琚缃负false.'; +local OSTypeErrTip = ""; +local pathErrTip = "" +local winDiskSymbolTip = ""; +local isAbsolutePath = false; +local stopOnEntry; --鐢ㄦ埛鍦╒SCode绔缃殑鏄惁鎵撳紑stopOnEntry +local userSetUseClib; --鐢ㄦ埛鍦╒SCode绔缃殑鏄惁鏄敤clib搴 +local autoPathMode = false; +local autoExt; --璋冭瘯鍣ㄥ惎鍔ㄦ椂鑷姩鑾峰彇鍒扮殑鍚庣紑, 鐢ㄤ簬妫娴媗ua铏氭嫙鏈鸿繑鍥炵殑璺緞鏄惁甯︽湁鏂囦欢鍚庣紑銆備粬鍙互鏄┖鍊兼垨鑰".lua"绛 +local luaProcessAsServer; +local testBreakpointFlag = false; -- 娴嬭瘯鏂偣鐨勬爣蹇椾綅銆傜粨鍚 LuaPanda.testBreakpoint() 娴嬭瘯鏂偣鏃犳硶鍋滄鐨勫師鍥 +--Step鎺у埗鏍囪浣 +local stepOverCounter = 0; --STEPOVER over璁℃暟鍣 +local stepOutCounter = 0; --STEPOVER out璁℃暟鍣 +local HOOK_LEVEL = 3; --璋冪敤鏍堝亸绉婚噺锛屼娇鐢╟lib鏃朵负3锛宭ua涓笉鍐嶄娇鐢ㄦ鍙橀噺锛岃屾槸閫氳繃鍑芥暟getSpecificFunctionStackLevel鑾峰彇 +local isUseLoadstring = 0; +local debugger_loadString; +--涓存椂鍙橀噺 +local recordBreakPointPath; --璁板綍鏈鍚庝竴涓猍鍙兘鍛戒腑]鐨勬柇鐐癸紝鐢ㄤ簬getInfo浠ュ強doctor鐨勬柇鐐规祴璇 +local coroutineCreate; --鐢ㄦ潵璁板綍lua鍘熷鐨刢oroutine.create鍑芥暟 +local stopConnectTime = 0; --鐢ㄦ潵涓存椂璁板綍stop鏂紑杩炴帴鐨勬椂闂 +local isInMainThread; +local receiveMsgTimer = 0; +local isUserSetClibPath = false; --鐢ㄦ埛鏄惁鍦ㄦ湰鏂囦欢涓嚜璁句簡clib璺緞 +local hitBpTwiceCheck; -- 鍛戒腑鏂偣鐨刅scode鏍¢獙缁撴灉锛岄粯璁rue (true鏄懡涓紝false鏄湭鍛戒腑) +local formatPathCache = {}; -- getinfo -> format +function this.formatPathCache() return formatPathCache; end +local fakeBreakPointCache = {}; --鍏朵腑鐢 璺緞-{琛屽彿鍒楄〃} 褰㈠紡淇濆瓨閿欒鍛戒腑淇℃伅 +function this.fakeBreakPointCache() return fakeBreakPointCache; end +--5.1/5.3鍏煎 +if _VERSION == "Lua 5.1" then + debugger_loadString = loadstring; +else + debugger_loadString = load; +end + +--鐢ㄦ埛鍦ㄦ帶鍒跺彴杈撳叆淇℃伅鐨勭幆澧冨彉閲 +local env = setmetatable({ }, { + __index = function( _ , varName ) + local ret = this.getWatchedVariable( varName, _G.LuaPanda.curStackId , false); + return ret; + end, + + __newindex = function( _ , varName, newValue ) + this.setVariableValue( varName, _G.LuaPanda.curStackId, newValue); + end +}); + +----------------------------------------------------------------------------- +-- 娴佺▼ +----------------------------------------------------------------------------- + +---this.bindServer 褰搇ua杩涚▼浣滀负Server鏃讹紝server缁戝畾鍑芥暟 +--- server 鍦╞ind鏃跺垱寤, 杩炴帴鎴愬姛鍚庡叧闂璴isten , disconnect鏃剁疆绌恒俽econnect鏃朵細鏌ヨserver锛屾病鏈夌殑璇濋噸鏂扮粦瀹氾紝濡傛灉宸插瓨鍦ㄧ洿鎺ccept +function this.bindServer(host, port) + server = sock + server:settimeout(listeningTimeoutSec); + assert(server:bind(host, port)); + server:setoption("reuseaddr", true); --闃叉宸茶繛鎺ョ姸鎬佷笅鏂扮殑杩炴帴杩涘叆锛屼笉鍐峳euse + assert(server:listen(0)); +end + +-- 浠ua浣滀负鏈嶅姟绔殑褰㈠紡鍚姩璋冭瘯鍣 +-- @host 缁戝畾ip , 榛樿 0.0.0.0 +-- @port 缁戝畾port, 榛樿 8818 +function this.startServer(host, port) + host = tostring(host or "0.0.0.0") ; + port = tonumber(port) or 8818; + luaProcessAsServer = true; + this.printToConsole("Debugger start as SERVER. bind host:" .. host .. " port:".. tostring(port), 1); + if sock ~= nil then + this.printToConsole("[Warning] 璋冭瘯鍣ㄥ凡缁忓惎鍔紝璇蜂笉瑕佸啀娆¤皟鐢╯tart()" , 1); + return; + end + + --灏濊瘯鍒濇杩炴帴 + this.changeRunState(runState.DISCONNECT); + if not this.reGetSock() then + this.printToConsole("[Error] LuaPanda debugger start success , but get Socket fail , please install luasocket!", 2); + return; + end + recordHost = host; + recordPort = port; + + this.bindServer(recordHost, recordPort); + local connectSuccess = server:accept(); + sock = connectSuccess; + + if connectSuccess then + this.printToConsole("First connect success!"); + this.connectSuccess(); + else + this.printToConsole("First connect failed!"); + this.changeHookState(hookState.DISCONNECT_HOOK); + end +end + +-- 鍚姩璋冭瘯鍣 +-- @host adapter绔痠p, 榛樿127.0.0.1 +-- @port adapter绔痯ort ,榛樿8818 +function this.start(host, port) + host = tostring(host or "127.0.0.1") ; + port = tonumber(port) or 8818; + this.printToConsole("Debugger start as CLIENT. connect host:" .. host .. " port:".. tostring(port), 1); + if sock ~= nil then + this.printToConsole("[Warning] 璋冭瘯鍣ㄥ凡缁忓惎鍔紝璇蜂笉瑕佸啀娆¤皟鐢╯tart()" , 1); + return; + end + + --灏濊瘯鍒濇杩炴帴 + this.changeRunState(runState.DISCONNECT); + if not this.reGetSock() then + this.printToConsole("[Error] Start debugger but get Socket fail , please install luasocket!", 2); + return; + end + recordHost = host; + recordPort = port; + + sock:settimeout(connectTimeoutSec); + local connectSuccess = this.sockConnect(sock); + + if connectSuccess then + this.printToConsole("First connect success!"); + this.connectSuccess(); + else + this.printToConsole("First connect failed!"); + this.changeHookState(hookState.DISCONNECT_HOOK); + end +end + +function this.sockConnect(sock) + if sock then + local connectSuccess, status = sock:connect(recordHost, recordPort); + if status == "connection refused" or (not connectSuccess and status == "already connected") then + this.reGetSock(); + end + + return connectSuccess + end + return nil; +end + +-- 杩炴帴鎴愬姛锛屽紑濮嬪垵濮嬪寲 +function this.connectSuccess() + if server then + server:close(); -- 鍋滄listen + end + + this.changeRunState(runState.WAIT_CMD); + this.printToConsole("connectSuccess", 1); + --璁剧疆鍒濆鐘舵 + local ret = this.debugger_wait_msg(); + + --鑾峰彇debugger鏂囦欢璺緞 + if DebuggerFileName == "" then + local info = debug.getinfo(1, "S") + for k,v in pairs(info) do + if k == "source" then + DebuggerFileName = tostring(v); + -- 浠庝唬鐮佷腑鍘诲悗缂 + autoExt = DebuggerFileName:gsub('.*[Ll][Uu][Aa][Pp][Aa][Nn][Dd][Aa]', ''); + + if hookLib ~= nil then + hookLib.sync_debugger_path(DebuggerFileName); + end + end + end + end + if DebuggerToolsName == "" then + DebuggerToolsName = tools.getFileSource(); + if hookLib ~= nil then + hookLib.sync_tools_path(DebuggerToolsName); + end + end + + if ret == false then + this.printToVSCode("[debugger error]鍒濆鍖栨湭瀹屾垚, 寤虹珛杩炴帴浣嗘帴鏀跺垵濮嬪寲娑堟伅澶辫触銆傝鏇存崲绔彛閲嶈瘯", 2); + return; + end + this.printToVSCode("debugger init success", 1); + + this.changeHookState(hookState.ALL_HOOK); + if hookLib == nil then + --鍗忕▼璋冭瘯 + this.changeCoroutinesHookState(); + end + +end + +--閲嶇疆鏁版嵁 +function this.clearData() + OSType = nil; + clibPath = nil; + -- reset breaks + breaks = {}; + formatPathCache = {}; + fakeBreakPointCache = {}; + this.breaks = breaks; + if hookLib ~= nil then + hookLib.sync_breakpoints(); --娓呯┖鏂偣淇℃伅 + hookLib.clear_pathcache(); --娓呯┖璺緞缂撳瓨 + end +end + +-- 鏈杩炴帴杩囩▼涓仠姝ttach ,浠ユ彁楂樿繍琛屾晥鐜 +function this.stopAttach() + openAttachMode = false; + this.printToConsole("Debugger stopAttach", 1); + this.clearData() + this.changeHookState( hookState.DISCONNECT_HOOK ); + stopConnectTime = os.time(); + this.changeRunState(runState.DISCONNECT); + if sock ~= nil then + sock:close(); + if luaProcessAsServer and server then server = nil; end; + end +end + +--鏂紑杩炴帴 +function this.disconnect() + this.printToConsole("Debugger disconnect", 1); + this.clearData() + this.changeHookState( hookState.DISCONNECT_HOOK ); + stopConnectTime = os.time(); + this.changeRunState(runState.DISCONNECT); + + if sock ~= nil then + sock:close(); + sock = nil; + server = nil; + end + + if recordHost == nil or recordPort == nil then + --寮傚父鎯呭喌澶勭悊, 鍦ㄨ皟鐢↙uaPanda.start()鍓嶉鍏堣皟鐢ㄤ簡LuaPanda.disconnect() + this.printToConsole("[Warning] User call LuaPanda.disconnect() before set debug ip & port, please call LuaPanda.start() first!", 2); + return; + end + + this.reGetSock(); +end + +function this.replaceCoroutineFuncs() + if hookLib == nil then + if coroutineCreate == nil and type(coroutine.create) == "function" then + this.printToConsole("change coroutine.create"); + coroutineCreate = coroutine.create; + coroutine.create = function(...) + local co = coroutineCreate(...) + table.insert(coroutinePool, co); + --杩愯鐘舵佷笅锛屽垱寤哄崗绋嬪嵆鍚姩hook + this.changeCoroutineHookState(co, currentHookState); + return co; + end + end + end +end + +----------------------------------------------------------------------------- +-- 璋冭瘯鍣ㄩ氱敤鏂规硶 +----------------------------------------------------------------------------- +-- 杩斿洖鏂偣淇℃伅 +function this.getBreaks() + return breaks; +end + +---testBreakpoint 娴嬭瘯鏂偣 +function this.testBreakpoint() + if recordBreakPointPath and recordBreakPointPath ~= "" then + -- testBreakpointFlag = false; + return this.breakpointTestInfo(); + else + local strTable = {}; + strTable[#strTable + 1] = "姝e湪鍑嗗杩涜鏂偣娴嬭瘯锛岃鎸夌収濡備笅姝ラ鎿嶄綔\n" + strTable[#strTable + 1] = "1. 璇穂鍒犻櫎]褰撳墠椤圭洰涓墍鏈夋柇鐐;\n" + strTable[#strTable + 1] = "2. 鍦ㄥ綋鍓嶅仠姝㈣鎵撲竴涓柇鐐;\n" + strTable[#strTable + 1] = "3. 鍐嶆杩愯 'LuaPanda.testBreakpoint()'" + testBreakpointFlag = true; + + return table.concat(strTable); + end +end + +-- 杩斿洖璺緞鐩稿叧淇℃伅 +-- cwd:閰嶇疆鐨勫伐绋嬭矾寰 | info["source"]:閫氳繃 debug.getinfo 鑾峰緱鎵ц鏂囦欢鐨勮矾寰 | format锛氭牸寮忓寲鍚庣殑鏂囦欢璺緞 +function this.breakpointTestInfo() + local ly = this.getSpecificFunctionStackLevel(lastRunFunction.func); + if type(ly) ~= "number" then + ly = 2; + end + local runSource = lastRunFunction["source"]; + if runSource == nil and hookLib ~= nil then + runSource = this.getPath(tostring(hookLib.get_last_source())); + end + local info = debug.getinfo(ly, "S"); + local NormalizedPath = this.formatOpath(info["source"]); + NormalizedPath = this.truncatedPath(NormalizedPath, truncatedOPath); + + local strTable = {} + local FormatedPath = tostring(runSource); + strTable[#strTable + 1] = "\n- BreakPoint Test:" + strTable[#strTable + 1] = "\nUser set lua extension: ." .. tostring(luaFileExtension); + strTable[#strTable + 1] = "\nAuto get lua extension: " .. tostring(autoExt); + if truncatedOPath and truncatedOPath ~= '' then + strTable[#strTable + 1] = "\nUser set truncatedOPath: " .. truncatedOPath; + end + strTable[#strTable + 1] = "\nGetInfo: ".. info["source"]; + strTable[#strTable + 1] = "\nNormalized: " .. NormalizedPath; + strTable[#strTable + 1] = "\nFormated: " .. FormatedPath; + if recordBreakPointPath and recordBreakPointPath ~= "" then + strTable[#strTable + 1] = "\nBreakpoint: " .. recordBreakPointPath; + end + + if not autoPathMode then + if isAbsolutePath then + strTable[#strTable + 1] = "\n璇存槑:浠巐ua铏氭嫙鏈鸿幏鍙栧埌鐨勬槸缁濆璺緞锛孎ormated浣跨敤GetInfo璺緞銆" .. winDiskSymbolTip; + else + strTable[#strTable + 1] = "\n璇存槑:浠巐ua铏氭嫙鏈鸿幏鍙栧埌鐨勮矾寰(GetInfo)鏄浉瀵硅矾寰勶紝璋冭瘯鍣ㄨ繍琛屼緷璧栫殑缁濆璺緞(Formated)鏄潵婧愪簬cwd+GetInfo鎷兼帴銆傚Formated璺緞閿欒璇峰皾璇曡皟鏁碿wd鎴栨敼鍙榁SCode鎵撳紑鏂囦欢澶圭殑浣嶇疆銆備篃鍙互鍦‵ormated瀵瑰簲鐨勬枃浠朵笅鎵撲竴涓柇鐐癸紝璋冩暣鐩村埌Formated鍜孊reaks Info涓柇鐐硅矾寰勫畬鍏ㄤ竴鑷淬" .. winDiskSymbolTip; + end + else + strTable[#strTable + 1] = "\n璇存槑:鑷姩璺緞(autoPathMode)妯″紡宸插紑鍚"; + if recordBreakPointPath and recordBreakPointPath ~= "" then + if string.find(recordBreakPointPath , FormatedPath, (-1) * string.len(FormatedPath) , true) then + -- 鐭矾寰勬柇鐐瑰懡涓 + if distinguishSameNameFile == false then + strTable[#strTable + 1] = "鏈枃浠朵腑鏂偣鍙甯稿懡涓" + strTable[#strTable + 1] = "鍚屽悕鏂囦欢涓殑鏂偣璇嗗埆(distinguishSameNameFile) 鏈紑鍚紝璇风‘淇 VSCode 鏂偣涓嶈瀛樺湪浜庡悓鍚 lua 鏂囦欢涓"; + else + strTable[#strTable + 1] = "鍚屽悕鏂囦欢涓殑鏂偣璇嗗埆(distinguishSameNameFile) 宸插紑鍚"; + if string.find(recordBreakPointPath, NormalizedPath, 1, true) then + strTable[#strTable + 1] = "鏈枃浠朵腑鏂偣鍙姝e父鍛戒腑" + else + strTable[#strTable + 1] = "鏂偣鍙兘鏃犳硶琚懡涓紝鍥犱负 lua 铏氭嫙鏈轰腑鑾峰緱鐨勮矾寰 Normalized 涓嶆槸鏂偣璺緞 Breakpoint 鐨勫瓙涓层 濡傛湁闇瑕侊紝鍙互鍦 launch.json 涓缃 truncatedOPath 鏉ュ幓闄 Normalized 閮ㄥ垎璺緞銆" + end + end + else + strTable[#strTable + 1] = "鏂偣鏈鍛戒腑锛屽師鍥犳槸 Formated 涓嶆槸 Breakpoint 璺緞鐨勫瓙涓诧紝鎴栬 Formated 鍜 Breakpoint 鏂囦欢鍚庣紑涓嶄竴鑷" + end + else + strTable[#strTable + 1] = "濡傛灉瑕佽繘琛屾柇鐐规祴璇曪紝璇蜂娇鐢 LuaPanda.testBreakpoint()銆" + end + end + return table.concat(strTable) +end + +--杩斿洖鐗堟湰鍙风瓑閰嶇疆 +function this.getBaseInfo() + local strTable = {}; + local jitVer = ""; + if jit and jit.version then + jitVer = "," .. tostring(jit.version); + end + + strTable[#strTable + 1] = "Lua Ver:" .. _VERSION .. jitVer .." | Adapter Ver:" .. tostring(adapterVer) .. " | Debugger Ver:" .. tostring(debuggerVer); + local moreInfoStr = ""; + if hookLib ~= nil then + local clibVer, forluaVer = hookLib.sync_getLibVersion(); + local clibStr = forluaVer ~= nil and tostring(clibVer) .. " for " .. tostring(math.ceil(forluaVer)) or tostring(clibVer); + strTable[#strTable + 1] = " | hookLib Ver:" .. clibStr; + moreInfoStr = moreInfoStr .. "璇存槑: 宸插姞杞 libpdebug 搴."; + else + moreInfoStr = moreInfoStr .. "璇存槑: 鏈兘鍔犺浇 libpdebug 搴撱傚師鍥犺浣跨敤 LuaPanda.doctor() 鏌ョ湅"; + end + + local outputIsUseLoadstring = false + if type(isUseLoadstring) == "number" and isUseLoadstring == 1 then + outputIsUseLoadstring = true; + end + + strTable[#strTable + 1] = " | supportREPL:".. tostring(outputIsUseLoadstring); + strTable[#strTable + 1] = " | useBase64EncodeString:".. tostring(isNeedB64EncodeStr); + strTable[#strTable + 1] = " | codeEnv:" .. tostring(OSType); + strTable[#strTable + 1] = " | distinguishSameNameFile:" .. tostring(distinguishSameNameFile) .. '\n'; + + strTable[#strTable + 1] = moreInfoStr; + if OSTypeErrTip ~= nil and OSTypeErrTip ~= '' then + strTable[#strTable + 1] = '\n' ..OSTypeErrTip; + end + return table.concat(strTable); +end + +--鑷姩璇婃柇褰撳墠鐜鐨勯敊璇紝骞惰緭鍑轰俊鎭 +function this.doctor() + local strTable = {}; + if debuggerVer ~= adapterVer then + strTable[#strTable + 1] = "\n- 寤鸿鏇存柊鐗堟湰\nLuaPanda VSCode鎻掍欢鐗堟湰鏄" .. adapterVer .. ", LuaPanda.lua鏂囦欢鐗堟湰鏄" .. debuggerVer .. "銆傚缓璁鏌ュ苟鏇存柊鍒版渶鏂扮増鏈"; + strTable[#strTable + 1] = "\n鏇存柊鏂瑰紡 : https://github.com/Tencent/LuaPanda/blob/master/Docs/Manual/update.md"; + strTable[#strTable + 1] = "\nRelease鐗堟湰: https://github.com/Tencent/LuaPanda/releases"; + end + --plibdebug + if hookLib == nil then + strTable[#strTable + 1] = "\n\n- libpdebug 搴撴病鏈夊姞杞絓n"; + if userSetUseClib then + --鐢ㄦ埛鍏佽浣跨敤clib鎻掍欢 + if isUserSetClibPath == true then + --鐢ㄦ埛鑷浜哻lib鍦板潃 + strTable[#strTable + 1] = "鐢ㄦ埛浣跨敤 LuaPanda.lua 涓 clibPath 鍙橀噺鎸囧畾浜 plibdebug 鐨勪綅缃: " .. clibPath; + if this.tryRequireClib("libpdebug", clibPath) then + strTable[#strTable + 1] = "\n寮曠敤鎴愬姛"; + else + strTable[#strTable + 1] = "\n寮曠敤閿欒:" .. loadclibErrReason; + end + else + --浣跨敤榛樿clib鍦板潃 + local clibExt, platform; + if OSType == "Darwin" then clibExt = "/?.so;"; platform = "mac"; + elseif OSType == "Linux" then clibExt = "/?.so;"; platform = "linux"; + else clibExt = "/?.dll;"; platform = "win"; end + local lua_ver; + if _VERSION == "Lua 5.1" then + lua_ver = "501"; + else + lua_ver = "503"; + end + local x86Path = clibPath .. platform .."/x86/".. lua_ver .. clibExt; + local x64Path = clibPath .. platform .."/x86_64/".. lua_ver .. clibExt; + + strTable[#strTable + 1] = "灏濊瘯寮曠敤x64搴: ".. x64Path; + if this.tryRequireClib("libpdebug", x64Path) then + strTable[#strTable + 1] = "\n寮曠敤鎴愬姛"; + else + strTable[#strTable + 1] = "\n寮曠敤閿欒:" .. loadclibErrReason; + strTable[#strTable + 1] = "\n灏濊瘯寮曠敤x86搴: ".. x86Path; + if this.tryRequireClib("libpdebug", x86Path) then + strTable[#strTable + 1] = "\n寮曠敤鎴愬姛"; + else + strTable[#strTable + 1] = "\n寮曠敤閿欒:" .. loadclibErrReason; + end + end + end + else + strTable[#strTable + 1] = "鍘熷洜鏄" .. loadclibErrReason; + end + end + + --path + --灏濊瘯鐩存帴璇诲綋鍓峠etinfo鎸囧悜鐨勬枃浠讹紝鐪嬭兘鍚︽壘鍒般傚鏋滆兘锛屾彁绀烘纭紝濡傛灉鎵句笉鍒帮紝缁欏嚭鎻愮ず锛屽缓璁帺瀹跺湪杩欎釜鏂囦欢涓墦涓涓柇鐐 + --妫鏌ユ柇鐐癸紝鏂囦欢鍜屽綋鍓嶆枃浠剁殑涓嶅悓锛岀粰鍑哄缓璁 + local runSource = lastRunFilePath; + if hookLib ~= nil then + runSource = this.getPath(tostring(hookLib.get_last_source())); + end + + -- 鍦ㄧ簿纭矾寰勬ā寮忎笅鐨勮矾寰勯敊璇娴 + if not autoPathMode and runSource and runSource ~= "" then + -- 璇绘枃浠 + local isFileExist = this.fileExists(runSource); + if not isFileExist then + strTable[#strTable + 1] = "\n\n- 璺緞瀛樺湪闂\n"; + --瑙f瀽璺緞锛屽緱鍒版枃浠跺悕锛屽埌鏂偣璺緞涓煡杩欎釜鏂囦欢鍚 + local pathArray = this.stringSplit(runSource, '/'); + --濡傛灉pathArray鍜屾柇鐐硅兘鍖归厤涓 + local fileMatch= false; + for key, _ in pairs(this.getBreaks()) do + if string.find(key, pathArray[#pathArray], 1, true) then + --鍜屾柇鐐瑰尮閰嶄簡 + fileMatch = true; + -- retStr = retStr .. "\n璇峰姣斿涓嬭矾寰:\n"; + strTable[#strTable + 1] = this.breakpointTestInfo(); + strTable[#strTable + 1] = "\nfilepath: " .. key; + if isAbsolutePath then + strTable[#strTable + 1] = "\n璇存槑:浠巐ua铏氭嫙鏈鸿幏鍙栧埌鐨勬槸缁濆璺緞锛宖ormat浣跨敤getinfo璺緞銆"; + else + strTable[#strTable + 1] = "\n璇存槑:浠巐ua铏氭嫙鏈鸿幏鍙栧埌鐨勬槸鐩稿璺緞锛岃皟璇曞櫒杩愯渚濊禆鐨勭粷瀵硅矾寰(format)鏄潵婧愪簬cwd+getinfo鎷兼帴銆"; + end + strTable[#strTable + 1] = "\nfilepath鏄疺SCode閫氳繃鑾峰彇鍒扮殑鏂囦欢姝g‘璺緞 , 瀵规瘮format鍜宖ilepath锛岃皟鏁磍aunch.json涓瑿WD锛屾垨鏀瑰彉VSCode鎵撳紑鏂囦欢澶圭殑浣嶇疆銆備娇format鍜宖ilepath涓鑷村嵆鍙俓n濡傛灉format鍜宖ilepath璺緞浠呭ぇ灏忓啓涓嶄竴鑷达紝璁剧疆launch.json涓 pathCaseSensitivity:false 鍙拷鐣ヨ矾寰勫ぇ灏忓啓"; + end + end + + if fileMatch == false then + --鏈兘鍜屾柇鐐瑰尮閰 + strTable[#strTable + 1] = "\n鎵句笉鍒版枃浠:" .. runSource .. ", 璇锋鏌ヨ矾寰勬槸鍚︽纭俓n鎴栬呭湪VSCode鏂囦欢" .. pathArray[#pathArray] .. "涓墦涓涓柇鐐瑰悗锛屽啀鎵ц涓娆octor鍛戒护锛屾煡鐪嬭矾寰勫垎鏋愮粨鏋溿"; + end + end + end + + --鏃ュ織绛夌骇瀵规ц兘鐨勫奖鍝 + if logLevel < 1 or consoleLogLevel < 1 then + strTable[#strTable + 1] = "\n\n- 鏃ュ織绛夌骇\n"; + if logLevel < 1 then + strTable[#strTable + 1] = "褰撳墠鏃ュ織绛夌骇鏄" .. logLevel .. ", 浼氫骇鐢熷ぇ閲忔棩蹇楋紝闄嶄綆璋冭瘯閫熷害銆傚缓璁皟鏁磍aunch.json涓璴ogLevel:1"; + end + if consoleLogLevel < 1 then + strTable[#strTable + 1] = "褰撳墠console鏃ュ織绛夌骇鏄" .. consoleLogLevel .. ", 杩囦綆鐨勬棩蹇楃瓑绾т細闄嶄綆璋冭瘯閫熷害锛屽缓璁皟鏁碙uaPanda.lua鏂囦欢澶撮儴consoleLogLevel=2"; + end + end + + if #strTable == 0 then + strTable[#strTable + 1] = "鏈娴嬪嚭闂"; + end + return table.concat(strTable); +end + +function this.fileExists(path) + local f=io.open(path,"r"); + if f~= nil then io.close(f) return true else return false end + end + +--杩斿洖涓浜涗俊鎭紝甯姪鐢ㄦ埛瀹氫綅闂 +function this.getInfo() + --鐢ㄦ埛璁剧疆椤 + local strTable = {}; + strTable[#strTable + 1] = "\n- Base Info: \n"; + strTable[#strTable + 1] = this.getBaseInfo(); + --宸茬粡鍔犺浇C搴擄紝x86/64 鏈兘鍔犺浇锛屽師鍥 + strTable[#strTable + 1] = "\n\n- User Setting: \n"; + strTable[#strTable + 1] = "stopOnEntry:" .. tostring(stopOnEntry) .. ' | '; + -- strTable[#strTable + 1] = "luaFileExtension:" .. luaFileExtension .. ' | '; + strTable[#strTable + 1] = "logLevel:" .. logLevel .. ' | ' ; + strTable[#strTable + 1] = "consoleLogLevel:" .. consoleLogLevel .. ' | '; + strTable[#strTable + 1] = "pathCaseSensitivity:" .. tostring(pathCaseSensitivity) .. ' | '; + strTable[#strTable + 1] = "attachMode:".. tostring(openAttachMode).. ' | '; + strTable[#strTable + 1] = "autoPathMode:".. tostring(autoPathMode).. ' | '; + + if userSetUseClib then + strTable[#strTable + 1] = "useCHook:true"; + else + strTable[#strTable + 1] = "useCHook:false"; + end + + if logLevel == 0 or consoleLogLevel == 0 then + strTable[#strTable + 1] = "\n璇存槑:鏃ュ織绛夌骇杩囦綆锛屼細褰卞搷鎵ц鏁堢巼銆傝璋冩暣logLevel鍜宑onsoleLogLevel鍊 >= 1"; + end + + strTable[#strTable + 1] = "\n\n- Path Info: \n"; + strTable[#strTable + 1] = "clibPath: " .. tostring(clibPath) .. '\n'; + strTable[#strTable + 1] = "debugger: " .. DebuggerFileName .. " | " .. this.getPath(DebuggerFileName) .. '\n'; + strTable[#strTable + 1] = "cwd : " .. cwd .. '\n'; + strTable[#strTable + 1] = this.breakpointTestInfo(); + + if pathErrTip ~= nil and pathErrTip ~= '' then + strTable[#strTable + 1] = '\n' .. pathErrTip; + end + + strTable[#strTable + 1] = "\n\n- Breaks Info: \nUse 'LuaPanda.getBreaks()' to watch."; + return table.concat(strTable); +end + +--鍒ゆ柇鏄惁鍦ㄥ崗绋嬩腑 +function this.isInMain() + return isInMainThread; +end + +--娣诲姞璺緞锛屽皾璇曞紩鐢ㄥ簱銆傚畬鎴愬悗鎶奵path杩樺師锛岃繑鍥炲紩鐢ㄧ粨鏋渢rue/false +-- @libName 搴撳悕 +-- path lib鐨刢path璺緞 +function this.tryRequireClib(libName , libPath) + this.printToVSCode("tryRequireClib search : [" .. libName .. "] in "..libPath); + local savedCpath = package.cpath; + package.cpath = package.cpath .. ';' .. libPath; + this.printToVSCode("package.cpath:" .. package.cpath); + local status, err = pcall(function() hookLib = require(libName) end); + if status then + if type(hookLib) == "table" and this.getTableMemberNum(hookLib) > 0 then + this.printToVSCode("tryRequireClib success : [" .. libName .. "] in "..libPath); + package.cpath = savedCpath; + return true; + else + loadclibErrReason = "tryRequireClib fail : require success, but member function num <= 0; [" .. libName .. "] in "..libPath; + this.printToVSCode(loadclibErrReason); + hookLib = nil; + package.cpath = savedCpath; + return false; + end + else + -- 姝ゅ鑰冭檻鍒皌ryRequireClib浼氳璋冪敤涓ゆ锛屾棩蹇楃骇鍒缃负0锛岄槻姝㈣緭鍑轰笉蹇呰鐨勪俊鎭 + loadclibErrReason = err; + this.printToVSCode("[Require clib error]: " .. err, 0); + end + package.cpath = savedCpath; + return false +end +------------------------瀛楃涓插鐞------------------------- +-- 鍊掑簭鏌ユ壘瀛楃涓 a.b/c鏌ユ壘/ , 杩斿洖4 +-- @str 琚煡鎵剧殑闀夸覆 +-- @subPattern 鏌ユ壘鐨勫瓙涓, 涔熷彲浠ユ槸pattern +-- @plain plane text / pattern +-- @return 鏈壘鍒扮洰鏍囦覆杩斿洖nil. 鍚﹀垯杩斿洖鍊掑簭鎵惧埌鐨勫瓧涓蹭綅缃 +function this.revFindString(str, subPattern, plain) + local revStr = string.reverse(str); + local _, idx = string.find(revStr, subPattern, 1, plain); + if idx == nil then return nil end; + return string.len(revStr) - idx + 1; +end + +-- 鍙嶅簭瑁佸壀瀛楃涓 濡:print(subString("a.b/c", "/"))杈撳嚭c +-- @return 鏈壘鍒扮洰鏍囦覆杩斿洖nil. 鍚﹀垯杩斿洖琚鍓悗鐨勫瓧绗︿覆 +function this.revSubString(str, subStr, plain) + local idx = this.revFindString(str, subStr, plain) + if idx == nil then return nil end; + return string.sub(str, idx + 1, str.length) +end + +-- 鎶婂瓧绗︿覆鎸塺eps鍒嗗壊鎴愬苟鏀惧叆table +-- @str 鐩爣涓 +-- @reps 鍒嗗壊绗︺傛敞鎰忚繖涓垎闅旂鏄竴涓猵attern +function this.stringSplit( str, separator ) + local retStrTable = {} + string.gsub(str, '[^' .. separator ..']+', function ( word ) + table.insert(retStrTable, word) + end) + return retStrTable; +end + +-- 淇濆瓨CallbackId(閫氫俊搴忓垪鍙) +function this.setCallbackId( id ) + if id ~= nil and id ~= "0" then + recCallbackId = tostring(id); + end +end + +-- 璇诲彇CallbackId(閫氫俊搴忓垪鍙)銆傝鍙栧悗璁板綍鍊煎皢琚疆绌 +function this.getCallbackId() + if recCallbackId == nil then + recCallbackId = "0"; + end + local id = recCallbackId; + recCallbackId = "0"; + return id; +end + +-- reference from https://www.lua.org/pil/20.1.html +function this.trim (s) + return (string.gsub(s, "^%s*(.-)%s*$", "%1")) +end + +--杩斿洖table涓垚鍛樻暟閲(鏁板瓧key鍜岄潪鏁板瓧key涔嬪拰) +-- @t 鐩爣table +-- @return 鍏冪礌鏁伴噺 +function this.getTableMemberNum(t) + local retNum = 0; + if type(t) ~= "table" then + this.printToVSCode("[debugger Error] getTableMemberNum get "..tostring(type(t)), 2) + return retNum; + end + for k,v in pairs(t) do + retNum = retNum + 1; + end + return retNum; +end + +-- 鐢熸垚涓涓秷鎭疶able +function this.getMsgTable(cmd ,callbackId) + callbackId = callbackId or 0; + local msgTable = {}; + msgTable["cmd"] = cmd; + msgTable["callbackId"] = callbackId; + msgTable["info"] = {}; + return msgTable; +end + +function this.serializeTable(tab, name) + local sTable = tools.serializeTable(tab, name); + return sTable; +end +------------------------鏃ュ織鎵撳嵃鐩稿叧------------------------- +-- 鎶婃棩蹇楁墦鍗板湪VSCode绔 +-- @str: 鏃ュ織鍐呭 +-- @printLevel: all(0)/info(1)/error(2) +-- @type: 0:vscode console 1:vscode tip +function this.printToVSCode(str, printLevel, type) + type = type or 0; + printLevel = printLevel or 0; + if currentRunState == runState.DISCONNECT or logLevel > printLevel then + return; + end + + local sendTab = {}; + sendTab["callbackId"] = "0"; + if type == 0 then + sendTab["cmd"] = "output"; + elseif type == 1 then + sendTab["cmd"] = "tip"; + else -- type == 2 + sendTab["cmd"] = "debug_console"; + end + sendTab["info"] = {}; + sendTab["info"]["logInfo"] = tostring(str); + this.sendMsg(sendTab); +end + +-- 鎶婃棩蹇楁墦鍗板湪鎺у埗鍙 +-- @str: 鏃ュ織鍐呭 +-- @printLevel: all(0)/info(1)/error(2) +function this.printToConsole(str, printLevel) + printLevel = printLevel or 0; + if consoleLogLevel > printLevel then + return; + end + print("[LuaPanda] ".. tostring(str)); +end + +----------------------------------------------------------------------------- +-- 鎻愬崌鍏煎鎬ф柟娉 +----------------------------------------------------------------------------- +--鐢熸垚骞冲彴鏃犲叧鐨勮矾寰勩 +--return:nil(error)/path +function this.genUnifiedPath(path) + if path == "" or path == nil then + return ""; + end + --澶у皬鍐欎笉鏁忔劅鏃讹紝璺緞鍏ㄩ儴杞负灏忓啓 + if pathCaseSensitivity == false then + path = string.lower(path); + end + --缁熶竴璺緞鍏ㄩ儴鏇挎崲鎴/ + path = string.gsub(path, [[\]], "/"); + --澶勭悊 /../ /./ + local pathTab = this.stringSplit(path, '/'); + local newPathTab = {}; + for k, v in ipairs(pathTab) do + if v == '.' then + --continue + elseif v == ".." and #newPathTab >= 1 and newPathTab[#newPathTab]:sub(2,2) ~= ':' then + --newPathTab鏈夊厓绱狅紝鏈鍚庝竴椤逛笉鏄疿: + table.remove(newPathTab); + else + table.insert(newPathTab, v); + end + end + --閲嶆柊鎷煎悎鍚庡鏋滄槸mac璺緞绗竴浣嶆槸/ + local newpath = table.concat(newPathTab, '/'); + if path:sub(1,1) == '/' then + newpath = '/'.. newpath; + end + + --win涓嬫寜鐓inDiskSymbolUpper鐨勮缃慨鏀圭洏绗﹀ぇ灏 + if "Windows_NT" == OSType then + if winDiskSymbolUpper then + newpath = newpath:gsub("^%a:", string.upper); + winDiskSymbolTip = "璺緞涓璚indows鐩樼宸茶浆涓哄ぇ鍐欍" + else + newpath = newpath:gsub("^%a:", string.lower); + winDiskSymbolTip = "璺緞涓璚indows鐩樼宸茶浆涓哄皬鍐欍" + end + end + + return newpath; +end + +function this.getCacheFormatPath(source) + if source == nil then return formatPathCache end; + return formatPathCache[source]; +end + +function this.setCacheFormatPath(source, dest) + formatPathCache[source] = dest; +end + +-- 澶勭悊 opath(info.source) 鐨勫嚱鏁, 鐢熸垚涓涓鑼冪殑璺緞鍑芥暟(鍜孷Scode绔痗heckRightPath閫昏緫瀹屽叏涓鑷) +function this.formatOpath(opath) + -- delete @ + if opath:sub(1,1) == '@' then + opath = opath:sub(2); + end + -- change ./ to / + if opath:sub(1,2) == './' then + opath = opath:sub(2); + end + + opath = this.genUnifiedPath(opath); + + -- lower + if pathCaseSensitivity == false then + opath = string.lower(opath); + end + --鎶奻ilename鍘婚櫎鍚庣紑 + if autoExt == nil or autoExt == '' then + -- 鍦ㄨ櫄鎷熸満杩斿洖璺緞娌℃湁鍚庣紑鐨勬儏鍐典笅锛岀敤鎴峰繀椤昏嚜璁惧悗缂 + -- 纭畾filePath涓渶鍚庝竴涓.xxx 涓嶇瓑浜庣敤鎴烽厤缃殑鍚庣紑, 鍒欐妸鎵鏈夌殑. 杞负 / + if not opath:find(luaFileExtension , (-1) * luaFileExtension:len(), true) then + -- getinfo 璺緞娌℃湁鍚庣紑锛屾妸 . 鍏ㄩ儴鏇挎崲鎴 / 锛屾垜浠笉鍏佽鐢ㄦ埛鍦ㄦ枃浠讹紙鎴栨枃浠跺す锛夊悕绉颁腑鍑虹幇"." , 鍥犱负鏃犳硶鍖哄垎 + opath = string.gsub(opath, "%.", "/"); + else + -- 鏈夊悗缂锛岄偅涔堟妸闄ゅ悗缂澶栫殑閮ㄥ垎涓殑. 杞负 / + opath = this.changePotToSep(opath, luaFileExtension); + end + else + -- 铏氭嫙鏈鸿矾寰勬湁鍚庣紑 + opath = this.changePotToSep(opath, autoExt); + end + + -- 鎴彇 璺緞+鏂囦欢鍚 (涓嶅甫鍚庣紑) + -- change pot to / + -- opath = string.gsub(opath, "%.", "/"); + return opath; +end + +----------------------------------------------------------------------------- +-- 鍐呭瓨鐩稿叧 +----------------------------------------------------------------------------- +function this.sendLuaMemory() + local luaMem = collectgarbage("count"); + local sendTab = {}; + sendTab["callbackId"] = "0"; + sendTab["cmd"] = "refreshLuaMemory"; + sendTab["info"] = {}; + sendTab["info"]["memInfo"] = tostring(luaMem); + this.sendMsg(sendTab); +end + +----------------------------------------------------------------------------- +-- 缃戠粶鐩稿叧鏂规硶 +----------------------------------------------------------------------------- +-- 鍒锋柊socket +-- @return true/false 鍒锋柊鎴愬姛/澶辫触 +function this.reGetSock() + if server then return true end + + if sock ~= nil then + pcall(function() sock:close() end); + end + + --call slua-unreal luasocket + sock = lua_extension and lua_extension.luasocket and lua_extension.luasocket().tcp(); + if sock == nil then + --call normal luasocket + if pcall(function() sock = require("socket.core").tcp(); end) then + this.printToConsole("reGetSock success"); + else + --call custom function to get socket + if customGetSocketInstance and pcall( function() sock = customGetSocketInstance(); end ) then + this.printToConsole("reGetSock custom success"); + else + this.printToConsole("[Error] reGetSock fail", 2); + return false; + end + end + else + --set ue4 luasocket + this.printToConsole("reGetSock ue4 success"); + end + return true; +end + +-- 瀹氭椂(浠ュ嚱鏁皉eturn涓烘椂鏈) 杩涜attach杩炴帴 +-- 杩斿洖鍊 hook 鍙互缁х画寰涓嬭蛋鏃惰繑鍥1 锛屾棤闇缁х画鏃惰繑鍥0 +function this.reConnect() + if currentHookState == hookState.DISCONNECT_HOOK then + if os.time() - stopConnectTime < attachInterval then + -- 鏈埌閲嶈繛鏃堕棿 + -- this.printToConsole("Reconnect time less than 1s"); + -- this.printToConsole("os.time:".. os.time() .. " | stopConnectTime:" ..stopConnectTime); + return 0; + end + this.printToConsole("Reconnect !"); + if sock == nil then + this.reGetSock(); + end + + local connectSuccess; + if luaProcessAsServer == true and currentRunState == runState.DISCONNECT then + -- 鍦 Server 妯″紡涓嬶紝浠ュ強褰撳墠澶勪簬鏈繛鎺ョ姸鎬佷笅锛屾墠灏濊瘯accept鏂伴摼鎺ャ傚鏋滀笉鍒ゆ柇鍙兘浼氬嚭鐜板娆¤繛鎺ワ紝瀵艰嚧sock琚鐩 + if server == nil then + this.bindServer(recordHost, recordPort); + end + + sock = server:accept(); + connectSuccess = sock; + else + sock:settimeout(connectTimeoutSec); + connectSuccess = this.sockConnect(sock); + end + + if connectSuccess then + this.printToConsole("reconnect success"); + this.connectSuccess(); + return 1; + else + this.printToConsole("reconnect failed" ); + stopConnectTime = os.time(); + return 0; + end + end + -- 涓嶅繀閲嶈繛锛屾甯哥户缁繍琛 + return 1; +end + +-- 鍚慳dapter鍙戞秷鎭 +-- @sendTab 娑堟伅浣搕able +function this.sendMsg( sendTab ) + if isNeedB64EncodeStr and sendTab["info"] ~= nil then + for _, v in ipairs(sendTab["info"]) do + if v["type"] == "string" then + v["value"] = tools.base64encode(v["value"]) + end + end + end + + local sendStr = json.encode(sendTab); + if currentRunState == runState.DISCONNECT then + this.printToConsole("[debugger error] disconnect but want sendMsg:" .. sendStr, 2); + this.disconnect(); + return; + end + + local succ,err; + if pcall(function() succ,err = sock:send(sendStr..TCPSplitChar.."\n"); end) then + if succ == nil then + if err == "closed" then + this.disconnect(); + end + end + end +end + +-- 澶勭悊 鏀跺埌鐨勬秷鎭 +-- @dataStr 鎺ユ敹鐨勬秷鎭痡son +function this.dataProcess( dataStr ) + this.printToVSCode("debugger get:"..dataStr); + local dataTable = json.decode(dataStr); + if dataTable == nil then + this.printToVSCode("[error] Json is error", 2); + return; + end + + if dataTable.callbackId ~= "0" then + this.setCallbackId(dataTable.callbackId); + end + + if dataTable.cmd == "continue" then + local info = dataTable.info; + if info.isFakeHit == "true" and info.fakeBKPath and info.fakeBKLine then + -- 璁剧疆鏍¢獙缁撴灉鏍囧織浣嶏紝浠ヤ究hook娴佺▼鐭ラ亾缁撴灉 + hitBpTwiceCheck = false; + if hookLib ~= nil and hookLib.set_bp_twice_check_res then + -- 鎶婄粨鏋滃悓姝ョ粰C + hookLib.set_bp_twice_check_res(0); + end + -- 鎶婂亣鏂偣鐨勪俊鎭姞鍏ache + if nil == fakeBreakPointCache[info.fakeBKPath] then + fakeBreakPointCache[info.fakeBKPath] = {}; + end + table.insert(fakeBreakPointCache[info.fakeBKPath] ,info.fakeBKLine); + else + this.changeRunState(runState.RUN); + end + local msgTab = this.getMsgTable("continue", this.getCallbackId()); + this.sendMsg(msgTab); + + elseif dataTable.cmd == "stopOnStep" then + this.changeRunState(runState.STEPOVER); + local msgTab = this.getMsgTable("stopOnStep", this.getCallbackId()); + this.sendMsg(msgTab); + this.changeHookState(hookState.ALL_HOOK); + + elseif dataTable.cmd == "stopOnStepIn" then + this.changeRunState(runState.STEPIN); + local msgTab = this.getMsgTable("stopOnStepIn", this.getCallbackId()); + this.sendMsg(msgTab); + this.changeHookState(hookState.ALL_HOOK); + + elseif dataTable.cmd == "stopOnStepOut" then + this.changeRunState(runState.STEPOUT); + local msgTab = this.getMsgTable("stopOnStepOut", this.getCallbackId()); + this.sendMsg(msgTab); + this.changeHookState(hookState.ALL_HOOK); + + elseif dataTable.cmd == "setBreakPoint" then + this.printToVSCode("dataTable.cmd == setBreakPoint"); + -- 璁剧疆鏂偣鏃讹紝鎶 fakeBreakPointCache 娓呯┖銆傝繖鏄竴涓畝鍗曠殑鍋氭硶锛屼篃鍙互娓呴櫎鍏蜂綋鐨勬潯鐩 + fakeBreakPointCache = {} + local bkPath = dataTable.info.path; + bkPath = this.genUnifiedPath(bkPath); + if testBreakpointFlag then + recordBreakPointPath = bkPath; + end + if autoPathMode then + -- 鑷姩璺緞妯″紡涓嬶紝浠呬繚鐣欐枃浠跺悕 + -- table[鏂囦欢鍚.鍚庣紑] -- [fullpath] -- [line , type] + -- | - [fullpath] -- [line , type] + + local bkShortPath = this.getFilenameFromPath(bkPath); + if breaks[bkShortPath] == nil then + breaks[bkShortPath] = {}; + end + breaks[bkShortPath][bkPath] = dataTable.info.bks; + -- 褰搗涓虹┖鏃讹紝浠庢柇鐐瑰垪琛ㄤ腑鍘婚櫎鏂囦欢 + for k, v in pairs(breaks[bkShortPath]) do + if next(v) == nil then + breaks[bkShortPath][k] = nil; + end + end + else + if breaks[bkPath] == nil then + breaks[bkPath] = {}; + end + -- 涓ょ骇 bk path 鏄负浜嗗拰鑷姩璺緞妯″紡缁撴瀯淇濇寔涓鑷 + breaks[bkPath][bkPath] = dataTable.info.bks; + -- 褰搗涓虹┖鏃讹紝浠庢柇鐐瑰垪琛ㄤ腑鍘婚櫎鏂囦欢 + for k, v in pairs(breaks[bkPath]) do + if next(v) == nil then + breaks[bkPath][k] = nil; + end + end + end + + -- 褰搗涓虹┖鏃讹紝浠庢柇鐐瑰垪琛ㄤ腑鍘婚櫎鏂囦欢 + for k, v in pairs(breaks) do + if next(v) == nil then + breaks[k] = nil; + end + end + + --sync breaks to c + if hookLib ~= nil then + hookLib.sync_breakpoints(); + end + + if currentRunState ~= runState.WAIT_CMD then + if hookLib == nil then + local fileBP, G_BP =this.checkHasBreakpoint(lastRunFilePath); + if fileBP == false then + if G_BP == true then + this.changeHookState(hookState.MID_HOOK); + else + this.changeHookState(hookState.LITE_HOOK); + end + else + this.changeHookState(hookState.ALL_HOOK); + end + end + else + local msgTab = this.getMsgTable("setBreakPoint", this.getCallbackId()); + this.sendMsg(msgTab); + return; + end + --鍏朵粬鏃舵満鏀跺埌breaks娑堟伅 + local msgTab = this.getMsgTable("setBreakPoint", this.getCallbackId()); + this.sendMsg(msgTab); + -- 鎵撳嵃璋冭瘯淇℃伅 + this.printToVSCode("LuaPanda.getInfo()\n" .. this.getInfo()) + this.debugger_wait_msg(); + elseif dataTable.cmd == "setVariable" then + if currentRunState == runState.STOP_ON_ENTRY or + currentRunState == runState.HIT_BREAKPOINT or + currentRunState == runState.STEPOVER_STOP or + currentRunState == runState.STEPIN_STOP or + currentRunState == runState.STEPOUT_STOP then + local msgTab = this.getMsgTable("setVariable", this.getCallbackId()); + local varRefNum = tonumber(dataTable.info.varRef); + local newValue = tostring(dataTable.info.newValue); + local needFindVariable = true; --濡傛灉鍙橀噺鏄熀纭绫诲瀷锛岀洿鎺ヨ祴鍊硷紝needFindVariable = false; 濡傛灉鍙橀噺鏄紩鐢ㄧ被鍨嬶紝needFindVariable = true + local varName = tostring(dataTable.info.varName); + -- 鏍规嵁棣栨湯鍚湁" ' 鍒ゆ柇 newValue 鏄惁鏄瓧绗︿覆 + local first_chr = string.sub(newValue, 1, 1); + local end_chr = string.sub(newValue, -1, -1); + if first_chr == end_chr then + if first_chr == "'" or first_chr == '"' then + newValue = string.sub(newValue, 2, -2); + needFindVariable = false; + end + end + --鏁板瓧锛宯il锛宖alse锛宼rue鐨勫鐞 + if newValue == "nil" and needFindVariable == true then newValue = nil; needFindVariable = false; + elseif newValue == "true" and needFindVariable == true then newValue = true; needFindVariable = false; + elseif newValue == "false" and needFindVariable == true then newValue = false; needFindVariable = false; + elseif tonumber(newValue) and needFindVariable == true then newValue = tonumber(newValue); needFindVariable = false; + end + + -- 濡傛灉鏂板兼槸鍩虹绫诲瀷锛屽垯涓嶉渶閬嶅巻 + if dataTable.info.stackId ~= nil and tonumber(dataTable.info.stackId) ~= nil and tonumber(dataTable.info.stackId) > 1 then + this.curStackId = tonumber(dataTable.info.stackId); + else + this.printToVSCode("鏈兘鑾峰彇鍒板爢鏍堝眰绾э紝榛樿浣跨敤 this.curStackId;") + end + + if varRefNum < 10000 then + -- 濡傛灉淇敼鐨勬槸涓涓 寮曠敤鍙橀噺锛岄偅涔堝彲鐩存帴璧嬪笺備絾杩樻槸瑕佽蛋鍙橀噺鏌ヨ杩囩▼銆傛煡鎵惧拰璧嬪艰繃绋嬮兘闇瑕乻teakId銆 鐩墠缁欏紩鐢ㄥ彉閲忚祴鍊糘bject锛宻teak鍙兘鏈夐棶棰 + msgTab.info = this.createSetValueRetTable(varName, newValue, needFindVariable, this.curStackId, variableRefTab[varRefNum]); + else + -- 濡傛灉淇敼鐨勬槸涓涓熀纭绫诲瀷 + local setLimit; --璁剧疆妫绱㈠彉閲忕殑闄愬畾鍖哄煙 + if varRefNum >= 10000 and varRefNum < 20000 then setLimit = "local"; + elseif varRefNum >= 20000 and varRefNum < 30000 then setLimit = "global"; + elseif varRefNum >= 30000 then setLimit = "upvalue"; + end + msgTab.info = this.createSetValueRetTable(varName, newValue, needFindVariable, this.curStackId, nil, setLimit); + end + + this.sendMsg(msgTab); + this.debugger_wait_msg(); + end + + elseif dataTable.cmd == "getVariable" then + --浠呭湪鍋滄鏃跺鐞嗘秷鎭紝鍏朵粬鏃跺埢鏀跺埌姝ゆ秷鎭紝涓㈠純 + if currentRunState == runState.STOP_ON_ENTRY or + currentRunState == runState.HIT_BREAKPOINT or + currentRunState == runState.STEPOVER_STOP or + currentRunState == runState.STEPIN_STOP or + currentRunState == runState.STEPOUT_STOP then + --鍙戦佸彉閲忕粰娓告垙锛屽苟淇濇寔涔嬪墠鐨勭姸鎬,绛夊緟鍐嶆鎺ユ敹鏁版嵁 + --dataTable.info.varRef 10000~20000灞閮ㄥ彉閲 + -- 20000~30000鍏ㄥ眬鍙橀噺 + -- 30000~ upvalue + -- 1000~2000灞閮ㄥ彉閲忕殑鏌ヨ锛2000~3000鍏ㄥ眬锛3000~4000upvalue + local msgTab = this.getMsgTable("getVariable", this.getCallbackId()); + local varRefNum = tonumber(dataTable.info.varRef); + if varRefNum < 10000 then + --鏌ヨ鍙橀噺, 姝ゆ椂蹇界暐 stackId + local varTable = this.getVariableRef(dataTable.info.varRef, true); + msgTab.info = varTable; + elseif varRefNum >= 10000 and varRefNum < 20000 then + --灞閮ㄥ彉閲 + if dataTable.info.stackId ~= nil and tonumber(dataTable.info.stackId) > 1 then + this.curStackId = tonumber(dataTable.info.stackId); + if type(currentCallStack[this.curStackId - 1]) ~= "table" or type(currentCallStack[this.curStackId - 1].func) ~= "function" then + local str = "getVariable getLocal currentCallStack " .. this.curStackId - 1 .. " Error\n" .. this.serializeTable(currentCallStack, "currentCallStack"); + this.printToVSCode(str, 2); + msgTab.info = {}; + else + local stackId = this.getSpecificFunctionStackLevel(currentCallStack[this.curStackId - 1].func); --鍘婚櫎鍋忕Щ閲 + local varTable = this.getVariable(stackId, true); + msgTab.info = varTable; + end + end + + elseif varRefNum >= 20000 and varRefNum < 30000 then + --鍏ㄥ眬鍙橀噺 + local varTable = this.getGlobalVariable(); + msgTab.info = varTable; + elseif varRefNum >= 30000 then + --upValue + if dataTable.info.stackId ~= nil and tonumber(dataTable.info.stackId) > 1 then + this.curStackId = tonumber(dataTable.info.stackId); + if type(currentCallStack[this.curStackId - 1]) ~= "table" or type(currentCallStack[this.curStackId - 1].func) ~= "function" then + local str = "getVariable getUpvalue currentCallStack " .. this.curStackId - 1 .. " Error\n" .. this.serializeTable(currentCallStack, "currentCallStack"); + this.printToVSCode(str, 2); + msgTab.info = {}; + else + local varTable = this.getUpValueVariable(currentCallStack[this.curStackId - 1 ].func, true); + msgTab.info = varTable; + end + end + end + this.sendMsg(msgTab); + this.debugger_wait_msg(); + end + elseif dataTable.cmd == "initSuccess" then + --鍒濆鍖栦細浼犺繃鏉ヤ竴浜涘彉閲忥紝杩欓噷璁板綍杩欎簺鍙橀噺 + --Base64 + if dataTable.info.isNeedB64EncodeStr == "true" then + isNeedB64EncodeStr = true; + else + isNeedB64EncodeStr = false; + end + --path + luaFileExtension = dataTable.info.luaFileExtension; + local TempFilePath = dataTable.info.TempFilePath; + if TempFilePath:sub(-1, -1) == [[\]] or TempFilePath:sub(-1, -1) == [[/]] then + TempFilePath = TempFilePath:sub(1, -2); + end + TempFilePath_luaString = TempFilePath; + cwd = this.genUnifiedPath(dataTable.info.cwd); + --logLevel + logLevel = tonumber(dataTable.info.logLevel) or 1; + --autoPathMode + if dataTable.info.autoPathMode == "true" then + autoPathMode = true; + else + autoPathMode = false; + end + + if dataTable.info.pathCaseSensitivity == "true" then + pathCaseSensitivity = true; + truncatedOPath = dataTable.info.truncatedOPath or ""; + else + pathCaseSensitivity = false; + truncatedOPath = string.lower(dataTable.info.truncatedOPath or ""); + end + + if dataTable.info.distinguishSameNameFile == "true" then + distinguishSameNameFile = true; + else + distinguishSameNameFile = false; + end + + --OS type + if nil == OSType then + --鐢ㄦ埛鏈富鍔ㄨ缃甇SType, 鎺ユ敹VSCode浼犳潵鐨勬暟鎹 + if type(dataTable.info.OSType) == "string" then + OSType = dataTable.info.OSType; + else + OSType = "Windows_NT"; + OSTypeErrTip = "鏈兘妫娴嬪嚭OSType, 鍙兘鏄痭ode os搴撴湭鑳藉姞杞斤紝绯荤粺浣跨敤榛樿璁剧疆Windows_NT" + end + else + --鐢ㄦ埛鑷OSType, 浣跨敤鐢ㄦ埛鐨勮缃 + end + + --妫娴嬬敤鎴锋槸鍚﹁嚜璁句簡clib璺緞 + isUserSetClibPath = false; + if nil == clibPath then + --鐢ㄦ埛鏈缃甤libPath, 鎺ユ敹VSCode浼犳潵鐨勬暟鎹 + if type(dataTable.info.clibPath) == "string" then + clibPath = dataTable.info.clibPath; + else + clibPath = ""; + pathErrTip = "鏈兘姝g‘鑾峰彇libpdebug搴撴墍鍦ㄤ綅缃, 鍙兘鏃犳硶鍔犺浇libpdebug搴撱"; + end + else + --鐢ㄦ埛鑷clibPath + isUserSetClibPath = true; + end + + --鏌ユ壘c++鐨刪ook搴撴槸鍚﹀瓨鍦. 褰搇ua5.4鏃堕粯璁や笉浣跨敤c搴 + if tostring(dataTable.info.useCHook) == "true" and "Lua 5.4" ~= _VERSION then + userSetUseClib = true; --鐢ㄦ埛纭畾浣跨敤clib + if isUserSetClibPath == true then --濡傛灉鐢ㄦ埛鑷浜哻lib璺緞 + if luapanda_chook ~= nil then + hookLib = luapanda_chook; + else + if not(this.tryRequireClib("libpdebug", clibPath)) then + this.printToVSCode("Require clib failed, use Lua to continue debug, use LuaPanda.doctor() for more information.", 1); + end + end + else + local clibExt, platform; + if OSType == "Darwin" then clibExt = "/?.so;"; platform = "mac"; + elseif OSType == "Linux" then clibExt = "/?.so;"; platform = "linux"; + else clibExt = "/?.dll;"; platform = "win"; end + + local lua_ver; + if _VERSION == "Lua 5.1" then + lua_ver = "501"; + else + lua_ver = "503"; + end + + local x86Path = clibPath.. platform .."/x86/".. lua_ver .. clibExt; + local x64Path = clibPath.. platform .."/x86_64/".. lua_ver .. clibExt; + + if luapanda_chook ~= nil then + hookLib = luapanda_chook; + else + if not(this.tryRequireClib("libpdebug", x64Path) or this.tryRequireClib("libpdebug", x86Path)) then + this.printToVSCode("Require clib failed, use Lua to continue debug, use LuaPanda.doctor() for more information.", 1); + end + end + end + else + userSetUseClib = false; + end + + --adapter鐗堟湰淇℃伅 + adapterVer = tostring(dataTable.info.adapterVersion); + local msgTab = this.getMsgTable("initSuccess", this.getCallbackId()); + --鍥炰紶鏄惁浣跨敤浜唋ib锛屾槸鍚︽湁loadstring鍑芥暟 + local isUseHookLib = 0; + if hookLib ~= nil then + isUseHookLib = 1; + --鍚屾鏁版嵁缁檆 hook + local luaVerTable = this.stringSplit(debuggerVer , '%.'); + local luaVerNum = luaVerTable[1] * 10000 + luaVerTable[2] * 100 + luaVerTable[3]; + if hookLib.sync_lua_debugger_ver then + hookLib.sync_lua_debugger_ver(luaVerNum); + end + -- hookLib.sync_config(logLevel, pathCaseSensitivity and 1 or 0, autoPathMode and 1 or 0); + hookLib.sync_config(logLevel, pathCaseSensitivity and 1 or 0); + hookLib.sync_tempfile_path(TempFilePath_luaString); + hookLib.sync_cwd(cwd); + hookLib.sync_file_ext(luaFileExtension); + end + --detect LoadString + isUseLoadstring = 0; + if debugger_loadString ~= nil and type(debugger_loadString) == "function" then + if(pcall(debugger_loadString("return 0"))) then + isUseLoadstring = 1; + end + end + local tab = { debuggerVer = tostring(debuggerVer) , UseHookLib = tostring(isUseHookLib) , UseLoadstring = tostring(isUseLoadstring), isNeedB64EncodeStr = tostring(isNeedB64EncodeStr) }; + msgTab.info = tab; + this.sendMsg(msgTab); + --涓婇潰getBK涓細鍒ゆ柇褰撳墠鐘舵佹槸鍚AIT_CMD, 鎵浠ユ渶鍚庡啀鍒囨崲鐘舵併 + stopOnEntry = dataTable.info.stopOnEntry; + if dataTable.info.stopOnEntry == "true" then + this.changeRunState(runState.STOP_ON_ENTRY); --鍋滄鍦⊿TOP_ON_ENTRY鍐嶆帴鏀禸reaks娑堟伅 + else + this.debugger_wait_msg(1); --绛夊緟1s bk娑堟伅 濡傛灉鏀跺埌鎴栬秴鏃(娌℃湁鏂偣)灏卞紑濮嬭繍琛 + this.changeRunState(runState.RUN); + end + + elseif dataTable.cmd == "getWatchedVariable" then + local msgTab = this.getMsgTable("getWatchedVariable", this.getCallbackId()); + local stackId = tonumber(dataTable.info.stackId); + --loadstring绯荤粺鍑芥暟, watch鎻掍欢鍔犺浇 + if isUseLoadstring == 1 then + --浣跨敤loadstring + this.curStackId = stackId; + local retValue = this.processWatchedExp(dataTable.info); + msgTab.info = retValue + this.sendMsg(msgTab); + this.debugger_wait_msg(); + return; + else + --鏃х殑鏌ユ壘鏂瑰紡 + local wv = this.getWatchedVariable(dataTable.info.varName, stackId, true); + if wv ~= nil then + msgTab.info = wv; + end + this.sendMsg(msgTab); + this.debugger_wait_msg(); + end + elseif dataTable.cmd == "stopRun" then + --鍋滄hook锛屽凡涓嶅湪澶勭悊浠讳綍鏂偣淇℃伅锛屼篃灏变笉浼氫骇鐢熸棩蹇楃瓑銆傚彂閫佹秷鎭悗绛夊緟鍓嶇涓诲姩鏂紑杩炴帴 + local msgTab = this.getMsgTable("stopRun", this.getCallbackId()); + this.sendMsg(msgTab); + if not luaProcessAsServer then + this.disconnect(); + end + elseif "LuaGarbageCollect" == dataTable.cmd then + this.printToVSCode("collect garbage!"); + collectgarbage("collect"); + --鍥炴敹鍚庡埛涓涓嬪唴瀛 + this.sendLuaMemory(); + this.debugger_wait_msg(); + elseif "runREPLExpression" == dataTable.cmd then + this.curStackId = tonumber(dataTable.info.stackId); + local retValue = this.processExp(dataTable.info); + local msgTab = this.getMsgTable("runREPLExpression", this.getCallbackId()); + msgTab.info = retValue + this.sendMsg(msgTab); + this.debugger_wait_msg(); + else + end +end + +-- 鍙橀噺璧嬪肩殑澶勭悊鍑芥暟銆傚熀鏈昏緫鏄厛浠庡綋鍓嶆爤甯э紙curStackId锛変腑鍙 newValue 浠h〃鐨勫彉閲忥紝鎵惧埌涔嬪悗鍐嶆妸鎵惧埌鐨勫奸氳繃setVariableValue鍐欏洖銆 +-- @varName 琚缃肩殑鍙橀噺鍚 +-- @newValue 鏂板肩殑鍚嶅瓧锛屽畠鏄竴涓猻tring +-- @needFindVariable 鏄惁闇瑕佹煡鎵惧紩鐢ㄥ彉閲忋傦紙鐢ㄦ埛杈撳叆鐨勬槸鍚︽槸涓涓狾bject锛 +-- @curStackId 褰撳墠鏍堝抚锛堟煡鎵惧拰鍙橀噺璧嬪肩敤锛 +-- @assigndVar 琚洿鎺ヨ祴鍊硷紙鐪佸幓鏌ユ壘杩囩▼锛 +-- @setLimit 璧嬪兼椂鐨勯檺鍒惰寖鍥达紙local upvalue global锛 +function this.createSetValueRetTable(varName, newValue, needFindVariable, curStackId, assigndVar , setLimit) + local info; + local getVarRet; + -- needFindVariable == true锛屽垯浣跨敤getWatchedVariable澶勭悊锛堝彲閫, 鐢ㄦ潵鏀寔 a = b (b涓哄彉閲忕殑鎯呭喌)锛夈 + if needFindVariable == false then + getVarRet = {}; + getVarRet[1] = {variablesReference = 0, value = newValue, name = varName, type = type(newValue)}; + else + getVarRet = this.getWatchedVariable( tostring(newValue), curStackId, true); + end + if getVarRet ~= nil then + -- newValue璧嬪彉閲忕湡瀹炲 + local realVarValue; + local displayVarValue = getVarRet[1].value; + if needFindVariable == true then + if tonumber(getVarRet[1].variablesReference) > 0 then + realVarValue = variableRefTab[tonumber(getVarRet[1].variablesReference)]; + else + if getVarRet[1].type == 'number' then realVarValue = tonumber(getVarRet[1].value) end + if getVarRet[1].type == 'string' then + realVarValue = tostring(getVarRet[1].value); + local first_chr = string.sub(realVarValue, 1, 1); + local end_chr = string.sub(realVarValue, -1, -1); + if first_chr == end_chr then + if first_chr == "'" or first_chr == '"' then + realVarValue = string.sub(realVarValue, 2, -2); + displayVarValue = realVarValue; + end + end + end + if getVarRet[1].type == 'boolean' then + if getVarRet[1].value == "true" then + realVarValue = true; + else + realVarValue = false; + end + end + if getVarRet[1].type == 'nil' then realVarValue = nil end + end + else + realVarValue = getVarRet[1].value; + end + + local setVarRet; + if type(assigndVar) ~= table then + setVarRet = this.setVariableValue( varName, curStackId, realVarValue, setLimit ); + else + assigndVar[varName] = realVarValue; + setVarRet = true; + end + + if getVarRet[1].type == "string" then + displayVarValue = '"' .. displayVarValue .. '"'; + end + + if setVarRet ~= false and setVarRet ~= nil then + local retTip = "鍙橀噺 ".. varName .." 璧嬪兼垚鍔"; + info = { success = "true", name = getVarRet[1].name , type = getVarRet[1].type , value = displayVarValue, variablesReference = tostring(getVarRet[1].variablesReference), tip = retTip}; + else + info = { success = "false", type = type(realVarValue), value = displayVarValue, tip = "鎵句笉鍒拌璁剧疆鐨勫彉閲"}; + end + + else + info = { success = "false", type = nil, value = nil, tip = "杈撳叆鐨勫兼棤鎰忎箟"}; + end + return info +end + +--鎺ユ敹娑堟伅 +--杩欓噷缁存姢涓涓帴鏀舵秷鎭槦鍒楋紝鍥犱负Lua绔湭鍋氶殧鏂淇濇姢锛屽彉閲忚祴鍊兼椂璇锋敞鎰忓叾涓笉瑕佸寘鍚殧鏂 |*| +-- @timeoutSec 瓒呮椂鏃堕棿 +-- @return boolean 鎴愬姛/澶辫触 +function this.receiveMessage( timeoutSec ) + timeoutSec = timeoutSec or MAX_TIMEOUT_SEC; + sock:settimeout(timeoutSec); + --濡傛灉闃熷垪涓繕鏈夋秷鎭紝鐩存帴鍙栧嚭鏉ヤ氦缁檇ataProcess澶勭悊 + if #recvMsgQueue > 0 then + local saved_cmd = recvMsgQueue[1]; + table.remove(recvMsgQueue, 1); + this.dataProcess(saved_cmd); + return true; + end + + if currentRunState == runState.DISCONNECT then + this.disconnect(); + return false; + end + + if sock == nil then + this.printToConsole("[debugger error]鎺ユ敹淇℃伅澶辫触 | reason: socket == nil", 2); + return; + end + local response, err = sock:receive(); + if response == nil then + if err == "closed" then + this.printToConsole("[debugger error]鎺ユ敹淇℃伅澶辫触 | reason:"..err, 2); + this.disconnect(); + end + return false; + else + + --鍒ゆ柇鏄惁鏄竴鏉℃秷鎭紝鍒嗘媶 + local proc_response = string.sub(response, 1, -1 * (TCPSplitChar:len() + 1 )); + local match_res = string.find(proc_response, TCPSplitChar, 1, true); + if match_res == nil then + --鍗曟潯 + this.dataProcess(proc_response); + else + --鏈夌矘鍖 + repeat + --寰呭鐞嗗懡浠 + local str1 = string.sub(proc_response, 1, match_res - 1); + table.insert(recvMsgQueue, str1); + --鍓╀綑鍖归厤 + local str2 = string.sub(proc_response, match_res + TCPSplitChar:len() , -1); + match_res = string.find(str2, TCPSplitChar, 1, true); + until not match_res + this.receiveMessage(); + end + return true; + end +end + +--杩欓噷涓嶇敤寰幆锛屽湪澶栭潰澶勭悊瀹屾秷鎭細鍦ㄨ皟鐢ㄥ洖鏉 +-- @timeoutSec 绛夊緟鏃堕棿s +-- @entryFlag 鍏ュ彛鏍囪锛岀敤鏉ユ爣璇嗘槸浠庡摢閲岃皟鍏ョ殑 +function this.debugger_wait_msg(timeoutSec) + timeoutSec = timeoutSec or MAX_TIMEOUT_SEC; + + if currentRunState == runState.WAIT_CMD then + local ret = this.receiveMessage(timeoutSec); + return ret; + end + + if currentRunState == runState.STEPOVER or + currentRunState == runState.STEPIN or + currentRunState == runState.STEPOUT or + currentRunState == runState.RUN then + this.receiveMessage(0); + return + end + + if currentRunState == runState.STEPOVER_STOP or + currentRunState == runState.STEPIN_STOP or + currentRunState == runState.STEPOUT_STOP or + currentRunState == runState.HIT_BREAKPOINT or + currentRunState == runState.STOP_ON_ENTRY + then + this.sendLuaMemory(); + this.receiveMessage(MAX_TIMEOUT_SEC); + return + end +end + +----------------------------------------------------------------------------- +-- 璋冭瘯鍣ㄦ牳蹇冩柟娉 +----------------------------------------------------------------------------- + +------------------------鍫嗘爤绠$悊------------------------- + + +--getStackTable闇瑕佸缓绔媠tackTable锛屼繚瀛樻瘡灞傜殑lua鍑芥暟瀹炰緥(鐢ㄦ潵鍙杣pvalue)锛屼繚瀛樺嚱鏁板睍绀哄眰绾у拰ly鐨勫叧绯(渚夸簬鏍规嵁鍓嶇浼犳潵鐨剆tackId鏌ュ眬閮ㄥ彉閲) +-- @level 瑕佽幏鍙栫殑灞傜骇 +function this.getStackTable( level ) + local functionLevel = 0 + if hookLib ~= nil then + functionLevel = level or HOOK_LEVEL; + else + functionLevel = level or this.getSpecificFunctionStackLevel(lastRunFunction.func); + end + local stackTab = {}; + local userFuncSteakLevel = 0; --鐢ㄦ埛鍑芥暟鐨剆teaklevel + local clevel = 0 + repeat + local info = debug.getinfo(functionLevel, "SlLnf") + if info == nil then + break; + end + if info.source ~= "=[C]" then + local ss = {}; + ss.file = this.getPath(info); + local oPathFormated = this.formatOpath(info.source) ; --浠巐ua铏氭嫙鏈鸿幏寰楃殑鍘熷璺緞, 瀹冪敤浜庡府鍔╁畾浣峍Scode绔師濮媗ua鏂囦欢鐨勪綅缃(瀛樺湪閲嶅悕鏂囦欢鐨勬儏鍐)銆 + ss.oPath = this.truncatedPath(oPathFormated, truncatedOPath); + ss.name = "鏂囦欢鍚"; --杩欓噷瑕佸仛鎴彇 + ss.line = tostring(info.currentline); + --浣跨敤hookLib鏃讹紝鍫嗘爤鏈夊亸绉婚噺锛岃繖閲岀粺涓璋冪敤鏍堥《缂栧彿2 + local ssindex = functionLevel - 3; + if hookLib ~= nil then + ssindex = ssindex + 2; + end + ss.index = tostring(ssindex); + table.insert(stackTab,ss); + --鎶婃暟鎹瓨鍏urrentCallStack + local callStackInfo = {}; + callStackInfo.name = ss.file; + callStackInfo.line = ss.line; + callStackInfo.func = info.func; --淇濆瓨鐨刦unction + callStackInfo.realLy = functionLevel; --鐪熷疄鍫嗘爤灞俧unctionLevel(浠卍ebug鏃剁敤) + table.insert(currentCallStack, callStackInfo); + + --level璧嬪 + if userFuncSteakLevel == 0 then + userFuncSteakLevel = functionLevel; + end + else + local callStackInfo = {}; + callStackInfo.name = info.source; + callStackInfo.line = info.currentline; --C鍑芥暟琛屽彿 + callStackInfo.func = info.func; --淇濆瓨鐨刦unction + callStackInfo.realLy = functionLevel; --鐪熷疄鍫嗘爤灞俧unctionLevel(浠卍ebug鏃剁敤) + table.insert(currentCallStack, callStackInfo); + clevel = clevel + 1 + end + functionLevel = functionLevel + 1; + until info == nil + return stackTab, userFuncSteakLevel; +end + +-- 鎶婅矾寰勪腑鍘婚櫎鍚庣紑閮ㄥ垎鐨.鍙樹负/, +-- @filePath 琚浛鎹㈢殑璺緞 +-- @ext 鍚庣紑(鍚庣紑鍓嶇殑 . 涓嶄細琚浛鎹) +function this.changePotToSep(filePath, ext) + local idx = filePath:find(ext, (-1) * ext:len() , true) + if idx then + local tmp = filePath:sub(1, idx - 1):gsub("%.", "/"); + filePath = tmp .. ext; + end + return filePath; +end + +--- this.truncatedPath 浠 beTruncatedPath 瀛楃涓蹭腑鍘婚櫎 rep 鍖归厤鍒扮殑閮ㄥ垎 +function this.truncatedPath(beTruncatedPath, rep) + if beTruncatedPath and beTruncatedPath ~= '' and rep and rep ~= "" then + local _, lastIdx = string.find(beTruncatedPath , rep); + if lastIdx then + beTruncatedPath = string.sub(beTruncatedPath, lastIdx + 1); + end + end + return beTruncatedPath; +end + +--杩欎釜鏂规硶鏄牴鎹殑cwd鍜宭uaFileExtension瀵筭etInfo鑾峰彇鍒扮殑璺緞杩涜鏍囧噯鍖 +-- @info getInfo鑾峰彇鐨勫寘鍚皟鐢ㄤ俊鎭痶able +function this.getPath( info ) + local filePath = info; + if type(info) == "table" then + filePath = info.source; + end + --灏濊瘯浠嶤ache涓幏鍙栬矾寰 + local cachePath = this.getCacheFormatPath(filePath); + if cachePath~= nil and type(cachePath) == "string" then + return cachePath; + end + + -- originalPath鏄痝etInfo鐨勫師濮嬭矾寰勶紝鍚庨潰鐢ㄦ潵濉厖璺緞缂撳瓨鐨刱ey + local originalPath = filePath; + + --濡傛灉璺緞澶撮儴鏈堾,鍘婚櫎 + if filePath:sub(1,1) == '@' then + filePath = filePath:sub(2); + end + + --濡傛灉璺緞澶撮儴鏈./,鍘婚櫎 + if filePath:sub(1,2) == './' then + filePath = filePath:sub(3); + end + -- getPath鐨勫弬鏁拌矾寰勫彲鑳芥潵鑷簬hook, 涔熷彲鑳芥槸涓涓凡鏍囧噯鐨勮矾寰 + if userDotInRequire then + if autoExt == nil or autoExt == '' then + -- 鍦ㄨ櫄鎷熸満杩斿洖璺緞娌℃湁鍚庣紑鐨勬儏鍐典笅锛岀敤鎴峰繀椤昏嚜璁惧悗缂 + -- 纭畾filePath涓渶鍚庝竴涓.xxx 涓嶇瓑浜庣敤鎴烽厤缃殑鍚庣紑, 鍒欐妸鎵鏈夌殑. 杞负 / + if not filePath:find(luaFileExtension , (-1) * luaFileExtension:len(), true) then + -- getinfo 璺緞娌℃湁鍚庣紑锛屾妸 . 鍏ㄩ儴鏇挎崲鎴 / 锛屾垜浠笉鍏佽鐢ㄦ埛鍦ㄦ枃浠讹紙鎴栨枃浠跺す锛夊悕绉颁腑鍑虹幇"." , 鍥犱负鏃犳硶鍖哄垎 + filePath = string.gsub(filePath, "%.", "/"); + else + -- 鏈夊悗缂锛岄偅涔堟妸闄ゅ悗缂澶栫殑閮ㄥ垎涓殑. 杞负 / + filePath = this.changePotToSep(filePath, luaFileExtension); + end + + else + -- 铏氭嫙鏈鸿矾寰勬湁鍚庣紑 + filePath = this.changePotToSep(filePath, autoExt); + end + end + + --鍚庣紑澶勭悊 + if luaFileExtension ~= "" then + --鍒ゆ柇鍚庣紑涓槸鍚﹀寘鍚%1绛夐瓟娉曞瓧绗.鐢ㄤ簬浠巐ua铏氭嫙鏈鸿幏鍙栧埌鐨勮矾寰勫惈.鐨勬儏鍐 + if string.find(luaFileExtension, "%%%d") then + filePath = string.gsub(filePath, "%.[%w%.]+$", luaFileExtension); + else + filePath = string.gsub(filePath, "%.[%w%.]+$", ""); + filePath = filePath .. "." .. luaFileExtension; + end + end + + + if not autoPathMode then + --缁濆璺緞鍜岀浉瀵硅矾寰勭殑澶勭悊 | 鑻ュ湪Mac涓嬩互/寮澶达紝鎴栬呭湪Win涓嬩互*:寮澶达紝璇存槑鏄粷瀵硅矾寰勶紝涓嶉渶瑕佸啀鎷笺 + if filePath:sub(1,1) == [[/]] or filePath:sub(1,2):match("^%a:") then + isAbsolutePath = true; + else + isAbsolutePath = false; + if cwd ~= "" then + --鏌ョ湅filePath涓槸鍚﹀寘鍚玞wd + local matchRes = string.find(filePath, cwd, 1, true); + if matchRes == nil then + filePath = cwd.."/"..filePath; + end + end + end + end + filePath = this.genUnifiedPath(filePath); + + if autoPathMode then + -- 鑷姩璺緞妯″紡涓嬶紝鍙繚鐣欐枃浠跺悕 + filePath = this.getFilenameFromPath(filePath) + end + --鏀惧叆Cache涓紦瀛 + this.setCacheFormatPath(originalPath, filePath); + return filePath; +end + +--浠庤矾寰勪腑鑾峰彇[鏂囦欢鍚.鍚庣紑] +function this.getFilenameFromPath(path) + if path == nil then + return ''; + end + + return string.match(path, "([^/]*)$"); +end + +--鑾峰彇褰撳墠鍑芥暟鐨勫爢鏍堝眰绾 +--鍘熺悊鏄悜涓婃煡鎵撅紝閬囧埌DebuggerFileName灏辫皟杩囥備絾鏄彲鑳藉瓨鍦ㄤ唬鐮佹鍜孋瀵艰嚧涓嶇‘瀹氭с傜洰鍓嶄娇鐢╣etSpecificFunctionStackLevel浠f浛銆 +function this.getCurrentFunctionStackLevel() + -- print(debug.traceback("===getCurrentFunctionStackLevel Stack trace===")) + local funclayer = 2; + repeat + local info = debug.getinfo(funclayer, "S"); --閫氳繃name鏉ュ垽鏂 + if info ~= nil then + local matchRes = ((info.source == DebuggerFileName) or (info.source == DebuggerToolsName)); + if matchRes == false then + return (funclayer - 1); + end + end + funclayer = funclayer + 1; + until not info + return 0; +end + +--鑾峰彇鎸囧畾鍑芥暟鐨勫爢鏍堝眰绾 +--閫氬父鐢ㄦ潵鑾峰彇鏈鍚庝竴涓敤鎴峰嚱鏁扮殑灞傜骇锛岀敤娉曟槸浠巆urrentCallStack鍙栫敤鎴风偣鍑荤殑鏍堬紝鍐嶄娇鐢ㄦ湰鍑芥暟鍙栧搴斿眰绾с +-- @func 琚幏鍙栧眰绾х殑function +function this.getSpecificFunctionStackLevel( func ) + local funclayer = 2; + repeat + local info = debug.getinfo(funclayer, "f"); --閫氳繃name鏉ュ垽鏂 + if info ~= nil then + if info.func == func then + return (funclayer - 1); + end + end + funclayer = funclayer + 1; + until not info + return 0; +end + +--妫鏌ュ綋鍓嶅爢鏍堟槸鍚︽槸Lua +-- @checkLayer 鎸囧畾鐨勬爤灞 +function this.checkCurrentLayerisLua( checkLayer ) + local info = debug.getinfo(checkLayer, "S"); + if info == nil then + return nil; + end + info.source = this.genUnifiedPath(info.source); + if info ~= nil then + for k,v in pairs(info) do + if k == "what" then + if v == "C" then + return false; + else + return true; + end + end + end + end + return nil; +end + +-- 鍦 fakeBreakPointCache 涓煡璇㈡鏂偣鏄惁鐪熷疄瀛樺湪 +-- 鍥犱负鍚屽悕鏂囦欢鐨勫奖鍝嶏紝 鏈変簺鏂偣鏄懡涓敊璇殑銆傜粡杩嘨Scode鏍¢獙鍚庯紝杩欎簺閿欒鍛戒腑鐨勬柇鐐逛俊鎭瀛樺湪fakeBreakPointCache涓 +function this.checkRealHitBreakpoint( oPath, line ) + -- 鍦ㄥ亣鍛戒腑鍒楄〃涓悳绱紝濡傛灉鏈鏈夎繃鍋囧懡涓褰曪紝杩斿洖 false + if oPath and fakeBreakPointCache[oPath] then + for _, value in ipairs(fakeBreakPointCache[oPath]) do + if tonumber(value) == tonumber(line) then + this.printToVSCode("cache hit bp in same name file. source:" .. tostring(oPath) .. " line:" .. tostring(line)); + return false; + end + end + end + return true; +end + +------------------------鏂偣澶勭悊------------------------- +--- this.isHitBreakpoint 鍒ゆ柇鏂偣鏄惁鍛戒腑銆傝繖涓柟娉曞湪c mod浠ュ強lua涓兘鏈夎皟鐢 +-- @param breakpointPath 鏂囦欢鍚+鍚庣紑 +-- @param opath getinfo path +-- @param curLine 褰撳墠鎵ц琛屽彿 +function this.isHitBreakpoint(breakpointPath, opath, curLine) + if breaks[breakpointPath] then + local oPathFormated; + for fullpath, fullpathNode in pairs(breaks[breakpointPath]) do + recordBreakPointPath = fullpath; --杩欓噷鏄负浜嗗吋瀹圭敤鎴锋柇鐐硅鍙锋病鏈夋墦瀵圭殑鎯呭喌 + local line_hit,cur_node = false,{}; + for _, node in ipairs(fullpathNode) do + if tonumber(node["line"]) == tonumber(curLine) then + line_hit = true; -- fullpath 鏂囦欢涓 鏈夎鍙峰懡涓 + cur_node = node; + recordBreakPointPath = fullpath; --琛屽彿鍛戒腑鍚庯紝鍐嶈缃竴娆★紝淇濊瘉璺緞鍑嗙‘ + break; + end + end + + -- 鍦╨ua绔笉鐭ラ亾鏄惁鏈夊悓鍚嶆枃浠讹紝鍩烘湰鎬濊矾鏄厛鍙栨枃浠跺悕锛岀敤鏂囦欢鍚嶅拰breakpointArray 杩涜鍖归厤銆 + -- 褰撴枃浠跺悕鍖归厤涓婃椂锛屽彲鑳藉瓨鍦ㄥ涓悓鍚嶆枃浠朵腑瀛樺湪鏂偣鐨勬儏鍐点傝繖鏃跺欓渶瑕佺敤 oPath 鍜 fullpath 杩涜瀵规瘮锛屽彇鍑烘纭殑銆 + -- 褰撴湰鍦版枃浠朵腑鏈夋柇鐐癸紝lua鍋氫簡鍒濇鍛戒腑鍚庯紝鍙兘瀛樺湪 stack , 鏂偣鏂囦欢鏈夊悓鍚嶇殑鎯呭喌銆傝繖灏遍渶瑕乿scode绔篃闇瑕乧heckfullpath鍑芥暟锛屼娇鐢╫path杩涜鏂囦欢鏍¢獙銆 + if line_hit then + if oPathFormated == nil then + -- 涓轰簡閬垮厤鎬ц兘娑堣楋紝浠呭湪琛屽彿鍛戒腑鏃舵墠澶勭悊 opath 鍒版爣鍑嗗寲璺緞 + oPathFormated = this.formatOpath(opath); + -- 鎴彇 + oPathFormated = this.truncatedPath(oPathFormated, truncatedOPath); + end + + if (not distinguishSameNameFile) or (string.match(fullpath, oPathFormated ) and this.checkRealHitBreakpoint(opath, curLine)) then + -- type鏄疶S涓殑鏋氫妇绫诲瀷锛屽叾瀹氫箟鍦˙reakPoint.tx鏂囦欢涓 + -- enum BreakpointType { + -- conditionBreakpoint = 0, + -- logPoint, + -- lineBreakpoint + -- } + + -- 澶勭悊鏂偣 + if cur_node["type"] == "0" then + -- condition breakpoint + -- 娉ㄦ剰姝ゅ涓嶈浣跨敤灏捐皟鐢紝鍚﹀垯浼氬奖鍝嶈皟鐢ㄦ爤锛屽鑷碙ua5.3鍜孡ua5.1涓皟鐢ㄦ爤灞傜骇涓嶅悓 + local conditionRet = this.IsMeetCondition(cur_node["condition"]); + -- this.printToVSCode("Condition BK: condition:" .. cur_node["condition"] .. " conditionRet " .. tostring(conditionRet)); + return conditionRet; + elseif cur_node["type"] == "1" then + -- log point + this.printToVSCode("[LogPoint Output]: " .. cur_node["logMessage"], 2, 2); + return false; + else + -- line breakpoint + return true; + end + end + end + end + else + testBreakpointFlag = false; --濡傛灉鐢ㄦ埛鎵撳紑浜嗘祴璇曟柇鐐圭殑鏍囧織浣嶈屾湭涓诲姩鍏抽棴锛屼細鍦╨ua缁х画杩愯鏃跺叧闂 + recordBreakPointPath = ''; --褰撳垏鎹㈡枃浠舵椂缃┖锛岄伩鍏嶆彁绀虹粰鐢ㄦ埛閿欒淇℃伅 + end + return false; +end + +-- 鏉′欢鏂偣澶勭悊鍑芥暟 +-- 杩斿洖true琛ㄧず鏉′欢鎴愮珛 +-- @conditionExp 鏉′欢琛ㄨ揪寮 +function this.IsMeetCondition(conditionExp) + -- 鍒ゆ柇鏉′欢涔嬪墠鏇存柊鍫嗘爤淇℃伅 + currentCallStack = {}; + variableRefTab = {}; + variableRefIdx = 1; + if hookLib then + this.getStackTable(4); + else + this.getStackTable(); + end + + this.curStackId = 2; --鍦ㄧ敤鎴风┖闂存渶涓婂眰鎵ц + + local conditionExpTable = {["varName"] = conditionExp} + local retTable = this.processWatchedExp(conditionExpTable) + + local isMeetCondition = false; + local function HandleResult() + if retTable[1]["isSuccess"] == "true" then + if retTable[1]["value"] == "nil" or (retTable[1]["value"] == "false" and retTable[1]["type"] == "boolean") then + isMeetCondition = false; + else + isMeetCondition = true; + end + else + isMeetCondition = false; + end + end + + xpcall(HandleResult, function() isMeetCondition = false; end) + return isMeetCondition; +end + +--鍔犲叆鏂偣鍑芥暟 +function this.BP() + this.printToConsole("BP()"); + if hookLib == nil then + if currentHookState == hookState.DISCONNECT_HOOK then + this.printToConsole("BP() but NO HOOK"); + return; + end + + local co, isMain = coroutine.running(); + if _VERSION == "Lua 5.1" then + if co == nil then + isMain = true; + else + isMain = false; + end + end + + if isMain == true then + this.printToConsole("BP() in main"); + else + this.printToConsole("BP() in coroutine"); + debug.sethook(co, this.debug_hook, "lrc"); + end + hitBP = true; + else + if hookLib.get_libhook_state() == hookState.DISCONNECT_HOOK then + this.printToConsole("BP() but NO C HOOK"); + return; + end + + --clib, set hitBP + hookLib.sync_bp_hit(1); + end + this.changeHookState(hookState.ALL_HOOK); + return true; +end + +-- 妫鏌ュ綋鍓嶆枃浠朵腑鏄惁鏈夋柇鐐 +-- 濡傛灉濉啓鍙傛暟fileName 杩斿洖fileName涓湁鏃犳柇鐐癸紝 鍏ㄥ眬鏈夋棤鏂偣 +-- fileName涓虹┖锛岃繑鍥炲叏灞鏄惁鏈夋柇鐐 +function this.checkHasBreakpoint(fileName) + local hasBk = false; + --鏈夋棤鍏ㄥ眬鏂偣 + if next(breaks) == nil then + hasBk = false; + else + hasBk = true; + end + --褰撳墠鏂囦欢涓槸鍚︽湁鏂偣 + if fileName ~= nil then + return breaks[fileName] ~= nil, hasBk; + else + return hasBk; + end +end + +function this.checkfuncHasBreakpoint(sLine, eLine, fileName) + if breaks[fileName] == nil then + return false; + end + sLine = tonumber(sLine); + eLine = tonumber(eLine); + + --璧峰琛屽彿>缁撴潫琛屽彿锛屾垨鑰卻Line = eLine = 0 + if sLine >= eLine then + return true; + end + + if this.getTableMemberNum(breaks[fileName]) <= 0 then + return false; + else + for k,v in pairs(breaks[fileName]) do + for _, node in ipairs(v) do + if tonumber(node.line) > sLine and tonumber(node.line) <= eLine then + return true; + end + end + end + end + return false; +end +------------------------HOOK妯″潡------------------------- +-- 閽╁瓙鍑芥暟 +-- @event 鎵ц鐘舵(call,return,line) +-- @line 琛屽彿 +function this.debug_hook(event, line) + if this.reConnect() == 0 then return; end + + if logLevel == 0 then + local logTable = {"-----enter debug_hook-----\n", "event:", event, " line:", tostring(line), " currentHookState:",currentHookState," currentRunState:", currentRunState}; + local logString = table.concat(logTable); + this.printToVSCode(logString); + end + + --litehook 浠呴潪闃诲鎺ユ敹鏂偣 + if currentHookState == hookState.LITE_HOOK then + local ti = os.time(); + if ti - receiveMsgTimer > 1 then + this.debugger_wait_msg(0); + receiveMsgTimer = ti; + end + return; + end + + --杩愯涓 + local info; + local co, isMain = coroutine.running(); + if _VERSION == "Lua 5.1" then + if co == nil then + isMain = true; + else + isMain = false; + end + end + isInMainThread = isMain; + if isMain == true then + info = debug.getinfo(2, "Slf") + else + info = debug.getinfo(co, 2, "Slf") + end + info.event = event; + + this.real_hook_process(info); +end + +function this.real_hook_process(info) + local jumpFlag = false; + local event = info.event; + + --濡傛灉褰撳墠琛屽湪Debugger涓紝涓嶅仛澶勭悊 + local matchRes = ((info.source == DebuggerFileName) or (info.source == DebuggerToolsName)); + if matchRes == true then + return; + end + + --鍗充娇MID hook鍦–涓, 鎴栬呮槸Run鎴栬呭崟姝ユ椂涔熸帴鏀舵秷鎭 + if currentRunState == runState.RUN or + currentRunState == runState.STEPOVER or + currentRunState == runState.STEPIN or + currentRunState == runState.STEPOUT then + local ti = os.time(); + if ti - receiveMsgTimer > 1 then + this.debugger_wait_msg(0); + receiveMsgTimer = ti; + end + end + + --涓嶅鐞咰鍑芥暟 + if info.source == "=[C]" then + this.printToVSCode("current method is C"); + return; + end + + --涓嶅鐞 slua "temp buffer" + if info.source == "temp buffer" then + this.printToVSCode("current method is in temp buffer"); + return; + end + + --涓嶅鐞 xlua "chunk" + if info.source == "chunk" then + this.printToVSCode("current method is in chunk"); + return; + end + + --lua 浠g爜娈电殑澶勭悊锛岀洰鍓嶆殏涓嶈皟璇曚唬鐮佹銆 + if info.short_src:match("%[string \"") then + --褰搒hortSrc涓嚭鐜癧string鏃禲銆傝妫鏌ヤ竴涓媠ource, 鍖哄埆鏄矾寰勮繕鏄唬鐮佹. 鏂规硶鏄湅璺緞涓湁娌℃湁\t \n ; + if info.source:match("[\n;=]") then + --鏄唬鐮佹锛岃皟杩 + this.printToVSCode("hook jump Code String!"); + jumpFlag = true; + end + end + + --鏍囧噯璺緞澶勭悊 + if jumpFlag == false then + info.orininal_source = info.source; --浣跨敤 info.orininal_source 璁板綍lua铏氭嫙鏈轰紶鏉ョ殑鍘熷璺緞 + info.source = this.getPath(info); + end + --鏈鎵ц鐨勫嚱鏁板拰涓婃鎵ц鐨勫嚱鏁颁綔瀵规瘮锛岄槻姝㈠湪涓琛屽仠鐣欎袱娆 + if lastRunFunction["currentline"] == info["currentline"] and lastRunFunction["source"] == info["source"] and lastRunFunction["func"] == info["func"] and lastRunFunction["event"] == event then + this.printToVSCode("run twice"); + end + --璁板綍鏈鍚庝竴娆¤皟鐢ㄤ俊鎭 + if jumpFlag == false then + lastRunFunction = info; + lastRunFunction["event"] = event; + lastRunFilePath = info.source; + end + --杈撳嚭鍑芥暟淇℃伅鍒板墠鍙 + if logLevel == 0 and jumpFlag == false then + local logTable = {"[lua hook] event:", tostring(event), " currentRunState:",tostring(currentRunState)," currentHookState:",tostring(currentHookState)," jumpFlag:", tostring(jumpFlag)}; + for k,v in pairs(info) do + table.insert(logTable, tostring(k)); + table.insert(logTable, ":"); + table.insert(logTable, tostring(v)); + table.insert(logTable, " "); + end + local logString = table.concat(logTable); + this.printToVSCode(logString); + end + + --浠呭湪line鏃跺仛鏂偣鍒ゆ柇銆傝繘浜嗘柇鐐逛箣鍚庝笉鍐嶈繘鍏ユ湰娆TEP绫诲瀷鐨勫垽鏂紝鐢ˋflag鍋氭爣璁 + local isHit = false; + if tostring(event) == "line" and jumpFlag == false then + if currentRunState == runState.RUN or currentRunState == runState.STEPOVER or currentRunState == runState.STEPIN or currentRunState == runState.STEPOUT then + --鏂偣鍒ゆ柇 + isHit = this.isHitBreakpoint(info.source, info.orininal_source, info.currentline) or hitBP; + if isHit == true then + this.printToVSCode("HitBreakpoint!"); + --澶囦唤淇℃伅 + local recordStepOverCounter = stepOverCounter; + local recordStepOutCounter = stepOutCounter; + local recordCurrentRunState = currentRunState; + --璁℃暟鍣ㄦ竻0 + stepOverCounter = 0; + stepOutCounter = 0; + this.changeRunState(runState.HIT_BREAKPOINT); + hitBpTwiceCheck = true; -- 鍛戒腑鏍囧織榛樿璁剧疆涓簍rue, 濡傛灉鏍¢獙閫氳繃锛屼細淇濈暀杩欎釜鏍囪锛屾牎楠屽け璐ヤ細淇敼 + if hitBP then + hitBP = false; --hitBP鏄柇鐐圭‖鎬у懡涓爣璁 + --鍙戞秷鎭苟绛夊緟 + this.SendMsgWithStack("stopOnCodeBreakpoint"); + else + --鍙戞秷鎭苟绛夊緟 + this.SendMsgWithStack("stopOnBreakpoint"); + --鑻ヤ簩娆℃牎楠屾湭鍛戒腑锛屾仮澶嶇姸鎬 + if hitBpTwiceCheck == false then + isHit = false; + -- 纭鏈懡涓紝鎶婄姸鎬佹仮澶嶏紝缁х画杩愯 + this.changeRunState(recordCurrentRunState); + stepOverCounter = recordStepOverCounter; + stepOutCounter = recordStepOutCounter; + end + end + end + end + end + + if isHit == true then + return; + end + + if currentRunState == runState.STEPOVER then + -- line stepOverCounter!= 0 涓嶄綔鎿嶄綔 + -- line stepOverCounter == 0 鍋滄 + if event == "line" and stepOverCounter <= 0 and jumpFlag == false then + stepOverCounter = 0; + this.changeRunState(runState.STEPOVER_STOP) + this.SendMsgWithStack("stopOnStep"); + elseif event == "return" or event == "tail return" then + --5.1涓槸tail return + if stepOverCounter ~= 0 then + stepOverCounter = stepOverCounter - 1; + end + elseif event == "call" then + stepOverCounter = stepOverCounter + 1; + end + elseif currentRunState == runState.STOP_ON_ENTRY then + --鍦↙ua鍏ュ彛鐐瑰鐩存帴鍋滀綇 + if event == "line" and jumpFlag == false then + --鍒濆鍖栧唴瀛樺垎鏋愮殑鍙橀噺 + -- MemProfiler.getSystemVar(); + --杩欓噷瑕佸垽鏂竴涓嬫槸Lua鐨勫叆鍙g偣锛屽惁鍒欏仠鍒 + this.SendMsgWithStack("stopOnEntry"); + end + elseif currentRunState == runState.STEPIN then + if event == "line" and jumpFlag == false then + this.changeRunState(runState.STEPIN_STOP) + this.SendMsgWithStack("stopOnStepIn"); + end + elseif currentRunState == runState.STEPOUT then + --line 涓嶅仛鎿嶄綔 + --in 璁℃暟鍣+1 + --out 璁℃暟鍣-1 + if jumpFlag == false then + if stepOutCounter <= -1 then + stepOutCounter = 0; + this.changeRunState(runState.STEPOUT_STOP) + this.SendMsgWithStack("stopOnStepOut"); + end + end + + if event == "return" or event == "tail return" then + stepOutCounter = stepOutCounter - 1; + elseif event == "call" then + stepOutCounter = stepOutCounter + 1; + end + end + + --鍦≧UN鏃舵鏌ュ苟鏀瑰彉鐘舵 + if hookLib == nil then + if currentRunState == runState.RUN and jumpFlag == false and currentHookState ~= hookState.DISCONNECT_HOOK then + local fileBP, G_BP = this.checkHasBreakpoint(lastRunFilePath); + if fileBP == false then + --鏂囦欢鏃犳柇鐐 + if G_BP == true then + this.changeHookState(hookState.MID_HOOK); + else + this.changeHookState(hookState.LITE_HOOK); + end + else + --鏂囦欢鏈夋柇鐐, 鍒ゆ柇鍑芥暟鍐呮槸鍚︽湁鏂偣 + local funHasBP = this.checkfuncHasBreakpoint(lastRunFunction.linedefined, lastRunFunction.lastlinedefined, lastRunFilePath); + if funHasBP then + --鍑芥暟瀹氫箟鑼冨洿鍐 + this.changeHookState(hookState.ALL_HOOK); + else + this.changeHookState(hookState.MID_HOOK); + end + end + + --MID_HOOK鐘舵佷笅锛宺eturn闇瑕佸湪涓嬩竴娆ook妫鏌ユ枃浠讹紙return鏃讹紝杩樻槸褰撳墠鏂囦欢锛屾鏌ユ枃浠舵椂鐘舵佹棤娉曡浆鎹級 + if (event == "return" or event == "tail return") and currentHookState == hookState.MID_HOOK then + this.changeHookState(hookState.ALL_HOOK); + end + end + end +end + +-- 鍚慥scode鍙戦佹爣鍑嗛氱煡娑堟伅锛宑mdStr鏄秷鎭被鍨 +-- @cmdStr 鍛戒护瀛 +function this.SendMsgWithStack(cmdStr) + local msgTab = this.getMsgTable(cmdStr); + local userFuncLevel = 0; + msgTab["stack"] , userFuncLevel= this.getStackTable(); + if userFuncLevel ~= 0 then + lastRunFunction["func"] = debug.getinfo( (userFuncLevel - 1) , 'f').func; + end + this.sendMsg(msgTab); + this.debugger_wait_msg(); +end + +-- hook鐘舵佹敼鍙 +-- @s 鐩爣鐘舵 +function this.changeHookState( s ) + if hookLib == nil and currentHookState == s then + return; + end + + this.printToConsole("change hook state :"..s) + if s ~= hookState.DISCONNECT_HOOK then + this.printToVSCode("change hook state : "..s) + end + + currentHookState = s; + if s == hookState.DISCONNECT_HOOK then + --涓轰簡瀹炵幇閫氱敤attach妯″紡锛宺equire鍗冲紑濮媓ook锛屽埄鐢╮浣滀负鏃舵満鍙戣捣杩炴帴 + if openAttachMode == true then + if hookLib then hookLib.lua_set_hookstate(hookState.DISCONNECT_HOOK); else debug.sethook(this.debug_hook, "r", 1000000); end + else + if hookLib then hookLib.endHook(); else debug.sethook(); end + end + elseif s == hookState.LITE_HOOK then + if hookLib then hookLib.lua_set_hookstate(hookState.LITE_HOOK); else debug.sethook(this.debug_hook, "r"); end + elseif s == hookState.MID_HOOK then + if hookLib then hookLib.lua_set_hookstate(hookState.MID_HOOK); else debug.sethook(this.debug_hook, "rc"); end + elseif s == hookState.ALL_HOOK then + if hookLib then hookLib.lua_set_hookstate(hookState.ALL_HOOK); else debug.sethook(this.debug_hook, "lrc");end + end + --coroutine + if hookLib == nil then + this.changeCoroutinesHookState(); + end +end + +-- 杩愯鐘舵佹満锛岀姸鎬佸彉鏇 +-- @s 鐩爣鐘舵 +-- @isFromHooklib 1:浠巐ibc搴撲腑鍙戞潵鐨勭姸鎬佹敼鍙 | 0:lua鍙戞潵鐨勭姸鎬佹敼鍙 +function this.changeRunState(s , isFromHooklib) + local msgFrom; + if isFromHooklib == 1 then + msgFrom = "libc"; + else + msgFrom = "lua"; + end + + --WAIT_CMD鐘舵佷細绛夊緟鎺ユ敹娑堟伅锛屼互涓嬩袱涓姸鎬佷笅涓嶈兘鍙戞秷鎭 + this.printToConsole("changeRunState :"..s.. " | from:"..msgFrom); + if s ~= runState.DISCONNECT and s ~= runState.WAIT_CMD then + this.printToVSCode("changeRunState :"..s.." | from:"..msgFrom); + end + + if hookLib ~= nil and isFromHooklib ~= 1 then + hookLib.lua_set_runstate(s); + end + currentRunState = s; + --鐘舵佸垏鎹㈡椂锛屾竻闄よ褰曟爤淇℃伅鐨勭姸鎬 + currentCallStack = {}; + variableRefTab = {}; + variableRefIdx = 1; +end + +-- 淇敼鍗忕▼鐘舵 +-- @s hook鏍囧織浣 +function this.changeCoroutinesHookState(s) + s = s or currentHookState; + this.printToConsole("change [Coroutine] HookState: "..tostring(s)); + for k ,co in pairs(coroutinePool) do + if coroutine.status(co) == "dead" then + coroutinePool[k] = nil + else + this.changeCoroutineHookState(co, s) + end + end +end + +function this.changeCoroutineHookState(co, s) + if s == hookState.DISCONNECT_HOOK then + if openAttachMode == true then + debug.sethook(co, this.debug_hook, "r", 1000000); + else + debug.sethook(co, this.debug_hook, ""); + end + elseif s == hookState.LITE_HOOK then + debug.sethook(co , this.debug_hook, "r"); + elseif s == hookState.MID_HOOK then + debug.sethook(co , this.debug_hook, "rc"); + elseif s == hookState.ALL_HOOK then + debug.sethook(co , this.debug_hook, "lrc"); + end +end +-------------------------鍙橀噺澶勭悊鐩稿叧----------------------------- + +--娓呯┖REPL鐨別nv鐜 +function this.clearEnv() + if this.getTableMemberNum(env) > 0 then + --娓呯┖env table + env = setmetatable({}, getmetatable(env)); + end +end + +--杩斿洖REPL鐨別nv鐜 +function this.showEnv() + return env; +end + +-- 鐢ㄦ埛瑙傚療table鐨勬煡鎵惧嚱鏁般傜敤tableVarName浣滀负key鍘绘煡閫愬眰绾ф煡鎵緍ealVar鏄惁鍖归厤 +-- @tableVarName 鏄敤鎴疯瀵熺殑鍙橀噺鍚嶏紝宸茬粡鎸夊眰绾ц瑙f瀽鎴恡able銆傛瘮濡傜敤鎴疯緭鍑篴.b.c锛宼ableVarName鏄 a = { b = { c } } +-- @realVar 鏄緟鏌ヨ table +-- @return 杩斿洖鏌ュ埌鐨則able銆傛病鏌ュ埌杩斿洖nil +function this.findTableVar( tableVarName, realVar) + if type(tableVarName) ~= "table" or type(realVar) ~= "table" then + return nil; + end + + local layer = 2; + local curVar = realVar; + local jumpOutFlag = false; + repeat + if tableVarName[layer] ~= nil then + --杩欓噷浼樺厛灞曠ず鏁板瓧key锛屾瘮濡俛{"1" = "aa", [1] = "bb"} 浼氬睍绀篬1]鐨勫 + local tmpCurVar = nil; + xpcall(function() tmpCurVar = curVar[tonumber(tableVarName[layer])]; end , function() tmpCurVar = nil end ); + if tmpCurVar == nil then + xpcall(function() curVar = curVar[tostring(tableVarName[layer])]; end , function() curVar = nil end ); + else + curVar = tmpCurVar; + end + layer = layer + 1; + if curVar == nil then + return nil; + end + else + --鎵惧埌 + jumpOutFlag = true; + end + until(jumpOutFlag == true) + return curVar; +end + +-- 鏍规嵁浼犲叆淇℃伅鐢熸垚杩斿洖鐨勫彉閲忎俊鎭 +-- @variableName 鍙橀噺鍚 +-- @variableIns 鍙橀噺瀹炰緥 +-- @return 鍖呭惈鍙橀噺淇℃伅鐨勬牸寮忓寲table +function this.createWatchedVariableInfo(variableName, variableIns) + local var = {}; + var.name = variableName; + var.type = tostring(type(variableIns)); + xpcall(function() var.value = tostring(variableIns) end , function() var.value = tostring(type(variableIns)) .. " [value can't trans to string]" end ); + var.variablesReference = "0"; --杩欎釜鍦版柟蹇呴』鐢ㄢ0鈥濓紝 浠ュ厤variableRefTab[0]鍑洪敊 + + if var.type == "table" or var.type == "function" or var.type == "userdata" then + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = variableIns; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(variableIns); + var.value = memberNum .." Members ".. var.value; + end + elseif var.type == "string" then + var.value = '"' ..variableIns.. '"'; + end + return var; +end + +-- 璁剧疆 global 鍙橀噺 +-- @varName 琚慨鏀圭殑鍙橀噺鍚 +-- @newValue 鏂扮殑鍊 +function this.setGlobal(varName, newValue) + _G[varName] = newValue; + this.printToVSCode("[setVariable success] 宸茶缃 _G.".. varName .. " = " .. tostring(newValue) ); + return true; +end + +-- 璁剧疆 upvalue 鍙橀噺 +-- @varName 琚慨鏀圭殑鍙橀噺鍚 +-- @newValue 鏂扮殑鍊 +-- @stackId 鍙橀噺鎵鍦╯tack鏍堝眰 +-- @tableVarName 鍙橀噺鍚嶆媶鍒嗘垚鐨勬暟缁 +function this.setUpvalue(varName, newValue, stackId, tableVarName) + local ret = false; + local upTable = this.getUpValueVariable(currentCallStack[stackId - 1 ].func, true); + for i, realVar in ipairs(upTable) do + if realVar.name == varName then + if #tableVarName > 0 and type(realVar) == "table" then + --澶勭悊a.b.c鐨則able绫诲瀷 + local findRes = this.findTableVar(tableVarName, variableRefTab[realVar.variablesReference]); + if findRes ~= nil then + --鍛戒腑 + local setVarRet = debug.setupvalue (currentCallStack[stackId - 1 ].func, i, newValue); + if setVarRet == varName then + this.printToConsole("[setVariable success1] 宸茶缃 upvalue ".. varName .. " = " .. tostring(newValue) ); + ret = true; + else + this.printToConsole("[setVariable error1] 鏈兘璁剧疆 upvalue ".. varName .. " = " .. tostring(newValue).." , 杩斿洖缁撴灉: ".. tostring(setVarRet)); + end + return ret; + end + else + --鍛戒腑 + local setVarRet = debug.setupvalue (currentCallStack[stackId - 1 ].func, i, newValue); + if setVarRet == varName then + this.printToConsole("[setVariable success] 宸茶缃 upvalue ".. varName .. " = " .. tostring(newValue) ); + ret = true; + else + this.printToConsole("[setVariable error] 鏈兘璁剧疆 upvalue ".. varName .. " = " .. tostring(newValue).." , 杩斿洖缁撴灉: ".. tostring(setVarRet)); + end + return ret; + end + end + end + return ret; +end + +-- 璁剧疆local 鍙橀噺 +-- @varName 琚慨鏀圭殑鍙橀噺鍚 +-- @newValue 鏂扮殑鍊 +-- @tableVarName 鍙橀噺鍚嶆媶鍒嗘垚鐨勬暟缁 +function this.setLocal( varName, newValue, tableVarName, stackId) + local istackId = tonumber(stackId); + local offset = (istackId and istackId - 2) or 0; + local layerVarTab, ly = this.getVariable(nil , true, offset); + local ret = false; + for i, realVar in ipairs(layerVarTab) do + if realVar.name == varName then + if #tableVarName > 0 and type(realVar) == "table" then + --澶勭悊a.b.c鐨則able绫诲瀷 + local findRes = this.findTableVar(tableVarName, variableRefTab[realVar.variablesReference]); + if findRes ~= nil then + --鍛戒腑 + local setVarRet = debug.setlocal(ly , layerVarTab[i].index, newValue); + if setVarRet == varName then + this.printToConsole("[setVariable success1] 宸茶缃 local ".. varName .. " = " .. tostring(newValue) ); + ret = true; + else + this.printToConsole("[setVariable error1] 鏈兘璁剧疆 local ".. varName .. " = " .. tostring(newValue).." , 杩斿洖缁撴灉: ".. tostring(setVarRet)); + end + return ret; + end + else + + local setVarRet = debug.setlocal(ly , layerVarTab[i].index, newValue); + + if setVarRet == varName then + this.printToConsole("[setVariable success] 宸茶缃 local ".. varName .. " = " .. tostring(newValue) ); + ret = true; + else + this.printToConsole("[setVariable error] 鏈兘璁剧疆 local ".. varName .. " = " .. tostring(newValue) .." , 杩斿洖缁撴灉: ".. tostring(setVarRet)); + end + return ret; + end + end + end + return ret; +end + + +-- 璁剧疆鍙橀噺鐨勫 +-- @varName 琚慨鏀圭殑鍙橀噺鍚 +-- @curStackId 璋冪敤鏍堝眰绾(浠呭湪鍥哄畾鏍堝眰鏌ユ壘) +-- @newValue 鏂扮殑鍊 +-- @limit 闄愬埗绗︼紝 10000琛ㄧず浠呭湪灞閮ㄥ彉閲忔煡鎵 锛20000 global, 30000 upvalue +function this.setVariableValue (varName, stackId, newValue , limit) + this.printToConsole("setVariableValue | varName:" .. tostring(varName) .. " stackId:".. tostring(stackId) .." newValue:" .. tostring(newValue) .." limit:"..tostring(limit) ) + if tostring(varName) == nil or tostring(varName) == "" then + --璧嬪奸敊璇 + this.printToConsole("[setVariable Error] 琚祴鍊肩殑鍙橀噺鍚嶄负绌", 2 ); + this.printToVSCode("[setVariable Error] 琚祴鍊肩殑鍙橀噺鍚嶄负绌", 2 ); + return false; + end + + --鏀寔a.b.c褰㈠紡銆傚垏鍓瞯arName + local tableVarName = {}; + if varName:match('%.') then + tableVarName = this.stringSplit(varName , '%.'); + if type(tableVarName) ~= "table" or #tableVarName < 1 then + return false; + end + varName = tableVarName[1]; + end + + if limit == "local" then + local ret = this.setLocal( varName, newValue, tableVarName, stackId); + return ret; + elseif limit == "upvalue" then + local ret = this.setUpvalue(varName, newValue, stackId, tableVarName); + return ret + elseif limit == "global" then + local ret = this.setGlobal(varName, newValue); + return ret; + else + local ret = this.setLocal( varName, newValue, tableVarName, stackId) or this.setUpvalue(varName, newValue, stackId, tableVarName) or this.setGlobal(varName, newValue); + this.printToConsole("set Value res :".. tostring(ret)); + return ret; + end +end + +-- 鎸夌収local -> upvalue -> _G 椤哄簭鏌ユ壘瑙傚療鍙橀噺 +-- @varName 鐢ㄦ埛杈撳叆鐨勫彉閲忓悕 +-- @stackId 璋冪敤鏍堝眰绾(浠呭湪鍥哄畾鏍堝眰鏌ユ壘) +-- @isFormatVariable 鏄惁鎶婂彉閲忔牸寮忓寲涓篤SCode鎺ユ敹鐨勫舰寮 +-- @return 鏌ュ埌杩斿洖淇℃伅锛屾煡涓嶅埌杩斿洖nil +function this.getWatchedVariable( varName , stackId , isFormatVariable ) + this.printToConsole("getWatchedVariable | varName:" .. tostring(varName) .. " stackId:".. tostring(stackId) .." isFormatVariable:" .. tostring(isFormatVariable) ) + if tostring(varName) == nil or tostring(varName) == "" then + return nil; + end + + if type(currentCallStack[stackId - 1]) ~= "table" or type(currentCallStack[stackId - 1].func) ~= "function" then + local str = "getWatchedVariable currentCallStack " .. stackId - 1 .. " Error\n" .. this.serializeTable(currentCallStack, "currentCallStack"); + this.printToVSCode(str, 2); + return nil; + end + + --orgname 璁板綍鍘熷悕瀛. 鐢ㄦ潵澶勭悊a.b.c鐨勫舰寮 + local orgname = varName; + --鏀寔a.b.c褰㈠紡銆傚垏鍓瞯arName + local tableVarName = {}; + if varName:match('%.') then + tableVarName = this.stringSplit(varName , '%.'); + if type(tableVarName) ~= "table" or #tableVarName < 1 then + return nil; + end + varName = tableVarName[1]; + end + --鐢ㄦ潵杩斿洖锛屽甫鏈夋煡鍒板彉閲忕殑table + local varTab = {}; + local ly = this.getSpecificFunctionStackLevel(currentCallStack[stackId - 1].func); + + local layerVarTab = this.getVariable(ly, isFormatVariable); + local upTable = this.getUpValueVariable(currentCallStack[stackId - 1 ].func, isFormatVariable); + local travelTab = {}; + table.insert(travelTab, layerVarTab); + table.insert(travelTab, upTable); + for _, layerVarTab in ipairs(travelTab) do + for i,realVar in ipairs(layerVarTab) do + if realVar.name == varName then + if #tableVarName > 0 and type(realVar) == "table" then + --澶勭悊a.b.c鐨則able绫诲瀷 + local findRes = this.findTableVar(tableVarName, variableRefTab[realVar.variablesReference]); + if findRes ~= nil then + --鍛戒腑 + if isFormatVariable then + local var = this.createWatchedVariableInfo( orgname , findRes ); + table.insert(varTab, var); + return varTab; + else + return findRes.value; + end + end + else + --鍛戒腑 + if isFormatVariable then + table.insert(varTab, realVar); + return varTab; + else + return realVar.value; + end + end + end + end + end + + --鍦ㄥ叏灞鍙橀噺_G涓煡鎵 + if _G[varName] ~= nil then + --鍛戒腑 + if #tableVarName > 0 and type(_G[varName]) == "table" then + local findRes = this.findTableVar(tableVarName, _G[varName]); + if findRes ~= nil then + if isFormatVariable then + local var = this.createWatchedVariableInfo( orgname , findRes ); + table.insert(varTab, var); + return varTab; + else + return findRes; + end + end + else + if isFormatVariable then + local var = this.createWatchedVariableInfo( varName , _G[varName] ); + table.insert(varTab, var); + return varTab; + else + return _G[varName]; + end + end + end + this.printToConsole("getWatchedVariable not find variable"); + return nil; +end + +-- 鏌ヨ寮曠敤鍙橀噺 +-- @refStr 鍙橀噺璁板綍id(variableRefTab绱㈠紩) +-- @return 鏍煎紡鍖栫殑鍙橀噺淇℃伅table +function this.getVariableRef( refStr ) + local varRef = tonumber(refStr); + local varTab = {}; + + if tostring(type(variableRefTab[varRef])) == "table" then + for n,v in pairs(variableRefTab[varRef]) do + local var = {}; + if type(n) == "string" then + var.name = '"' .. tostring(n) .. '"'; + else + var.name = tostring(n); + end + var.type = tostring(type(v)); + xpcall(function() var.value = tostring(v) end , function() var.value = tostring(type(v)) .. " [value can't trans to string]" end ); + var.variablesReference = "0"; + if var.type == "table" or var.type == "function" or var.type == "userdata" then + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = v; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(v); + var.value = memberNum .." Members ".. ( var.value or '' ); + end + elseif var.type == "string" then + var.value = '"' ..v.. '"'; + end + table.insert(varTab, var); + end + --鑾峰彇涓涓媘tTable + local mtTab = getmetatable(variableRefTab[varRef]); + if mtTab ~= nil and type(mtTab) == "table" then + local var = {}; + var.name = "_Metatable_"; + var.type = tostring(type(mtTab)); + xpcall(function() var.value = "鍏冭〃 "..tostring(mtTab); end , function() var.value = "鍏冭〃 [value can't trans to string]" end ); + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = mtTab; + variableRefIdx = variableRefIdx + 1; + table.insert(varTab, var); + end + elseif tostring(type(variableRefTab[varRef])) == "function" then + --鍙杣pvalue + varTab = this.getUpValueVariable(variableRefTab[varRef], true); + elseif tostring(type(variableRefTab[varRef])) == "userdata" then + --鍙杕t table + local udMtTable = getmetatable(variableRefTab[varRef]); + if udMtTable ~= nil and type(udMtTable) == "table" then + local var = {}; + var.name = "_Metatable_"; + var.type = tostring(type(udMtTable)); + xpcall(function() var.value = "鍏冭〃 "..tostring(udMtTable); end , function() var.value = "鍏冭〃 [value can't trans to string]" end ); + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = udMtTable; + variableRefIdx = variableRefIdx + 1; + table.insert(varTab, var); + + if traversalUserData and udMtTable.__pairs ~= nil and type(udMtTable.__pairs) == "function" then + for n,v in pairs(variableRefTab[varRef]) do + local var = {}; + var.name = tostring(n); + var.type = tostring(type(v)); + xpcall(function() var.value = tostring(v) end , function() var.value = tostring(type(v)) .. " [value can't trans to string]" end ); + var.variablesReference = "0"; + if var.type == "table" or var.type == "function" or var.type == "userdata" then + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = v; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(v); + var.value = memberNum .." Members ".. ( var.value or '' ); + end + elseif var.type == "string" then + var.value = '"' ..v.. '"'; + end + table.insert(varTab, var); + end + end + end + end + return varTab; +end + +-- 鑾峰彇鍏ㄥ眬鍙橀噺銆傛柟娉曞拰鍐呭瓨绠$悊涓幏鍙栧叏灞鍙橀噺鐨勬柟娉曚竴鏍 +-- @return 鏍煎紡鍖栫殑淇℃伅, 鑻ユ湭鎵惧埌杩斿洖绌簍able +function this.getGlobalVariable( ... ) + --鎴愭湰姣旇緝楂橈紝杩欓噷鍙兘閬嶅巻_G涓殑鎵鏈夊彉閲忥紝骞跺幓闄ょ郴缁熷彉閲忥紝鍐嶈繑鍥炵粰瀹㈡埛绔 + local varTab = {}; + for k,v in pairs(_G) do + local var = {}; + var.name = tostring(k); + var.type = tostring(type(v)); + xpcall(function() var.value = tostring(v) end , function() var.value = tostring(type(v)) .." [value can't trans to string]" end ); + var.variablesReference = "0"; + if var.type == "table" or var.type == "function" or var.type == "userdata" then + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = v; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(v); + var.value = memberNum .." Members ".. ( var.value or '' ); + end + elseif var.type == "string" then + var.value = '"' ..v.. '"'; + end + table.insert(varTab, var); + end + return varTab; +end + +-- 鑾峰彇upValues +-- @isFormatVariable true杩斿洖[鍊糫 true杩斿洖[鏍煎紡鍖栫殑鏁版嵁] +function this.getUpValueVariable( checkFunc , isFormatVariable) + local isGetValue = true; + if isFormatVariable == true then + isGetValue = false; + end + + --閫氳繃Debug鑾峰彇褰撳墠鍑芥暟鐨凢unc + checkFunc = checkFunc or lastRunFunction.func; + + local varTab = {}; + if checkFunc == nil then + return varTab; + end + local i = 1 + repeat + local n, v = debug.getupvalue(checkFunc, i) + if n then + + local var = {}; + var.name = n; + var.type = tostring(type(v)); + var.variablesReference = "0"; + + if isGetValue == false then + xpcall(function() var.value = tostring(v) end , function() var.value = tostring(type(v)) .. " [value can't trans to string]" end ); + if var.type == "table" or var.type == "function" or var.type == "userdata" then + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = v; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(v); + var.value = memberNum .." Members ".. ( var.value or '' ); + end + elseif var.type == "string" then + var.value = '"' ..v.. '"'; + end + else + var.value = v; + end + + table.insert(varTab, var); + i = i + 1 + end + until not n + return varTab; +end + +-- 鑾峰彇灞閮ㄥ彉閲 checkLayer鏄鏌ヨ鐨勫眰绾э紝濡傛灉涓嶈缃垯鏌ヨ褰撳墠灞傜骇 +-- @isFormatVariable 鏄惁鍙栧硷紝true:鍙栧肩殑tostring +function this.getVariable( checkLayer, isFormatVariable , offset) + local isGetValue = true; + if isFormatVariable == true then + isGetValue = false; + end + + local ly = 0; + if checkLayer ~= nil and type(checkLayer) == "number" then ly = checkLayer + 1; + else ly = this.getSpecificFunctionStackLevel(lastRunFunction.func); end + + if ly == 0 then + this.printToVSCode("[error]鑾峰彇灞傛澶辫触锛", 2); + return; + end + local varTab = {}; + local stacklayer = ly; + local k = 1; + + if type(offset) == 'number' then + stacklayer = stacklayer + offset; + end + + repeat + local n, v = debug.getlocal(stacklayer, k) + if n == nil then + break; + end + + --(*temporary)鏄郴缁熷彉閲忥紝杩囨护鎺夈傝繖閲屽亣璁(*temporary)浠呭嚭鐜板湪鏈鍚 + if "(*temporary)" ~= tostring(n) then + local var = {}; + var.name = n; + var.type = tostring(type(v)); + var.variablesReference = "0"; + var.index = k; + + if isGetValue == false then + xpcall(function() var.value = tostring(v) end , function() var.value = tostring(type(v)) .. " [value can't trans to string]" end ); + if var.type == "table" or var.type == "function" or var.type == "userdata" then + var.variablesReference = variableRefIdx; + variableRefTab[variableRefIdx] = v; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(v); + var.value = memberNum .." Members ".. ( var.value or '' ); + end + elseif var.type == "string" then + var.value = '"' ..v.. '"'; + end + else + var.value = v; + end + + local sameIdx = this.checkSameNameVar(varTab, var); + if sameIdx ~= 0 then + varTab[sameIdx] = var; + else + table.insert(varTab, var); + end + end + k = k + 1 + until n == nil + return varTab, stacklayer - 1; +end + +--妫鏌ュ彉閲忓垪琛ㄤ腑鐨勫悓鍚嶅彉閲 +function this.checkSameNameVar(varTab, var) + for k , v in pairs(varTab) do + if v.name == var.name then + return k; + end + end + return 0; +end + +-- 鎵ц琛ㄨ揪寮 +function this.processExp(msgTable) + local retString; + local var = {}; + var.isSuccess = "true"; + if msgTable ~= nil then + local expression = this.trim(tostring(msgTable.Expression)); + local isCmd = false; + if isCmd == false then + --鍏煎鏃х増p 鍛戒护 + if expression:find("p ", 1, true) == 1 then + expression = expression:sub(3); + end + + local expressionWithReturn = "return " .. expression; + local f = debugger_loadString(expressionWithReturn) or debugger_loadString(expression); + --鍒ゆ柇缁撴灉锛屽鏋滆〃杈惧紡閿欒浼氳繑鍥瀗il + if type(f) == "function" then + if _VERSION == "Lua 5.1" then + setfenv(f , env); + else + debug.setupvalue(f, 1, env); + end + --琛ㄨ揪寮忚鏈夐敊璇鐞 + xpcall(function() retString = f() end , function() retString = "杈撳叆閿欒鎸囦护銆俓n + 璇锋鏌ユ寚浠ゆ槸鍚︽纭甛n + 鎸囦护浠呰兘鍦╗鏆傚仠鍦ㄦ柇鐐规椂]杈撳叆, 璇蜂笉瑕佸湪绋嬪簭鎸佺画杩愯鏃惰緭鍏"; var.isSuccess = false; end) + else + retString = "鎸囦护鎵ц閿欒銆俓n + 璇锋鏌ユ寚浠ゆ槸鍚︽纭甛n + 鍙互鐩存帴杈撳叆琛ㄨ揪寮忥紝鎵ц鍑芥暟鎴栧彉閲忓悕锛屽苟瑙傚療鎵ц缁撴灉"; + var.isSuccess = false; + end + end + end + + var.name = "Exp"; + var.type = tostring(type(retString)); + xpcall(function() var.value = tostring(retString) end , function(e) var.value = tostring(type(retString)) .. " [value can't trans to string] ".. e; var.isSuccess = false; end); + var.variablesReference = "0"; + if var.type == "table" or var.type == "function" or var.type == "userdata" then + variableRefTab[variableRefIdx] = retString; + var.variablesReference = variableRefIdx; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(retString); + var.value = memberNum .." Members ".. var.value; + end + elseif var.type == "string" then + var.value = '"' ..retString.. '"'; + end + --string鎵ц瀹屾瘯鍚庢竻绌篹nv鐜 + this.clearEnv(); + local retTab = {} + table.insert(retTab ,var); + return retTab; +end + +--鎵ц鍙橀噺瑙傚療琛ㄨ揪寮 +function this.processWatchedExp(msgTable) + local retString; + local expression = "return ".. tostring(msgTable.varName) + this.printToConsole("processWatchedExp | expression: " .. expression); + local f = debugger_loadString(expression); + local var = {}; + var.isSuccess = "true"; + --鍒ゆ柇缁撴灉锛屽鏋滆〃杈惧紡閿欒浼氳繑鍥瀗il + if type(f) == "function" then + --琛ㄨ揪寮忔纭 + if _VERSION == "Lua 5.1" then + setfenv(f , env); + else + debug.setupvalue(f, 1, env); + end + xpcall(function() retString = f() end , function() retString = "杈撳叆浜嗛敊璇殑鍙橀噺淇℃伅"; var.isSuccess = "false"; end) + else + retString = "鏈兘鎵惧埌鍙橀噺鐨勫"; + var.isSuccess = "false"; + end + + var.name = msgTable.varName; + var.type = tostring(type(retString)); + xpcall(function() var.value = tostring(retString) end , function() var.value = tostring(type(retString)) .. " [value can't trans to string]"; var.isSuccess = "false"; end ); + var.variablesReference = "0"; + + if var.type == "table" or var.type == "function" or var.type == "userdata" then + variableRefTab[variableRefIdx] = retString; + var.variablesReference = variableRefIdx; + variableRefIdx = variableRefIdx + 1; + if var.type == "table" then + local memberNum = this.getTableMemberNum(retString); + var.value = memberNum .." Members ".. var.value; + end + elseif var.type == "string" then + var.value = '"' ..retString.. '"'; + end + + local retTab = {} + table.insert(retTab ,var); + return retTab; +end + + +function tools.getFileSource() + local info = debug.getinfo(1, "S") + for k,v in pairs(info) do + if k == "source" then + return v; + end + end +end + +--搴忓垪鍖栧苟鎵撳嵃table +function tools.printTable(t, name ,indent) + local str = (tools.show(t, name, indent)); + print(str); +end + +--搴忓垪鍖栧苟杩斿洖table +function tools.serializeTable(t, name, indent) + local str = (tools.show(t, name, indent)) + return str +end + +--[[ +Author: Julio Manuel Fernandez-Diaz +Date: January 12, 2007 +Modified slightly by RiciLake to avoid the unnecessary table traversal in tablecount() +Formats tables with cycles recursively to any depth. +The output is returned as a string. +References to other tables are shown as values. +Self references are indicated. +The string returned is "Lua code", which can be procesed +(in the case in which indent is composed by spaces or "--"). +Userdata and function keys and values are shown as strings, +which logically are exactly not equivalent to the original code. +This routine can serve for pretty formating tables with +proper indentations, apart from printing them: +print(table.show(t, "t")) -- a typical use +Heavily based on "Saving tables with cycles", PIL2, p. 113. +Arguments: +t is the table. +name is the name of the table (optional) +indent is a first indentation (optional). +--]] +function tools.show(t, name, indent) + local cart -- a container + local autoref -- for self references + + local function isemptytable(t) return next(t) == nil end + + local function basicSerialize (o) + local so = tostring(o) + if type(o) == "function" then + local info = debug.getinfo(o, "S") + -- info.name is nil because o is not a calling level + if info.what == "C" then + return string.format("%q", so .. ", C function") + else + -- the information is defined through lines + return string.format("%q", so .. ", defined in (" .. + info.linedefined .. "-" .. info.lastlinedefined .. + ")" .. info.source) + end + elseif type(o) == "number" or type(o) == "boolean" then + return so + else + return string.format("%q", so) + end + end + + local function addtocart (value, name, indent, saved, field) + indent = indent or "" + saved = saved or {} + field = field or name + + cart = cart .. indent .. field + + if type(value) ~= "table" then + cart = cart .. " = " .. basicSerialize(value) .. ";\n" + else + if saved[value] then + cart = cart .. " = {}; -- " .. saved[value] + .. " (self reference)\n" + autoref = autoref .. name .. " = " .. saved[value] .. ";\n" + else + saved[value] = name + --if tablecount(value) == 0 then + if isemptytable(value) then + cart = cart .. " = {};\n" + else + cart = cart .. " = {\n" + for k, v in pairs(value) do + k = basicSerialize(k) + local fname = string.format("%s[%s]", name, k) + field = string.format("[%s]", k) + -- three spaces between levels + addtocart(v, fname, indent .. " ", saved, field) + end + cart = cart .. indent .. "};\n" + end + end + end + end + + name = name or "PRINT_Table" + if type(t) ~= "table" then + return name .. " = " .. basicSerialize(t) + end + cart, autoref = "", "" + addtocart(t, name, indent) + return cart .. autoref +end + +----------------------------------------------------------------------------- +-- JSON4Lua: JSON encoding / decoding support for the Lua language. +-- json Module. +-- Author: Craig Mason-Jones +-- Homepage: http://github.com/craigmj/json4lua/ +-- Version: 1.0.0 +-- This module is released under the MIT License (MIT). +-- Please see LICENCE.txt for details. +-- +-- USAGE: +-- This module exposes two functions: +-- json.encode(o) +-- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string. +-- json.decode(json_string) +-- Returns a Lua object populated with the data encoded in the JSON string json_string. +-- +-- REQUIREMENTS: +-- compat-5.1 if using Lua 5.0 +-- +-- CHANGELOG +-- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix). +-- Fixed Lua 5.1 compatibility issues. +-- Introduced json.null to have null values in associative arrays. +-- json.encode() performance improvement (more than 50%) through table.concat rather than .. +-- Introduced decode ability to ignore /**/ comments in the JSON string. +-- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays. +----------------------------------------------------------------------------- + +function tools.createJson() + ----------------------------------------------------------------------------- + -- Imports and dependencies + ----------------------------------------------------------------------------- + local math = require('math') + local string = require("string") + local table = require("table") + + ----------------------------------------------------------------------------- + -- Module declaration + ----------------------------------------------------------------------------- + local json = {} -- Public namespace + local json_private = {} -- Private namespace + + -- Public constants + json.EMPTY_ARRAY={} + json.EMPTY_OBJECT={} + + -- Public functions + + -- Private functions + local decode_scanArray + local decode_scanComment + local decode_scanConstant + local decode_scanNumber + local decode_scanObject + local decode_scanString + local decode_scanWhitespace + local encodeString + local isArray + local isEncodable + + ----------------------------------------------------------------------------- + -- PUBLIC FUNCTIONS + ----------------------------------------------------------------------------- + --- Encodes an arbitrary Lua object / variable. + -- @param v The Lua object / variable to be JSON encoded. + -- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) + function json.encode (v) + -- Handle nil values + if v==nil then + return "null" + end + + local vtype = type(v) + + -- Handle strings + if vtype=='string' then + return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string + end + + -- Handle booleans + if vtype=='number' or vtype=='boolean' then + return tostring(v) + end + + -- Handle tables + if vtype=='table' then + local rval = {} + -- Consider arrays separately + local bArray, maxCount = isArray(v) + if bArray then + for i = 1,maxCount do + table.insert(rval, json.encode(v[i])) + end + else -- An object, not an array + for i,j in pairs(v) do + if isEncodable(i) and isEncodable(j) then + table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) + end + end + end + if bArray then + return '[' .. table.concat(rval,',') ..']' + else + return '{' .. table.concat(rval,',') .. '}' + end + end + + -- Handle null values + if vtype=='function' and v==json.null then + return 'null' + end + + assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. tostring(v)) + end + + + --- Decodes a JSON string and returns the decoded value as a Lua data structure / value. + -- @param s The string to scan. + -- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. + -- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, + -- and the position of the first character after + -- the scanned JSON object. + function json.decode(s, startPos) + startPos = startPos and startPos or 1 + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') + local curChar = string.sub(s,startPos,startPos) + -- Object + if curChar=='{' then + return decode_scanObject(s,startPos) + end + -- Array + if curChar=='[' then + return decode_scanArray(s,startPos) + end + -- Number + if string.find("+-0123456789.e", curChar, 1, true) then + return decode_scanNumber(s,startPos) + end + -- String + if curChar==[["]] or curChar==[[']] then + return decode_scanString(s,startPos) + end + if string.sub(s,startPos,startPos+1)=='/*' then + return json.decode(s, decode_scanComment(s,startPos)) + end + -- Otherwise, it must be a constant + return decode_scanConstant(s,startPos) + end + + --- The null function allows one to specify a null value in an associative array (which is otherwise + -- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } + function json.null() + return json.null -- so json.null() will also return null ;-) + end + ----------------------------------------------------------------------------- + -- Internal, PRIVATE functions. + -- Following a Python-like convention, I have prefixed all these 'PRIVATE' + -- functions with an underscore. + ----------------------------------------------------------------------------- + + --- Scans an array from JSON into a Lua object + -- startPos begins at the start of the array. + -- Returns the array and the next starting position + -- @param s The string being scanned. + -- @param startPos The starting position for the scan. + -- @return table, int The scanned array as a table, and the position of the next character to scan. + function decode_scanArray(s,startPos) + local array = {} -- The return value + local stringLen = string.len(s) + assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) + startPos = startPos + 1 + -- Infinite loop for array elements + local index = 1 + repeat + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') + local curChar = string.sub(s,startPos,startPos) + if (curChar==']') then + return array, startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') + local object + object, startPos = json.decode(s,startPos) + array[index] = object + index = index + 1 + until false + end + + --- Scans a comment and discards the comment. + -- Returns the position of the next character following the comment. + -- @param string s The JSON string to scan. + -- @param int startPos The starting position of the comment + function decode_scanComment(s, startPos) + assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) + local endPos = string.find(s,'*/',startPos+2) + assert(endPos~=nil, "Unterminated comment in string at " .. startPos) + return endPos+2 + end + + --- Scans for given constants: true, false or null + -- Returns the appropriate Lua type, and the position of the next character to read. + -- @param s The string being scanned. + -- @param startPos The position in the string at which to start scanning. + -- @return object, int The object (true, false or nil) and the position at which the next character should be + -- scanned. + function decode_scanConstant(s, startPos) + local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } + local constNames = {"true","false","null"} + + for i,k in pairs(constNames) do + if string.sub(s,startPos, startPos + string.len(k) -1 )==k then + return consts[k], startPos + string.len(k) + end + end + assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) + end + + --- Scans a number from the JSON encoded string. + -- (in fact, also is able to scan numeric +- eqns, which is not + -- in the JSON spec.) + -- Returns the number, and the position of the next character + -- after the number. + -- @param s The string being scanned. + -- @param startPos The position at which to start scanning. + -- @return number, int The extracted number and the position of the next character to scan. + function decode_scanNumber(s,startPos) + local endPos = startPos+1 + local stringLen = string.len(s) + local acceptableChars = "+-0123456789.e" + while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) + and endPos<=stringLen + ) do + endPos = endPos + 1 + end + -- local stringValue = 'return ' .. string.sub(s, startPos, endPos - 1) + -- local stringEval = loadstring(stringValue) + -- assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) + local numberValue = string.sub(s, startPos, endPos - 1) + return numberValue, endPos + end + + --- Scans a JSON object into a Lua object. + -- startPos begins at the start of the object. + -- Returns the object and the next starting position. + -- @param s The string being scanned. + -- @param startPos The starting position of the scan. + -- @return table, int The scanned object as a table and the position of the next character to scan. + function decode_scanObject(s,startPos) + local object = {} + local stringLen = string.len(s) + local key, value + assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) + startPos = startPos + 1 + repeat + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') + local curChar = string.sub(s,startPos,startPos) + if (curChar=='}') then + return object,startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') + -- Scan the key + key, startPos = json.decode(s,startPos) + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + startPos = decode_scanWhitespace(s,startPos) + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) + startPos = decode_scanWhitespace(s,startPos+1) + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + value, startPos = json.decode(s,startPos) + object[key]=value + until false -- infinite loop while key-value pairs are found + end + + -- START SoniEx2 + -- Initialize some things used by decode_scanString + -- You know, for efficiency + local escapeSequences = { + ["\\t"] = "\t", + ["\\f"] = "\f", + ["\\r"] = "\r", + ["\\n"] = "\n", + ["\\b"] = "\b" + } + setmetatable(escapeSequences, {__index = function(t,k) + -- skip "\" aka strip escape + return string.sub(k,2) + end}) + -- END SoniEx2 + + --- Scans a JSON string from the opening inverted comma or single quote to the + -- end of the string. + -- Returns the string extracted as a Lua string, + -- and the position of the next non-string character + -- (after the closing inverted comma or single quote). + -- @param s The string being scanned. + -- @param startPos The starting position of the scan. + -- @return string, int The extracted string as a Lua string, and the next character to parse. + function decode_scanString(s,startPos) + assert(startPos, 'decode_scanString(..) called without start position') + local startChar = string.sub(s,startPos,startPos) + -- START SoniEx2 + -- PS: I don't think single quotes are valid JSON + assert(startChar == [["]] or startChar == [[']],'decode_scanString called for a non-string') + --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) + local t = {} + local i,j = startPos,startPos + while string.find(s, startChar, j+1) ~= j+1 do + local oldj = j + i,j = string.find(s, "\\.", j+1) + local x,y = string.find(s, startChar, oldj+1) + if not i or x < i then + i,j = x,y-1 + end + table.insert(t, string.sub(s, oldj+1, i-1)) + if string.sub(s, i, j) == "\\u" then + local a = string.sub(s,j+1,j+4) + j = j + 4 + local n = tonumber(a, 16) + assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) + -- math.floor(x/2^y) == lazy right shift + -- a % 2^b == bitwise_and(a, (2^b)-1) + -- 64 = 2^6 + -- 4096 = 2^12 (or 2^6 * 2^6) + local x + if n < 0x80 then + x = string.char(n % 0x80) + elseif n < 0x800 then + -- [110x xxxx] [10xx xxxx] + x = string.char(0xC0 + (math.floor(n/64) % 0x20), 0x80 + (n % 0x40)) + else + -- [1110 xxxx] [10xx xxxx] [10xx xxxx] + x = string.char(0xE0 + (math.floor(n/4096) % 0x10), 0x80 + (math.floor(n/64) % 0x40), 0x80 + (n % 0x40)) + end + table.insert(t, x) + else + table.insert(t, escapeSequences[string.sub(s, i, j)]) + end + end + table.insert(t,string.sub(j, j+1)) + assert(string.find(s, startChar, j+1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") + return table.concat(t,""), j+2 + -- END SoniEx2 + end + + --- Scans a JSON string skipping all whitespace from the current start position. + -- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. + -- @param s The string being scanned + -- @param startPos The starting position where we should begin removing whitespace. + -- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string + -- was reached. + function decode_scanWhitespace(s,startPos) + local whitespace=" \n\r\t" + local stringLen = string.len(s) + while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do + startPos = startPos + 1 + end + return startPos + end + + --- Encodes a string to be JSON-compatible. + -- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) + -- @param s The string to return as a JSON encoded (i.e. backquoted string) + -- @return The string appropriately escaped. + + local escapeList = { + ['"'] = '\\"', + ['\\'] = '\\\\', + ['/'] = '\\/', + ['\b'] = '\\b', + ['\f'] = '\\f', + ['\n'] = '\\n', + ['\r'] = '\\r', + ['\t'] = '\\t' + } + + function json_private.encodeString(s) + local s = tostring(s) + return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat + end + + -- Determines whether the given Lua type is an array or a table / dictionary. + -- We consider any table an array if it has indexes 1..n for its n items, and no + -- other data in the table. + -- I think this method is currently a little 'flaky', but can't think of a good way around it yet... + -- @param t The table to evaluate as an array + -- @return boolean, number True if the table can be represented as an array, false otherwise. If true, + -- the second returned value is the maximum + -- number of indexed elements in the array. + function isArray(t) + -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable + -- (with the possible exception of 'n') + if (t == json.EMPTY_ARRAY) then return true, 0 end + if (t == json.EMPTY_OBJECT) then return false end + + local maxIndex = 0 + for k,v in pairs(t) do + if (type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair + if (not isEncodable(v)) then return false end -- All array elements must be encodable + maxIndex = math.max(maxIndex,k) + else + if (k=='n') then + if v ~= (t.n or #t) then return false end -- False if n does not hold the number of elements + else -- Else of (k=='n') + if isEncodable(v) then return false end + end -- End of (k~='n') + end -- End of k,v not an indexed pair + end -- End of loop across all pairs + return true, maxIndex + end + + --- Determines whether the given Lua object / table / variable can be JSON encoded. The only + -- types that are JSON encodable are: string, boolean, number, nil, table and json.null. + -- In this implementation, all other types are ignored. + -- @param o The object to examine. + -- @return boolean True if the object should be JSON encoded, false if it should be ignored. + function isEncodable(o) + local t = type(o) + return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or + (t=='function' and o==json.null) + end + return json +end + +-- Sourced from http://lua-users.org/wiki/BaseSixtyFour + +-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de> +-- licensed under the terms of the LGPL2 + +-- character table string +local base64CharTable='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +-- encoding +function tools.base64encode(data) + return ((data:gsub('.', function(x) + local r,b='',x:byte() + for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end + return r; + end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) + if (#x < 6) then return '' end + local c=0 + for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end + return base64CharTable:sub(c+1,c+1) + end)..({ '', '==', '=' })[#data%3+1]) +end + +-- decoding +function tools.base64decode(data) + data = string.gsub(data, '[^'..base64CharTable..'=]', '') + return (data:gsub('.', function(x) + if (x == '=') then return '' end + local r,f='',(base64CharTable:find(x)-1) + for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end + return r; + end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) + if (#x ~= 8) then return '' end + local c=0 + for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end + return string.char(c) + end)) +end + +-- tools鍙橀噺 +json = tools.createJson(); --json澶勭悊 +this.printToConsole("load LuaPanda success", 1); +this.replaceCoroutineFuncs() +return this; diff --git a/Resources/Libraries/socket/core.dll b/Resources/Libraries/socket/core.dll Binary files differnew file mode 100644 index 0000000..9febf20 --- /dev/null +++ b/Resources/Libraries/socket/core.dll diff --git a/Resources/Scripts/EditorApplication.lua b/Resources/Scripts/EditorApplication.lua index c8438cf..c4d9389 100644 --- a/Resources/Scripts/EditorApplication.lua +++ b/Resources/Scripts/EditorApplication.lua @@ -1,4 +1,4 @@ -require "./Scripts/Utils/Utils" +require("LuaPanda").start("127.0.0.1",8818); local Debug = GameLab.Debug local GUI = GameLab.Editor.GUI @@ -20,8 +20,10 @@ local guiWindow = GUI.GUIWindow.New() guiWindow:SetContainnerWindow(mainWindow) guiWindow:SetPosition({0,0, 500, 400}) +Debug.Log(package.cpath) + while true do app:PullMessage() -end
\ No newline at end of file +end
\ No newline at end of file diff --git a/Resources/readme.txt b/Resources/readme.txt index e3b0dc5..f45359b 100644 --- a/Resources/readme.txt +++ b/Resources/readme.txt @@ -1 +1,7 @@ -缂栬緫鍣ㄧ敤鐨勮祫婧
\ No newline at end of file +缂栬緫鍣ㄨ祫婧 +* DefaultContent 杩愯鏃剁浉鍏崇殑鑴氭湰鍜岃祫婧 +* Icon 鍥炬爣鏂囦欢 +* Images 鍥剧墖璧勬簮 +* Libraries 缂栬緫鍣ㄧ敤鍒扮殑妯″潡 +* Scripts 缂栬緫鍣ㄨ剼鏈 +* Shaders 缂栬緫鍣ㄧ敤鍒扮殑鐫鑹插櫒
\ No newline at end of file |