Skip to content

Commit

Permalink
fix: class as userdata proxy unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
X3ZvaWQ committed Jan 22, 2024
1 parent 9ee4260 commit 5a596b7
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 121 deletions.
4 changes: 2 additions & 2 deletions src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ export default class LuaGlobal extends LuaThread {
return value;
}

public set(name: string, value: unknown): void {
this.pushValue(value);
public set(name: string, value: unknown, options?: PushValueOptions): void {
this.pushValue(value, options);
this.luaApi.lua_setglobal(this.address, name);
}

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export { default as LuaMultiReturn } from './multireturn';
// use the bindings rather than the wrappers.
export { default as LuaApi } from './api';
export { default as Lua } from './lua';
export { JsType } from './type-bind';
export * from './definitions';
7 changes: 6 additions & 1 deletion src/lua.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default class Lua {
const funcPointer = registerFuncCallFunction(this.global);
const callPointer = registerCallFunction(this.global);

JsType.create('js-function', (value: any) => typeof value === 'function')
JsType.create('js-function', (value: any) => typeof value === 'function' && !value.toString().startsWith('class'))
.gc(gcPointer)
.index(indexPointer)
.newindex(newIndexPointer)
Expand All @@ -128,6 +128,11 @@ export default class Lua {
.gc(gcPointer)
.call(callPointer)
.index((target, index) => {
const result = target[index];
if (typeof result === 'function') {
// XXX: this is a hack to make sure that the function is called with the correct this
return result.bind(target);
}
return target[index];
})
.newindex((target: any, index: any, value: any) => {
Expand Down
1 change: 1 addition & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ declare interface JsTypeDefinition {

declare interface PushValueOptions {
refs?: Map<any, number>;
metatable?: Record<string, any>;
}

declare interface GetValueOptions {
Expand Down
18 changes: 5 additions & 13 deletions test/debug.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,9 @@ import { Lua } from '../dist/index.js';
// This file was created as a sandbox to test and debug on vscode
const lua = await Lua.create();

const a = { name: 'a' };
const b = { name: 'b' };
b.a = a;
a.b = b;
lua.global.loadString(`
local i = 0
while true do i = i + 1 end
`);

lua.global.pushValue(a);
// lua.global.luaApi.lua_setglobal(lua.global.address, 'x');
// lua.doStringSync(`
// print(x.b.a.b.a)
// `);
const res = lua.global.getValue(-1);

//console.log(res.b.a);
console.log(res);
await lua.global.run(0, { timeout: 5 }).catch((e) => console.log(e));
199 changes: 94 additions & 105 deletions test/engine.test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
const { LuaThread, LuaType, LuaReturn } = require('..');
const { JsType, LuaThread, LuaType, LuaReturn } = require('../dist/index');
const { expect } = require('chai');
// const { getEngine, getFactory } = require('./utils');
const { Lua } = require('..');
const { setTimeout } = require('node:timers/promises');
// const { EventEmitter } = require('events');
const jestMock = require('jest-mock');

// class TestClass {
// static hello() {
// return 'world';
// }
class TestClass {
static hello() {
return 'world';
}

// constructor(name) {
// this.name = name;
// }
constructor(name) {
this.name = name;
}

// getName() {
// return this.name;
// }
// }
getName() {
return this.name;
}
}

describe('Engine', () => {
let intervals = [];
Expand Down Expand Up @@ -421,122 +421,111 @@ describe('Engine', () => {

lua.global.pushValue(a);
const res = lua.global.getValue(-1);
console.log(res.b.a);
console.log(res);

expect(res.b.a).to.be.eql(res);
});

// it('wrap a js object (with metatable)', async () => {
// const engine = await getEngine();
// engine.global.set('TestClass', {
// create: (name) => {
// return decorate(
// {
// instance: decorateUserdata(new TestClass(name)),
// },
// {
// metatable: {
// __name: 'js_TestClass',
// __index: (self, key) => {
// if (key === 'name') {
// return self.instance.getName();
// }
// return null;
// },
// },
// },
// );
// },
// });
it('wrap a js object (with metatable)', async () => {
const lua = await Lua.create();

// const res = await engine.doString(`
// local instance = TestClass.create("demo name")
// return instance.name
// `);
// expect(res).to.be.equal('demo name');
// });
JsType.create('js_TestClass', (target) => target instanceof TestClass)
.index((self, key) => {
if (key === 'name') {
return self.getName();
}
return null;
})
.priority(1)
.apply(lua.global);

// it('wrap a js object using proxy', async () => {
// const engine = await getEngine();
// engine.global.set('TestClass', {
// create: (name) => new TestClass(name),
// });
// const res = await engine.doString(`
// local instance = TestClass.create("demo name 2")
// return instance:getName()
// `);
// expect(res).to.be.equal('demo name 2');
// });
lua.ctx.TestClass = {
create: (name) => new TestClass(name),
};

// it('wrap a js object using proxy and apply metatable in lua', async () => {
// const lua = await Lua.create();
// lua.ctx.TestClass = {
// create: (name) => new TestClass(name),
// };
// const res = await lua.doString(`
// local instance = TestClass.create("demo name 2")

// -- Based in the simple lua classes tutotial
// local Wrapped = {}
// Wrapped.__index = Wrapped

// function Wrapped:create(name)
// local wrapped = {}
// wrapped.instance = TestClass.create(name)
// setmetatable(wrapped, Wrapped)
// return wrapped
// end
const res = await lua.doString(`
local instance = TestClass.create("demo name")
return instance.name
`);
expect(res).to.be.equal('demo name');
});

// function Wrapped:getName()
// return "wrapped: "..self.instance:getName()
// end
it('wrap a js object using proxy', async () => {
const lua = await Lua.create();
lua.ctx.TestClass = {
create: (name) => new TestClass(name),
};
const res = await lua.doString(`
local instance = TestClass.create("demo name 2")
return instance:getName()
`);
expect(res).to.be.equal('demo name 2');
});

// local wr = Wrapped:create("demo")
// return wr:getName()
// `);
// expect(res).to.be.equal('wrapped: demo');
// });
it('wrap a js object using proxy and apply metatable in lua', async () => {
const lua = await Lua.create();
lua.ctx.TestClass = {
create: (name) => new TestClass(name),
};
const res = await lua.doString(`
local instance = TestClass.create("demo name 2")
// it('classes should be a userdata when proxied', async () => {
// const engine = await getEngine();
// engine.global.set('obj', { TestClass });
-- Based in the simple lua classes tutotial
local Wrapped = {}
Wrapped.__index = Wrapped
// const testClass = await engine.doString(`
// return obj.TestClass
// `);
function Wrapped:create(name)
local wrapped = {}
wrapped.instance = TestClass.create(name)
setmetatable(wrapped, Wrapped)
return wrapped
end
// expect(testClass).to.be.equal(TestClass);
// });
function Wrapped:getName()
return "wrapped: "..self.instance:getName()
end
local wr = Wrapped:create("demo")
return wr:getName()
`);
expect(res).to.be.equal('wrapped: demo');
});

it('classes should be a userdata when proxied', async () => {
const lua = await Lua.create();
lua.ctx.obj = { TestClass };

const testClass = await lua.doString(`
return obj.TestClass
`);
expect(testClass).to.be.equal(TestClass);
});

// ?? TODO: Fix this test
// it('timeout blocking lua program', async () => {
// const engine = await getEngine();
// engine.global.loadString(`
// const lua = await Lua.create();
// lua.global.loadString(`
// local i = 0
// while true do i = i + 1 end
// `);

// await expect(engine.global.run(0, { timeout: 5 })).eventually.to.be.rejectedWith('thread timeout exceeded');
// await expect(lua.global.run(0, { timeout: 5 })).eventually.to.be.rejectedWith('thread timeout exceeded');
// });

// it('overwrite lib function', async () => {
// const engine = await getEngine();
it('overwrite lib function', async () => {
const lua = await Lua.create();

// let output = '';
// engine.global.getTable(LuaLibraries.Base, (index) => {
// engine.global.setField(index, 'print', (val) => {
// // Not a proper print implementation.
// output += `${val}\n`;
// });
// });
let output = '';
lua.ctx.print = (val) => {
output += `${val}\n`;
};

// await engine.doString(`
// print("hello")
// print("world")
// `);
await lua.doString(`
print("hello")
print("world")
`);

// expect(output).to.be.equal('hello\nworld\n');
// });
expect(output).to.be.equal('hello\nworld\n');
});

// it('inject a userdata with a metatable should succeed', async () => {
// const engine = await getEngine();
Expand Down

0 comments on commit 5a596b7

Please sign in to comment.