summaryrefslogtreecommitdiff
path: root/script/vm/function.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/vm/function.lua')
-rw-r--r--script/vm/function.lua551
1 files changed, 551 insertions, 0 deletions
diff --git a/script/vm/function.lua b/script/vm/function.lua
new file mode 100644
index 00000000..1ba01363
--- /dev/null
+++ b/script/vm/function.lua
@@ -0,0 +1,551 @@
+local createMulti = require 'vm.multi'
+local valueMgr = require 'vm.value'
+local localMgr = require 'vm.local'
+local sourceMgr = require 'vm.source'
+local listMgr = require 'vm.list'
+
+local Watch = setmetatable({}, {__mode = 'kv'})
+
+---@class emmyFunction
+local mt = {}
+mt.__index = mt
+mt.type = 'function'
+mt._runed = 0
+mt._top = 0
+
+function mt:getSource()
+ return listMgr.get(self.source)
+end
+
+function mt:getUri()
+ local source = self:getSource()
+ return source and source.uri or ''
+end
+
+function mt:push(source, ischunk)
+ if self._removed then
+ return
+ end
+ self._top = self._top + 1
+ self.locals[self._top] = {}
+ self.finishs[self._top] = source and source.finish or math.maxinteger
+end
+
+function mt:markChunk()
+ self.chunk[self._top] = true
+end
+
+function mt:pop()
+ if self._removed then
+ return
+ end
+ local closed = self.finishs[self._top]
+ local closedLocals = self.locals[self._top]
+ self.locals[self._top] = nil
+ self.chunk[self._top] = nil
+ for _, loc in pairs(closedLocals) do
+ loc:close(closed)
+ end
+ self._top = self._top - 1
+end
+
+function mt:saveLocal(name, loc)
+ if self._removed then
+ return
+ end
+ if loc.type ~= 'local' then
+ error('saveLocal必须是local')
+ end
+ if not loc:getSource() then
+ return
+ end
+ local old = self:loadLocal(name)
+ if old then
+ loc:shadow(old)
+ end
+ self.locals[self._top][name] = loc
+end
+
+function mt:saveUpvalue(name, loc)
+ if self._removed then
+ return
+ end
+ if loc.type ~= 'local' then
+ error('saveLocal必须是local')
+ end
+ self.upvalues[name] = loc
+end
+
+function mt:loadLocal(name)
+ for i = self._top, 1, -1 do
+ local locals = self.locals[i]
+ local loc = locals[name]
+ if loc then
+ return loc
+ end
+ if self.chunk[i] then
+ break
+ end
+ end
+ local uv = self.upvalues[name]
+ if uv then
+ return uv
+ end
+ return nil
+end
+
+function mt:eachLocal(callback)
+ local mark = {}
+ for i = self._top, 1, -1 do
+ local locals = self.locals[i]
+ for name, loc in pairs(locals) do
+ if not mark[name] then
+ mark[name] = true
+ local res = callback(name, loc)
+ if res ~= nil then
+ return res
+ end
+ end
+ end
+ if self.chunk[i] then
+ break
+ end
+ end
+ for name, loc in pairs(self.upvalues) do
+ if not mark[name] then
+ mark[name] = true
+ local res = callback(name, loc)
+ if res ~= nil then
+ return res
+ end
+ end
+ end
+ return nil
+end
+
+function mt:saveLabel(label)
+ if self._removed then
+ return
+ end
+ if not self._label then
+ self._label = {}
+ end
+ self._label[#self._label+1] = label
+end
+
+function mt:loadLabel(name)
+ if not self._label then
+ return nil
+ end
+ for _, label in ipairs(self._label) do
+ if label:getName() == name then
+ return label
+ end
+ end
+ return nil
+end
+
+function mt:rawSetReturn(index, value)
+ if self._removed then
+ return
+ end
+ self:set('hasReturn', true)
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ if value then
+ self.returns:set(index, value)
+ if self._global then
+ value:markGlobal()
+ end
+ end
+end
+
+function mt:setReturn(index, value)
+ local emmy = self._emmyReturns and self._emmyReturns[index]
+ if emmy then
+ if emmy:bindType() or emmy:bindGeneric() then
+ return
+ end
+ end
+ return self:rawSetReturn(index, value)
+end
+
+function mt:mergeReturn(index, value)
+ if self._removed then
+ return
+ end
+ local emmy = self._emmyReturns and self._emmyReturns[index]
+ if emmy then
+ if emmy:bindType() or emmy:bindGeneric() then
+ return
+ end
+ end
+ self:set('hasReturn', true)
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ if value then
+ if self.returns[index] then
+ self.returns[index]:mergeValue(value)
+ self.returns[index] = value
+ else
+ self.returns:set(index, value)
+ end
+ end
+ if self._global then
+ value:markGlobal()
+ end
+end
+
+function mt:getReturn(index)
+ if self._removed then
+ return nil
+ end
+ if self.maxReturns and index and self.maxReturns < index then
+ return nil
+ end
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ if index then
+ return self.returns:get(index)
+ else
+ return self.returns
+ end
+end
+
+function mt:returnDots(index)
+ if not self.returns then
+ self.returns = createMulti()
+ end
+ --self.returns[index] = createMulti()
+end
+
+function mt:loadDots()
+ if not self._dots then
+ self._dots = createMulti()
+ end
+ self._dotsLoad = true
+ return self._dots
+end
+
+function mt:setObject(value, source)
+ self._objectValue = value
+ self._objectSource = source
+end
+
+function mt:getObject()
+ return self._objectSource, self._objectValue
+end
+
+function mt:hasRuned()
+ return self._runed > 0
+end
+
+function mt:needSkip()
+ return self._runed > 3
+end
+
+---@param vm VM
+function mt:run(vm)
+ if self._removed then
+ return
+ end
+ if not self:getSource() then
+ return
+ end
+
+ self._runed = self._runed + 1
+
+ -- 第一次运行函数时,创建函数的参数
+ if self._runed == 1 then
+ -- 如果是面向对象形式的函数,创建隐藏的参数self
+ if self._objectSource then
+ local loc = localMgr.create('self', vm:instantSource(self._objectSource), self._objectValue)
+ loc:set('hide', true)
+ loc:set('start', self:getSource().start)
+ loc:close(self:getSource().finish)
+ self:saveUpvalue('self', loc)
+ self.args[#self.args+1] = loc
+ end
+
+ -- 显性声明的参数
+ self:createArgs(vm)
+ end
+
+ if self:needSkip() then
+ return
+ end
+
+ -- 向局部变量中填充参数
+ for i, loc in ipairs(self.args) do
+ loc:setValue(self.argValues[i])
+ local emmyParam = self:findEmmyParamByName(loc:getName())
+ if emmyParam then
+ local typeObj = emmyParam:bindType()
+ if typeObj then
+ loc:getValue():setEmmy(typeObj)
+ end
+ local genericObj = emmyParam:bindGeneric()
+ if genericObj then
+ genericObj:setValue(loc:getValue())
+ end
+ end
+ end
+ if self._dots then
+ local emmyParam = self:findEmmyParamByName('...')
+ self._dots = createMulti()
+ for i = #self.args + 1, #self.argValues do
+ local value = self.argValues[i]
+ self._dots:push(value)
+ if emmyParam then
+ local typeObj = emmyParam:bindType()
+ if typeObj then
+ value:setEmmy(typeObj)
+ end
+ local genericObj = emmyParam:bindGeneric()
+ if genericObj then
+ genericObj:setValue(value)
+ end
+ end
+ end
+ if emmyParam then
+ local typeObj = emmyParam:bindType()
+ if typeObj then
+ self._dots:setEmmy(typeObj)
+ end
+ local genericObj = emmyParam:bindGeneric()
+ if genericObj then
+ local value = self._dots:first()
+ if value then
+ genericObj:setValue(value)
+ end
+ end
+ end
+ end
+
+ -- 填充返回值
+ if self._emmyReturns then
+ for i, rtn in ipairs(self._emmyReturns) do
+ local value = vm:createValue('nil', rtn:getSource())
+ local typeObj = rtn:bindType()
+ if typeObj then
+ value:setEmmy(typeObj)
+ end
+ local genericObj = rtn:bindGeneric()
+ if genericObj then
+ local destValue = genericObj:getValue()
+ if destValue then
+ value:mergeType(destValue)
+ end
+ end
+ self:rawSetReturn(i, value)
+ end
+ end
+end
+
+function mt:eachEmmyReturn(callback)
+ if not self._emmyReturns then
+ return
+ end
+ for _, rtn in ipairs(self._emmyReturns) do
+ callback(rtn)
+ end
+end
+
+function mt:setArgs(values)
+ for i = 1, #self.argValues do
+ self.argValues[i] = nil
+ end
+ for i = 1, #values do
+ self.argValues[i] = values[i]
+ end
+end
+
+function mt:findEmmyParamByName(name)
+ local params = self._emmyParams
+ if not params then
+ return nil
+ end
+ for i = #params, 1, -1 do
+ local param = params[i]
+ if param:getName() == name then
+ return param
+ end
+ end
+ return nil
+end
+
+function mt:findEmmyParamByIndex(index)
+ local arg = self.args[index]
+ if not arg then
+ return nil
+ end
+ local name = arg:getName()
+ return self:findEmmyParamByName(name)
+end
+
+function mt:addArg(name, source, value, close)
+ local loc = localMgr.create(name, source, value)
+ loc:close(close)
+ self:saveUpvalue(name, loc)
+ self.args[#self.args+1] = loc
+ return loc
+end
+
+function mt:createArg(vm, arg, close)
+ vm:instantSource(arg)
+ arg:set('arg', self)
+ if arg.type == 'name' then
+ vm:instantSource(arg)
+ local value = valueMgr.create('nil', arg)
+ self:addArg(arg[1], arg, value, close)
+ elseif arg.type == '...' then
+ self._dots = createMulti()
+ self._dotsSource = arg
+ end
+end
+
+function mt:createLibArg(arg, source)
+ if arg.type == '...' then
+ self._dots = createMulti()
+ else
+ local name = arg.name or '_'
+ local loc = localMgr.create(name, source, valueMgr.create('any', source))
+ self:saveUpvalue(name, loc)
+ self.args[#self.args+1] = loc
+ end
+end
+
+function mt:hasDots()
+ return self._dots ~= nil
+end
+
+function mt:createArgs(vm)
+ if not self:getSource() then
+ return
+ end
+ local args = self:getSource().arg
+ if not args then
+ return
+ end
+ local close = self:getSource().finish
+ if args.type == 'list' then
+ for _, arg in ipairs(args) do
+ self:createArg(vm, arg, close)
+ end
+ else
+ self:createArg(vm, args, close)
+ end
+end
+
+function mt:set(name, v)
+ if not self._flag then
+ self._flag = {}
+ end
+ self._flag[name] = v
+end
+
+function mt:get(name)
+ if not self._flag then
+ return nil
+ end
+ return self._flag[name]
+end
+
+function mt:getSource()
+ if self._removed then
+ return nil
+ end
+ return listMgr.get(self.source)
+end
+
+function mt:kill()
+ if self._removed then
+ return
+ end
+ self._removed = true
+ listMgr.clear(self.id)
+end
+
+function mt:markGlobal()
+ if self._global then
+ return
+ end
+ self._global = true
+ if self.returns then
+ self.returns:eachValue(function (_, v)
+ v:markGlobal()
+ end)
+ end
+end
+
+function mt:setEmmy(params, returns, overLoads)
+ if params then
+ self._emmyParams = params
+ for _, param in ipairs(params) do
+ param:getSource():set('emmy function', self)
+ param:getSource()[1]:set('emmy function', self)
+ end
+ end
+ if returns then
+ self._emmyReturns = returns
+ for _, rtn in ipairs(returns) do
+ rtn:getSource():set('emmy function', self)
+ end
+ end
+ if overLoads then
+ self._emmyOverLoads = overLoads
+ for _, ol in ipairs(overLoads) do
+ ol:getSource():set('emmy function', self)
+ end
+ end
+end
+
+---@param comment string
+function mt:setComment(comment)
+ self._comment = comment
+end
+
+---@return string
+function mt:getComment()
+ return self._comment
+end
+
+function mt:getEmmyParams()
+ return self._emmyParams
+end
+
+function mt:getEmmyOverLoads()
+ return self._emmyOverLoads
+end
+
+local function create(source)
+ if not source then
+ error('Function need source')
+ end
+ local id = source.id
+ if not id then
+ error('Not instanted source')
+ end
+ local self = setmetatable({
+ source = id,
+ locals = {},
+ upvalues = {},
+ chunk = {},
+ finishs = {},
+ args = {},
+ argValues = {},
+ }, mt)
+
+ local id = listMgr.add(self)
+ self.id = id
+ Watch[self] = id
+ return self
+end
+
+return {
+ create = create,
+ watch = Watch,
+}