luaJIT error handling with FFI calls a non-existent method - lua

What I want to avoid is to capture/ignore the exception when FFI calls a non-existent method.
For example, the following code calls the non_existent_method. However, pcall cannot handle the error.
local ffi = require "ffi"
local C = ffi.C
ffi.cdef [[
int non_existent_method(int num);
]]
local ok, ret = pcall(C.non_existent_method, 1)
if not ok then
return
end
I got the following error with OpenResty/lua-nginx-module.
lua entry thread aborted: runtime error: dlsym(RTLD_DEFAULT, awd): symbol not found

One more apporach would be to call the index metamethod directly:
You might want to wrap that into a function:
local ffi_mt = getmetatable(ffi.C)
function is_valid_ffi_call(sym)
return pcall(ffi_mt.__index, ffi.C, sym)
end
example:
ffi.cdef[[void (*foo)();]]
ffi.cdef[[int puts(const char *);]]
a,func = is_valid_ffi_call("foo")
-- false, "error message: symbol not found or missing declaration, whatever it is"
a,func = is_valid_ffi_call("puts")
-- true, cdata<int ()>: 0x7ff93ad307f0

One possible solution would be wrapping the C.non_existent_method call with a lua function.
For example
local ok, ret = pcall(function()
return C.non_existent_method(len)
end)
if not ok then
......
end

Related

lua - differece of : and . when define function?

hope you having great day , i'm test some stuff with lua, and i got some error
i have this codes in Object.lua
local Object = {name = "Object"}
Object.__index = Object
function Object.new()
local t = setmetatable({}, Object)
return t
end
function Object:keys(table)
local keyset={}
local n=0
for k,v in pairs(table) do
n=n+1
keyset[n]=k
end
return keyset
end
return Object
and i call Object.keys in "Other" script like this
local Object = require("lua.app.common.utils.Object")
local t = {
[1] = a,
[2] = b,
[3]= c
}
Object.keys(t)
and this cause error with
Exception has occurred: bad argument #1 to 'for iterator' (table expected, got nil),
because the parameter 'table' is passed as nil ( i don't know why thuogh debug mode just says its a nil)
on the other hand, if i fix object.keys(table) to object:keys(table), everything works fine, why this error happening?
Declaring the method keys with a colon adds an implicit self parameter as the first argument which requires you to call the method in the same way, using the colon.
Calling the method with the period notation expects you to supply the self parameter but because you only pass in t this is interpreted as self and then nil is added for your table parameter.
Declaring the method with a colon you actually have this
function Object:keys(self, table)
but calling it with the period means you have done this
Object.keys(t, nil)
hence the "table expected, got nil" error

How do I invoke a class method using pcall in lua?

How do I invoke a class method using pcall in Lua?
I tried pcall(instance:method, arg) but it doesn't work.
I also tried pcall(instance.method, instance, arg) but that doesn't work either.
I googled for a solution but I couldn't get one.
An example:
local ValueOwnerMap = {}
ValueOwnerMap.__index = ValueOwnerMap
function ValueOwnerMap:create(key_prefix)
local instance = {}
setmetatable(instance, ValueOwnerMap)
instance.key = key_prefix .. ':value-owner-map'
return instance
end
function ValueOwnerMap:get(value)
return redis.call('HGET', self.key, value)
end
function ValueOwnerMap:put(value, owner_id)
return redis.call('HSETNX', self.key, value, owner_id)
end
function ValueOwnerMap:del(value)
return redis.call('HDEL', self.key, value)
end
local value_owner_map = ValueOwnerMap:create('owner:key')
local success, data = pcall(value_owner_map:put, 'a_value', 'a_owner_id')
instance:method(arg) is sugar for instance.method(instance,arg). So try
pcall(value_owner_map.put, value_owner_map, 'a_value', 'a_owner_id')
The following line replaces the last line of the block in the question. It works.
local success, data = pcall(function () value_owner_map:put('a_value', 'a_owner_id') end)
Thank you all for sharing
pcall (f, arg1, ยทยทยท)
Calls function f with the given arguments in protected mode. This means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. lua ref
But Calls for functions in protected mode have some limitations especially when you are using ':' operator 'so-called synthetic sugar' of lua.
one way to WAR this limitation is to put inside a function
pcall(function () value_owner_map:put('a_value', 'a_owner_id') end)
This approach also catches the errors as usual:
local ok, msg = pcall(function () error('Phony Error') end)
if ok then
print("No error")
else
print("Got error".. tostring(msg))
end
-- Result:
-- Got error test.lua:53: Phony Error

Simple LZW Compression doesnt work

I wrote simple class to compress data. Here it is:
LZWCompressor = {}
function LZWCompressor.new()
local self = {}
self.mDictionary = {}
self.mDictionaryLen = 0
-- ...
self.Encode = function(sInput)
self:InitDictionary(true)
local s = ""
local ch = ""
local len = string.len(sInput)
local result = {}
local dic = self.mDictionary
local temp = 0
for i = 1, len do
ch = string.sub(sInput, i, i)
temp = s..ch
if dic[temp] then
s = temp
else
result[#result + 1] = dic[s]
self.mDictionaryLen = self.mDictionaryLen + 1
dic[temp] = self.mDictionaryLen
s = ch
end
end
result[#result + 1] = dic[s]
return result
end
-- ...
return self
end
And i run it by:
local compressor = LZWCompression.new()
local encodedData = compressor:Encode("I like LZW, but it doesnt want to compress this text.")
print("Input length:",string.len(originalString))
print("Output length:",#encodedData)
local decodedString = compressor:Decode(encodedData)
print(decodedString)
print(originalString == decodedString)
But when i finally run it by lua, it shows that interpreter expected string, not Table. That was strange thing, because I pass argument of type string. To test Lua's logs, i wrote at beggining of function:
print(typeof(sInput))
I got output "Table" and lua's error. So how to fix it? Why lua displays that string (That i have passed) is a table? I use Lua 5.3.
Issue is in definition of method Encode(), and most likely Decode() has same problem.
You create Encode() method using dot syntax: self.Encode = function(sInput),
but then you're calling it with colon syntax: compressor:Encode(data)
When you call Encode() with colon syntax, its first implicit argument will be compressor itself (table from your error), not the data.
To fix it, declare Encode() method with colon syntax: function self:Encode(sInput), or add 'self' as first argument explicitly self.Encode = function(self, sInput)
The code you provided should not run at all.
You define function LZWCompressor.new() but call CLZWCompression.new()
Inside Encode you call self:InitDictionary(true) which has not been defined.
Maybe you did not paste all relevant code here.
The reason for the error you get though is that you call compressor:Encode(sInput) which is equivalent to compressor.Encode(self, sInput). (syntactic sugar) As function parameters are not passed by name but by their position sInput inside Encode is now compressor, not your string.
Your first argument (which happens to be self, a table) is then passed to string.len which expects a string.
So you acutally call string.len(compressor) which of course results in an error.
Please make sure you know how to call and define functions and how to use self properly!

Not sure why extending/injecting an instance method in to a lua object isn't working

In this example, I'm using lunit and am attempting to inject an instance method in to an instance of LuaSocket and am failing to see why the following isn't working.
-- Using lunit for unit testing
local lunit = require('lunitx')
_ENV = lunit.module('enhanced', 'seeall')
local socket = require('socket')
-- connect(2) to the service tcp/echo
local conn, connErr = socket.connect('127.0.0.1', '7')
function conn:receiveLine(...)
local line, err = self:receive('*l')
assert_string(line, err)
return line
end
function conn:sendLine(...)
local bytesSent, err = self:send(... .. '\n')
assert_number(bytesSent, err)
return bytesSent
end
The error message I'm getting is:
attempt to call method 'sendLine' (a nil value)
?? This seems like there is something obvious happening here, but I'm missing the required detail.
Ass-u-me'ing getmetatable(conn).__index == getmetatable(conn) source of confusion.
In this case, conn's metatable's __index metamethod is pointing to a different table than expected, so method resolution isn't happening against the table at getmetatable(conn).
function setup()
-- Update the table at __index, not conn's metatable
local mt = getmetatable(conn).__index
function mt:receiveLine(...)
local line, err = self:receive('*l')
assert_string(line, err)
return line
end
function mt:sendLine(...)
local bytesSent, err = self:send(... .. '\n')
assert_number(bytesSent, err)
return bytesSent
end
end
I was able to tease this out by testing to see if __index was pointing to conn's metatable, which it wasn't:
assert_equal(getmetatable(conn), getmetatable(conn).__index)
Generically, if there is a __newindex handler on getmetatable(conn) that is intercepting new table entries, use rawset() on __index's table.

_ENV & classes in Lua are not being compatible

_ENV currently hates using classes. I'm attempting to create a Latin kind of Lua, and some things can't precisely be made with _ENV but it's a lot more efficient. setfenv was being uncooperative as well.
The subsequent code keeps returning the old attempt to call a nil value error. It specifies line 20, in which loadstring() is being checked, and line 23, where in the main chunk it is calling the __latin() function.
function __latin(code)
__predecessor = [===[
function typographia(value)
print(value);
end
chorda = {};
chorda.__index = chorda;
function chorda.sub(chorda, cChorda, fChorda)
return string.sub(chorda, cChorda, fChorda);
end
function chorda:sub(chorda, cChorda, fChorda)
return string.sub(chorda, cChorda, fChorda);
end
--[[ define values --]]
_ENV = {salve="Salve, munde!",typographia=typographia,print=print,chorda=chorda,chorda.sub=chorda.sub}; ]===];
__finalizer = __predecessor .. " " .. code;
local status, err = pcall(loadstring(__finalizer));
print(err);
if (err == nil) then loadstring(__finalizer)(); end
end
__latin('typographia(salve); chorda.sub(salve, 1, 3);');
You're getting "attempt to call a nil value" because loadstring returns nil (so you're calling pcall with nil). loadstring returns nil because the code you're compiling (__predecessor) contains invalid Lua:
_ENV = {
chorda.sub = chorda.sub -- can't do this
}
FYI: The loadstring/pcall stuff is irrelevant to your problem. Had you stripped it out before posting, you would have found this error yourself.

Resources