Commit 9b59c3eb authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Revised handling of cyclic table loops in LuaTable:

Instead of checking each discovered table against one global set of already discovered tables, this check is only performed against all predecessing tables. Though this method is more expensive, it allows for a more complete syntax/data tree while still avoiding cyclic loops.

refs #643
parent 678cdc50
......@@ -58,7 +58,6 @@ namespace campvis {
void GlobalLuaTable::populateValueMap() {
_valueMap.clear();
_discoveredTables.clear();
LuaStateMutexType::scoped_lock lock(_luaVmState.getMutex());
lua_State* L = _luaVmState.rawState();
......@@ -68,4 +67,8 @@ namespace campvis {
lua_pop(L, 1);
}
LuaTable* GlobalLuaTable::getParentTable() {
return nullptr;
}
}
......@@ -57,6 +57,7 @@ namespace campvis {
virtual void pushField(const std::string& name) override;
virtual void popRecursive() override;
virtual void populateValueMap() override;
virtual LuaTable* getParentTable() override;
};
}
......
......@@ -49,10 +49,13 @@ namespace campvis {
}
std::shared_ptr<RegularLuaTable> LuaTable::getTable(const std::string& name) {
// check whether this table has a field of given name and of type LUA_TTABLE
auto it = _valueMap.find(name);
if (it != _valueMap.end()) {
if (!it->second.luaTable)
it->second.luaTable = std::shared_ptr<RegularLuaTable>(new RegularLuaTable(this->shared_from_this(), name));
if (it != _valueMap.end() && it->second.luaType == LUA_TTABLE) {
// check whether the corresponding table needs to be initialized
if (!it->second.luaTable) {
it->second.luaTable = std::make_shared<RegularLuaTable>(this->shared_from_this(), name);
}
return it->second.luaTable;
}
......@@ -61,10 +64,12 @@ namespace campvis {
}
std::shared_ptr<MetatableLuaTable> LuaTable::getMetatable(const std::string& name) {
// check whether this table has a field of given name, which has a metatable
auto it = _valueMap.find(name);
if (it != _valueMap.end()) {
if (it != _valueMap.end() && it->second.hasMetatable) {
// check whether the corresponding table needs to be initialized
if (!it->second.luaMetatable)
it->second.luaMetatable = std::shared_ptr<MetatableLuaTable>(new MetatableLuaTable(this->shared_from_this(), name));
it->second.luaMetatable = std::make_shared<MetatableLuaTable>(this->shared_from_this(), name);
return it->second.luaMetatable;
}
......@@ -94,8 +99,8 @@ namespace campvis {
void LuaTable::iterateOverTableAndPopulateValueMap(lua_State* L) {
void* tablePtr = hvalue(index2addr(L, -1));
if (_discoveredTables.find(tablePtr) == _discoveredTables.end()) {
_discoveredTables.insert(std::make_pair(tablePtr, shared_from_this()));
if (!getParentTable() || !getParentTable()->checkIfAlreadyDiscovered(tablePtr)) {
_luaTablePointer = tablePtr;
// iterate over table
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
......@@ -128,8 +133,16 @@ namespace campvis {
getTable(name);
}
}
else {
ValueStruct vs = { LUA_TNIL, nullptr, nullptr, false, false };
_valueMap.insert(std::make_pair("...", vs));
}
}
std::map<void*, std::weak_ptr<LuaTable>> LuaTable::_discoveredTables;
bool LuaTable::checkIfAlreadyDiscovered(void* luaTablePointer) {
LuaTable* parent = getParentTable();
return (_luaTablePointer == luaTablePointer)
|| ((parent != nullptr) ? parent->checkIfAlreadyDiscovered(luaTablePointer) : false);
}
}
......@@ -208,13 +208,30 @@ namespace campvis {
*/
void iterateOverTableAndPopulateValueMap(lua_State* L);
/**
* Returns this LuaTable's parent LuaTable.
* To be implemented in sub classes. Used to detect cyclic table loops.
*/
virtual LuaTable* getParentTable() = 0;
/**
* Checks whether the Lua table with the given Lua VM raw pointer has already been discovered.
* Use this method to detect cyclic table loops. It recursively checks all parent tables
* whether they correspond to the given raw pointer.
* \param luaTablePointer Raw pointer to the Lua table in the Lua VM.
* \return True if one of the parents of corresponds to the given Lua VM raw pointer.
*/
bool checkIfAlreadyDiscovered(void* luaTablePointer);
/// Reference to the LuaVmState from which the table originates
LuaVmState& _luaVmState;
/// Raw pointer to the Lua table in the Lua VM. Used to detect cyclic table loops.
void* _luaTablePointer;
/// value map of this lua table, mirroring the contents.
std::map<std::string, ValueStruct> _valueMap;
/// Map of already discovered tables to avoid endless loops through cyclic tables.
static std::map<void*, std::weak_ptr<LuaTable>> _discoveredTables;
};
}
......
......@@ -102,4 +102,8 @@ namespace campvis {
_parent->popRecursive();
}
LuaTable* MetatableLuaTable::getParentTable() {
return _parent.get();
}
}
......@@ -33,6 +33,7 @@ namespace campvis {
virtual void pushField(const std::string& name) override;
virtual void popRecursive() override;
virtual void populateValueMap() override;
virtual LuaTable* getParentTable() override;
private:
std::shared_ptr<LuaTable> _parent; ///< Lua table in which this metatable is stored
......
......@@ -105,4 +105,8 @@ namespace campvis {
_parent->popRecursive();
}
LuaTable* RegularLuaTable::getParentTable() {
return _parent.get();
}
}
......@@ -57,6 +57,7 @@ namespace campvis {
virtual void pushField(const std::string& name) override;
virtual void popRecursive() override;
virtual void populateValueMap() override;
virtual LuaTable* getParentTable() override;
private:
std::shared_ptr<LuaTable> _parent; ///< Lua table in which this table is stored.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment