Skip to content

Commit 48f13ef

Browse files
committed
Add LuaScriptInstance struct (#5)
Now script instances are a struct with a table for storing data. This makes it possible to always call setter functions on __newindex, regardless of if there already was a value in the table. The methods `rawget` and `rawset` were added to index the table directly, without calling getters/setters or indexing the owner Object.
1 parent 9e83b06 commit 48f13ef

9 files changed

+195
-109
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ LUA_INIT_SCRIPT_SRC = \
9393
src/godot_pool_vector3_array.lua \
9494
src/godot_pool_color_array.lua \
9595
src/pluginscript_script.lua \
96+
src/pluginscript_instance.lua \
9697
src/pluginscript_class_metadata.lua \
9798
src/pluginscript_callbacks.lua \
9899
src/late_globals.lua \

src/godot_class.lua

-76
Original file line numberDiff line numberDiff line change
@@ -224,79 +224,3 @@ local MethodBindByName = {
224224
end,
225225
}
226226

227-
228-
--- Script instance metatable, the Lua side of a Lua script instance.
229-
-- These are created when a PluginScript is instanced and are only directly
230-
-- accessible in the script's functions as the `self` parameter.
231-
-- @type ScriptInstance
232-
local instance_methods = {
233-
fillvariant = function(var, self)
234-
api.godot_variant_new_object(var, rawget(self, '__owner'))
235-
end,
236-
--- Forwards a call to `__owner` using `Object:pcall`.
237-
-- @function pcall
238-
-- @param method Method name
239-
-- @param ...
240-
-- @treturn[1] bool `true` if method exists
241-
-- @return[1] Method result
242-
-- @treturn[2] bool `false` if method does not exist
243-
-- @see Object.pcall
244-
pcall = function(self, ...)
245-
return rawget(self, '__owner'):pcall(...)
246-
end,
247-
--- Forwards a call to `__owner` using `Object:call`.
248-
-- @function call
249-
-- @param method Method name
250-
-- @param ...
251-
-- @return[1] Method result
252-
-- @treturn[2] nil If method does not exist or errors
253-
-- @see Object.call
254-
call = function(self, ...)
255-
return rawget(self, '__owner'):call(...)
256-
end,
257-
}
258-
local ScriptInstance = {
259-
--- `Object` that this script instance is attached to.
260-
-- This is the Godot side of the instance.
261-
-- @field __owner
262-
263-
--- Lua script table, the one returned by the Lua script when loading it as a PluginScript.
264-
-- Note that calling `Object:get_script` will return an `Object` rather
265-
-- than this table.
266-
-- @field __script
267-
268-
--- Try indexing `__script`, then `__owner`.
269-
-- @function __index
270-
-- @param key
271-
-- @return
272-
-- @see Object.__index
273-
__index = function(self, key)
274-
local script_value = instance_methods[key] or rawget(self, '__script')[key]
275-
if script_value ~= nil then return script_value end
276-
return rawget(self, '__owner')[key]
277-
end,
278-
--- Calls `Object:set` if `key` is the name of a property known to base class, `rawset` otherwise.
279-
-- @function __newindex
280-
-- @param key
281-
-- @param value
282-
__newindex = function(self, key, value)
283-
if self:get_class_wrapper():has_property(key) then
284-
Object_set(rawget(self, '__owner'), key, value)
285-
else
286-
rawset(self, key, value)
287-
end
288-
end,
289-
--- Returns a Lua string representation of `__owner`, as per `Object:to_string`.
290-
-- @function __tostring
291-
-- @treturn string
292-
__tostring = function(self)
293-
return tostring(rawget(self, '__owner'))
294-
end,
295-
--- Concatenates values.
296-
-- @function __concat
297-
-- @param a First value, stringified with `GD.str`
298-
-- @param b First value, stringified with `GD.str`
299-
-- @treturn String
300-
__concat = concat_gdvalues,
301-
}
302-

src/language_gdnative.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,10 @@ static godot_pluginscript_instance_data *lps_instance_init(godot_pluginscript_sc
202202
lps_push_callback(lps_L, "instance_init");
203203
lua_pushlightuserdata(lps_L, (void *) data);
204204
lua_pushlightuserdata(lps_L, (void *) owner);
205-
if (lua_pcall(lps_L, 2, 0, 0) != LUA_OK) {
206-
return NULL;
207-
}
208-
return owner;
205+
void *result = NULL;
206+
lua_pushlightuserdata(lps_L, (void *) &result);
207+
lua_pcall(lps_L, 3, 0, 0);
208+
return result;
209209
}
210210

211211
static void lps_instance_finish(godot_pluginscript_instance_data *data) {

src/lua_object_helper.lua

+4-5
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,28 @@
2727
-- and are only unreferenced when `LuaObject_destroy` is called.
2828
-- Thus this struct must used with care.
2929
--
30-
-- This is used internally for having `LuaScriptWrapper` be a struct that
31-
-- reference tables.
30+
-- This is used internally for having `LuaScriptWrapper` and `LuaScriptInstance`
31+
-- be structs that reference tables.
3232
--
3333
-- typedef struct {
3434
-- void *address;
3535
-- } lps_lua_object;
3636
--
3737
-- @module LuaObject
38-
-- @local
3938

4039
ffi_cdef[[
4140
typedef struct {
4241
void *address;
4342
} lps_lua_object;
4443
]]
4544

45+
local lps_lua_objects = {}
46+
4647
--- Helper function to convert a `void *` to a table index.
4748
local function pointer_to_index(ptr)
4849
return tonumber(ffi_cast('uintptr_t', ptr))
4950
end
5051

51-
local lps_lua_objects = {}
52-
5352
--- Gets the Lua object referenced by a LuaObject
5453
local function LuaObject_get(self)
5554
return lps_lua_objects[pointer_to_index(self.address)]

src/pluginscript_callbacks.lua

+32-16
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ pluginscript_callbacks.wrap_callback = wrap_callback
8484
-- void (*)(const godot_string *name, const godot_variant *value);
8585
pluginscript_callbacks.language_add_global_constant = wrap_callback(function(name, value)
8686
name = tostring(ffi_cast('godot_string *', name))
87+
8788
lps_callstack:push('add_global', string_quote(name))
89+
8890
_G[name] = ffi_cast('godot_variant *', value):unbox()
8991
end)
9092

@@ -96,6 +98,7 @@ pluginscript_callbacks.script_init = wrap_callback(function(manifest, path, sour
9698
err = ffi_cast('godot_error *', err)
9799

98100
lps_callstack:push('script_load', '@', string_quote(path))
101+
99102
local script, err_message = loadstring(source, path)
100103
if not script then
101104
local line, msg = string_match(err_message, ERROR_LINE_MESSAGE_PATT)
@@ -115,6 +118,7 @@ pluginscript_callbacks.script_init = wrap_callback(function(manifest, path, sour
115118
return
116119
end
117120

121+
local base_class
118122
local known_properties = {}
119123
for k, v in pairs(script) do
120124
if k == 'class_name' then
@@ -130,7 +134,8 @@ pluginscript_callbacks.script_init = wrap_callback(function(manifest, path, sour
130134
)
131135
return
132136
end
133-
manifest.base = ffi_gc(StringName(cls.class_name), nil)
137+
base_class = cls.class_name
138+
manifest.base = ffi_gc(StringName(base_class), nil)
134139
elseif type(v) == 'function' then
135140
local method = method_to_dictionary(v)
136141
method.name = String(k)
@@ -149,63 +154,70 @@ pluginscript_callbacks.script_init = wrap_callback(function(manifest, path, sour
149154
manifest.properties:append(prop_dict)
150155
end
151156
end
152-
manifest.data = LuaScriptWrapper_new(path, known_properties, script)
157+
manifest.data = LuaScriptWrapper_new(path, base_class, known_properties, script)
153158
err[0] = Error.OK
154159
end)
155160

156161
-- void (*)(godot_pluginscript_script_data *data);
157162
pluginscript_callbacks.script_finish = wrap_callback(function(data)
158163
local script = ffi_cast('lps_lua_script *', data)
164+
159165
lps_callstack:push('script_finish')
166+
160167
LuaScriptWrapper_destroy(script)
161168
end)
162169

163-
-- void (*)(godot_pluginscript_script_data *data, godot_object *owner);
164-
pluginscript_callbacks.instance_init = wrap_callback(function(data, owner)
170+
-- void (*)(godot_pluginscript_script_data *data, godot_object *owner, void **result);
171+
pluginscript_callbacks.instance_init = wrap_callback(function(data, owner, result)
165172
local script = ffi_cast('lps_lua_script *', data)
166173
owner = ffi_cast('godot_object *', owner)
174+
result = ffi_cast('lps_script_instance **', result)
167175

168176
lps_callstack:push('_init', '@', string_quote(script.__path))
169-
local instance = setmetatable({
170-
__owner = owner,
171-
__script = script,
172-
}, ScriptInstance)
177+
178+
local instance = LuaScriptInstance_new(owner, script)
173179
for name, prop in pairs(script.__properties) do
174180
if not prop.getter then
175181
local property_initializer = property_initializer_for_type[prop.type]
176182
if property_initializer then
177-
rawset(instance, name, property_initializer(prop.default_value))
183+
instance:rawset(name, property_initializer(prop.default_value))
178184
end
179185
end
180186
end
181187
local _init = script._init
182188
if _init then
183189
_init(instance)
184190
end
191+
result[0] = instance
185192
set_lua_instance(owner, instance)
186193
end)
187194

188195
-- void (*)(godot_pluginscript_instance_data *data);
189196
pluginscript_callbacks.instance_finish = wrap_callback(function(data)
197+
local self = ffi_cast('lps_script_instance *', data)
198+
190199
lps_callstack:push('finish')
191-
set_lua_instance(data, nil)
200+
201+
set_lua_instance(self.__owner, nil)
202+
LuaScriptInstance_destroy(self)
192203
end)
193204

194205
-- godot_bool (*)(godot_pluginscript_instance_data *data, const godot_string *name, const godot_variant *value);
195206
pluginscript_callbacks.instance_set_prop = wrap_callback(function(data, name, value)
196-
local self = get_lua_instance(data)
207+
local self = ffi_cast('lps_script_instance *', data)
197208
name = tostring(ffi_cast('godot_string *', name))
198209
value = ffi_cast('godot_variant *', value)
199210

200211
local script = self.__script
201212
lps_callstack:push('set', string_quote(name), '@', string_quote(script.__path))
213+
202214
local prop = script.__properties[name]
203215
if prop then
204216
local setter = prop.setter
205217
if setter then
206218
setter(self, value:unbox())
207219
else
208-
rawset(self, name, value:unbox())
220+
self:rawset(name, value:unbox())
209221
end
210222
return true
211223
else
@@ -219,20 +231,21 @@ end, false)
219231

220232
-- godot_bool (*)(godot_pluginscript_instance_data *data, const godot_string *name, godot_variant *ret);
221233
pluginscript_callbacks.instance_get_prop = wrap_callback(function(data, name, ret)
222-
local self = get_lua_instance(data)
234+
local self = ffi_cast('lps_script_instance *', data)
223235
name = tostring(ffi_cast('godot_string *', name))
224236
ret = ffi_cast('godot_variant *', ret)
225237

226238
local script = self.__script
227239
lps_callstack:push('get', string_quote(name), '@', string_quote(script.__path))
240+
228241
local prop = script.__properties[name]
229242
if prop then
230243
local getter, value = prop.getter, nil
231244
if getter then
232245
value = getter(self)
233246
else
234247
-- Avoid infinite recursion from `self[name]`, since `__index` may call `Object:get`
235-
value = rawget(self, name)
248+
value = self:rawget(name)
236249
if value == nil then
237250
value = prop.default_value
238251
end
@@ -254,14 +267,15 @@ end, false)
254267

255268
-- void (*)(godot_pluginscript_instance_data *data, const godot_string_name *method, const godot_variant **args, int argcount, godot_variant *ret, godot_variant_call_error *error);
256269
pluginscript_callbacks.instance_call_method = wrap_callback(function(data, name, args, argcount, ret, err)
257-
local self = get_lua_instance(data)
270+
local self = ffi_cast('lps_script_instance *', data)
258271
name = tostring(ffi_cast('godot_string_name *', name))
259272
args = ffi_cast('godot_variant **', args)
260273
ret = ffi_cast('godot_variant *', ret)
261274
err = ffi_cast('godot_variant_call_error *', err)
262275

263276
local script = self.__script
264277
lps_callstack:push('call', name, '@', script.__path)
278+
265279
local method = script[name]
266280
if method ~= nil then
267281
local args_table = {}
@@ -284,9 +298,11 @@ end)
284298

285299
-- void (*)(godot_pluginscript_instance_data *data, int notification);
286300
pluginscript_callbacks.instance_notification = wrap_callback(function(data, what)
287-
local self = get_lua_instance(data)
301+
local self = ffi_cast('lps_script_instance *', data)
302+
288303
local script = self.__script
289304
lps_callstack:push('_notification', '@', script.__path)
305+
290306
local _notification = script._notification
291307
if _notification then
292308
return _notification(self, what)

0 commit comments

Comments
 (0)