-
Notifications
You must be signed in to change notification settings - Fork 170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Workaround to make __gc work on tables #790
Conversation
spec/gc_spec.lua
Outdated
setmetatable(table, { __gc = function() end }) | ||
stub(getmetatable(table), '__gc') | ||
|
||
local table_with_gc = GC.enable(table) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
variable 'table_with_gc' is never accessed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's OK. We need to define it so it's garbage collected later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I approved this one manually in codeclimate.
d8ab645
to
996cd84
Compare
gateway/src/apicast/gc.lua
Outdated
|
||
mt.__index = table | ||
mt.__newindex = table | ||
mt.__table = table |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice to also hide the this metatable by:
mt.__metatable = getmetatable(table)
Then getmetatable(proxy)
will return the metatable returned by getmetatable(table)
.
This would not propagate if someone would change the metatable of table
directly, but that can be avoided by changing the API a bit.
gateway/src/apicast/gc.lua
Outdated
end | ||
end | ||
|
||
function _M.enable(table) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could rename this library to setmetatable_gc(table, mt)
.
It could make more sense when using the __metatable
key.
All changes to GC'd objects metatables should be done with this method.
gateway/src/apicast/gc.lua
Outdated
mt.__newindex = table | ||
mt.__table = table | ||
|
||
mt.__len = function() return #table end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should avoid allocating functions for each object.
These can be shared by (even when using __metatable
):
local rawgetmetatable = debug.getmetatable
local getmetatable = getmetatable
local function __len(t)
return #(rawgetmetatable(t).__table)
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know about debug.getmetatable
👍
gateway/src/apicast/gc.lua
Outdated
end | ||
|
||
local function __call(proxy, ...) | ||
local t = getmetatable(proxy).__table |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should defined this as:
getmetatable(proxy).__table(...)
Because now it basically defines all tables callable.
Unfortunately that can yield rather cryptic error message: attempt to call field '__table' (a nil value)
.
Unfortunately Lua tries to be helpful and the error message depends on how you call the table:
attempt to call local 'varname' (a table value)
attempt to call a table value
So replicating those messages might not be possible.
Based on how the function is stored (or if it is a field).
The only workaround I can imagine is using pcall
like:
local function __call(self, ...)
local mt = rawgetmetatable(self)
local ret = { pcall(mt.__table, ...) }
local ok = table.remove(ret, 1)
if ok then
return unpack(ret)
else
error(ret[1], 2)
end
end
That would throw error: ERROR: gc.lua:66: attempt to call a table value
(even with correct line pointing to the original caller).
spec/gc_spec.lua
Outdated
stub(getmetatable(test_table), '__gc') | ||
|
||
local table_with_gc = GC.enable(test_table) | ||
table_with_gc = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could avoid that CC error by assert(table_with_gc)
right ?
spec/gc_spec.lua
Outdated
|
||
local table_with_gc = GC.set_metatable_gc(test_table, test_metatable) | ||
assert(table_with_gc) | ||
table_with_gc = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
value assigned to variable 'table_with_gc' is unused
gateway/src/apicast/gc.lua
Outdated
return pairs(original_table(proxy)) | ||
end | ||
|
||
function _M.set_metatable_gc(table, metatable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shadowing upvalue 'table' on line 11
510c797
to
4dd556e
Compare
@mikz I addressed your comments. In some cases, I adopted a slightly different solution. Can you take a look? |
gateway/src/apicast/gc.lua
Outdated
end | ||
|
||
local function __tostring(proxy) | ||
local mt = getmetatable(proxy) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not calling tostring(original_table(proxy))
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was needed because I wasn't calling setmetatable(t, metatable)
at the beginning of set_metatable_gc
. I needed to do that, otherwise, __index would not work.
I fixed this in a new commit. Please check it. I'll squash it after review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 This is great!
In LuaJIT and Lua 5.1, the
__gc
metamethod does not work with tables, it only works with "userdata". This PR introduces a module that implements a workaround to make it work with tables.