summaryrefslogtreecommitdiff
path: root/script-beta/vm/getGlobals.lua
blob: d7470d47e5577f48f653a5ad49c763d04d1a4e26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
local guide   = require 'parser.guide'
local vm      = require 'vm.vm'
local files   = require 'files'
local library = require 'library'
local util    = require 'utility'
local config  = require 'config'

local function searchRawset(ref, results)
    if guide.getKeyName(ref) ~= 's|rawset' then
        return
    end
    local call = ref.parent
    if call.type ~= 'call' or call.node ~= ref then
        return
    end
    if not call.args then
        return
    end
    local arg1 = call.args[1]
    if arg1.special ~= '_G' then
        -- 不会吧不会吧,不会真的有人写成 `rawset(_G._G._G, 'xxx', value)` 吧
        return
    end
    results[#results+1] = call
end

local function searchG(ref, results)
    while ref and guide.getKeyName(ref) == 's|_G' do
        results[#results+1] = ref
        ref = ref.next
    end
    if ref then
        results[#results+1] = ref
        searchRawset(ref, results)
    end
end

local function searchEnvRef(ref, results)
    if     ref.type == 'setglobal'
    or     ref.type == 'getglobal' then
        results[#results+1] = ref
        searchG(ref, results)
    elseif ref.type == 'getlocal' then
        results[#results+1] = ref.next
        searchG(ref.next, results)
    end
end

local function getGlobalsOfFile(uri)
    local globals = {}
    local ast = files.getAst(uri)
    if not ast then
        return globals
    end
    local results = {}
    local env = guide.getENV(ast.ast)
    if env.ref then
        for _, ref in ipairs(env.ref) do
            searchEnvRef(ref, results)
        end
    end
    local mark = {}
    for _, res in ipairs(results) do
        if mark[res] then
            goto CONTINUE
        end
        mark[res] = true
        local name = guide.getSimpleName(res)
        if name then
            if not globals[name] then
                globals[name] = {}
            end
            globals[name][#globals[name]+1] = res
        end
        ::CONTINUE::
    end
    return globals
end

local function insertLibrary(results, name)
    if name:sub(1, 2) == 's|' then
        local libname = name:sub(3)
        results[#results+1] = library.global[libname]
        local asName = config.config.runtime.special[libname]
        results[#results+1] = library.global[asName]
    end
end

local function getGlobals(name)
    local results = {}
    for uri in files.eachFile() do
        local cache = files.getCache(uri)
        cache.globals = cache.globals or getGlobalsOfFile(uri)
        if name == '*' then
            for _, sources in util.sortPairs(cache.globals) do
                for _, source in ipairs(sources) do
                    results[#results+1] = source
                end
            end
        else
            if cache.globals[name] then
                for _, source in ipairs(cache.globals[name]) do
                    results[#results+1] = source
                end
            end
        end
    end
    insertLibrary(results, name)
    return results
end

function vm.getGlobals(name)
    local cache = vm.getCache('getGlobals')[name]
    if cache ~= nil then
        return cache
    end
    cache = getGlobals(name)
    vm.getCache('getGlobals')[name] = cache
    return cache
end

function vm.getGlobalSets(name)
    local cache = vm.getCache('getGlobalSets')[name]
    if cache ~= nil then
        return cache
    end
    cache = {}
    local refs = getGlobals(name)
    for _, source in ipairs(refs) do
        if vm.isSet(source) then
            cache[#cache+1] = source
        end
    end
    vm.getCache('getGlobalSets')[name] = cache
    return cache
end