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