mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-16 02:39:48 -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.
|
* Whether this callback should work like a getter.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Callbacks that are getters do not appear as methods on a component's
|
* 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>
|
* proxy. Instead they are accessed as fields, for example in Lua via the
|
||||||
* metamethod, i.e. it is accessed as a field, with it's only parameter
|
* proxy's <tt>__index</tt> metamethod, with it's only parameter being the
|
||||||
* being the accessed key.
|
* 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;
|
boolean getter() default false;
|
||||||
|
|
||||||
@ -105,9 +114,18 @@ public @interface Callback {
|
|||||||
* Whether this callback should work like a setter.
|
* Whether this callback should work like a setter.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Callbacks that are setters do not appear as methods on a component's
|
* 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>
|
* proxy. Instead they are accessed as fields, for example in Lua via the
|
||||||
* metamethod, i.e. it is accessed as a field, with it's only parameters
|
* proxy's <tt>__newindex</tt> metamethod, with it's only tow parameters
|
||||||
* being the accessed key and the assigned value.
|
* 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;
|
boolean setter() default false;
|
||||||
}
|
}
|
||||||
|
@ -468,12 +468,65 @@ end
|
|||||||
|
|
||||||
local libcomponent
|
local libcomponent
|
||||||
|
|
||||||
|
-- Caching proxy objects for lower memory use.
|
||||||
local proxyCache = setmetatable({}, {__mode="v"})
|
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 = {
|
local componentCallback = {
|
||||||
__call = function(self, ...)
|
__call = function(self, ...)
|
||||||
return invoke(component, not not proxyDirectCache[self], self.address, self.name, ...)
|
return libcomponent.invoke(self.address, self.name, ...)
|
||||||
end,
|
end,
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
return libcomponent.doc(self.address, self.name) or "function"
|
return libcomponent.doc(self.address, self.name) or "function"
|
||||||
@ -493,16 +546,7 @@ libcomponent = {
|
|||||||
invoke = function(address, method, ...)
|
invoke = function(address, method, ...)
|
||||||
checkArg(1, address, "string")
|
checkArg(1, address, "string")
|
||||||
checkArg(2, method, "string")
|
checkArg(2, method, "string")
|
||||||
local methods, reason = spcall(component.methods, address)
|
return invoke(component, isDirect(address, method), address, method, ...)
|
||||||
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)
|
|
||||||
end,
|
end,
|
||||||
list = function(filter, exact)
|
list = function(filter, exact)
|
||||||
checkArg(1, filter, "string", "nil")
|
checkArg(1, filter, "string", "nil")
|
||||||
@ -516,7 +560,31 @@ libcomponent = {
|
|||||||
end})
|
end})
|
||||||
end,
|
end,
|
||||||
methods = function(address)
|
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,
|
end,
|
||||||
proxy = function(address)
|
proxy = function(address)
|
||||||
local type, reason = spcall(component.type, address)
|
local type, reason = spcall(component.type, address)
|
||||||
@ -530,15 +598,19 @@ libcomponent = {
|
|||||||
if proxyCache[address] then
|
if proxyCache[address] then
|
||||||
return proxyCache[address]
|
return proxyCache[address]
|
||||||
end
|
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)
|
local methods, reason = spcall(component.methods, address)
|
||||||
if not methods then
|
if not methods then
|
||||||
return nil, reason
|
return nil, reason
|
||||||
end
|
end
|
||||||
for method, direct in pairs(methods) do
|
for method, info in pairs(methods) do
|
||||||
|
if not info.getter and not info.setter then
|
||||||
proxy[method] = setmetatable({address=address,name=method}, componentCallback)
|
proxy[method] = setmetatable({address=address,name=method}, componentCallback)
|
||||||
proxyDirectCache[proxy[method]] = direct
|
else
|
||||||
|
proxy.fields[method] = info
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
setmetatable(proxy, componentProxy)
|
||||||
proxyCache[address] = proxy
|
proxyCache[address] = proxy
|
||||||
return proxy
|
return proxy
|
||||||
end,
|
end,
|
||||||
|
@ -57,7 +57,13 @@ class ComponentAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
|
|||||||
lua.newTable()
|
lua.newTable()
|
||||||
for ((name, annotation) <- machine.methods(component.host)) {
|
for ((name, annotation) <- machine.methods(component.host)) {
|
||||||
lua.pushString(name)
|
lua.pushString(name)
|
||||||
|
lua.newTable()
|
||||||
lua.pushBoolean(annotation.direct)
|
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)
|
lua.rawSet(-3)
|
||||||
}
|
}
|
||||||
1
|
1
|
||||||
|
@ -48,7 +48,13 @@ class ComponentAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) {
|
|||||||
withComponent(args.checkjstring(1), component => {
|
withComponent(args.checkjstring(1), component => {
|
||||||
val table = LuaValue.tableOf()
|
val table = LuaValue.tableOf()
|
||||||
for ((name, annotation) <- machine.methods(component.host)) {
|
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
|
table
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user