mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-15 10:21:45 -04:00
Lua-side implementation of getter/setter callbacks.
This commit is contained in:
parent
818caae9d0
commit
b3862a99d1
@ -95,9 +95,18 @@ public @interface Callback {
|
||||
* Whether this callback should work like a getter.
|
||||
* <p/>
|
||||
* Callbacks that are getters do not appear as methods on a component's
|
||||
* proxy. Instead they are accessed via the proxy's <tt>__index</tt>
|
||||
* metamethod, i.e. it is accessed as a field, with it's only parameter
|
||||
* being the accessed key.
|
||||
* proxy. Instead they are accessed as fields, for example in Lua via the
|
||||
* proxy's <tt>__index</tt> metamethod, with it's only parameter being the
|
||||
* accessed key.
|
||||
* <p/>
|
||||
* Note: if you wish to have a field that is read/write, that is you need
|
||||
* both a getter and a setter, you have to implement them in the same
|
||||
* method. This a limitation due to callback names being unique. You can
|
||||
* differentiate between contexts by checking the number of arguments.
|
||||
* <p/>
|
||||
* <em>Important</em>: this only works in environments (for components),
|
||||
* it does <em>not</em> work for userdata (<tt>Value</tt> objects). For
|
||||
* userdata, use the <tt>apply</tt> method instead.
|
||||
*/
|
||||
boolean getter() default false;
|
||||
|
||||
@ -105,9 +114,18 @@ public @interface Callback {
|
||||
* Whether this callback should work like a setter.
|
||||
* <p/>
|
||||
* Callbacks that are setters do not appear as methods on a component's
|
||||
* proxy. Instead they are accessed via the proxy's <tt>__newindex</tt>
|
||||
* metamethod, i.e. it is accessed as a field, with it's only parameters
|
||||
* being the accessed key and the assigned value.
|
||||
* proxy. Instead they are accessed as fields, for example in Lua via the
|
||||
* proxy's <tt>__newindex</tt> metamethod, with it's only tow parameters
|
||||
* being the accessed key and the new value.
|
||||
* <p/>
|
||||
* Note: if you wish to have a field that is read/write, that is you need
|
||||
* both a getter and a setter, you have to implement them in the same
|
||||
* method. This a limitation due to callback names being unique. You can
|
||||
* differentiate between contexts by checking the number of arguments.
|
||||
* <p/>
|
||||
* <em>Important</em>: this only works in environments (for components),
|
||||
* it does <em>not</em> work for userdata (<tt>Value</tt> objects). For
|
||||
* userdata, use the <tt>unapply</tt> method instead.
|
||||
*/
|
||||
boolean setter() default false;
|
||||
}
|
||||
|
@ -468,12 +468,65 @@ end
|
||||
|
||||
local libcomponent
|
||||
|
||||
-- Caching proxy objects for lower memory use.
|
||||
local proxyCache = setmetatable({}, {__mode="v"})
|
||||
local proxyDirectCache = setmetatable({}, {__mode="k"})
|
||||
|
||||
-- Short-term caching of callback directness for improved performance.
|
||||
local directCache = setmetatable({}, {__mode="k"})
|
||||
local function isDirect(address, method)
|
||||
local cacheKey = address..":"..method
|
||||
local cachedValue = directCache[cacheKey]
|
||||
if cachedValue ~= nil then
|
||||
return cachedValue
|
||||
end
|
||||
local methods, reason = spcall(component.methods, address)
|
||||
if methods then
|
||||
for name, info in pairs(methods) do
|
||||
if name == method then
|
||||
directCache[cacheKey] = info.direct
|
||||
return info.direct
|
||||
end
|
||||
end
|
||||
end
|
||||
error("no such method", 1)
|
||||
end
|
||||
|
||||
local componentProxy = {
|
||||
__index = function(self, key)
|
||||
if self.fields[key] and self.fields[key].getter then
|
||||
return libcomponent.invoke(self.address, key)
|
||||
else
|
||||
rawget(self, key)
|
||||
end
|
||||
end,
|
||||
__newindex = function(self, key, value)
|
||||
if self.fields[key] and self.fields[key].setter then
|
||||
return libcomponent.invoke(self.address, key, value)
|
||||
elseif self.fields[key] and self.fields[key].getter then
|
||||
error("field is read-only")
|
||||
else
|
||||
rawset(self, key, value)
|
||||
end
|
||||
end,
|
||||
__pairs = function(self)
|
||||
local keyProxy, keyField, value
|
||||
return function()
|
||||
if not keyField then
|
||||
repeat
|
||||
keyProxy, value = next(self, keyProxy)
|
||||
until not keyProxy or keyProxy ~= "fields"
|
||||
end
|
||||
if not keyProxy then
|
||||
keyField, value = next(self.fields, keyField)
|
||||
end
|
||||
return keyProxy or keyField, value
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local componentCallback = {
|
||||
__call = function(self, ...)
|
||||
return invoke(component, not not proxyDirectCache[self], self.address, self.name, ...)
|
||||
return libcomponent.invoke(self.address, self.name, ...)
|
||||
end,
|
||||
__tostring = function(self)
|
||||
return libcomponent.doc(self.address, self.name) or "function"
|
||||
@ -493,16 +546,7 @@ libcomponent = {
|
||||
invoke = function(address, method, ...)
|
||||
checkArg(1, address, "string")
|
||||
checkArg(2, method, "string")
|
||||
local methods, reason = spcall(component.methods, address)
|
||||
if not methods then
|
||||
return nil, reason
|
||||
end
|
||||
for name, direct in pairs(methods) do
|
||||
if name == method then
|
||||
return invoke(component, direct, address, method, ...)
|
||||
end
|
||||
end
|
||||
error("no such method", 1)
|
||||
return invoke(component, isDirect(address, method), address, method, ...)
|
||||
end,
|
||||
list = function(filter, exact)
|
||||
checkArg(1, filter, "string", "nil")
|
||||
@ -516,7 +560,31 @@ libcomponent = {
|
||||
end})
|
||||
end,
|
||||
methods = function(address)
|
||||
return spcall(component.methods, address)
|
||||
local result, reason = spcall(component.methods, address)
|
||||
-- Transform to pre 1.4 format to avoid breaking scripts.
|
||||
if type(result) == "table" then
|
||||
for k, v in pairs(result) do
|
||||
if not v.getter and not v.setter then
|
||||
result[k] = v.direct
|
||||
else
|
||||
result[k] = nil
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
return result, reason
|
||||
end,
|
||||
fields = function(address)
|
||||
local result, reason = spcall(component.methods, address)
|
||||
if type(result) == "table" then
|
||||
for k, v in pairs(result) do
|
||||
if not v.getter and not v.setter then
|
||||
result[k] = nil
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
return result, reason
|
||||
end,
|
||||
proxy = function(address)
|
||||
local type, reason = spcall(component.type, address)
|
||||
@ -530,15 +598,19 @@ libcomponent = {
|
||||
if proxyCache[address] then
|
||||
return proxyCache[address]
|
||||
end
|
||||
local proxy = {address = address, type = type, slot = slot}
|
||||
local proxy = {address = address, type = type, slot = slot, fields = {}}
|
||||
local methods, reason = spcall(component.methods, address)
|
||||
if not methods then
|
||||
return nil, reason
|
||||
end
|
||||
for method, direct in pairs(methods) do
|
||||
proxy[method] = setmetatable({address=address,name=method}, componentCallback)
|
||||
proxyDirectCache[proxy[method]] = direct
|
||||
for method, info in pairs(methods) do
|
||||
if not info.getter and not info.setter then
|
||||
proxy[method] = setmetatable({address=address,name=method}, componentCallback)
|
||||
else
|
||||
proxy.fields[method] = info
|
||||
end
|
||||
end
|
||||
setmetatable(proxy, componentProxy)
|
||||
proxyCache[address] = proxy
|
||||
return proxy
|
||||
end,
|
||||
|
@ -57,7 +57,13 @@ class ComponentAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
|
||||
lua.newTable()
|
||||
for ((name, annotation) <- machine.methods(component.host)) {
|
||||
lua.pushString(name)
|
||||
lua.newTable()
|
||||
lua.pushBoolean(annotation.direct)
|
||||
lua.setField(-2, "direct")
|
||||
lua.pushBoolean(annotation.getter)
|
||||
lua.setField(-2, "getter")
|
||||
lua.pushBoolean(annotation.setter)
|
||||
lua.setField(-2, "setter")
|
||||
lua.rawSet(-3)
|
||||
}
|
||||
1
|
||||
|
@ -48,7 +48,13 @@ class ComponentAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) {
|
||||
withComponent(args.checkjstring(1), component => {
|
||||
val table = LuaValue.tableOf()
|
||||
for ((name, annotation) <- machine.methods(component.host)) {
|
||||
table.set(name, LuaValue.valueOf(annotation.direct))
|
||||
table.set(name, LuaValue.tableOf(Array(
|
||||
LuaValue.valueOf("direct"),
|
||||
LuaValue.valueOf(annotation.direct),
|
||||
LuaValue.valueOf("getter"),
|
||||
LuaValue.valueOf(annotation.getter),
|
||||
LuaValue.valueOf("setter"),
|
||||
LuaValue.valueOf(annotation.setter))))
|
||||
}
|
||||
table
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user