1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #include "LuaMetaType.h"
5 #include "Lua.h"
6 #include "LuaObject.h"
7 #include "PropertyMap.h"
8 
9 // if found, returns true, leaves item to return to lua on top of stack
10 // if not found, returns false
get_method(lua_State * l,int metatable,int name)11 static bool get_method(lua_State *l, int metatable, int name)
12 {
13 	LUA_DEBUG_START(l);
14 
15 	name = lua_absindex(l, name);
16 
17 	// look up the name in the list of methods.
18 	lua_getfield(l, metatable, "methods");
19 	lua_pushvalue(l, name); // make a copy of the name
20 	lua_rawget(l, -2);		// look it up in the methods table
21 	lua_remove(l, -2);		// remove the methods table
22 
23 	// found something, return it
24 	if (!lua_isnil(l, -1)) {
25 		LUA_DEBUG_END(l, 1);
26 		return true;
27 	}
28 	lua_pop(l, 1);
29 
30 	// otherwise, just return
31 	LUA_DEBUG_END(l, 0);
32 	return false;
33 }
34 
35 // if found, returns true, leaves attribute entry on top of stack
36 // if not found, returns false
get_attr_entry(lua_State * l,int metatable,int name)37 static bool get_attr_entry(lua_State *l, int metatable, int name)
38 {
39 	LUA_DEBUG_START(l);
40 
41 	name = lua_absindex(l, name);
42 
43 	// lookup the name in the list of attributes
44 	lua_getfield(l, metatable, "attrs");
45 	lua_pushvalue(l, name); // make a copy of the name
46 	lua_rawget(l, -2);		// look it up in the methods table
47 	lua_remove(l, -2);		// remove the attributes table
48 
49 	// found an attribute entry, don't evaluate
50 	if (!lua_isnil(l, -1)) {
51 		LUA_DEBUG_END(l, 1);
52 		return true;
53 	}
54 	lua_pop(l, 1);
55 
56 	// not found
57 	LUA_DEBUG_END(l, 0);
58 	return false;
59 }
60 
61 // takes the metatable, name on top of stack
62 // Look up a method or attribute, evaluating the attribute entry as a getter.
get_method_or_attr(lua_State * l)63 static bool get_method_or_attr(lua_State *l)
64 {
65 	LUA_DEBUG_START(l);
66 
67 	// if we have a method, call it
68 	if (get_method(l, -2, -1)) {
69 		LUA_DEBUG_END(l, 1);
70 		return true;
71 	} else if (get_attr_entry(l, -2, -1)) {
72 		// Someone may have put a non-function value in the attributes
73 		// weird, but ok, we can handle it
74 		if (lua_isfunction(l, -1)) {
75 			lua_pushvalue(l, 1);			// push the self object
76 			pi_lua_protected_call(l, 1, 1); // call the attribute entry
77 
78 			LUA_DEBUG_END(l, 1);
79 			return true;
80 		}
81 
82 		LUA_DEBUG_END(l, 1);
83 		return true;
84 	}
85 
86 	LUA_DEBUG_END(l, 0);
87 	return false;
88 }
89 
l_index(lua_State * l)90 static int l_index(lua_State *l)
91 {
92 	// userdata are typed, tables are not
93 	bool typeless = lua_istable(l, 1);
94 	assert(typeless || lua_isuserdata(l, 1));
95 
96 	// typeless objects have no parents, and have only one method table, so this is easy
97 	if (typeless) {
98 		lua_getmetatable(l, 1);
99 		lua_pushvalue(l, 2);
100 
101 		// if we have something in the first metatable, return it
102 		if (get_method_or_attr(l))
103 			return 1;
104 
105 		// otherwise, nothing further to look up
106 		return 0;
107 	}
108 
109 	// normal userdata object
110 	// first check properties. we don't need to drill through lua if the
111 	// property is already available
112 	lua_getuservalue(l, 1);
113 	if (!lua_isnil(l, -1)) {
114 		lua_pushvalue(l, 2); // push the key
115 		lua_gettable(l, -2); // get the property from the table
116 		if (!lua_isnil(l, -1)) {
117 			return 1; // return the property if it is set
118 		}
119 
120 		lua_pop(l, 1);
121 	}
122 	lua_pop(l, 1);
123 
124 	// push the metatype registry here for later
125 	lua_getfield(l, LUA_REGISTRYINDEX, "LuaMetaTypes");
126 	int metaTypeRegistry = lua_gettop(l);
127 
128 	// push metatable, name onto the top of the stack
129 	lua_getmetatable(l, 1);
130 	lua_pushvalue(l, 2);
131 	while (1) {
132 		// got a method or an attribute handler from this metatype
133 		if (get_method_or_attr(l)) {
134 			// clean up the stack
135 			lua_insert(l, -3);
136 			lua_pop(l, 2);
137 			return 1;
138 		}
139 
140 		// if there's no parent metatable, get out
141 		lua_getfield(l, -2, "parent");
142 		if (lua_isnil(l, -1))
143 			break;
144 
145 		std::string parentName = lua_tostring(l, -1);
146 		lua_pop(l, 1); // pop the parent name
147 
148 		lua_getfield(l, metaTypeRegistry, parentName.c_str());
149 		if (lua_isnil(l, -1))
150 			return luaL_error(l, "Encountered invalid parent metatype name %s", parentName.c_str());
151 
152 		lua_replace(l, -3); // replace the metatable with the parent
153 	}
154 
155 	return 0;
156 }
157 
l_newindex(lua_State * l)158 static int l_newindex(lua_State *l)
159 {
160 	// userdata are typed, tables are not
161 	bool typeless = lua_istable(l, 1);
162 	assert(typeless || lua_isuserdata(l, 1));
163 
164 	// Attribute setters are not enabled for typeless objects.
165 	if (typeless) {
166 		lua_rawset(l, 1); // set the value in the table
167 		return 0;
168 	}
169 
170 	// Once we've dealt with the chance of a typeless object, the only thing
171 	// left is userdata.
172 
173 	// first check properties. we don't need to drill through the metatype stack
174 	// if the property is already available
175 	lua_getuservalue(l, 1);
176 
177 	// Ensure the object already has the property defined
178 	// Properties take precedence over attrs only if they've been previously set
179 	// (use setprop if you want to be sure you're setting a property)
180 	bool hasProperty = false;
181 	if (!lua_isnil(l, -1)) {
182 		lua_pushvalue(l, 2);
183 		lua_gettable(l, -2);
184 		hasProperty = !lua_isnil(l, -1);
185 		lua_pop(l, 1);
186 	}
187 
188 	// if the object is a valid propertied object, call its setters
189 	if (hasProperty) {
190 		auto *properties = LuaObjectBase::GetPropertiesFromObject(l, 1);
191 
192 		std::string name = lua_tostring(l, 2);
193 		if (lua_isnumber(l, 3)) {
194 			properties->Set(name, lua_tonumber(l, 2));
195 		} else if (lua_isstring(l, 3)) {
196 			properties->Set(name, lua_tostring(l, 3));
197 		}
198 
199 		return 0;
200 	}
201 	lua_pop(l, 1);
202 
203 	// push the metatype registry here for later
204 	lua_getfield(l, LUA_REGISTRYINDEX, "LuaMetaTypes");
205 	int metaTypeRegistry = lua_gettop(l);
206 
207 	// Check the metatable for attributes
208 	lua_getmetatable(l, 1); // get the metatable
209 	while (true) {
210 		// if we have an attribute handler, call it
211 		// if the attribute entry is a non-function value, it is considered immutable
212 		if (get_attr_entry(l, -1, 2)) {
213 			if (lua_isfunction(l, -1)) {
214 				lua_remove(l, -2);	 // remove the metatable
215 				lua_pushvalue(l, 1); // push the self object
216 				lua_pushvalue(l, 3); // push the value
217 				pi_lua_protected_call(l, 2, 0);
218 				return 0;
219 			}
220 			lua_pop(l, 1);
221 		}
222 
223 		lua_getfield(l, -1, "parent"); // get the parent field from the metatable
224 		if (lua_isnil(l, -1))		   // hit the end of the chain, nothing here
225 			break;
226 
227 		std::string parentName = lua_tostring(l, -1);
228 		lua_pop(l, 1);
229 
230 		lua_getfield(l, metaTypeRegistry, parentName.c_str());
231 		if (lua_isnil(l, -1))
232 			return luaL_error(l, "Encountered invalid parent metatype name %s", parentName.c_str());
233 
234 		lua_remove(l, -2); // replace the metatable with the parent
235 	}
236 
237 	return luaL_error(l, "Attempt to set undefined property %s on %s", lua_tostring(l, 2), lua_tostring(l, 1));
238 }
239 
get_names_from_table(lua_State * l,std::vector<std::string> & names,const std::string & prefix,bool methodsOnly)240 static void get_names_from_table(lua_State *l, std::vector<std::string> &names, const std::string &prefix, bool methodsOnly)
241 {
242 	lua_pushnil(l);
243 	while (lua_next(l, -2)) {
244 
245 		// only include string keys. the . syntax doesn't work for anything
246 		// else
247 		if (lua_type(l, -2) != LUA_TSTRING) {
248 			lua_pop(l, 1);
249 			continue;
250 		}
251 
252 		// only include callable things if requested
253 		if (methodsOnly && lua_type(l, -1) != LUA_TFUNCTION) {
254 			lua_pop(l, 1);
255 			continue;
256 		}
257 
258 		size_t str_size = 0;
259 		const char *name = lua_tolstring(l, -2, &str_size);
260 		// anything starting with an underscore is hidden
261 		if (name[0] == '_') {
262 			lua_pop(l, 1);
263 			continue;
264 		}
265 
266 		// check if the name begins with the prefix
267 		if (strncmp(name, prefix.c_str(), std::min(str_size, prefix.size())) == 0)
268 			// push back the portion of the name not matching the prefix (for completion)
269 			names.push_back(std::string(name + prefix.size()));
270 
271 		lua_pop(l, 1);
272 	}
273 }
274 
GetNames(std::vector<std::string> & names,const std::string & prefix,bool methodsOnly)275 void LuaMetaTypeBase::GetNames(std::vector<std::string> &names, const std::string &prefix, bool methodsOnly)
276 {
277 	// never show hidden names
278 	if (!prefix.empty() && prefix[0] == '_')
279 		return;
280 
281 	lua_State *l = Lua::manager->GetLuaState();
282 
283 	LUA_DEBUG_START(l);
284 
285 	// work out if/how we can deal with the value
286 	bool typeless;
287 	if (lua_istable(l, -1))
288 		// we can always look into tables
289 		typeless = true;
290 
291 	else if (lua_isuserdata(l, -1)) {
292 		// two known types of userdata
293 		// - LuaObject, metatable has a "type" field
294 		// - RO table proxy, no type
295 		lua_getmetatable(l, -1);
296 		lua_getfield(l, -1, "type");
297 		typeless = lua_isnil(l, -1);
298 		lua_pop(l, 2);
299 	}
300 
301 	else {
302 		// it's a non-table object with a metatable
303 		// this realistically can only be a string, but let's work with it anyways
304 		lua_getmetatable(l, -1);
305 		if (lua_isnil(l, -1)) {
306 			lua_pop(l, 1);
307 			return; // if no metatable, can't do anything
308 		}
309 
310 		lua_getfield(l, -1, "__index");
311 		typeless = lua_istable(l, -1);
312 		lua_pop(l, 2);
313 
314 		// if the __index field isn't a table, we can't introspect into it
315 		if (!typeless)
316 			return;
317 	}
318 
319 	LUA_DEBUG_CHECK(l, 0);
320 
321 	if (typeless) {
322 		// if the object we're getting names from is a table, search it for names
323 		if (lua_istable(l, -1))
324 			get_names_from_table(l, names, prefix, methodsOnly);
325 
326 		// Check the metatable indexes
327 		// Get the metatable of the object, then check it for an __index field
328 		lua_pushvalue(l, -1);
329 		while (lua_getmetatable(l, -1)) {
330 			lua_getfield(l, -2, "__index");
331 
332 			// Replace the metatable with the index table to keep a stable stack size.
333 			lua_replace(l, -3);
334 			lua_pop(l, 1);
335 
336 			if (lua_istable(l, -1))
337 				get_names_from_table(l, names, prefix, methodsOnly);
338 			else
339 				break;
340 		}
341 		lua_pop(l, 1);
342 
343 		return;
344 	}
345 
346 	// properties
347 	if (!methodsOnly) {
348 		lua_getuservalue(l, -1);
349 		if (!lua_isnil(l, -1))
350 			get_names_from_table(l, names, prefix, false);
351 		lua_pop(l, 1);
352 	}
353 
354 	// check the metatable (and its parents) for methods and attributes
355 	lua_getmetatable(l, -1);
356 	while (1) {
357 		lua_getfield(l, -1, "methods");
358 		get_names_from_table(l, names, prefix, methodsOnly);
359 		lua_pop(l, 1);
360 
361 		if (!methodsOnly) {
362 			lua_getfield(l, -1, "attrs");
363 			get_names_from_table(l, names, prefix, false);
364 			lua_pop(l, 1);
365 		}
366 
367 		lua_getfield(l, -1, "parent");
368 		std::string parent = lua_tostring(l, -1);
369 		if (!lua_isnil(l, -1))
370 			parent = lua_tostring(l, -1);
371 
372 		lua_pop(l, 1); // pop the parent's name
373 
374 		bool hasParent = !parent.empty() && GetMetatableFromName(l, parent.c_str());
375 		if (hasParent)
376 			lua_remove(l, -2); // replace the previous metatype with the parent
377 		else
378 			break; // the old metatype is the only thing on the stack
379 	}
380 
381 	lua_pop(l, 1); // pop the metatable
382 
383 	LUA_DEBUG_END(l, 0);
384 }
385 
GetMetatableFromName(lua_State * l,const char * name)386 bool LuaMetaTypeBase::GetMetatableFromName(lua_State *l, const char *name)
387 {
388 	luaL_getsubtable(l, LUA_REGISTRYINDEX, "LuaMetaTypes");
389 	lua_getfield(l, -1, name);
390 	lua_remove(l, -2);
391 	if (lua_isnil(l, -1)) {
392 		lua_pop(l, 1);
393 		return false;
394 	}
395 
396 	return true;
397 }
398 
GetMetatable() const399 void LuaMetaTypeBase::GetMetatable() const
400 {
401 	assert(IsValid());
402 	LUA_DEBUG_START(m_lua);
403 
404 	luaL_getsubtable(m_lua, LUA_REGISTRYINDEX, "LuaMetaTypes");
405 	lua_rawgeti(m_lua, -1, m_ref);
406 	lua_remove(m_lua, -2);
407 	assert(lua_type(m_lua, -1) == LUA_TTABLE);
408 
409 	LUA_DEBUG_END(m_lua, 1);
410 }
411 
CreateMetaType(lua_State * l)412 void LuaMetaTypeBase::CreateMetaType(lua_State *l)
413 {
414 	luaL_getsubtable(l, LUA_REGISTRYINDEX, "LuaMetaTypes");
415 	m_lua = l;
416 
417 	// if the type name is empty, we're creating a "throwaway" object with no parent either
418 	if (!m_typeName.empty()) {
419 		// Warn if we're double-initializing a type name
420 		lua_getfield(l, -1, m_typeName.c_str());
421 		if (!lua_isnil(l, -1))
422 			Output("Double-initialization of lua metatype %s. Do you have a name conflict?\n", m_typeName.c_str());
423 		lua_pop(l, 1);
424 	}
425 
426 	lua_newtable(l);
427 
428 	if (!m_typeName.empty()) {
429 		// Set the entry LuaMetaTypes[typename] = metatype
430 		lua_pushvalue(l, -1);
431 		lua_setfield(l, -3, m_typeName.c_str());
432 	}
433 
434 	// Store this metatable via a numeric ref index for easy access
435 	lua_pushvalue(l, -1);
436 	m_ref = luaL_ref(l, -3);
437 
438 	// set the type name on the metatable
439 	lua_pushstring(l, m_typeName.c_str());
440 	lua_setfield(l, -2, "type");
441 
442 	if (!m_parent.empty()) {
443 		lua_pushstring(l, m_parent.c_str());
444 		lua_setfield(l, -2, "parent");
445 	}
446 
447 	// create the attributes table
448 	lua_newtable(l);
449 	lua_setfield(l, -2, "attrs");
450 
451 	// create the methods table
452 	lua_newtable(l);
453 	lua_setfield(l, -2, "methods");
454 
455 	lua_pushcclosure(l, &l_index, 0);
456 	lua_setfield(l, -2, "__index");
457 
458 	lua_pushcclosure(l, &l_newindex, 0);
459 	lua_setfield(l, -2, "__newindex");
460 
461 	// replace the LuaMetaTypes registry table, leaving the created metatype on the stack
462 	lua_replace(l, -2);
463 }
464