Skip to content

Commit 41f9777

Browse files
committed
Add new feature getmetatable type infer
1 parent dc4c9f2 commit 41f9777

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

script/parser/compile.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ local Specials = {
108108
['rawset'] = true,
109109
['rawget'] = true,
110110
['setmetatable'] = true,
111+
['getmetatable'] = true,
111112
['require'] = true,
112113
['dofile'] = true,
113114
['loadfile'] = true,

script/vm/compiler.lua

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,9 +588,21 @@ local function getReturnOfSetMetaTable(args)
588588
local mt = args[2]
589589
local node = vm.createNode()
590590
if tbl then
591-
node:merge(vm.compileNode(tbl))
591+
local tblNode = vm.compileNode(tbl)
592+
node:merge(tblNode)
593+
594+
-- 存储元表信息,将mt节点作为源节点的元表
595+
if mt then
596+
local mtNode = vm.compileNode(mt)
597+
-- 为节点添加metatable属性
598+
for n in tblNode:eachObject() do
599+
n.metatable = mtNode
600+
end
601+
end
592602
end
603+
593604
if mt then
605+
-- 合并__index属性到返回节点
594606
vm.compileByParentNodeAll(mt, '__index', function (src)
595607
for n in vm.compileNode(src):eachObject() do
596608
if n.type == 'global'
@@ -602,8 +614,49 @@ local function getReturnOfSetMetaTable(args)
602614
end
603615
end)
604616
end
617+
605618
--过滤nil
606-
node:remove 'nil'
619+
node:remove 'nil'
620+
return node
621+
end
622+
623+
---@param args parser.object[]
624+
---@return vm.node
625+
local function getReturnOfGetMetaTable(args)
626+
local obj = args[1]
627+
local node = vm.createNode()
628+
if not obj then
629+
return node
630+
end
631+
632+
local objNode = vm.compileNode(obj)
633+
-- 尝试遍历对象的所有可能类型
634+
for n in objNode:eachObject() do
635+
-- 检查是否有metatable属性
636+
if n.metatable then
637+
node:merge(n.metatable)
638+
end
639+
640+
-- 检查是否有setmetatable调用
641+
if n.value and n.value.type == 'call' and n.value.node and n.value.node.special == 'setmetatable' and n.value.args and n.value.args[2] then
642+
-- 直接返回setmetatable的第二个参数
643+
node:merge(vm.compileNode(n.value.args[2]))
644+
end
645+
646+
-- 如果是__metatable字段的处理,优先使用它
647+
vm.compileByParentNodeAll(obj, '__metatable', function (src)
648+
for metaObj in vm.compileNode(src):eachObject() do
649+
node:merge(metaObj)
650+
end
651+
end)
652+
end
653+
654+
-- 如果没有找到任何元表信息,创建一个空表类型
655+
if node:isEmpty() then
656+
node:addTable()
657+
end
658+
659+
node:remove 'nil'
607660
return node
608661
end
609662

@@ -1821,6 +1874,13 @@ local compilerSwitch = util.switch()
18211874
vm.setNode(source, getReturnOfSetMetaTable(args))
18221875
return
18231876
end
1877+
if func.special == 'getmetatable' then
1878+
if not args then
1879+
return
1880+
end
1881+
vm.setNode(source, getReturnOfGetMetaTable(args))
1882+
return
1883+
end
18241884
if func.special == 'pcall' and index > 1 then
18251885
if not args then
18261886
return

0 commit comments

Comments
 (0)