loadstring()

First off, openLuup is a massive achievement! Having only just gotten round to taking a look at it I think it will be incredibly useful to be able to move plugins off native Veras and thus isolate them as I move more towards my Veras being “radio only” devices :slight_smile:

Having also read the manual fully, I noticed this snippet:

“nil device parameter in luup.variable_watch” - this IMO should be plastered all over the Wiki, forum, stickies, etc - I’ve been using Vera since its’ first incarnation and didn’t know this was possible! I’m sure many others are in the same situation … using that little nugget of information I refactored one of my plugins and reduced my number of watches from 849 to less than 30 … pretty sure that can only be a good thing memory and CPU wise :slight_smile:

Now onto hopefully a simple question - is the implementation of loadstring() different under openLuup / AltUI (latest versions)?

I’ve been trying to get one of my plugins working and the following code results in “deviceFromSerializedData” always being nil:

assert(loadstring("local deviceFromSerializedData="..deviceserialized))()

“deviceserialized” is just a lua table serialized using RexBeckets’ code from http://forum.micasaverde.com/index.php/topic,18679.msg154958.html#msg154958

Most of the lua tables are pretty simple, just a few keys/values:

{watcher="w_right_ascension",veradeviceid=7,}

Any ideas?

Thanks,

Martyn

We actually have to thank @vosmont for that discovery - see here: http://forum.micasaverde.com/index.php/topic,34567.msg254736.html#msg254736

Now onto hopefully a simple question - is the implementation of loadstring() different under openLuup / AltUI (latest versions)?

No, it’s native Lua, so not changed in any way. HOWEVER there is a very subtle difference because of the way that openLuup handles code environments, which could lead to an effect like this (I will explain it when you persuade me that this is actually generating the problem! But it is the mechanism whereby openLuup uses almost no memory and Vera constantly runs out of it.)

I've been trying to get one of my plugins working and the following code results in "deviceFromSerializedData" always being nil:

Are you absolutely sure that code is correct? From my understanding of loadstring(), I would expect your local variable never to be in scope of the calling program. If you made it global, however, you might run into the effect you speak of in openLuup.

In my development system, the following

local y= loadstring ("local foo = {a = 42, b = 'hello'}") ()
print (foo)

prints ‘nil’. Whereas

local y= loadstring ("foo = {a = 42, b = 'hello'}") ()
print (foo)

prints ‘table: 0x00146b08’ or suchlike.

I have to say that’s a ghastly way to serialise/deserialise data - you would be far better off using a json encode/decode combination.

Are you absolutely sure that code is correct? From my understanding of loadstring(), I would expect your local variable never to be in scope of the calling program.

Yes, my bad, the “local” got added while I was trying to debug why the original code didn’t work :-[

I have to say that's a ghastly way to serialise/deserialise data - you would be far better off using a json encode/decode combination.

Indeed, it’s one of those cases of if it’s not broken, don’t fix it, especially on a live system :slight_smile:

I’ve already made some improvements to my code just from the few hours of it running under openLuup / AltUI - much nicer to work like this without fear of breaking the house!

OK, well that’s fair enough.

As written previously, this works…

local y= loadstring ("foo = {a = 42, b = 'hello'}") ()
print (foo)

…on Vera, but not under openLuup. You can verify this with AltUI and Lua Test code.

However, this should work on either system:

local y= loadstring ("foo = {a = 42, b = 'hello'}") 
setfenv (y,_G)
y()
print (foo)

Try it. If it works for you and you want to know why, ask.

Thanks for that :slight_smile:

Rough guess would be that the original code puts foo into proper global scope, but you’re sandboxing each plugin? So foo is nil in the sandbox because it’s in a different scope?

Your new code also puts y in proper global scope so when you execute it y() it has no problem seeing foo?

Something like that?

As a side note, I took the plunge and moved to json.encode & json.decode anyway, thanks for the kick up the ass to do that ;D

Some remarks :slight_smile:

if you want to serialize/deserialize data, you should use json :

local status, json = pcall(require, "dkjson")
if (type(json) ~= "table") then
	-- UI5
	json = require("json")
end

-- the value to deserialize
local response = '{"key":"value"}'

local decodeSuccess, jsonResponse = pcall(json.decode, response)
if (not decodeSuccess) then
	luup.log("decode error: " .. tostring(jsonResponse))
else
	-- do something with jsonResponse
	-- jsonResponse.key = "value"
end

if you want to use “loadstring”, here is what I use :

local functionContent = "luup.log(tostring(myParam))"

local chunk, strError = loadstring("return function(myParam) \n" .. functionContent .. "\nend")
if (chunk == nil) then
	luup.log("load error: " .. tostring(strError))
else
	-- Put the chunk in the plugin environment
	setfenv(chunk, getfenv(1))
	local myFunc = chunk()
	local ok, err = pcall(chunk, "myText")
	if not ok then
		luup.log("function error: " .. tostring(err))
	end
end

If you put your code into another module, there’s a problem with the global environment of the module (not the the same as the plugin).
But it works in Vera.

local status, myModule = pcall(require, moduleName)

Er yes, something like that :wink: OK, you’re good. openLuup uses only one global Lua instance and sandboxes the plugins. This means that _G for each plugin is separate from each other and from the _G of the Lua instance into which loadstring() will place any globals by default.

As a side note, I took the plunge and moved to json.encode & json.decode anyway, thanks for the kick up the ass to do that ;D

A pleasure!