lua_parameter_dictionary.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2016 The Cartographer Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // When a LuaParameterDictionary is constructed, a new Lua state (i.e. an
18 // independent Lua interpreter) is fired up to evaluate the Lua code. The code
19 // is expected to return a Lua table that contains key/value pairs that are the
20 // key/value pairs of our parameter dictionary.
21 //
22 // We keep the Lua interpreter around and the table on the stack and reference
23 // it in the Get* methods instead of moving its contents from Lua into a C++ map
24 // since we can only know in the Get* methods what data type to expect in the
25 // table.
26 //
27 // Some of the methods below documentation the current stack with the following
28 // notation: S: <bottom> ... <top>
29 
31 
32 #include <algorithm>
33 #include <cmath>
34 #include <functional>
35 #include <memory>
36 
37 namespace cartographer {
38 namespace common {
39 
40 namespace {
41 
42 // Replace the string at the top of the stack through a quoted version that Lua
43 // can read back.
44 void QuoteStringOnStack(lua_State* L) {
45  CHECK(lua_isstring(L, -1)) << "Top of stack is not a string value.";
46  int current_index = lua_gettop(L);
47 
48  // S: ... string
49  lua_pushglobaltable(L); // S: ... string globals
50  lua_getfield(L, -1, "string"); // S: ... string globals <string module>
51  lua_getfield(L, -1,
52  "format"); // S: ... string globals <string module> format
53  lua_pushstring(L, "%q"); // S: ... string globals <string module> format "%q"
54  lua_pushvalue(L, current_index); // S: ... string globals <string module>
55  // format "%q" string
56 
57  lua_call(L, 2, 1); // S: ... string globals <string module> quoted
58  lua_replace(L, current_index); // S: ... quoted globals <string module>
59 
60  lua_pop(L, 2); // S: ... quoted
61 }
62 
63 // Sets the given 'dictionary' as the value of the "this" key in Lua's registry
64 // table.
65 void SetDictionaryInRegistry(lua_State* L, LuaParameterDictionary* dictionary) {
66  lua_pushstring(L, "this");
67  lua_pushlightuserdata(L, dictionary);
68  lua_settable(L, LUA_REGISTRYINDEX);
69 }
70 
71 // Gets the 'dictionary' from the "this" key in Lua's registry table.
72 LuaParameterDictionary* GetDictionaryFromRegistry(lua_State* L) {
73  lua_pushstring(L, "this");
74  lua_gettable(L, LUA_REGISTRYINDEX);
75  void* return_value = lua_isnil(L, -1) ? nullptr : lua_touserdata(L, -1);
76  lua_pop(L, 1);
77  CHECK(return_value != nullptr);
78  return reinterpret_cast<LuaParameterDictionary*>(return_value);
79 }
80 
81 // CHECK()s if a Lua method returned an error.
82 void CheckForLuaErrors(lua_State* L, int status) {
83  CHECK_EQ(status, 0) << lua_tostring(L, -1);
84 }
85 
86 // Returns 'a' if 'condition' is true, else 'b'.
87 int LuaChoose(lua_State* L) {
88  CHECK_EQ(lua_gettop(L), 3) << "choose() takes (condition, a, b).";
89  CHECK(lua_isboolean(L, 1)) << "condition is not a boolean value.";
90 
91  const bool condition = lua_toboolean(L, 1);
92  if (condition) {
93  lua_pushvalue(L, 2);
94  } else {
95  lua_pushvalue(L, 3);
96  }
97  return 1;
98 }
99 
100 // Pushes a value to the Lua stack.
101 void PushValue(lua_State* L, const int key) { lua_pushinteger(L, key); }
102 void PushValue(lua_State* L, const string& key) {
103  lua_pushstring(L, key.c_str());
104 }
105 
106 // Reads the value with the given 'key' from the Lua dictionary and pushes it to
107 // the top of the stack.
108 template <typename T>
109 void GetValueFromLuaTable(lua_State* L, const T& key) {
110  PushValue(L, key);
111  lua_rawget(L, -2);
112 }
113 
114 // CHECK() that the topmost parameter on the Lua stack is a table.
115 void CheckTableIsAtTopOfStack(lua_State* L) {
116  CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
117 }
118 
119 // Returns true if 'key' is in the table at the top of the Lua stack.
120 template <typename T>
121 bool HasKeyOfType(lua_State* L, const T& key) {
122  CheckTableIsAtTopOfStack(L);
123  PushValue(L, key);
124  lua_rawget(L, -2);
125  const bool key_not_found = lua_isnil(L, -1);
126  lua_pop(L, 1); // Pop the item again.
127  return !key_not_found;
128 }
129 
130 // Iterates over the integer keys of the table at the top of the stack of 'L•
131 // and pushes the values one by one. 'pop_value' is expected to pop a value and
132 // put them into a C++ container.
133 void GetArrayValues(lua_State* L, const std::function<void()>& pop_value) {
134  int idx = 1;
135  while (true) {
136  GetValueFromLuaTable(L, idx);
137  if (lua_isnil(L, -1)) {
138  lua_pop(L, 1);
139  break;
140  }
141  pop_value();
142  ++idx;
143  }
144 }
145 
146 } // namespace
147 
148 std::unique_ptr<LuaParameterDictionary>
150  const string& code, std::unique_ptr<FileResolver> file_resolver) {
151  return std::unique_ptr<LuaParameterDictionary>(new LuaParameterDictionary(
152  code, ReferenceCount::NO, std::move(file_resolver)));
153 }
154 
156  const string& code, std::unique_ptr<FileResolver> file_resolver)
158  std::move(file_resolver)) {}
159 
161  const string& code, ReferenceCount reference_count,
162  std::unique_ptr<FileResolver> file_resolver)
163  : L_(luaL_newstate()),
165  file_resolver_(std::move(file_resolver)),
166  reference_count_(reference_count) {
167  CHECK_NOTNULL(L_);
168  SetDictionaryInRegistry(L_, this);
169 
170  luaL_openlibs(L_);
171 
172  lua_register(L_, "choose", LuaChoose);
173  lua_register(L_, "include", LuaInclude);
174  lua_register(L_, "read", LuaRead);
175 
176  CheckForLuaErrors(L_, luaL_loadstring(L_, code.c_str()));
177  CheckForLuaErrors(L_, lua_pcall(L_, 0, 1, 0));
178  CheckTableIsAtTopOfStack(L_);
179 }
180 
182  lua_State* const L, ReferenceCount reference_count,
183  std::shared_ptr<FileResolver> file_resolver)
184  : L_(lua_newthread(L)),
185  file_resolver_(std::move(file_resolver)),
186  reference_count_(reference_count) {
187  CHECK_NOTNULL(L_);
188 
189  // Make sure this is never garbage collected.
190  CHECK(lua_isthread(L, -1));
191  index_into_reference_table_ = luaL_ref(L, LUA_REGISTRYINDEX);
192 
193  CHECK(lua_istable(L, -1)) << "Topmost item on Lua stack is not a table!";
194  lua_xmove(L, L_, 1); // Moves the table and the coroutine over.
195  CheckTableIsAtTopOfStack(L_);
196 }
197 
201  }
202  if (index_into_reference_table_ > 0) {
203  luaL_unref(L_, LUA_REGISTRYINDEX, index_into_reference_table_);
204  } else {
205  lua_close(L_);
206  }
207 }
208 
209 std::vector<string> LuaParameterDictionary::GetKeys() const {
210  CheckTableIsAtTopOfStack(L_);
211  std::vector<string> keys;
212 
213  lua_pushnil(L_); // Push the first key
214  while (lua_next(L_, -2) != 0) {
215  lua_pop(L_, 1); // Pop value, keep key.
216  if (!lua_isnumber(L_, -1)) {
217  keys.emplace_back(lua_tostring(L_, -1));
218  }
219  }
220  return keys;
221 }
222 
223 bool LuaParameterDictionary::HasKey(const string& key) const {
224  return HasKeyOfType(L_, key);
225 }
226 
227 string LuaParameterDictionary::GetString(const string& key) {
229  GetValueFromLuaTable(L_, key);
230  return PopString(Quoted::NO);
231 }
232 
234  CHECK(lua_isstring(L_, -1)) << "Top of stack is not a string value.";
235  if (quoted == Quoted::YES) {
236  QuoteStringOnStack(L_);
237  }
238 
239  const string value = lua_tostring(L_, -1);
240  lua_pop(L_, 1);
241  return value;
242 }
243 
244 double LuaParameterDictionary::GetDouble(const string& key) {
246  GetValueFromLuaTable(L_, key);
247  return PopDouble();
248 }
249 
251  CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
252  const double value = lua_tonumber(L_, -1);
253  lua_pop(L_, 1);
254  return value;
255 }
256 
257 int LuaParameterDictionary::GetInt(const string& key) {
259  GetValueFromLuaTable(L_, key);
260  return PopInt();
261 }
262 
264  CHECK(lua_isnumber(L_, -1)) << "Top of stack is not a number value.";
265  const int value = lua_tointeger(L_, -1);
266  lua_pop(L_, 1);
267  return value;
268 }
269 
270 bool LuaParameterDictionary::GetBool(const string& key) {
272  GetValueFromLuaTable(L_, key);
273  return PopBool();
274 }
275 
277  CHECK(lua_isboolean(L_, -1)) << "Top of stack is not a boolean value.";
278  const bool value = lua_toboolean(L_, -1);
279  lua_pop(L_, 1);
280  return value;
281 }
282 
283 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::GetDictionary(
284  const string& key) {
286  GetValueFromLuaTable(L_, key);
288 }
289 
290 std::unique_ptr<LuaParameterDictionary> LuaParameterDictionary::PopDictionary(
291  ReferenceCount reference_count) const {
292  CheckTableIsAtTopOfStack(L_);
293  std::unique_ptr<LuaParameterDictionary> value(
294  new LuaParameterDictionary(L_, reference_count, file_resolver_));
295  // The constructor lua_xmove()s the value, no need to pop it.
296  CheckTableIsAtTopOfStack(L_);
297  return value;
298 }
299 
300 string LuaParameterDictionary::DoToString(const string& indent) const {
301  string result = "{";
302  bool dictionary_is_empty = true;
303 
304  const auto top_of_stack_to_string = [this, indent,
305  &dictionary_is_empty]() -> string {
306  dictionary_is_empty = false;
307 
308  const int value_type = lua_type(L_, -1);
309  switch (value_type) {
310  case LUA_TBOOLEAN:
311  return PopBool() ? "true" : "false";
312  break;
313  case LUA_TSTRING:
314  return PopString(Quoted::YES);
315  break;
316  case LUA_TNUMBER: {
317  const double value = PopDouble();
318  if (std::isinf(value)) {
319  return value < 0 ? "-math.huge" : "math.huge";
320  } else {
321  return std::to_string(value);
322  }
323  } break;
324  case LUA_TTABLE: {
325  std::unique_ptr<LuaParameterDictionary> subdict(
327  return subdict->DoToString(indent + " ");
328  } break;
329  default:
330  LOG(FATAL) << "Unhandled type " << lua_typename(L_, value_type);
331  }
332  };
333 
334  // Integer (array) keys.
335  for (int i = 1; i; ++i) {
336  GetValueFromLuaTable(L_, i);
337  if (lua_isnil(L_, -1)) {
338  lua_pop(L_, 1);
339  break;
340  }
341  result.append("\n");
342  result.append(indent);
343  result.append(" ");
344  result.append(top_of_stack_to_string());
345  result.append(",");
346  }
347 
348  // String keys.
349  std::vector<string> keys = GetKeys();
350  if (!keys.empty()) {
351  std::sort(keys.begin(), keys.end());
352  for (const string& key : keys) {
353  GetValueFromLuaTable(L_, key);
354  result.append("\n");
355  result.append(indent);
356  result.append(" ");
357  result.append(key);
358  result.append(" = ");
359  result.append(top_of_stack_to_string());
360  result.append(",");
361  }
362  }
363  result.append("\n");
364  result.append(indent);
365  result.append("}");
366 
367  if (dictionary_is_empty) {
368  return "{}";
369  }
370  return result;
371 }
372 
373 string LuaParameterDictionary::ToString() const { return DoToString(""); }
374 
376  std::vector<double> values;
377  GetArrayValues(L_, [&values, this] { values.push_back(PopDouble()); });
378  return values;
379 }
380 
381 std::vector<std::unique_ptr<LuaParameterDictionary>>
383  std::vector<std::unique_ptr<LuaParameterDictionary>> values;
384  GetArrayValues(L_, [&values, this] {
385  values.push_back(PopDictionary(reference_count_));
386  });
387  return values;
388 }
389 
391  std::vector<string> values;
392  GetArrayValues(L_,
393  [&values, this] { values.push_back(PopString(Quoted::NO)); });
394  return values;
395 }
396 
397 void LuaParameterDictionary::CheckHasKey(const string& key) const {
398  CHECK(HasKey(key)) << "Key '" << key << "' not in dictionary:\n"
399  << ToString();
400 }
401 
403  CheckHasKey(key);
404  reference_counts_[key]++;
405 }
406 
408  for (const auto& key : GetKeys()) {
409  CHECK_EQ(1, reference_counts_.count(key))
410  << "Key '" << key << "' was used the wrong number of times.";
411  CHECK_EQ(1, reference_counts_.at(key))
412  << "Key '" << key << "' was used the wrong number of times.";
413  }
414  reference_counts_.clear();
415 }
416 
418  const int temp = GetInt(key); // Will increase reference count.
419  CHECK_GE(temp, 0) << temp << " is negative.";
420  return temp;
421 }
422 
423 // Lua function to run a script in the current Lua context. Takes the filename
424 // as its only argument.
426  CHECK_EQ(lua_gettop(L), 1);
427  CHECK(lua_isstring(L, -1)) << "include takes a filename.";
428 
429  LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
430  const string basename = lua_tostring(L, -1);
431  const string filename =
432  parameter_dictionary->file_resolver_->GetFullPathOrDie(basename);
433  if (std::find(parameter_dictionary->included_files_.begin(),
434  parameter_dictionary->included_files_.end(),
435  filename) != parameter_dictionary->included_files_.end()) {
436  string error_msg = "Tried to include " + filename +
437  " twice. Already included files in order of inclusion: ";
438  for (const string& filename : parameter_dictionary->included_files_) {
439  error_msg.append(filename);
440  error_msg.append("\n");
441  }
442  LOG(FATAL) << error_msg;
443  }
444  parameter_dictionary->included_files_.push_back(filename);
445  lua_pop(L, 1);
446  CHECK_EQ(lua_gettop(L), 0);
447 
448  const string content =
449  parameter_dictionary->file_resolver_->GetFileContentOrDie(basename);
450  CheckForLuaErrors(
451  L, luaL_loadbuffer(L, content.c_str(), content.size(), filename.c_str()));
452  CheckForLuaErrors(L, lua_pcall(L, 0, LUA_MULTRET, 0));
453 
454  return lua_gettop(L);
455 }
456 
457 // Lua function to read a file into a string.
459  CHECK_EQ(lua_gettop(L), 1);
460  CHECK(lua_isstring(L, -1)) << "read takes a filename.";
461 
462  LuaParameterDictionary* parameter_dictionary = GetDictionaryFromRegistry(L);
463  const string file_content =
464  parameter_dictionary->file_resolver_->GetFileContentOrDie(
465  lua_tostring(L, -1));
466  lua_pushstring(L, file_content.c_str());
467  return 1;
468 }
469 
470 } // namespace common
471 } // namespace cartographer
std::unique_ptr< LuaParameterDictionary > PopDictionary(ReferenceCount reference_count) const
const std::shared_ptr< FileResolver > file_resolver_
std::vector< std::unique_ptr< LuaParameterDictionary > > GetArrayValuesAsDictionaries()
LuaParameterDictionary(const string &code, std::unique_ptr< FileResolver > file_resolver)
static std::unique_ptr< LuaParameterDictionary > NonReferenceCounted(const string &code, std::unique_ptr< FileResolver > file_resolver)
float value
string DoToString(const string &indent) const
std::unique_ptr< LuaParameterDictionary > GetDictionary(const string &key)


cartographer
Author(s):
autogenerated on Mon Jun 10 2019 12:51:39