From 03a56a63e1c943e985b73082be34749a3bd5533b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=80=E8=90=8C=E5=B0=8F=E6=B1=90?= Date: Wed, 20 Nov 2019 11:00:32 +0800 Subject: =?UTF-8?q?searcher=E5=A4=AA=E9=9A=BE=E6=8B=BC=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E6=88=91=E4=BB=AC=E8=BF=98=E6=98=AF=E5=8F=ABvm=E5=90=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server-beta/src/core/definition.lua | 4 +- .../src/core/diagnostics/global-in-nil-env.lua | 1 - .../src/core/diagnostics/redundant-parameter.lua | 16 +- .../src/core/diagnostics/undefined-env-child.lua | 10 +- .../src/core/diagnostics/undefined-global.lua | 14 +- .../src/core/diagnostics/unused-function.lua | 14 +- server-beta/src/core/highlight.lua | 14 +- server-beta/src/core/reference.lua | 8 +- server-beta/src/core/rename.lua | 26 +- server-beta/src/files.lua | 26 +- server-beta/src/searcher/eachDef.lua | 65 --- server-beta/src/searcher/eachField.lua | 163 ------- server-beta/src/searcher/eachRef.lua | 505 --------------------- server-beta/src/searcher/getGlobal.lua | 6 - server-beta/src/searcher/getGlobals.lua | 45 -- server-beta/src/searcher/getLibrary.lua | 60 --- server-beta/src/searcher/getLinks.lua | 48 -- server-beta/src/searcher/getValue.lua | 454 ------------------ server-beta/src/searcher/init.lua | 10 - server-beta/src/searcher/searcher.lua | 81 ---- server-beta/src/searcher/special.lua | 0 server-beta/src/service/service.lua | 14 +- server-beta/src/vm/eachDef.lua | 65 +++ server-beta/src/vm/eachField.lua | 163 +++++++ server-beta/src/vm/eachRef.lua | 504 ++++++++++++++++++++ server-beta/src/vm/getGlobal.lua | 6 + server-beta/src/vm/getGlobals.lua | 45 ++ server-beta/src/vm/getLibrary.lua | 60 +++ server-beta/src/vm/getLinks.lua | 48 ++ server-beta/src/vm/getValue.lua | 452 ++++++++++++++++++ server-beta/src/vm/init.lua | 10 + server-beta/src/vm/special.lua | 0 server-beta/src/vm/vm.lua | 81 ++++ 33 files changed, 1507 insertions(+), 1511 deletions(-) delete mode 100644 server-beta/src/searcher/eachDef.lua delete mode 100644 server-beta/src/searcher/eachField.lua delete mode 100644 server-beta/src/searcher/eachRef.lua delete mode 100644 server-beta/src/searcher/getGlobal.lua delete mode 100644 server-beta/src/searcher/getGlobals.lua delete mode 100644 server-beta/src/searcher/getLibrary.lua delete mode 100644 server-beta/src/searcher/getLinks.lua delete mode 100644 server-beta/src/searcher/getValue.lua delete mode 100644 server-beta/src/searcher/init.lua delete mode 100644 server-beta/src/searcher/searcher.lua delete mode 100644 server-beta/src/searcher/special.lua create mode 100644 server-beta/src/vm/eachDef.lua create mode 100644 server-beta/src/vm/eachField.lua create mode 100644 server-beta/src/vm/eachRef.lua create mode 100644 server-beta/src/vm/getGlobal.lua create mode 100644 server-beta/src/vm/getGlobals.lua create mode 100644 server-beta/src/vm/getLibrary.lua create mode 100644 server-beta/src/vm/getLinks.lua create mode 100644 server-beta/src/vm/getValue.lua create mode 100644 server-beta/src/vm/init.lua create mode 100644 server-beta/src/vm/special.lua create mode 100644 server-beta/src/vm/vm.lua (limited to 'server-beta/src') diff --git a/server-beta/src/core/definition.lua b/server-beta/src/core/definition.lua index a7008926..865fc7cb 100644 --- a/server-beta/src/core/definition.lua +++ b/server-beta/src/core/definition.lua @@ -1,7 +1,7 @@ local guide = require 'parser.guide' local workspace = require 'workspace' local files = require 'files' -local searcher = require 'searcher' +local vm = require 'vm' local function findDef(source, callback) if source.type ~= 'local' @@ -17,7 +17,7 @@ local function findDef(source, callback) and source.type ~= 'goto' then return end - searcher.eachDef(source, function (info) + vm.eachDef(source, function (info) if info.mode == 'declare' or info.mode == 'set' or info.mode == 'return' then diff --git a/server-beta/src/core/diagnostics/global-in-nil-env.lua b/server-beta/src/core/diagnostics/global-in-nil-env.lua index d8cc0075..9a0d4f35 100644 --- a/server-beta/src/core/diagnostics/global-in-nil-env.lua +++ b/server-beta/src/core/diagnostics/global-in-nil-env.lua @@ -1,6 +1,5 @@ local files = require 'files' local guide = require 'parser.guide' -local searcher = require 'searcher' local lang = require 'language' -- TODO: 检查路径是否可达 diff --git a/server-beta/src/core/diagnostics/redundant-parameter.lua b/server-beta/src/core/diagnostics/redundant-parameter.lua index a60c8cdb..ec14188e 100644 --- a/server-beta/src/core/diagnostics/redundant-parameter.lua +++ b/server-beta/src/core/diagnostics/redundant-parameter.lua @@ -1,12 +1,12 @@ -local files = require 'files' -local guide = require 'parser.guide' -local searcher = require 'searcher' -local lang = require 'language' -local define = require 'proto.define' -local await = require 'await' +local files = require 'files' +local guide = require 'parser.guide' +local vm = require 'vm' +local lang = require 'language' +local define = require 'proto.define' +local await = require 'await' local function countLibraryArgs(source) - local func = searcher.getLibrary(source) + local func = vm.getLibrary(source) if not func then return nil end @@ -66,7 +66,7 @@ return function (uri, callback) local func = source.node local funcArgs - searcher.eachDef(func, function (info) + vm.eachDef(func, function (info) if info.mode == 'value' then local src = info.source if src.type == 'function' then diff --git a/server-beta/src/core/diagnostics/undefined-env-child.lua b/server-beta/src/core/diagnostics/undefined-env-child.lua index 4169a413..df096cb8 100644 --- a/server-beta/src/core/diagnostics/undefined-env-child.lua +++ b/server-beta/src/core/diagnostics/undefined-env-child.lua @@ -1,7 +1,7 @@ -local files = require 'files' -local guide = require 'parser.guide' -local searcher = require 'searcher' -local lang = require 'language' +local files = require 'files' +local guide = require 'parser.guide' +local vm = require 'vm' +local lang = require 'language' return function (uri, callback) local ast = files.getAst(uri) @@ -14,7 +14,7 @@ return function (uri, callback) if source.node.tag == '_ENV' then return end - local setInENV = searcher.eachRef(source, function (info) + local setInENV = vm.eachRef(source, function (info) if info.mode == 'set' then return true end diff --git a/server-beta/src/core/diagnostics/undefined-global.lua b/server-beta/src/core/diagnostics/undefined-global.lua index c3fbccbf..ed81ced3 100644 --- a/server-beta/src/core/diagnostics/undefined-global.lua +++ b/server-beta/src/core/diagnostics/undefined-global.lua @@ -1,8 +1,8 @@ -local files = require 'files' -local searcher = require 'searcher' -local lang = require 'language' -local library = require 'library' -local config = require 'config' +local files = require 'files' +local vm = require 'vm' +local lang = require 'language' +local library = require 'library' +local config = require 'config' return function (uri, callback) local ast = files.getAst(uri) @@ -13,7 +13,7 @@ return function (uri, callback) local globalCache = {} -- 遍历全局变量,检查所有没有 mode['set'] 的全局变量 - local globals = searcher.getGlobals(ast.ast) + local globals = vm.getGlobals(ast.ast) for key, infos in pairs(globals) do if infos.mode['set'] == true then goto CONTINUE @@ -35,7 +35,7 @@ return function (uri, callback) local uris = files.findGlobals(key) for i = 1, #uris do local destAst = files.getAst(uris[i]) - local destGlobals = searcher.getGlobals(destAst.ast) + local destGlobals = vm.getGlobals(destAst.ast) if destGlobals[key] and destGlobals[key].mode['set'] then globalCache[key] = true goto CONTINUE diff --git a/server-beta/src/core/diagnostics/unused-function.lua b/server-beta/src/core/diagnostics/unused-function.lua index ca3a751d..6c53cdf7 100644 --- a/server-beta/src/core/diagnostics/unused-function.lua +++ b/server-beta/src/core/diagnostics/unused-function.lua @@ -1,9 +1,9 @@ -local files = require 'files' -local guide = require 'parser.guide' -local searcher = require 'searcher' -local define = require 'proto.define' -local lang = require 'language' -local await = require 'await' +local files = require 'files' +local guide = require 'parser.guide' +local vm = require 'vm' +local define = require 'proto.define' +local lang = require 'language' +local await = require 'await' return function (uri, callback) local ast = files.getAst(uri) @@ -22,7 +22,7 @@ return function (uri, callback) return end local hasSet - local hasGet = searcher.eachRef(source, function (info) + local hasGet = vm.eachRef(source, function (info) if info.mode == 'get' then return true elseif info.mode == 'set' diff --git a/server-beta/src/core/highlight.lua b/server-beta/src/core/highlight.lua index 7ae5333a..61e3f91a 100644 --- a/server-beta/src/core/highlight.lua +++ b/server-beta/src/core/highlight.lua @@ -1,7 +1,7 @@ -local guide = require 'parser.guide' -local files = require 'files' -local searcher = require 'searcher' -local define = require 'proto.define' +local guide = require 'parser.guide' +local files = require 'files' +local vm = require 'vm' +local define = require 'proto.define' local function ofLocal(source, callback) callback(source) @@ -21,7 +21,7 @@ local function ofField(source, uri, callback) if parent.type == 'tableindex' or parent.type == 'tablefield' then local tbl = parent.parent - searcher.eachField(tbl, function (info) + vm.eachField(tbl, function (info) if info.key ~= myKey then return end @@ -32,7 +32,7 @@ local function ofField(source, uri, callback) callback(info.source) end) else - searcher.eachField(parent.node, function (info) + vm.eachField(parent.node, function (info) if info.key ~= myKey then return end @@ -58,7 +58,7 @@ local function ofIndex(source, uri, callback) end local function ofLabel(source, callback) - searcher.eachRef(source, function (info) + vm.eachRef(source, function (info) callback(info.source) end) end diff --git a/server-beta/src/core/reference.lua b/server-beta/src/core/reference.lua index 9ffd8fe0..7e265e97 100644 --- a/server-beta/src/core/reference.lua +++ b/server-beta/src/core/reference.lua @@ -1,6 +1,6 @@ -local guide = require 'parser.guide' -local files = require 'files' -local searcher = require 'searcher' +local guide = require 'parser.guide' +local files = require 'files' +local vm = require 'vm' local function isFunction(source, offset) if source.type ~= 'function' then @@ -27,7 +27,7 @@ local function findRef(source, offset, callback) and not isFunction(source, offset) then return end - searcher.eachRef(source, function (info) + vm.eachRef(source, function (info) if info.mode == 'declare' or info.mode == 'set' or info.mode == 'get' diff --git a/server-beta/src/core/rename.lua b/server-beta/src/core/rename.lua index 6f4f26e6..4c67bc9c 100644 --- a/server-beta/src/core/rename.lua +++ b/server-beta/src/core/rename.lua @@ -1,9 +1,9 @@ -local files = require 'files' -local searcher = require 'searcher' -local guide = require 'parser.guide' -local proto = require 'proto' -local define = require 'proto.define' -local util = require 'utility' +local files = require 'files' +local vm = require 'vm' +local guide = require 'parser.guide' +local proto = require 'proto' +local define = require 'proto.define' +local util = require 'utility' local Forcing @@ -127,10 +127,10 @@ local function renameField(source, newname, callback) if parent.type == 'setfield' or parent.type == 'getfield' then local dot = parent.dot - local newstr = '[' .. util.vieString('"', newname) .. ']' + local newstr = '[' .. util.viewString('"', newname) .. ']' callback(source, dot.start, source.finish, newstr) elseif parent.type == 'tablefield' then - local newstr = '[' .. util.vieString('"', newname) .. ']' + local newstr = '[' .. util.viewString('"', newname) .. ']' callback(source, source.start, source.finish, newstr) elseif parent.type == 'getmethod' then if not askForcing(newname) then @@ -144,7 +144,7 @@ local function renameField(source, newname, callback) -- function mt:name () end --> mt['newname'] = function (self) end local newstr = string.format('%s[%s] = function ' , text:sub(parent.start, parent.node.finish) - , util.vieString('"', newname) + , util.viewString('"', newname) ) callback(source, func.start, parent.finish, newstr) local pl = text:find('(', parent.finish, true) @@ -164,7 +164,7 @@ local function renameGlobal(source, newname, callback) callback(source, source.start, source.finish, newname) return true end - local newstr = '_ENV[' .. util.vieString('"', newname) .. ']' + local newstr = '_ENV[' .. util.viewString('"', newname) .. ']' -- function name () end --> _ENV['newname'] = function () end if source.value and source.value.type == 'function' and source.value.start < source.start then @@ -176,7 +176,7 @@ local function renameGlobal(source, newname, callback) end local function ofField(source, newname, callback) - return searcher.eachRef(source, function (info) + return vm.eachRef(source, function (info) local src = info.source if src.type == 'tablefield' or src.type == 'getfield' @@ -192,7 +192,7 @@ local function ofField(source, newname, callback) end if src.type == 'string' then local quo = src[2] - local text = util.vieString(quo, newname) + local text = util.viewString(quo, newname) callback(src, src.start, src.finish, text) return elseif src.type == 'field' @@ -217,7 +217,7 @@ local function rename(source, newname, callback) if not isValidName(newname) and not askForcing(newname)then return false end - searcher.eachRef(source, function (info) + vm.eachRef(source, function (info) callback(info.source, info.source.start, info.source.finish, newname) end) elseif source.type == 'local' then diff --git a/server-beta/src/files.lua b/server-beta/src/files.lua index 34c8c2de..34cba439 100644 --- a/server-beta/src/files.lua +++ b/server-beta/src/files.lua @@ -1,10 +1,10 @@ -local platform = require 'bee.platform' -local config = require 'config' -local glob = require 'glob' -local furi = require 'file-uri' -local parser = require 'parser' -local searcher = require 'searcher.searcher' -local guide = require 'parser.guide' +local platform = require 'bee.platform' +local config = require 'config' +local glob = require 'glob' +local furi = require 'file-uri' +local parser = require 'parser' +local vm = require 'vm.vm' +local guide = require 'parser.guide' local m = {} @@ -69,13 +69,13 @@ function m.setText(uri, text) return end file.text = text - file.searcher = nil + file.vm = nil file.lines = nil file.ast = nil file.globals = nil file.links = nil m.globalVersion = m.globalVersion + 1 - searcher.refreshCache() + vm.refreshCache() local diagnostic = require 'provider.diagnostic' diagnostic.refresh(originUri) @@ -123,7 +123,7 @@ function m.remove(uri) m.fileMap[uri] = nil m.globalVersion = m.globalVersion + 1 - searcher.refreshCache() + vm.refreshCache() local diagnostic = require 'service.diagnostic' diagnostic.refresh(file.uri) @@ -135,7 +135,7 @@ function m.removeAll() m.fileMap[uri] = nil end m.globalVersion = m.globalVersion + 1 - searcher.refreshCache() + vm.refreshCache() end --- 遍历文件 @@ -225,7 +225,7 @@ function m.findGlobals(name) file.globals = {} local ast = m.getAst(uri) if ast then - local globals = searcher.getGlobals(ast.ast) + local globals = vm.getGlobals(ast.ast) for name in pairs(globals) do file.globals[name] = true end @@ -248,7 +248,7 @@ function m.findLinkTo(uri) if file.links == nil then local ast = m.getAst(file.uri) if ast then - file.links = searcher.getLinks(ast.ast) + file.links = vm.getLinks(ast.ast) else file.links = false end diff --git a/server-beta/src/searcher/eachDef.lua b/server-beta/src/searcher/eachDef.lua deleted file mode 100644 index bc9f9e1c..00000000 --- a/server-beta/src/searcher/eachDef.lua +++ /dev/null @@ -1,65 +0,0 @@ -local searcher = require 'searcher.searcher' -local guide = require 'parser.guide' -local files = require 'files' - -local function checkPath(source, info) - if source.type == 'goto' then - return true - end - local src = info.source - local mode = guide.getPath(source, src) - if not mode then - return true - end - if mode == 'before' then - return false - end - return true -end - -function searcher.eachDef(source, callback) - local results = {} - local valueUris = {} - local sourceUri = guide.getRoot(source).uri - searcher.eachRef(source, function (info) - if info.mode == 'declare' - or info.mode == 'set' - or info.mode == 'return' - or info.mode == 'value' then - results[#results+1] = info - local src = info.source - if info.mode == 'return' then - local uri = guide.getRoot(src).uri - valueUris[uri] = info.source - end - end - end) - - for _, info in ipairs(results) do - local src = info.source - local destUri = guide.getRoot(src).uri - -- 如果是同一个文件,则检查位置关系后放行 - if sourceUri == destUri then - if checkPath(source, info) then - callback(info) - end - goto CONTINUE - end - -- 如果是global或field,则直接放行(因为无法确定顺序) - if src.type == 'setindex' - or src.type == 'setfield' - or src.type == 'setmethod' - or src.type == 'tablefield' - or src.type == 'tableindex' - or src.type == 'setglobal' then - callback(info) - goto CONTINUE - end - -- 如果不是同一个文件,则必须在该文件 return 后才放行 - if valueUris[destUri] then - callback(info) - goto CONTINUE - end - ::CONTINUE:: - end -end diff --git a/server-beta/src/searcher/eachField.lua b/server-beta/src/searcher/eachField.lua deleted file mode 100644 index 6245108c..00000000 --- a/server-beta/src/searcher/eachField.lua +++ /dev/null @@ -1,163 +0,0 @@ -local guide = require 'parser.guide' -local searcher = require 'searcher.searcher' - -local function ofTabel(value, callback) - for _, field in ipairs(value) do - if field.type == 'tablefield' - or field.type == 'tableindex' then - callback { - source = field, - key = guide.getKeyName(field), - value = field.value, - mode = 'set', - } - end - end -end - -local function ofENV(source, callback) - if source.type == 'getlocal' then - local parent = source.parent - if parent.type == 'getfield' - or parent.type == 'getmethod' - or parent.type == 'getindex' then - callback { - source = parent, - key = guide.getKeyName(parent), - mode = 'get', - } - end - elseif source.type == 'getglobal' then - callback { - source = source, - key = guide.getKeyName(source), - mode = 'get', - } - elseif source.type == 'setglobal' then - callback { - source = source, - key = guide.getKeyName(source), - mode = 'set', - value = source.value, - } - end -end - -local function ofSpecialArg(source, callback) - local args = source.parent - local call = args.parent - local func = call.node - local name = func.special - if name == 'rawset' then - if args[1] == source and args[2] then - callback { - source = call, - key = guide.getKeyName(args[2]), - value = args[3], - mode = 'set', - } - end - elseif name == 'rawget' then - if args[1] == source and args[2] then - callback { - source = call, - key = guide.getKeyName(args[2]), - mode = 'get', - } - end - elseif name == 'setmetatable' then - if args[1] == source and args[2] then - searcher.eachField(args[2], function (info) - if info.key == 's|__index' and info.value then - searcher.eachField(info.value, callback) - end - end) - end - end -end - -local function ofVar(source, callback) - local parent = source.parent - if not parent then - return - end - if parent.type == 'getfield' - or parent.type == 'getmethod' - or parent.type == 'getindex' then - callback { - source = parent, - key = guide.getKeyName(parent), - mode = 'get', - } - return - end - if parent.type == 'setfield' - or parent.type == 'setmethod' - or parent.type == 'setindex' then - callback { - source = parent, - key = guide.getKeyName(parent), - value = parent.value, - mode = 'set', - } - return - end - if parent.type == 'callargs' then - ofSpecialArg(source, callback) - end -end - -local function eachField(source, callback) - searcher.eachRef(source, function (info) - local src = info.source - if src.tag == '_ENV' then - if src.ref then - for _, ref in ipairs(src.ref) do - ofENV(ref, callback) - end - end - elseif src.type == 'getlocal' - or src.type == 'getglobal' - or src.type == 'getfield' - or src.type == 'getmethod' - or src.type == 'getindex' then - ofVar(src, callback) - elseif src.type == 'table' then - ofTabel(src, callback) - end - end) -end - ---- 获取所有的field -function searcher.eachField(source, callback) - local cache = searcher.cache.eachField[source] - if cache then - for i = 1, #cache do - callback(cache[i]) - end - return - end - local unlock = searcher.lock('eachField', source) - if not unlock then - return - end - cache = {} - searcher.cache.eachField[source] = cache - local mark = {} - eachField(source, function (info) - local src = info.source - if mark[src] then - return - end - mark[src] = true - cache[#cache+1] = info - end) - unlock() - searcher.eachRef(source, function (info) - local src = info.source - searcher.cache.eachField[src] = cache - end) - for i = 1, #cache do - callback(cache[i]) - end -end diff --git a/server-beta/src/searcher/eachRef.lua b/server-beta/src/searcher/eachRef.lua deleted file mode 100644 index d8e3bd6f..00000000 --- a/server-beta/src/searcher/eachRef.lua +++ /dev/null @@ -1,505 +0,0 @@ -local guide = require 'parser.guide' -local files = require 'files' -local workspace = require 'workspace' -local searcher = require 'searcher.searcher' - -local function ofCall(func, index, callback) - searcher.eachRef(func, function (info) - local src = info.source - local returns - if info.mode == 'main' then - returns = src.returns - else - local funcDef = src.value - returns = funcDef and funcDef.returns - end - if returns then - -- 搜索函数第 index 个返回值 - for _, rtn in ipairs(returns) do - local val = rtn[index] - if val then - callback { - source = val, - mode = 'return', - } - searcher.eachRef(val, callback) - end - end - end - end) -end - -local function ofCallSelect(call, index, callback) - local slc = call.parent - if slc.index == index then - searcher.eachRef(slc.parent, callback) - return - end - if call.extParent then - for i = 1, #call.extParent do - slc = call.extParent[i] - if slc.index == index then - searcher.eachRef(slc.parent, callback) - return - end - end - end -end - -local function ofReturn(rtn, index, callback) - local func = guide.getParentFunction(rtn) - if not func then - return - end - -- 搜索函数调用的第 index 个接收值 - if func.type == 'main' then - searcher.eachRef(func, callback) - else - searcher.eachRef(func, function (info) - local source = info.source - local call = source.parent - if not call or call.type ~= 'call' then - return - end - ofCallSelect(call, index, callback) - end) - end -end - -local function ofSpecialCall(call, func, index, callback) - local name = func.special - if name == 'setmetatable' then - if index == 1 then - local args = call.args - if args[1] then - searcher.eachRef(args[1], callback) - end - if args[2] then - searcher.eachField(args[2], function (info) - if info.key == 's|__index' then - searcher.eachRef(info.source, callback) - if info.value then - searcher.eachRef(info.value, callback) - end - end - end) - end - end - elseif name == 'require' then - if index == 1 then - local result = searcher.getLinkUris(call) - if result then - local myUri = guide.getRoot(call).uri - for _, uri in ipairs(result) do - if not files.eq(uri, myUri) then - local ast = files.getAst(uri) - if ast then - ofCall(ast.ast, 1, callback) - end - end - end - end - end - end -end - -local function ofValue(value, callback) - if value.type == 'select' then - -- 检查函数返回值 - local call = value.vararg - if call.type == 'call' then - ofCall(call.node, value.index, callback) - ofSpecialCall(call, call.node, value.index, callback) - end - return - end - - if value.type == 'table' - or value.type == 'string' - or value.type == 'number' - or value.type == 'boolean' - or value.type == 'nil' - or value.type == 'function' then - callback { - source = value, - mode = 'value', - } - end - - searcher.eachRef(value, callback) - - local parent = value.parent - if parent.type == 'local' - or parent.type == 'setglobal' - or parent.type == 'setlocal' - or parent.type == 'setfield' - or parent.type == 'setmethod' - or parent.type == 'setindex' - or parent.type == 'tablefield' - or parent.type == 'tableindex' then - if parent.value == value then - searcher.eachRef(parent, callback) - end - end - if parent.type == 'return' then - for i = 1, #parent do - if parent[i] == value then - ofReturn(parent, i, callback) - break - end - end - end -end - -local function ofSelf(loc, callback) - -- self 的2个特殊引用位置: - -- 1. 当前方法定义时的对象(mt) - local method = loc.method - local node = method.node - searcher.eachRef(node, callback) - -- 2. 调用该方法时传入的对象 -end - ---- 自己作为赋值的值 -local function asValue(source, callback) - local parent = source.parent - if parent and parent.value == source then - if guide.getKeyString(parent) == '__index' then - if parent.type == 'tablefield' - or parent.type == 'tableindex' then - local t = parent.parent - local args = t.parent - if args[2] == t then - local call = args.parent - local func = call.node - if func.special == 'setmetatable' then - searcher.eachRef(args[1], callback) - end - end - end - end - end -end - -local function getCallRecvs(call) - local parent = call.parent - if parent.type ~= 'select' then - return nil - end - local exParent = call.exParent - local recvs = {} - recvs[1] = parent.parent - if exParent then - for _, p in ipairs(exParent) do - recvs[#recvs+1] = p.parent - end - end - return recvs -end - ---- 自己作为函数的参数 -local function asArg(source, callback) - local parent = source.parent - if not parent then - return - end - if parent.type == 'callargs' then - local call = parent.parent - local func = call.node - local name = func.special - if name == 'setmetatable' then - if parent[1] == source then - if parent[2] then - searcher.eachField(parent[2], function (info) - if info.key == 's|__index' then - searcher.eachRef(info.source, callback) - if info.value then - searcher.eachRef(info.value, callback) - end - end - end) - end - end - local recvs = getCallRecvs(call) - if recvs and recvs[1] then - searcher.eachRef(recvs[1], callback) - end - end - end -end - -local function ofLocal(loc, callback) - -- 方法中的 self 使用了一个虚拟的定义位置 - if loc.tag ~= 'self' then - callback { - source = loc, - mode = 'declare', - } - end - if loc.ref then - for _, ref in ipairs(loc.ref) do - if ref.type == 'getlocal' then - callback { - source = ref, - mode = 'get', - } - asValue(ref, callback) - elseif ref.type == 'setlocal' then - callback { - source = ref, - mode = 'set', - } - if ref.value then - ofValue(ref.value, callback) - end - end - end - end - if loc.tag == 'self' then - ofSelf(loc, callback) - end - if loc.value then - ofValue(loc.value, callback) - end - if loc.tag == '_ENV' and loc.ref then - for _, ref in ipairs(loc.ref) do - if ref.type == 'getlocal' then - local parent = ref.parent - if parent.type == 'getfield' - or parent.type == 'getindex' then - if guide.getKeyName(parent) == '_G' then - callback { - source = parent, - mode = 'get', - } - end - end - elseif ref.type == 'getglobal' then - if guide.getKeyString(ref) == '_G' then - callback { - source = ref, - mode = 'get', - } - end - end - end - end -end - -local function ofGlobal(source, callback) - local key = guide.getKeyName(source) - local node = source.node - if node.tag == '_ENV' then - local uris = files.findGlobals(key) - for _, uri in ipairs(uris) do - local ast = files.getAst(uri) - local globals = searcher.getGlobals(ast.ast) - if globals[key] then - for _, info in ipairs(globals[key]) do - callback(info) - if info.value then - ofValue(info.value, callback) - end - end - end - end - else - searcher.eachField(node, function (info) - if key == info.key then - callback { - source = info.source, - mode = info.mode, - } - if info.value then - ofValue(info.value, callback) - end - end - end) - end -end - -local function ofField(source, callback) - local parent = source.parent - local key = guide.getKeyName(source) - if parent.type == 'tablefield' - or parent.type == 'tableindex' then - local tbl = parent.parent - searcher.eachField(tbl, function (info) - if key == info.key then - callback { - source = info.source, - mode = info.mode, - } - if info.value then - ofValue(info.value, callback) - end - end - end) - else - local node = parent.node - searcher.eachField(node, function (info) - if key == info.key then - callback { - source = info.source, - mode = info.mode, - } - if info.value then - ofValue(info.value, callback) - end - end - end) - end -end - -local function ofLiteral(source, callback) - local parent = source.parent - if not parent then - return - end - if parent.type == 'setindex' - or parent.type == 'getindex' - or parent.type == 'tableindex' then - ofField(source, callback) - end -end - -local function ofLabel(source, callback) - callback { - source = source, - mode = 'set', - } - if source.ref then - for _, ref in ipairs(source.ref) do - callback { - source = ref, - mode = 'get', - } - end - end -end - -local function ofGoTo(source, callback) - local name = source[1] - local label = guide.getLabel(source, name) - if label then - ofLabel(label, callback) - end -end - -local function ofMain(source, callback) - callback { - source = source, - mode = 'main', - } - local myUri = source.uri - local uris = files.findLinkTo(myUri) - if not uris then - return - end - for _, uri in ipairs(uris) do - local ast = files.getAst(uri) - if ast then - local links = searcher.getLinks(ast.ast) - if links then - for linkUri, calls in pairs(links) do - if files.eq(linkUri, myUri) then - for i = 1, #calls do - ofCallSelect(calls[i], 1, callback) - end - end - end - end - end - end -end - -local function eachRef(source, callback) - local stype = source.type - if stype == 'local' then - ofLocal(source, callback) - elseif stype == 'getlocal' - or stype == 'setlocal' then - ofLocal(source.node, callback) - elseif stype == 'setglobal' - or stype == 'getglobal' then - ofGlobal(source, callback) - elseif stype == 'field' - or stype == 'method' then - ofField(source, callback) - elseif stype == 'setfield' - or stype == 'getfield' then - ofField(source.field, callback) - elseif stype == 'setmethod' - or stype == 'getmethod' then - ofField(source.method, callback) - elseif stype == 'number' - or stype == 'boolean' - or stype == 'string' then - ofLiteral(source, callback) - elseif stype == 'goto' then - ofGoTo(source, callback) - elseif stype == 'label' then - ofLabel(source, callback) - elseif stype == 'table' - or stype == 'function' then - ofValue(source, callback) - elseif stype == 'main' then - ofMain(source, callback) - end - asArg(source, callback) -end - ---- 判断2个对象是否拥有相同的引用 -function searcher.isSameRef(a, b) - local cache = searcher.cache.eachRef[a] - if cache then - -- 相同引用的source共享同一份cache - return cache == searcher.cache.eachRef[b] - else - return searcher.eachRef(a, function (info) - if info.source == b then - return true - end - end) or false - end -end - ---- 获取所有的引用 -function searcher.eachRef(source, callback) - local cache = searcher.cache.eachRef[source] - if cache then - for i = 1, #cache do - local res = callback(cache[i]) - if res ~= nil then - return res - end - end - return - end - local unlock = searcher.lock('eachRef', source) - if not unlock then - return - end - cache = {} - searcher.cache.eachRef[source] = cache - local mark = {} - eachRef(source, function (info) - local src = info.source - if mark[src] then - return - end - mark[src] = true - cache[#cache+1] = info - end) - unlock() - for i = 1, #cache do - local src = cache[i].source - searcher.cache.eachRef[src] = cache - end - for i = 1, #cache do - local res = callback(cache[i]) - if res ~= nil then - return res - end - end -end diff --git a/server-beta/src/searcher/getGlobal.lua b/server-beta/src/searcher/getGlobal.lua deleted file mode 100644 index 226f2473..00000000 --- a/server-beta/src/searcher/getGlobal.lua +++ /dev/null @@ -1,6 +0,0 @@ -local searcher = require 'searcher.searcher' - -function searcher.getGlobal(source) - searcher.getGlobals(source) - return searcher.cache.getGlobal[source] -end diff --git a/server-beta/src/searcher/getGlobals.lua b/server-beta/src/searcher/getGlobals.lua deleted file mode 100644 index 13e69e16..00000000 --- a/server-beta/src/searcher/getGlobals.lua +++ /dev/null @@ -1,45 +0,0 @@ -local guide = require 'parser.guide' -local searcher = require 'searcher.searcher' - -local function getGlobals(root) - local env = guide.getENV(root) - local cache = {} - local mark = {} - searcher.eachField(env, function (info) - local src = info.source - if mark[src] then - return - end - mark[src] = true - local name = info.key - if not name then - return - end - if not cache[name] then - cache[name] = { - key = name, - mode = {}, - } - end - cache[name][#cache[name]+1] = info - cache[name].mode[info.mode] = true - searcher.cache.getGlobal[src] = name - end) - return cache -end - -function searcher.getGlobals(source) - source = guide.getRoot(source) - local cache = searcher.cache.getGlobals[source] - if cache ~= nil then - return cache - end - local unlock = searcher.lock('getGlobals', source) - if not unlock then - return nil - end - cache = getGlobals(source) or false - searcher.cache.getGlobals[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/searcher/getLibrary.lua b/server-beta/src/searcher/getLibrary.lua deleted file mode 100644 index a2620295..00000000 --- a/server-beta/src/searcher/getLibrary.lua +++ /dev/null @@ -1,60 +0,0 @@ -local searcher = require 'searcher.searcher' -local library = require 'library' -local guide = require 'parser.guide' - -local function checkStdLibrary(source) - local globalName = searcher.getGlobal(source) - if not globalName then - return nil - end - local name = globalName:match '^s|(.+)$' - if library.global[name] then - return library.global[name] - end -end - -local function getLibrary(source) - local lib = checkStdLibrary(source) - if lib then - return lib - end - return searcher.eachRef(source, function (info) - local src = info.source - if src.type ~= 'getfield' - and src.type ~= 'getmethod' - and src.type ~= 'getindex' then - return - end - local node = src.node - local nodeGlobalName = searcher.getGlobal(node) - if not nodeGlobalName then - return - end - local nodeName = nodeGlobalName:match '^s|(.+)$' - local nodeLib = library.global[nodeName] - if not nodeLib then - return - end - if not nodeLib.child then - return - end - local key = guide.getKeyString(src) - local defLib = nodeLib.child[key] - return defLib - end) -end - -function searcher.getLibrary(source) - local cache = searcher.cache.getLibrary[source] - if cache ~= nil then - return cache - end - local unlock = searcher.lock('getLibrary', source) - if not unlock then - return - end - cache = getLibrary(source) or false - searcher.cache.getLibrary[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/searcher/getLinks.lua b/server-beta/src/searcher/getLinks.lua deleted file mode 100644 index 3e204e1f..00000000 --- a/server-beta/src/searcher/getLinks.lua +++ /dev/null @@ -1,48 +0,0 @@ -local guide = require 'parser.guide' -local searcher = require 'searcher.searcher' - -local function getLinks(root) - local cache = {} - local ok - guide.eachSpecialOf(root, 'require', function (source) - local call = source.parent - if call.type == 'call' then - local uris = searcher.getLinkUris(call) - if uris then - ok = true - for i = 1, #uris do - local uri = uris[i] - if not cache[uri] then - cache[uri] = {} - end - cache[uri][#cache[uri]+1] = call - end - end - end - end) - if not ok then - return nil - end - return cache -end - -function searcher.getLinks(source) - source = guide.getRoot(source) - local cache = searcher.cache.getLinks[source] - if cache ~= nil then - return cache - end - local unlock = searcher.lock('getLinks', source) - if not unlock then - return nil - end - local clock = os.clock() - cache = getLinks(source) or false - local passed = os.clock() - clock - if passed > 0.1 then - log.warn(('getLinks takes [%.3f] sec!'):format(passed)) - end - searcher.cache.getLinks[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/searcher/getValue.lua b/server-beta/src/searcher/getValue.lua deleted file mode 100644 index ae28b212..00000000 --- a/server-beta/src/searcher/getValue.lua +++ /dev/null @@ -1,454 +0,0 @@ -local searcher = require 'searcher.searcher' -local guide = require 'parser.guide' -local config = require 'config' - -local typeSort = { - ['boolean'] = 1, - ['string'] = 2, - ['integer'] = 3, - ['number'] = 4, - ['table'] = 5, - ['function'] = 6, - ['nil'] = math.maxinteger, -} - -NIL = setmetatable({''}, { __tostring = function () return 'nil' end }) - -local function merge(a, b) - local t = {} - for i = 1, #a do - t[#t+1] = a[i] - end - for i = 1, #b do - t[#t+1] = b[i] - end - return t -end - -local function checkLiteral(source) - if source.type == 'string' then - return { - type = 'string', - value = source[1], - source = source, - } - elseif source.type == 'nil' then - return { - type = 'nil', - value = NIL, - source = source, - } - elseif source.type == 'boolean' then - return { - type = 'boolean', - value = source[1], - source = source, - } - elseif source.type == 'number' then - if math.type(source[1]) == 'integer' then - return { - type = 'integer', - value = source[1], - source = source, - } - else - return { - type = 'number', - value = source[1], - source = source, - } - end - elseif source.type == 'table' then - return { - type = 'table', - source = source, - } - elseif source.type == 'function' then - return { - type = 'function', - source = source, - } - end -end - -local function checkUnary(source) - if source.type ~= 'unary' then - return - end - local op = source.op - if op.type == 'not' then - local isTrue = searcher.isTrue(source[1]) - local value = nil - if isTrue == true then - value = false - elseif isTrue == false then - value = true - end - return { - type = 'boolean', - value = value, - source = source, - } - elseif op.type == '#' then - return { - type = 'integer', - source = source, - } - elseif op.type == '~' then - local l = searcher.getLiteral(source[1], 'integer') - return { - type = 'integer', - value = l and ~l or nil, - source = source, - } - elseif op.type == '-' then - local v = searcher.getLiteral(source[1], 'integer') - if v then - return { - type = 'integer', - value = - v, - source = source, - } - end - v = searcher.getLiteral(source[1], 'number') - return { - type = 'number', - value = v and -v or nil, - source = source, - } - end -end - -local function checkBinary(source) - if source.type ~= 'binary' then - return - end - local op = source.op - if op.type == 'and' then - local isTrue = searcher.checkTrue(source[1]) - if isTrue == true then - return searcher.getValue(source[2]) - elseif isTrue == false then - return searcher.getValue(source[1]) - else - return merge( - searcher.getValue(source[1]), - searcher.getValue(source[2]) - ) - end - elseif op.type == 'or' then - local isTrue = searcher.checkTrue(source[1]) - if isTrue == true then - return searcher.getValue(source[1]) - elseif isTrue == false then - return searcher.getValue(source[2]) - else - return merge( - searcher.getValue(source[1]), - searcher.getValue(source[2]) - ) - end - elseif op.type == '==' then - local value = searcher.isSameValue(source[1], source[2]) - if value ~= nil then - return { - type = 'boolean', - value = value, - source = source, - } - end - local isSame = searcher.isSameRef(source[1], source[2]) - if isSame == true then - value = true - else - value = nil - end - return { - type = 'boolean', - value = value, - source = source, - } - elseif op.type == '~=' then - local value = searcher.isSameValue(source[1], source[2]) - if value ~= nil then - return { - type = 'boolean', - value = not value, - source = source, - } - end - local isSame = searcher.isSameRef(source[1], source[2]) - if isSame == true then - value = false - else - value = nil - end - return { - type = 'boolean', - value = value, - source = source, - } - elseif op.type == '<=' then - elseif op.type == '>=' - or op.type == '<' - or op.type == '>' then - return 'boolean' - end - if op.type == '|' - or op.type == '~' - or op.type == '&' - or op.type == '<<' - or op.type == '>>' then - return 'integer' - end - if op.type == '..' then - return 'string' - end - if op.type == '^' - or op.type == '/' then - return 'number' - end - -- 其他数学运算根据2侧的值决定,当2侧的值均为整数时返回整数 - if op.type == '+' - or op.type == '-' - or op.type == '*' - or op.type == '%' - or op.type == '//' then - if hasType('integer', searcher.getValue(source[1])) - and hasType('integer', searcher.getValue(source[2])) then - return 'integer' - else - return 'number' - end - end -end - -local function checkValue(source) - if source.value then - return searcher.getValue(source.value) - end -end - -local function checkCall(result, source) - if not source.parent then - return - end - if source.parent.type ~= 'call' then - return - end - if source.parent.node == source then - merge(result, 'function') - return - end -end - -local function checkNext(result, source) - local next = source.next - if not next then - return - end - if next.type == 'getfield' - or next.type == 'getindex' - or next.type == 'getmethod' - or next.type == 'setfield' - or next.type == 'setindex' - or next.type == 'setmethod' then - merge(result, 'table') - end -end - -local function checkDef(result, source) - searcher.eachDef(source, function (info) - local src = info.source - local tp = searcher.getValue(src) - if tp then - merge(result, tp) - end - end) -end - -local function typeInference(source) - local tp = checkLiteral(source) - or checkValue(source) - or checkUnary(source) - or checkBinary(source) - if tp then - return tp - end - - local result = {} - - checkCall(result, source) - checkNext(result, source) - checkDef(result, source) - - return dump(result) -end - -local function getValue(source) - local result = checkLiteral(source) - if result then - return { result } - end - local results = checkValue(source) - or checkUnary(source) - or checkBinary(source) - if results then - return results - end -end - -function searcher.checkTrue(source) - local values = searcher.getValue(source) - if not values then - return - end - -- 当前认为的结果 - local current - for i = 1, #values do - -- 新的结果 - local new - local v = values[i] - if v.type == 'nil' then - new = false - elseif v.type == 'boolean' then - if v.value == true then - new = true - elseif v.value == false then - new = false - end - end - if new ~= nil then - if current == nil then - current = new - else - -- 如果2个结果完全相反,则返回 nil 表示不确定 - if new ~= current then - return nil - end - end - end - end - return current -end - ---- 拥有某个类型的值 -function searcher.eachValueType(source, type, callback) - local values = searcher.getValue(source) - if not values then - return - end - for i = 1, #values do - local v = values[i] - if v.type == type then - local res = callback(v) - if res ~= nil then - return res - end - end - end -end - ---- 获取特定类型的字面量值 -function searcher.getLiteral(source, type) - local values = searcher.getValue(source) - if not values then - return nil - end - for i = 1, #values do - local v = values[i] - if v.type == type and v.value ~= nil then - return v.value - end - end - return nil -end - -function searcher.isSameValue(a, b) - local valuesA = searcher.getValue(a) - local valuesB = searcher.getValue(b) - if valuesA == valuesB and valuesA ~= nil then - return true - end - local values = {} - for i = 1, #valuesA do - local value = valuesA[i] - local literal = value.value - if literal then - values[literal] = false - end - end - for i = 1, #valuesB do - local value = valuesA[i] - local literal = value.value - if literal then - if values[literal] == nil then - return false - end - values[literal] = true - end - end - for k, v in pairs(values) do - if v == false then - return false - end - end - return true -end - -function searcher.typeInference(source) - local values = searcher.getValue(source) - if not values then - return 'any' - end - local types = {} - for _ = 1, #values do - local tp = values.type - if not types[tp] then - types[tp] = true - types[#types+1] = tp - end - end - if #types == 0 then - return 'any' - end - if #types == 1 then - return types[1] - end - table.sort(types, function (a, b) - local sa = typeSort[a] - local sb = typeSort[b] - if sa and sb then - return sa < sb - end - if not sa and not sb then - return a < b - end - if sa and not sb then - return true - end - if not sa and sb then - return false - end - return false - end) - return table.concat(types, '|') -end - -function searcher.getValue(source) - if not source then - return - end - local cache = searcher.cache.getValue[source] - if cache ~= nil then - return cache - end - local unlock = searcher.lock('getValue', source) - if not unlock then - return - end - cache = getValue(source) or false - searcher.cache.getValue[source] = cache - unlock() - return cache -end diff --git a/server-beta/src/searcher/init.lua b/server-beta/src/searcher/init.lua deleted file mode 100644 index 65753ccd..00000000 --- a/server-beta/src/searcher/init.lua +++ /dev/null @@ -1,10 +0,0 @@ -local searcher = require 'searcher.searcher' -require 'searcher.eachField' -require 'searcher.eachRef' -require 'searcher.eachDef' -require 'searcher.getGlobals' -require 'searcher.getLinks' -require 'searcher.getGlobal' -require 'searcher.getLibrary' -require 'searcher.getValue' -return searcher diff --git a/server-beta/src/searcher/searcher.lua b/server-beta/src/searcher/searcher.lua deleted file mode 100644 index e47e8c54..00000000 --- a/server-beta/src/searcher/searcher.lua +++ /dev/null @@ -1,81 +0,0 @@ -local guide = require 'parser.guide' -local util = require 'utility' - -local setmetatable = setmetatable -local assert = assert -local require = require -local type = type - -_ENV = nil - -local specials = { - ['_G'] = true, - ['rawset'] = true, - ['rawget'] = true, - ['setmetatable'] = true, - ['require'] = true, - ['dofile'] = true, - ['loadfile'] = true, -} - ----@class searcher -local m = {} - -function m.lock(tp, source) - if m.locked[tp][source] then - return nil - end - m.locked[tp][source] = true - return function () - m.locked[tp][source] = nil - end -end - ---- 获取link的uri -function m.getLinkUris(call) - local workspace = require 'workspace' - local func = call.node - local name = func.special - if name == 'require' then - local args = call.args - if not args[1] then - return nil - end - local literal = guide.getLiteral(args[1]) - if type(literal) ~= 'string' then - return nil - end - return workspace.findUrisByRequirePath(literal, true) - end -end - -m.cacheTracker = setmetatable({}, { __mode = 'kv' }) - ---- 刷新缓存 -function m.refreshCache() - if m.cache then - m.cache.dead = true - end - m.cache = { - eachRef = {}, - eachField = {}, - getGlobals = {}, - getLinks = {}, - getGlobal = {}, - specialName = {}, - getLibrary = {}, - getValue = {}, - specials = nil, - } - m.locked = { - eachRef = {}, - eachField = {}, - getGlobals = {}, - getLinks = {}, - getLibrary = {}, - getValue = {}, - } - m.cacheTracker[m.cache] = true -end - -return m diff --git a/server-beta/src/searcher/special.lua b/server-beta/src/searcher/special.lua deleted file mode 100644 index e69de29b..00000000 diff --git a/server-beta/src/service/service.lua b/server-beta/src/service/service.lua index 7b0b8ad5..e1cb604b 100644 --- a/server-beta/src/service/service.lua +++ b/server-beta/src/service/service.lua @@ -1,9 +1,9 @@ -local pub = require 'pub' -local thread = require 'bee.thread' -local await = require 'await' -local timer = require 'timer' -local proto = require 'proto' -local searcher = require 'searcher' +local pub = require 'pub' +local thread = require 'bee.thread' +local await = require 'await' +local timer = require 'timer' +local proto = require 'proto' +local vm = require 'vm' local m = {} m.type = 'service' @@ -84,7 +84,7 @@ function m.reportCache() local total = 0 local dead = 0 - for cache in pairs(searcher.cacheTracker) do + for cache in pairs(vm.cacheTracker) do total = total + 1 if cache.dead then dead = dead + 1 diff --git a/server-beta/src/vm/eachDef.lua b/server-beta/src/vm/eachDef.lua new file mode 100644 index 00000000..0274cbee --- /dev/null +++ b/server-beta/src/vm/eachDef.lua @@ -0,0 +1,65 @@ +local vm = require 'vm.vm' +local guide = require 'parser.guide' +local files = require 'files' + +local function checkPath(source, info) + if source.type == 'goto' then + return true + end + local src = info.source + local mode = guide.getPath(source, src) + if not mode then + return true + end + if mode == 'before' then + return false + end + return true +end + +function vm.eachDef(source, callback) + local results = {} + local valueUris = {} + local sourceUri = guide.getRoot(source).uri + vm.eachRef(source, function (info) + if info.mode == 'declare' + or info.mode == 'set' + or info.mode == 'return' + or info.mode == 'value' then + results[#results+1] = info + local src = info.source + if info.mode == 'return' then + local uri = guide.getRoot(src).uri + valueUris[uri] = info.source + end + end + end) + + for _, info in ipairs(results) do + local src = info.source + local destUri = guide.getRoot(src).uri + -- 如果是同一个文件,则检查位置关系后放行 + if sourceUri == destUri then + if checkPath(source, info) then + callback(info) + end + goto CONTINUE + end + -- 如果是global或field,则直接放行(因为无法确定顺序) + if src.type == 'setindex' + or src.type == 'setfield' + or src.type == 'setmethod' + or src.type == 'tablefield' + or src.type == 'tableindex' + or src.type == 'setglobal' then + callback(info) + goto CONTINUE + end + -- 如果不是同一个文件,则必须在该文件 return 后才放行 + if valueUris[destUri] then + callback(info) + goto CONTINUE + end + ::CONTINUE:: + end +end diff --git a/server-beta/src/vm/eachField.lua b/server-beta/src/vm/eachField.lua new file mode 100644 index 00000000..549a7dec --- /dev/null +++ b/server-beta/src/vm/eachField.lua @@ -0,0 +1,163 @@ +local guide = require 'parser.guide' +local vm = require 'vm.vm' + +local function ofTabel(value, callback) + for _, field in ipairs(value) do + if field.type == 'tablefield' + or field.type == 'tableindex' then + callback { + source = field, + key = guide.getKeyName(field), + value = field.value, + mode = 'set', + } + end + end +end + +local function ofENV(source, callback) + if source.type == 'getlocal' then + local parent = source.parent + if parent.type == 'getfield' + or parent.type == 'getmethod' + or parent.type == 'getindex' then + callback { + source = parent, + key = guide.getKeyName(parent), + mode = 'get', + } + end + elseif source.type == 'getglobal' then + callback { + source = source, + key = guide.getKeyName(source), + mode = 'get', + } + elseif source.type == 'setglobal' then + callback { + source = source, + key = guide.getKeyName(source), + mode = 'set', + value = source.value, + } + end +end + +local function ofSpecialArg(source, callback) + local args = source.parent + local call = args.parent + local func = call.node + local name = func.special + if name == 'rawset' then + if args[1] == source and args[2] then + callback { + source = call, + key = guide.getKeyName(args[2]), + value = args[3], + mode = 'set', + } + end + elseif name == 'rawget' then + if args[1] == source and args[2] then + callback { + source = call, + key = guide.getKeyName(args[2]), + mode = 'get', + } + end + elseif name == 'setmetatable' then + if args[1] == source and args[2] then + vm.eachField(args[2], function (info) + if info.key == 's|__index' and info.value then + vm.eachField(info.value, callback) + end + end) + end + end +end + +local function ofVar(source, callback) + local parent = source.parent + if not parent then + return + end + if parent.type == 'getfield' + or parent.type == 'getmethod' + or parent.type == 'getindex' then + callback { + source = parent, + key = guide.getKeyName(parent), + mode = 'get', + } + return + end + if parent.type == 'setfield' + or parent.type == 'setmethod' + or parent.type == 'setindex' then + callback { + source = parent, + key = guide.getKeyName(parent), + value = parent.value, + mode = 'set', + } + return + end + if parent.type == 'callargs' then + ofSpecialArg(source, callback) + end +end + +local function eachField(source, callback) + vm.eachRef(source, function (info) + local src = info.source + if src.tag == '_ENV' then + if src.ref then + for _, ref in ipairs(src.ref) do + ofENV(ref, callback) + end + end + elseif src.type == 'getlocal' + or src.type == 'getglobal' + or src.type == 'getfield' + or src.type == 'getmethod' + or src.type == 'getindex' then + ofVar(src, callback) + elseif src.type == 'table' then + ofTabel(src, callback) + end + end) +end + +--- 获取所有的field +function vm.eachField(source, callback) + local cache = vm.cache.eachField[source] + if cache then + for i = 1, #cache do + callback(cache[i]) + end + return + end + local unlock = vm.lock('eachField', source) + if not unlock then + return + end + cache = {} + vm.cache.eachField[source] = cache + local mark = {} + eachField(source, function (info) + local src = info.source + if mark[src] then + return + end + mark[src] = true + cache[#cache+1] = info + end) + unlock() + vm.eachRef(source, function (info) + local src = info.source + vm.cache.eachField[src] = cache + end) + for i = 1, #cache do + callback(cache[i]) + end +end diff --git a/server-beta/src/vm/eachRef.lua b/server-beta/src/vm/eachRef.lua new file mode 100644 index 00000000..543a0c09 --- /dev/null +++ b/server-beta/src/vm/eachRef.lua @@ -0,0 +1,504 @@ +local guide = require 'parser.guide' +local files = require 'files' +local vm = require 'vm.vm' + +local function ofCall(func, index, callback) + vm.eachRef(func, function (info) + local src = info.source + local returns + if info.mode == 'main' then + returns = src.returns + else + local funcDef = src.value + returns = funcDef and funcDef.returns + end + if returns then + -- 搜索函数第 index 个返回值 + for _, rtn in ipairs(returns) do + local val = rtn[index] + if val then + callback { + source = val, + mode = 'return', + } + vm.eachRef(val, callback) + end + end + end + end) +end + +local function ofCallSelect(call, index, callback) + local slc = call.parent + if slc.index == index then + vm.eachRef(slc.parent, callback) + return + end + if call.extParent then + for i = 1, #call.extParent do + slc = call.extParent[i] + if slc.index == index then + vm.eachRef(slc.parent, callback) + return + end + end + end +end + +local function ofReturn(rtn, index, callback) + local func = guide.getParentFunction(rtn) + if not func then + return + end + -- 搜索函数调用的第 index 个接收值 + if func.type == 'main' then + vm.eachRef(func, callback) + else + vm.eachRef(func, function (info) + local source = info.source + local call = source.parent + if not call or call.type ~= 'call' then + return + end + ofCallSelect(call, index, callback) + end) + end +end + +local function ofSpecialCall(call, func, index, callback) + local name = func.special + if name == 'setmetatable' then + if index == 1 then + local args = call.args + if args[1] then + vm.eachRef(args[1], callback) + end + if args[2] then + vm.eachField(args[2], function (info) + if info.key == 's|__index' then + vm.eachRef(info.source, callback) + if info.value then + vm.eachRef(info.value, callback) + end + end + end) + end + end + elseif name == 'require' then + if index == 1 then + local result = vm.getLinkUris(call) + if result then + local myUri = guide.getRoot(call).uri + for _, uri in ipairs(result) do + if not files.eq(uri, myUri) then + local ast = files.getAst(uri) + if ast then + ofCall(ast.ast, 1, callback) + end + end + end + end + end + end +end + +local function ofValue(value, callback) + if value.type == 'select' then + -- 检查函数返回值 + local call = value.vararg + if call.type == 'call' then + ofCall(call.node, value.index, callback) + ofSpecialCall(call, call.node, value.index, callback) + end + return + end + + if value.type == 'table' + or value.type == 'string' + or value.type == 'number' + or value.type == 'boolean' + or value.type == 'nil' + or value.type == 'function' then + callback { + source = value, + mode = 'value', + } + end + + vm.eachRef(value, callback) + + local parent = value.parent + if parent.type == 'local' + or parent.type == 'setglobal' + or parent.type == 'setlocal' + or parent.type == 'setfield' + or parent.type == 'setmethod' + or parent.type == 'setindex' + or parent.type == 'tablefield' + or parent.type == 'tableindex' then + if parent.value == value then + vm.eachRef(parent, callback) + end + end + if parent.type == 'return' then + for i = 1, #parent do + if parent[i] == value then + ofReturn(parent, i, callback) + break + end + end + end +end + +local function ofSelf(loc, callback) + -- self 的2个特殊引用位置: + -- 1. 当前方法定义时的对象(mt) + local method = loc.method + local node = method.node + vm.eachRef(node, callback) + -- 2. 调用该方法时传入的对象 +end + +--- 自己作为赋值的值 +local function asValue(source, callback) + local parent = source.parent + if parent and parent.value == source then + if guide.getKeyString(parent) == '__index' then + if parent.type == 'tablefield' + or parent.type == 'tableindex' then + local t = parent.parent + local args = t.parent + if args[2] == t then + local call = args.parent + local func = call.node + if func.special == 'setmetatable' then + vm.eachRef(args[1], callback) + end + end + end + end + end +end + +local function getCallRecvs(call) + local parent = call.parent + if parent.type ~= 'select' then + return nil + end + local exParent = call.exParent + local recvs = {} + recvs[1] = parent.parent + if exParent then + for _, p in ipairs(exParent) do + recvs[#recvs+1] = p.parent + end + end + return recvs +end + +--- 自己作为函数的参数 +local function asArg(source, callback) + local parent = source.parent + if not parent then + return + end + if parent.type == 'callargs' then + local call = parent.parent + local func = call.node + local name = func.special + if name == 'setmetatable' then + if parent[1] == source then + if parent[2] then + vm.eachField(parent[2], function (info) + if info.key == 's|__index' then + vm.eachRef(info.source, callback) + if info.value then + vm.eachRef(info.value, callback) + end + end + end) + end + end + local recvs = getCallRecvs(call) + if recvs and recvs[1] then + vm.eachRef(recvs[1], callback) + end + end + end +end + +local function ofLocal(loc, callback) + -- 方法中的 self 使用了一个虚拟的定义位置 + if loc.tag ~= 'self' then + callback { + source = loc, + mode = 'declare', + } + end + if loc.ref then + for _, ref in ipairs(loc.ref) do + if ref.type == 'getlocal' then + callback { + source = ref, + mode = 'get', + } + asValue(ref, callback) + elseif ref.type == 'setlocal' then + callback { + source = ref, + mode = 'set', + } + if ref.value then + ofValue(ref.value, callback) + end + end + end + end + if loc.tag == 'self' then + ofSelf(loc, callback) + end + if loc.value then + ofValue(loc.value, callback) + end + if loc.tag == '_ENV' and loc.ref then + for _, ref in ipairs(loc.ref) do + if ref.type == 'getlocal' then + local parent = ref.parent + if parent.type == 'getfield' + or parent.type == 'getindex' then + if guide.getKeyName(parent) == '_G' then + callback { + source = parent, + mode = 'get', + } + end + end + elseif ref.type == 'getglobal' then + if guide.getKeyString(ref) == '_G' then + callback { + source = ref, + mode = 'get', + } + end + end + end + end +end + +local function ofGlobal(source, callback) + local key = guide.getKeyName(source) + local node = source.node + if node.tag == '_ENV' then + local uris = files.findGlobals(key) + for _, uri in ipairs(uris) do + local ast = files.getAst(uri) + local globals = vm.getGlobals(ast.ast) + if globals[key] then + for _, info in ipairs(globals[key]) do + callback(info) + if info.value then + ofValue(info.value, callback) + end + end + end + end + else + vm.eachField(node, function (info) + if key == info.key then + callback { + source = info.source, + mode = info.mode, + } + if info.value then + ofValue(info.value, callback) + end + end + end) + end +end + +local function ofField(source, callback) + local parent = source.parent + local key = guide.getKeyName(source) + if parent.type == 'tablefield' + or parent.type == 'tableindex' then + local tbl = parent.parent + vm.eachField(tbl, function (info) + if key == info.key then + callback { + source = info.source, + mode = info.mode, + } + if info.value then + ofValue(info.value, callback) + end + end + end) + else + local node = parent.node + vm.eachField(node, function (info) + if key == info.key then + callback { + source = info.source, + mode = info.mode, + } + if info.value then + ofValue(info.value, callback) + end + end + end) + end +end + +local function ofLiteral(source, callback) + local parent = source.parent + if not parent then + return + end + if parent.type == 'setindex' + or parent.type == 'getindex' + or parent.type == 'tableindex' then + ofField(source, callback) + end +end + +local function ofLabel(source, callback) + callback { + source = source, + mode = 'set', + } + if source.ref then + for _, ref in ipairs(source.ref) do + callback { + source = ref, + mode = 'get', + } + end + end +end + +local function ofGoTo(source, callback) + local name = source[1] + local label = guide.getLabel(source, name) + if label then + ofLabel(label, callback) + end +end + +local function ofMain(source, callback) + callback { + source = source, + mode = 'main', + } + local myUri = source.uri + local uris = files.findLinkTo(myUri) + if not uris then + return + end + for _, uri in ipairs(uris) do + local ast = files.getAst(uri) + if ast then + local links = vm.getLinks(ast.ast) + if links then + for linkUri, calls in pairs(links) do + if files.eq(linkUri, myUri) then + for i = 1, #calls do + ofCallSelect(calls[i], 1, callback) + end + end + end + end + end + end +end + +local function eachRef(source, callback) + local stype = source.type + if stype == 'local' then + ofLocal(source, callback) + elseif stype == 'getlocal' + or stype == 'setlocal' then + ofLocal(source.node, callback) + elseif stype == 'setglobal' + or stype == 'getglobal' then + ofGlobal(source, callback) + elseif stype == 'field' + or stype == 'method' then + ofField(source, callback) + elseif stype == 'setfield' + or stype == 'getfield' then + ofField(source.field, callback) + elseif stype == 'setmethod' + or stype == 'getmethod' then + ofField(source.method, callback) + elseif stype == 'number' + or stype == 'boolean' + or stype == 'string' then + ofLiteral(source, callback) + elseif stype == 'goto' then + ofGoTo(source, callback) + elseif stype == 'label' then + ofLabel(source, callback) + elseif stype == 'table' + or stype == 'function' then + ofValue(source, callback) + elseif stype == 'main' then + ofMain(source, callback) + end + asArg(source, callback) +end + +--- 判断2个对象是否拥有相同的引用 +function vm.isSameRef(a, b) + local cache = vm.cache.eachRef[a] + if cache then + -- 相同引用的source共享同一份cache + return cache == vm.cache.eachRef[b] + else + return vm.eachRef(a, function (info) + if info.source == b then + return true + end + end) or false + end +end + +--- 获取所有的引用 +function vm.eachRef(source, callback) + local cache = vm.cache.eachRef[source] + if cache then + for i = 1, #cache do + local res = callback(cache[i]) + if res ~= nil then + return res + end + end + return + end + local unlock = vm.lock('eachRef', source) + if not unlock then + return + end + cache = {} + vm.cache.eachRef[source] = cache + local mark = {} + eachRef(source, function (info) + local src = info.source + if mark[src] then + return + end + mark[src] = true + cache[#cache+1] = info + end) + unlock() + for i = 1, #cache do + local src = cache[i].source + vm.cache.eachRef[src] = cache + end + for i = 1, #cache do + local res = callback(cache[i]) + if res ~= nil then + return res + end + end +end diff --git a/server-beta/src/vm/getGlobal.lua b/server-beta/src/vm/getGlobal.lua new file mode 100644 index 00000000..373c907e --- /dev/null +++ b/server-beta/src/vm/getGlobal.lua @@ -0,0 +1,6 @@ +local vm = require 'vm.vm' + +function vm.getGlobal(source) + vm.getGlobals(source) + return vm.cache.getGlobal[source] +end diff --git a/server-beta/src/vm/getGlobals.lua b/server-beta/src/vm/getGlobals.lua new file mode 100644 index 00000000..699dd270 --- /dev/null +++ b/server-beta/src/vm/getGlobals.lua @@ -0,0 +1,45 @@ +local guide = require 'parser.guide' +local vm = require 'vm.vm' + +local function getGlobals(root) + local env = guide.getENV(root) + local cache = {} + local mark = {} + vm.eachField(env, function (info) + local src = info.source + if mark[src] then + return + end + mark[src] = true + local name = info.key + if not name then + return + end + if not cache[name] then + cache[name] = { + key = name, + mode = {}, + } + end + cache[name][#cache[name]+1] = info + cache[name].mode[info.mode] = true + vm.cache.getGlobal[src] = name + end) + return cache +end + +function vm.getGlobals(source) + source = guide.getRoot(source) + local cache = vm.cache.getGlobals[source] + if cache ~= nil then + return cache + end + local unlock = vm.lock('getGlobals', source) + if not unlock then + return nil + end + cache = getGlobals(source) or false + vm.cache.getGlobals[source] = cache + unlock() + return cache +end diff --git a/server-beta/src/vm/getLibrary.lua b/server-beta/src/vm/getLibrary.lua new file mode 100644 index 00000000..08f015a6 --- /dev/null +++ b/server-beta/src/vm/getLibrary.lua @@ -0,0 +1,60 @@ +local vm = require 'vm.vm' +local library = require 'library' +local guide = require 'parser.guide' + +local function checkStdLibrary(source) + local globalName = vm.getGlobal(source) + if not globalName then + return nil + end + local name = globalName:match '^s|(.+)$' + if library.global[name] then + return library.global[name] + end +end + +local function getLibrary(source) + local lib = checkStdLibrary(source) + if lib then + return lib + end + return vm.eachRef(source, function (info) + local src = info.source + if src.type ~= 'getfield' + and src.type ~= 'getmethod' + and src.type ~= 'getindex' then + return + end + local node = src.node + local nodeGlobalName = vm.getGlobal(node) + if not nodeGlobalName then + return + end + local nodeName = nodeGlobalName:match '^s|(.+)$' + local nodeLib = library.global[nodeName] + if not nodeLib then + return + end + if not nodeLib.child then + return + end + local key = guide.getKeyString(src) + local defLib = nodeLib.child[key] + return defLib + end) +end + +function vm.getLibrary(source) + local cache = vm.cache.getLibrary[source] + if cache ~= nil then + return cache + end + local unlock = vm.lock('getLibrary', source) + if not unlock then + return + end + cache = getLibrary(source) or false + vm.cache.getLibrary[source] = cache + unlock() + return cache +end diff --git a/server-beta/src/vm/getLinks.lua b/server-beta/src/vm/getLinks.lua new file mode 100644 index 00000000..6875771f --- /dev/null +++ b/server-beta/src/vm/getLinks.lua @@ -0,0 +1,48 @@ +local guide = require 'parser.guide' +local vm = require 'vm.vm' + +local function getLinks(root) + local cache = {} + local ok + guide.eachSpecialOf(root, 'require', function (source) + local call = source.parent + if call.type == 'call' then + local uris = vm.getLinkUris(call) + if uris then + ok = true + for i = 1, #uris do + local uri = uris[i] + if not cache[uri] then + cache[uri] = {} + end + cache[uri][#cache[uri]+1] = call + end + end + end + end) + if not ok then + return nil + end + return cache +end + +function vm.getLinks(source) + source = guide.getRoot(source) + local cache = vm.cache.getLinks[source] + if cache ~= nil then + return cache + end + local unlock = vm.lock('getLinks', source) + if not unlock then + return nil + end + local clock = os.clock() + cache = getLinks(source) or false + local passed = os.clock() - clock + if passed > 0.1 then + log.warn(('getLinks takes [%.3f] sec!'):format(passed)) + end + vm.cache.getLinks[source] = cache + unlock() + return cache +end diff --git a/server-beta/src/vm/getValue.lua b/server-beta/src/vm/getValue.lua new file mode 100644 index 00000000..b13d822d --- /dev/null +++ b/server-beta/src/vm/getValue.lua @@ -0,0 +1,452 @@ +local vm = require 'vm.vm' + +local typeSort = { + ['boolean'] = 1, + ['string'] = 2, + ['integer'] = 3, + ['number'] = 4, + ['table'] = 5, + ['function'] = 6, + ['nil'] = math.maxinteger, +} + +NIL = setmetatable({''}, { __tostring = function () return 'nil' end }) + +local function merge(a, b) + local t = {} + for i = 1, #a do + t[#t+1] = a[i] + end + for i = 1, #b do + t[#t+1] = b[i] + end + return t +end + +local function checkLiteral(source) + if source.type == 'string' then + return { + type = 'string', + value = source[1], + source = source, + } + elseif source.type == 'nil' then + return { + type = 'nil', + value = NIL, + source = source, + } + elseif source.type == 'boolean' then + return { + type = 'boolean', + value = source[1], + source = source, + } + elseif source.type == 'number' then + if math.type(source[1]) == 'integer' then + return { + type = 'integer', + value = source[1], + source = source, + } + else + return { + type = 'number', + value = source[1], + source = source, + } + end + elseif source.type == 'table' then + return { + type = 'table', + source = source, + } + elseif source.type == 'function' then + return { + type = 'function', + source = source, + } + end +end + +local function checkUnary(source) + if source.type ~= 'unary' then + return + end + local op = source.op + if op.type == 'not' then + local isTrue = vm.isTrue(source[1]) + local value = nil + if isTrue == true then + value = false + elseif isTrue == false then + value = true + end + return { + type = 'boolean', + value = value, + source = source, + } + elseif op.type == '#' then + return { + type = 'integer', + source = source, + } + elseif op.type == '~' then + local l = vm.getLiteral(source[1], 'integer') + return { + type = 'integer', + value = l and ~l or nil, + source = source, + } + elseif op.type == '-' then + local v = vm.getLiteral(source[1], 'integer') + if v then + return { + type = 'integer', + value = - v, + source = source, + } + end + v = vm.getLiteral(source[1], 'number') + return { + type = 'number', + value = v and -v or nil, + source = source, + } + end +end + +local function checkBinary(source) + if source.type ~= 'binary' then + return + end + local op = source.op + if op.type == 'and' then + local isTrue = vm.checkTrue(source[1]) + if isTrue == true then + return vm.getValue(source[2]) + elseif isTrue == false then + return vm.getValue(source[1]) + else + return merge( + vm.getValue(source[1]), + vm.getValue(source[2]) + ) + end + elseif op.type == 'or' then + local isTrue = vm.checkTrue(source[1]) + if isTrue == true then + return vm.getValue(source[1]) + elseif isTrue == false then + return vm.getValue(source[2]) + else + return merge( + vm.getValue(source[1]), + vm.getValue(source[2]) + ) + end + elseif op.type == '==' then + local value = vm.isSameValue(source[1], source[2]) + if value ~= nil then + return { + type = 'boolean', + value = value, + source = source, + } + end + local isSame = vm.isSameRef(source[1], source[2]) + if isSame == true then + value = true + else + value = nil + end + return { + type = 'boolean', + value = value, + source = source, + } + elseif op.type == '~=' then + local value = vm.isSameValue(source[1], source[2]) + if value ~= nil then + return { + type = 'boolean', + value = not value, + source = source, + } + end + local isSame = vm.isSameRef(source[1], source[2]) + if isSame == true then + value = false + else + value = nil + end + return { + type = 'boolean', + value = value, + source = source, + } + elseif op.type == '<=' then + elseif op.type == '>=' + or op.type == '<' + or op.type == '>' then + return 'boolean' + end + if op.type == '|' + or op.type == '~' + or op.type == '&' + or op.type == '<<' + or op.type == '>>' then + return 'integer' + end + if op.type == '..' then + return 'string' + end + if op.type == '^' + or op.type == '/' then + return 'number' + end + -- 其他数学运算根据2侧的值决定,当2侧的值均为整数时返回整数 + if op.type == '+' + or op.type == '-' + or op.type == '*' + or op.type == '%' + or op.type == '//' then + if hasType('integer', vm.getValue(source[1])) + and hasType('integer', vm.getValue(source[2])) then + return 'integer' + else + return 'number' + end + end +end + +local function checkValue(source) + if source.value then + return vm.getValue(source.value) + end +end + +local function checkCall(result, source) + if not source.parent then + return + end + if source.parent.type ~= 'call' then + return + end + if source.parent.node == source then + merge(result, 'function') + return + end +end + +local function checkNext(result, source) + local next = source.next + if not next then + return + end + if next.type == 'getfield' + or next.type == 'getindex' + or next.type == 'getmethod' + or next.type == 'setfield' + or next.type == 'setindex' + or next.type == 'setmethod' then + merge(result, 'table') + end +end + +local function checkDef(result, source) + vm.eachDef(source, function (info) + local src = info.source + local tp = vm.getValue(src) + if tp then + merge(result, tp) + end + end) +end + +local function typeInference(source) + local tp = checkLiteral(source) + or checkValue(source) + or checkUnary(source) + or checkBinary(source) + if tp then + return tp + end + + local result = {} + + checkCall(result, source) + checkNext(result, source) + checkDef(result, source) + + return dump(result) +end + +local function getValue(source) + local result = checkLiteral(source) + if result then + return { result } + end + local results = checkValue(source) + or checkUnary(source) + or checkBinary(source) + if results then + return results + end +end + +function vm.checkTrue(source) + local values = vm.getValue(source) + if not values then + return + end + -- 当前认为的结果 + local current + for i = 1, #values do + -- 新的结果 + local new + local v = values[i] + if v.type == 'nil' then + new = false + elseif v.type == 'boolean' then + if v.value == true then + new = true + elseif v.value == false then + new = false + end + end + if new ~= nil then + if current == nil then + current = new + else + -- 如果2个结果完全相反,则返回 nil 表示不确定 + if new ~= current then + return nil + end + end + end + end + return current +end + +--- 拥有某个类型的值 +function vm.eachValueType(source, type, callback) + local values = vm.getValue(source) + if not values then + return + end + for i = 1, #values do + local v = values[i] + if v.type == type then + local res = callback(v) + if res ~= nil then + return res + end + end + end +end + +--- 获取特定类型的字面量值 +function vm.getLiteral(source, type) + local values = vm.getValue(source) + if not values then + return nil + end + for i = 1, #values do + local v = values[i] + if v.type == type and v.value ~= nil then + return v.value + end + end + return nil +end + +function vm.isSameValue(a, b) + local valuesA = vm.getValue(a) + local valuesB = vm.getValue(b) + if valuesA == valuesB and valuesA ~= nil then + return true + end + local values = {} + for i = 1, #valuesA do + local value = valuesA[i] + local literal = value.value + if literal then + values[literal] = false + end + end + for i = 1, #valuesB do + local value = valuesA[i] + local literal = value.value + if literal then + if values[literal] == nil then + return false + end + values[literal] = true + end + end + for k, v in pairs(values) do + if v == false then + return false + end + end + return true +end + +function vm.typeInference(source) + local values = vm.getValue(source) + if not values then + return 'any' + end + local types = {} + for _ = 1, #values do + local tp = values.type + if not types[tp] then + types[tp] = true + types[#types+1] = tp + end + end + if #types == 0 then + return 'any' + end + if #types == 1 then + return types[1] + end + table.sort(types, function (a, b) + local sa = typeSort[a] + local sb = typeSort[b] + if sa and sb then + return sa < sb + end + if not sa and not sb then + return a < b + end + if sa and not sb then + return true + end + if not sa and sb then + return false + end + return false + end) + return table.concat(types, '|') +end + +function vm.getValue(source) + if not source then + return + end + local cache = vm.cache.getValue[source] + if cache ~= nil then + return cache + end + local unlock = vm.lock('getValue', source) + if not unlock then + return + end + cache = getValue(source) or false + vm.cache.getValue[source] = cache + unlock() + return cache +end diff --git a/server-beta/src/vm/init.lua b/server-beta/src/vm/init.lua new file mode 100644 index 00000000..bf63db1d --- /dev/null +++ b/server-beta/src/vm/init.lua @@ -0,0 +1,10 @@ +local vm = require 'vm.vm' +require 'vm.eachField' +require 'vm.eachRef' +require 'vm.eachDef' +require 'vm.getGlobals' +require 'vm.getLinks' +require 'vm.getGlobal' +require 'vm.getLibrary' +require 'vm.getValue' +return vm diff --git a/server-beta/src/vm/special.lua b/server-beta/src/vm/special.lua new file mode 100644 index 00000000..e69de29b diff --git a/server-beta/src/vm/vm.lua b/server-beta/src/vm/vm.lua new file mode 100644 index 00000000..23a691df --- /dev/null +++ b/server-beta/src/vm/vm.lua @@ -0,0 +1,81 @@ +local guide = require 'parser.guide' +local util = require 'utility' + +local setmetatable = setmetatable +local assert = assert +local require = require +local type = type + +_ENV = nil + +local specials = { + ['_G'] = true, + ['rawset'] = true, + ['rawget'] = true, + ['setmetatable'] = true, + ['require'] = true, + ['dofile'] = true, + ['loadfile'] = true, +} + +---@class vm +local m = {} + +function m.lock(tp, source) + if m.locked[tp][source] then + return nil + end + m.locked[tp][source] = true + return function () + m.locked[tp][source] = nil + end +end + +--- 获取link的uri +function m.getLinkUris(call) + local workspace = require 'workspace' + local func = call.node + local name = func.special + if name == 'require' then + local args = call.args + if not args[1] then + return nil + end + local literal = guide.getLiteral(args[1]) + if type(literal) ~= 'string' then + return nil + end + return workspace.findUrisByRequirePath(literal, true) + end +end + +m.cacheTracker = setmetatable({}, { __mode = 'kv' }) + +--- 刷新缓存 +function m.refreshCache() + if m.cache then + m.cache.dead = true + end + m.cache = { + eachRef = {}, + eachField = {}, + getGlobals = {}, + getLinks = {}, + getGlobal = {}, + specialName = {}, + getLibrary = {}, + getValue = {}, + specials = nil, + } + m.locked = { + eachRef = {}, + eachField = {}, + getGlobals = {}, + getLinks = {}, + getLibrary = {}, + getValue = {}, + } + m.cacheTracker[m.cache] = true +end + +return m -- cgit v1.2.3