summaryrefslogtreecommitdiff
path: root/script/vm/tracer.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/vm/tracer.lua')
-rw-r--r--script/vm/tracer.lua512
1 files changed, 351 insertions, 161 deletions
diff --git a/script/vm/tracer.lua b/script/vm/tracer.lua
index f03ab001..35b9db41 100644
--- a/script/vm/tracer.lua
+++ b/script/vm/tracer.lua
@@ -181,48 +181,324 @@ function mt:fastWardCasts(pos, node)
return node
end
----@param action parser.object
----@param topNode vm.node
----@param outNode? vm.node
----@return vm.node topNode
----@return vm.node outNode
-function mt:lookIntoChild(action, topNode, outNode)
- if not self.careMap[action]
- or self.mark[action] then
- return topNode, outNode or topNode
- end
- self.mark[action] = true
- topNode = self:fastWardCasts(action.start, topNode)
- if action.type == 'getlocal' then
- if action.node == self.source then
- self.nodes[action] = topNode
+local lookIntoChild = util.switch()
+ : case 'getlocal'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ if action.node == tracer.source then
+ tracer.nodes[action] = topNode
if outNode then
topNode = topNode:copy():setTruthy()
outNode = outNode:copy():setFalsy()
end
end
- elseif action.type == 'function' then
- self:lookIntoBlock(action, action.bstart, topNode:copy())
- elseif action.type == 'unary' then
- if not action[1] then
- goto RETURN
+ return topNode, outNode
+ end)
+ : case 'repeat'
+ : case 'loop'
+ : case 'for'
+ : case 'do'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ if action[1] then
+ tracer:lookIntoBlock(action, action.bstart, topNode:copy())
+ local lastAssign = tracer:getLastAssign(action.start, action.finish)
+ if lastAssign then
+ tracer:getNode(lastAssign)
+ end
+ if tracer.nodes[action] then
+ topNode = tracer.nodes[action]:copy()
+ end
end
- if action.op.type == 'not' then
- outNode = outNode or topNode:copy()
- outNode, topNode = self:lookIntoChild(action[1], topNode, outNode)
- outNode = outNode:copy()
+ return topNode, outNode
+ end)
+ : case 'in'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.exps, topNode)
+ if action[1] then
+ tracer:lookIntoBlock(action, action.bstart, topNode:copy())
+ local lastAssign = tracer:getLastAssign(action.start, action.finish)
+ if lastAssign then
+ tracer:getNode(lastAssign)
+ end
+ if tracer.nodes[action] then
+ topNode = tracer.nodes[action]:copy()
+ end
+ end
+ return topNode, outNode
+ end)
+ : case 'while'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ local blockNode, mainNode
+ if action.filter then
+ blockNode, mainNode = tracer:lookIntoChild(action.filter, topNode:copy(), topNode:copy())
+ else
+ blockNode = topNode:copy()
+ mainNode = topNode:copy()
+ end
+ if action[1] then
+ tracer:lookIntoBlock(action, action.bstart, blockNode:copy())
+ local lastAssign = tracer:getLastAssign(action.start, action.finish)
+ if lastAssign then
+ tracer:getNode(lastAssign)
+ end
+ if tracer.nodes[action] then
+ topNode = mainNode:merge(tracer.nodes[action])
+ end
+ end
+ if action.filter then
+ -- look into filter again
+ guide.eachSource(action.filter, function (src)
+ tracer.mark[src] = nil
+ end)
+ blockNode, topNode = tracer:lookIntoChild(action.filter, topNode:copy(), topNode:copy())
+ end
+ return topNode, outNode
+ end)
+ : case 'if'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ local hasElse
+ local mainNode = topNode:copy()
+ local blockNodes = {}
+ for _, subBlock in ipairs(action) do
+ tracer:resetCastsIndex(subBlock.start)
+ local blockNode = mainNode:copy()
+ if subBlock.filter then
+ blockNode, mainNode = tracer:lookIntoChild(subBlock.filter, blockNode, mainNode)
+ else
+ hasElse = true
+ mainNode:clear()
+ end
+ local mergedNode
+ if subBlock[1] then
+ tracer:lookIntoBlock(subBlock, subBlock.bstart, blockNode:copy())
+ local neverReturn = subBlock.hasReturn
+ or subBlock.hasGoTo
+ or subBlock.hasBreak
+ or subBlock.hasError
+ if neverReturn then
+ mergedNode = true
+ else
+ local lastAssign = tracer:getLastAssign(subBlock.start, subBlock.finish)
+ if lastAssign then
+ tracer:getNode(lastAssign)
+ end
+ if tracer.nodes[subBlock] then
+ blockNodes[#blockNodes+1] = tracer.nodes[subBlock]
+ mergedNode = true
+ end
+ end
+ end
+ if not mergedNode then
+ blockNodes[#blockNodes+1] = blockNode
+ end
+ end
+ if not hasElse and not topNode:hasKnownType() then
+ mainNode:merge(vm.declareGlobal('type', 'unknown'))
+ end
+ for _, blockNode in ipairs(blockNodes) do
+ mainNode:merge(blockNode)
+ end
+ topNode = mainNode
+ return topNode, outNode
+ end)
+ : case 'getfield'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.node, topNode)
+ tracer:lookIntoChild(action.field, topNode)
+ return topNode, outNode
+ end)
+ : case 'getmethod'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.node, topNode)
+ tracer:lookIntoChild(action.method, topNode)
+ return topNode, outNode
+ end)
+ : case 'getindex'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.node, topNode)
+ tracer:lookIntoChild(action.index, topNode)
+ return topNode, outNode
+ end)
+ : case 'setfield'
+ : case 'setmethod'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.node, topNode)
+ tracer:lookIntoChild(action.value, topNode)
+ return topNode, outNode
+ end)
+ : case 'setglobal'
+ : case 'setlocal'
+ : case 'tablefield'
+ : case 'tableexp'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.value, topNode)
+ return topNode, outNode
+ end)
+ : case 'setindex'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.node, topNode)
+ tracer:lookIntoChild(action.index, topNode)
+ tracer:lookIntoChild(action.value, topNode)
+ return topNode, outNode
+ end)
+ : case 'tableindex'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.index, topNode)
+ tracer:lookIntoChild(action.value, topNode)
+ return topNode, outNode
+ end)
+ : case 'local'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.value, topNode)
+ -- special treat for `local tp = type(x)`
+ if action.value
+ and action.ref
+ and action.value.type == 'select' then
+ local index = action.value.sindex
+ local call = action.value.vararg
+ if index == 1
+ and call.type == 'call'
+ and call.node
+ and call.node.special == 'type'
+ and call.args then
+ local getLoc = call.args[1]
+ if getLoc
+ and getLoc.type == 'getlocal'
+ and getLoc.node == tracer.source then
+ for _, ref in ipairs(action.ref) do
+ tracer:collectCare(ref)
+ end
+ end
+ end
end
- elseif action.type == 'binary' then
+ return topNode, outNode
+ end)
+ : case 'return'
+ : case 'table'
+ : case 'callargs'
+ : case 'list'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ for _, ret in ipairs(action) do
+ tracer:lookIntoChild(ret, topNode)
+ end
+ return topNode, outNode
+ end)
+ : case 'select'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoChild(action.vararg, topNode)
+ return topNode, outNode
+ end)
+ : case 'function'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ tracer:lookIntoBlock(action, action.bstart, topNode:copy())
+ return topNode, outNode
+ end)
+ : case 'paren'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ topNode, outNode = tracer:lookIntoChild(action.exp, topNode, outNode)
+ return topNode, outNode
+ end)
+ : case 'call'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ if action.node.special == 'assert' and action.args and action.args[1] then
+ for i = 2, #action.args do
+ tracer:lookIntoChild(action.args[i], topNode, topNode:copy())
+ end
+ topNode = tracer:lookIntoChild(action.args[1], topNode, topNode:copy())
+ end
+ tracer:lookIntoChild(action.node, topNode)
+ tracer:lookIntoChild(action.args, topNode)
+ return topNode, outNode
+ end)
+ : case 'binary'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
if not action[1] or not action[2] then
- goto RETURN
+ tracer:lookIntoChild(action[1], topNode)
+ tracer:lookIntoChild(action[2], topNode)
+ return topNode, outNode
end
if action.op.type == 'and' then
- topNode = self:lookIntoChild(action[1], topNode, topNode:copy())
- topNode = self:lookIntoChild(action[2], topNode, topNode:copy())
+ topNode = tracer:lookIntoChild(action[1], topNode, topNode:copy())
+ topNode = tracer:lookIntoChild(action[2], topNode, topNode:copy())
elseif action.op.type == 'or' then
outNode = outNode or topNode:copy()
- local topNode1, outNode1 = self:lookIntoChild(action[1], topNode, outNode)
- local topNode2, outNode2 = self:lookIntoChild(action[2], outNode1, outNode1:copy())
+ local topNode1, outNode1 = tracer:lookIntoChild(action[1], topNode, outNode)
+ local topNode2, outNode2 = tracer:lookIntoChild(action[2], outNode1, outNode1:copy())
topNode = vm.createNode(topNode1, topNode2)
outNode = outNode2:copy()
elseif action.op.type == '=='
@@ -235,25 +511,25 @@ function mt:lookIntoChild(action, topNode, outNode)
end
end
if not handler then
- goto RETURN
+ return topNode, outNode
end
if handler.type == 'getlocal'
- and handler.node == self.source then
+ and handler.node == tracer.source then
-- if x == y then
- topNode = self:lookIntoChild(handler, topNode, outNode)
+ topNode = tracer:lookIntoChild(handler, topNode, outNode)
local checkerNode = vm.compileNode(checker)
local checkerName = vm.getNodeName(checker)
if checkerName then
topNode = topNode:copy()
if action.op.type == '==' then
- topNode:narrow(self.uri, checkerName)
+ topNode:narrow(tracer.uri, checkerName)
if outNode then
outNode:removeNode(checkerNode)
end
else
topNode:removeNode(checkerNode)
if outNode then
- outNode:narrow(self.uri, checkerName)
+ outNode:narrow(tracer.uri, checkerName)
end
end
end
@@ -263,18 +539,18 @@ function mt:lookIntoChild(action, topNode, outNode)
and handler.args
and handler.args[1]
and handler.args[1].type == 'getlocal'
- and handler.args[1].node == self.source then
+ and handler.args[1].node == tracer.source then
-- if type(x) == 'string' then
- self:lookIntoChild(handler, topNode:copy())
+ tracer:lookIntoChild(handler, topNode:copy())
if action.op.type == '==' then
- topNode:narrow(self.uri, checker[1])
+ topNode:narrow(tracer.uri, checker[1])
if outNode then
outNode:remove(checker[1])
end
else
topNode:remove(checker[1])
if outNode then
- outNode:narrow(self.uri, checker[1])
+ outNode:narrow(tracer.uri, checker[1])
end
end
elseif handler.type == 'getlocal'
@@ -290,145 +566,59 @@ function mt:lookIntoChild(action, topNode, outNode)
and call.args
and call.args[1]
and call.args[1].type == 'getlocal'
- and call.args[1].node == self.source then
+ and call.args[1].node == tracer.source then
-- `local tp = type(x);if tp == 'string' then`
if action.op.type == '==' then
- topNode:narrow(self.uri, checker[1])
+ topNode:narrow(tracer.uri, checker[1])
if outNode then
outNode:remove(checker[1])
end
else
topNode:remove(checker[1])
if outNode then
- outNode:narrow(self.uri, checker[1])
+ outNode:narrow(tracer.uri, checker[1])
end
end
end
end
end
end
- elseif action.type == 'loop'
- or action.type == 'in'
- or action.type == 'repeat'
- or action.type == 'for'
- or action.type == 'do' then
- if action[1] then
- self:lookIntoBlock(action, action.bstart, topNode:copy())
- local lastAssign = self:getLastAssign(action.start, action.finish)
- if lastAssign then
- self:getNode(lastAssign)
- end
- if self.nodes[action] then
- topNode = self.nodes[action]:copy()
- end
- end
- elseif action.type == 'while' then
- local blockNode, mainNode
- if action.filter then
- blockNode, mainNode = self:lookIntoChild(action.filter, topNode:copy(), topNode:copy())
- else
- blockNode = topNode:copy()
- mainNode = topNode:copy()
- end
- if action[1] then
- self:lookIntoBlock(action, action.bstart, blockNode:copy())
- local lastAssign = self:getLastAssign(action.start, action.finish)
- if lastAssign then
- self:getNode(lastAssign)
- end
- if self.nodes[action] then
- topNode = mainNode:merge(self.nodes[action])
- end
- end
- if action.filter then
- -- look into filter again
- guide.eachSource(action.filter, function (src)
- self.mark[src] = nil
- end)
- blockNode, topNode = self:lookIntoChild(action.filter, topNode:copy(), topNode:copy())
- end
- elseif action.type == 'if' then
- local hasElse
- local mainNode = topNode:copy()
- local blockNodes = {}
- for _, subBlock in ipairs(action) do
- self:resetCastsIndex(subBlock.start)
- local blockNode = mainNode:copy()
- if subBlock.filter then
- blockNode, mainNode = self:lookIntoChild(subBlock.filter, blockNode, mainNode)
- else
- hasElse = true
- mainNode:clear()
- end
- local mergedNode
- if subBlock[1] then
- self:lookIntoBlock(subBlock, subBlock.bstart, blockNode:copy())
- local neverReturn = subBlock.hasReturn
- or subBlock.hasGoTo
- or subBlock.hasBreak
- or subBlock.hasError
- if neverReturn then
- mergedNode = true
- else
- local lastAssign = self:getLastAssign(subBlock.start, subBlock.finish)
- if lastAssign then
- self:getNode(lastAssign)
- end
- if self.nodes[subBlock] then
- blockNodes[#blockNodes+1] = self.nodes[subBlock]
- mergedNode = true
- end
- end
- end
- if not mergedNode then
- blockNodes[#blockNodes+1] = blockNode
- end
- end
- if not hasElse and not topNode:hasKnownType() then
- mainNode:merge(vm.declareGlobal('type', 'unknown'))
- end
- for _, blockNode in ipairs(blockNodes) do
- mainNode:merge(blockNode)
- end
- topNode = mainNode
- elseif action.type == 'call' then
- if action.node.special == 'assert' and action.args and action.args[1] then
- topNode = self:lookIntoChild(action.args[1], topNode, topNode:copy())
- end
- elseif action.type == 'paren' then
- topNode, outNode = self:lookIntoChild(action.exp, topNode, outNode)
- elseif action.type == 'setlocal' then
- if action.value then
- self:lookIntoChild(action.value, topNode)
- end
- elseif action.type == 'local' then
- if action.value
- and action.ref
- and action.value.type == 'select' then
- local index = action.value.sindex
- local call = action.value.vararg
- if index == 1
- and call.type == 'call'
- and call.node
- and call.node.special == 'type'
- and call.args then
- local getLoc = call.args[1]
- if getLoc
- and getLoc.type == 'getlocal'
- and getLoc.node == self.source then
- for _, ref in ipairs(action.ref) do
- self:collectCare(ref)
- end
- end
- end
+ tracer:lookIntoChild(action[1], topNode)
+ tracer:lookIntoChild(action[2], topNode)
+ return topNode, outNode
+ end)
+ : case 'unary'
+ ---@param tracer vm.tracer
+ ---@param action parser.object
+ ---@param topNode vm.node
+ ---@param outNode? vm.node
+ : call(function (tracer, action, topNode, outNode)
+ if not action[1] then
+ tracer:lookIntoChild(action[1], topNode)
+ return topNode, outNode
end
- end
- ::RETURN::
- guide.eachChild(action, function (src)
- if self.careMap[src] then
- self:lookIntoChild(src, topNode)
+ if action.op.type == 'not' then
+ outNode = outNode or topNode:copy()
+ outNode, topNode = tracer:lookIntoChild(action[1], topNode, outNode)
+ outNode = outNode:copy()
end
+ tracer:lookIntoChild(action[1], topNode)
+ return topNode, outNode
end)
+
+---@param action parser.object
+---@param topNode vm.node
+---@param outNode? vm.node
+---@return vm.node topNode
+---@return vm.node outNode
+function mt:lookIntoChild(action, topNode, outNode)
+ if not self.careMap[action]
+ or self.mark[action] then
+ return topNode, outNode or topNode
+ end
+ self.mark[action] = true
+ topNode = self:fastWardCasts(action.start, topNode)
+ topNode, outNode = lookIntoChild(action.type, self, action, topNode, outNode)
return topNode, outNode or topNode
end