1 /*
2  * Copyright (C) 2006-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19 
20 #include "scripting/lua_table.h"
21 
22 #include <memory>
23 
24 #include "base/log.h"
25 
LuaTable(lua_State * L)26 LuaTable::LuaTable(lua_State* L) : L_(L), warn_about_unaccessed_keys_(true) {
27 	// S: <table>
28 	lua_pushlightuserdata(L_, const_cast<LuaTable*>(this));  // S: this
29 	lua_pushvalue(L, -2);                                    // S: <table> this <table>
30 	lua_rawset(L, LUA_REGISTRYINDEX);
31 }
32 
~LuaTable()33 LuaTable::~LuaTable() {
34 	if (warn_about_unaccessed_keys_) {
35 		std::vector<std::string> unused_keys;
36 		std::set<std::string> all_keys = keys<std::string>();
37 		std::set_difference(all_keys.begin(), all_keys.end(), accessed_keys_.begin(),
38 		                    accessed_keys_.end(), std::back_inserter(unused_keys));
39 
40 		for (const std::string& unused_key : unused_keys) {
41 			// We must not throw in destructors as this can shadow other errors.
42 			log("ERROR: Unused key \"%s\" in LuaTable. Please report as a bug.\n", unused_key.c_str());
43 		}
44 	}
45 
46 	lua_pushlightuserdata(L_, const_cast<LuaTable*>(this));  // S: this
47 	lua_pushnil(L_);                                         // S: this nil
48 	lua_rawset(L_, LUA_REGISTRYINDEX);
49 }
50 
do_not_warn_about_unaccessed_keys()51 void LuaTable::do_not_warn_about_unaccessed_keys() {
52 	warn_about_unaccessed_keys_ = false;
53 }
54 
get_existing_table_value(const std::string & key) const55 void LuaTable::get_existing_table_value(const std::string& key) const {
56 	lua_pushstring(L_, key);
57 	check_if_key_was_in_table(key);
58 }
59 
get_existing_table_value(const int key) const60 void LuaTable::get_existing_table_value(const int key) const {
61 	const std::string key_as_string = boost::lexical_cast<std::string>(key);
62 	lua_pushint32(L_, key);
63 	check_if_key_was_in_table(key_as_string);
64 }
65 
check_if_key_was_in_table(const std::string & key) const66 void LuaTable::check_if_key_was_in_table(const std::string& key) const {
67 	// S: key
68 	lua_pushlightuserdata(L_, const_cast<LuaTable*>(this));  // S: this
69 	lua_rawget(L_, LUA_REGISTRYINDEX);                       // S: key table
70 	lua_pushvalue(L_, -2);                                   // S: key table key
71 
72 	lua_rawget(L_, -2);  // S: key table value
73 	lua_remove(L_, -2);  // S: key value
74 	lua_remove(L_, -2);  // S: value
75 
76 	if (lua_isnil(L_, -1)) {
77 		lua_pop(L_, 1);
78 		throw LuaTableKeyError(key);
79 	}
80 	accessed_keys_.insert(key);
81 }
82 
get_value() const83 template <> std::unique_ptr<LuaTable> LuaTable::get_value() const {
84 	lua_pushvalue(L_, -1);
85 	if (!lua_istable(L_, -1)) {
86 		lua_pop(L_, 1);
87 		throw LuaError("Could not convert value at the top of the stack to table value.");
88 	}
89 
90 	std::unique_ptr<LuaTable> rv(new LuaTable(L_));
91 	lua_pop(L_, 1);
92 	return rv;
93 }
94 
get_value() const95 template <> std::string LuaTable::get_value() const {
96 	lua_pushvalue(L_, -1);
97 	const char* str = lua_tostring(L_, -1);
98 	lua_pop(L_, 1);
99 	if (str == nullptr) {
100 		throw LuaError("Could not convert value at top of the stack to string.");
101 	}
102 	return str;
103 }
104 
get_value() const105 template <> int LuaTable::get_value() const {
106 	lua_pushvalue(L_, -1);
107 	int is_num;
108 	int return_value = lua_tointegerx(L_, -1, &is_num);
109 	lua_pop(L_, 1);
110 	if (!is_num) {
111 		throw LuaError("Could not convert value at top of the stack to integer.");
112 	}
113 	return return_value;
114 }
115 
get_string_with_default(const LuaTable & table,const std::string & key,const std::string & default_value)116 const std::string get_string_with_default(const LuaTable& table,
117                                           const std::string& key,
118                                           const std::string& default_value) {
119 	if (table.has_key(key)) {
120 		return table.get_string(key);
121 	} else {
122 		return default_value;
123 	}
124 }
125