ceifa / wasmoon

A real lua 5.4 VM with JS bindings made with webassembly
MIT License
449 stars 29 forks source link

Unable to handle null values between js and the lua vm #104

Closed janfrodej closed 3 months ago

janfrodej commented 6 months ago

The package does not handle null values properly when sending data structures between javascript and the lua vm.

First of all, the enableProxy setting needs to be set to false to ensure that data structures from javascript is not delivered as userdata, which is impossible to for iterate or recurse if such needs exists on the data.

If injectObjects is set to false, it will halt the execution with "decoration.target is null" when trying to deliver data to the lua vm. If injectObjects is set to true, it will successfully deliver the data structure to the lua vm, however any null-value attribute will be of type userdata.

The issue: it would be nice if null data can be delivered as nil in lua.

Example with injectObjects is false:

const lua = await factory.createEngine({ enableProxy: false, injectObjects: false  });

lua.global.set('null_isnot_nil', () => { const o={ notnil: null }; return o; });

in the lua code:

result = null_isnot_nil();

This will result in the mentioned: "decoration.target is null".

Example with injectObjects is true:

const lua = await factory.createEngine({ enableProxy: false, injectObjects: true });

in the lua code:

result = null_isnot_nil();

print (type(result));

This will give the answer "userdata". It would be nice that null values are delivered as nil in lua. Userdata is not particularly useful and userdata from javascript is also protected, so it is impossible to get the metatable of the data.

tims-bsquare commented 4 months ago

Hi @janfrodej When I PR'd adding the null type I wanted to have it distinct from nil because nil is equivalent to undefined in JS. This stemmed from a need to differentiate between null and undefined from a Lua perspective when making API calls to REST APIs where you want to explicitly send null.

> JSON.stringify({ a: undefined })
'{}'
> JSON.stringify({ a: null })
'{"a":null}'

Setting a field to undefined/nil means the field is not sent at all. If you want nil to act as null I'd recommend copying the current null type extension and instead of pushing userdata when null is seen use a lua_pushnil.

When you set injectObjects to true null is injected into Lua's global scope. So if you pass a null from JS to Lua then you can check if it's null within Lua by doing value == null.

ceifa commented 3 months ago

If injectObjects is set to false, it will halt the execution with "decoration.target is null" when trying to deliver data to the lua vm. If injectObjects is set to true, it will successfully deliver the data structure to the lua vm, however any null-value attribute will be of type userdata.

The issue: it would be nice if null data can be delivered as nil in lua.

That was the behaviour before @tims-bsquare, I think it makes sense when injectObjects is false. What do you think about a builtin type extension "lightnull" to be used in this case which just convert to nil/undefined?

tims-bsquare commented 3 months ago

I've put some comments on his PR and I think overall he has a fair point but I'm not certain on the implementation