summaryrefslogtreecommitdiff
path: root/script/vm/vm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/vm/vm.lua')
-rw-r--r--script/vm/vm.lua167
1 files changed, 167 insertions, 0 deletions
diff --git a/script/vm/vm.lua b/script/vm/vm.lua
new file mode 100644
index 00000000..e942d55e
--- /dev/null
+++ b/script/vm/vm.lua
@@ -0,0 +1,167 @@
+local guide = require 'parser.guide'
+local util = require 'utility'
+local files = require 'files'
+local timer = require 'timer'
+
+local setmetatable = setmetatable
+local assert = assert
+local require = require
+local type = type
+local running = coroutine.running
+local ipairs = ipairs
+local log = log
+local xpcall = xpcall
+local mathHuge = math.huge
+local collectgarbage = collectgarbage
+
+_ENV = nil
+
+---@class vm
+local m = {}
+
+function m.lock(tp, source)
+ local co = running()
+ local master = m.locked[co]
+ if not master then
+ master = {}
+ m.locked[co] = master
+ end
+ if not master[tp] then
+ master[tp] = {}
+ end
+ if master[tp][source] then
+ return nil
+ end
+ master[tp][source] = true
+ return function ()
+ master[tp][source] = nil
+ end
+end
+
+function m.isSet(src)
+ local tp = src.type
+ if tp == 'setglobal'
+ or tp == 'local'
+ or tp == 'setlocal'
+ or tp == 'setfield'
+ or tp == 'setmethod'
+ or tp == 'setindex'
+ or tp == 'tablefield'
+ or tp == 'tableindex' then
+ return true
+ end
+ if tp == 'call' then
+ local special = m.getSpecial(src.node)
+ if special == 'rawset' then
+ return true
+ end
+ end
+ return false
+end
+
+function m.isGet(src)
+ local tp = src.type
+ if tp == 'getglobal'
+ or tp == 'getlocal'
+ or tp == 'getfield'
+ or tp == 'getmethod'
+ or tp == 'getindex' then
+ return true
+ end
+ if tp == 'call' then
+ local special = m.getSpecial(src.node)
+ if special == 'rawget' then
+ return true
+ end
+ end
+ return false
+end
+
+function m.getArgInfo(source)
+ local callargs = source.parent
+ if not callargs or callargs.type ~= 'callargs' then
+ return nil
+ end
+ local call = callargs.parent
+ if not call or call.type ~= 'call' then
+ return nil
+ end
+ for i = 1, #callargs do
+ if callargs[i] == source then
+ return call.node, i
+ end
+ end
+ return nil
+end
+
+function m.getSpecial(source)
+ if not source then
+ return nil
+ end
+ return source.special
+end
+
+function m.getKeyName(source)
+ if not source then
+ return nil
+ end
+ if source.type == 'call' then
+ local special = m.getSpecial(source.node)
+ if special == 'rawset'
+ or special == 'rawget' then
+ return guide.getKeyNameOfLiteral(source.args[2])
+ end
+ end
+ return guide.getKeyName(source)
+end
+
+function m.mergeResults(a, b)
+ for _, r in ipairs(b) do
+ if not a[r] then
+ a[r] = true
+ a[#a+1] = r
+ end
+ end
+ return a
+end
+
+m.cacheTracker = setmetatable({}, { __mode = 'kv' })
+
+function m.flushCache()
+ if m.cache then
+ m.cache.dead = true
+ end
+ m.cacheVersion = files.globalVersion
+ m.cache = {}
+ m.cacheActiveTime = mathHuge
+ m.locked = setmetatable({}, { __mode = 'k' })
+ m.cacheTracker[m.cache] = true
+end
+
+function m.getCache(name)
+ if m.cacheVersion ~= files.globalVersion then
+ m.flushCache()
+ end
+ m.cacheActiveTime = timer.clock()
+ if not m.cache[name] then
+ m.cache[name] = {}
+ end
+ return m.cache[name]
+end
+
+local function init()
+ m.flushCache()
+
+ -- 可以在一段时间不活动后清空缓存,不过目前看起来没有必要
+ --timer.loop(1, function ()
+ -- if timer.clock() - m.cacheActiveTime > 10.0 then
+ -- log.info('Flush cache: Inactive')
+ -- m.flushCache()
+ -- collectgarbage()
+ -- end
+ --end)
+end
+
+xpcall(init, log.error)
+
+return m