diff options
Diffstat (limited to 'script/vm/tracer.lua')
-rw-r--r-- | script/vm/tracer.lua | 512 |
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 |