Closed timstableford closed 8 months ago
Indeed, and it should be under injectObjects
flag
Is there a workaround for this?
I implemented null as a type extension. Currently nil (lua) === undefined (js) and that mapping makes sense between those two languages. The only problem with null as a type extension is that it evaluates to truthy rather than falsy sadly. Example code below:
class NullTypeExtension extends LuaTypeExtension<null> {
private gcPointer: number;
public constructor(thread: LuaGlobal) {
super(thread, 'js_null');
this.gcPointer = thread.lua.module.addFunction((functionStateAddress: LuaState) => {
// Throws a lua error which does a jump if it does not match.
const userDataPointer = thread.lua.luaL_checkudata(functionStateAddress, 1, this.name);
const referencePointer = thread.lua.module.getValue(userDataPointer, '*');
thread.lua.unref(referencePointer);
return LuaReturn.Ok;
}, 'ii');
if (thread.lua.luaL_newmetatable(thread.address, this.name)) {
const metatableIndex = thread.lua.lua_gettop(thread.address);
// Mark it as uneditable
thread.lua.lua_pushstring(thread.address, 'protected metatable');
thread.lua.lua_setfield(thread.address, metatableIndex, '__metatable');
// Add the gc function
thread.lua.lua_pushcclosure(thread.address, this.gcPointer, 0);
thread.lua.lua_setfield(thread.address, metatableIndex, '__gc');
// Add an __index method that returns nothing.
thread.pushValue(() => null);
thread.lua.lua_setfield(thread.address, metatableIndex, '__index');
thread.pushValue(() => 'null');
thread.lua.lua_setfield(thread.address, metatableIndex, '__tostring');
thread.pushValue((self: unknown, other: unknown) => self === other);
thread.lua.lua_setfield(thread.address, metatableIndex, '__eq');
}
// Pop the metatable from the stack.
thread.lua.lua_pop(thread.address, 1);
// Create a new table, this is unique and will be the "null" value by attaching the
// metatable created above. The first argument is the target, the second options.
super.pushValue(thread, decorate({}, {}));
// Put it into the global field named null.
thread.lua.lua_setglobal(thread.address, 'null');
}
public getValue(thread: LuaThread, index: number): null {
const refUserData = thread.lua.luaL_testudata(thread.address, index, this.name);
if (!refUserData) {
throw new Error(`data does not have the expected metatable: ${this.name}`);
}
return null;
}
// any because LuaDecoration is not exported from the Lua lib.
public pushValue(thread: LuaThread, decoration: any): boolean {
if (decoration?.target !== null) {
return false;
}
// Rather than pushing a new value, get the global "null" onto the stack.
thread.lua.lua_getglobal(thread.address, 'null');
return true;
}
public close(): void {
this.thread.lua.module.removeFunction(this.gcPointer);
}
}
export default function createTypeExtension(thread: LuaGlobal): LuaTypeExtension<null> {
return new NullTypeExtension(thread);
}
Implemented in PR https://github.com/ceifa/wasmoon/pull/101
@ceifa can close this now :)
If a JS API explicitly needs the null value within an object it's not currently possible.
Eg
Will generate a JS object like this:
That's because nil works far more like JS's undefined than null.
I think the best solution is adding a null type extension and setting the global "null" to this type extension allowing it to be used like a primitive.