1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "nel/gui/lua_object.h"
24 #include "nel/gui/lua_ihm.h"
25 #include "nel/gui/lua_helper.h"
34 // *************************************************
35 CLuaObject::CLuaObject(CLuaState
&state
, const char *id
)
40 // *************************************************
41 CLuaObject::CLuaObject(CLuaState
&state
, const std::string
&id
)
43 pop(state
, id
.c_str());
47 // *************************************************
48 bool CLuaObject::isValid() const
50 return getLuaState() != NULL
;
53 // *************************************************
54 CLuaState
*CLuaObject::getLuaState() const
59 // *************************************************
60 CLuaObject::CLuaObject(const CLuaObject
&other
)
65 nlassert(other
.getLuaState());
66 pop(*other
.getLuaState());
69 // else ... copy of an invalid CLuaObject( is an invalid CLuaObject
72 // *************************************************
73 CLuaObject
&CLuaObject::operator=(const CLuaObject
&other
)
81 pop(*other
.getLuaState());
86 // *************************************************
87 bool CLuaObject::rawEqual(const CLuaObject
&other
) const
89 nlassert(other
.getLuaState() == getLuaState());
92 bool equal
= other
.getLuaState()->rawEqual(-1, -2);
93 getLuaState()->pop(2);
97 // *************************************************
98 CLuaObject::~CLuaObject()
103 // *************************************************
104 void CLuaObject::release()
108 CLuaStackChecker
lsc(_LuaState
);
109 _LuaState
->pushLightUserData((void *) this);
110 _LuaState
->pushNil();
111 _LuaState
->setTable(LUA_REGISTRYINDEX
);
116 // *************************************************
117 void CLuaObject::pop(CLuaState
&luaState
, const char *id
)
120 nlassert(luaState
.getTop() >= 1);
122 CLuaStackChecker
lsc(&luaState
);
123 luaState
.pushLightUserData((void *) this);
124 luaState
.pushValue(-2); // copy original object
125 luaState
.setTable(LUA_REGISTRYINDEX
);
128 _LuaState
= &luaState
;
132 // *************************************************
133 void CLuaObject::push() const
136 _LuaState
->pushLightUserData((void *) this);
137 _LuaState
->getTable(LUA_REGISTRYINDEX
);
140 // *************************************************
141 int CLuaObject::type() const
144 int type
= _LuaState
->type();
149 // *************************************************
150 const char *CLuaObject::getTypename() const
153 const char *typeName
= _LuaState
->getTypename(-1);
158 // *************************************************
159 bool CLuaObject::isNil() const { push(); bool result
= _LuaState
->isNil(); _LuaState
->pop(); return result
; }
160 bool CLuaObject::isNumber() const { push(); bool result
= _LuaState
->isNumber(); _LuaState
->pop(); return result
; }
161 bool CLuaObject::isInteger() const { push(); bool result
= _LuaState
->isInteger(); _LuaState
->pop(); return result
; }
162 bool CLuaObject::isBoolean() const { push(); bool result
= _LuaState
->isBoolean(); _LuaState
->pop(); return result
; }
163 bool CLuaObject::isString() const { push(); bool result
= _LuaState
->isString(); _LuaState
->pop(); return result
; }
164 bool CLuaObject::isFunction() const { push(); bool result
= _LuaState
->isFunction(); _LuaState
->pop(); return result
; }
165 bool CLuaObject::isCFunction() const { push(); bool result
= _LuaState
->isCFunction(); _LuaState
->pop(); return result
; }
166 bool CLuaObject::isTable() const { push(); bool result
= _LuaState
->isTable(); _LuaState
->pop(); return result
; }
167 bool CLuaObject::isUserData() const { push(); bool result
= _LuaState
->isUserData(); _LuaState
->pop(); return result
; }
168 bool CLuaObject::isLightUserData() const { push(); bool result
= _LuaState
->isLightUserData(); _LuaState
->pop(); return result
; }
169 bool CLuaObject::isRGBA() const
171 if (!isUserData()) return false;
174 return CLuaIHM::pop(*_LuaState
, dummy
);
177 // *************************************************
178 bool CLuaObject::toBoolean() const { push(); bool result
= _LuaState
->toBoolean(); _LuaState
->pop(); return result
; }
179 lua_Number
CLuaObject::toNumber() const { push(); lua_Number result
= _LuaState
->toNumber(); _LuaState
->pop(); return result
; }
180 lua_Integer
CLuaObject::toInteger() const { push(); lua_Integer result
= _LuaState
->toInteger(); _LuaState
->pop(); return result
; }
181 std::string
CLuaObject::toString() const
184 const char *str
= _LuaState
->toString();
185 std::string result
= str
? str
: "";
189 lua_CFunction
CLuaObject::toCFunction() const { push(); lua_CFunction result
= _LuaState
->toCFunction(); _LuaState
->pop(); return result
; }
190 void *CLuaObject::toUserData() const { push(); void *result
= _LuaState
->toUserData(); _LuaState
->pop(); return result
; }
191 const void *CLuaObject::toPointer() const { push(); const void *result
= _LuaState
->toPointer(); _LuaState
->pop(); return result
; }
192 NLMISC::CRGBA
CLuaObject::toRGBA() const
194 NLMISC::CRGBA result
;
196 if (CLuaIHM::pop(*_LuaState
, result
))
200 return NLMISC::CRGBA::Black
;
203 // *************************************************
204 CLuaObject::operator bool() const { return toBoolean(); }
205 CLuaObject::operator float() const { return (float) toNumber(); }
206 CLuaObject::operator double() const { return (double) toNumber(); }
207 CLuaObject::operator sint32() const { return (sint32
) toInteger(); }
208 CLuaObject::operator sint64() const { return (sint64
) toInteger(); }
209 CLuaObject::operator std::string() const { return toString(); }
212 // *************************************************
213 bool CLuaObject::isEnumerable() const
215 if (isTable()) return true;
216 CLuaStackRestorer
lsr(_LuaState
, _LuaState
->getTop());
218 if (_LuaState
->getMetaTable(-1))
220 _LuaState
->remove(-2);
221 _LuaState
->push("__next");
222 _LuaState
->getTable(-2);
223 return _LuaState
->isFunction();
228 // *************************************************
229 CLuaEnumeration
CLuaObject::enumerate()
233 throw ELuaNotATable(NLMISC::toString("Called CLuaObject::enumerate on an object that has no enumeration (not a table or no '__next' method in the metatable). Object is '%s' with type '%s'", getId().c_str(), getTypename()).c_str());
235 return CLuaEnumeration(*this);
238 // *************************************************
239 CLuaObject
CLuaObject::operator[](const char *key
) const
245 _LuaState
->pushNil();
246 CLuaObject
result(*_LuaState
);
249 CLuaStackChecker
lsc(_LuaState
);
251 _LuaState
->push(key
);
252 _LuaState
->getTable(-2);
253 CLuaObject
subObject(*_LuaState
, concatId(getId(), key
));
254 _LuaState
->pop(); // pop the sub object
258 // *************************************************
259 CLuaObject
CLuaObject::operator[](double key
) const
264 _LuaState
->pushNil();
265 CLuaObject
result(*_LuaState
);
268 CLuaStackChecker
lsc(_LuaState
);
270 _LuaState
->push(key
);
271 _LuaState
->getTable(-2);
272 CLuaObject
subObject(*_LuaState
, concatId(getId(), NLMISC::toString(key
)));
273 _LuaState
->pop(); // pop the sub object
277 // *************************************************
278 CLuaObject
CLuaObject::at(const char *key
) const
280 if (!isEnumerable()) throw ELuaNotATable(NLMISC::toString("Can't get key '%s' in object '%s' because type is '%s', it is not a table.", key
, getId().c_str(), getTypename()).c_str());
281 return operator[](key
);
284 // *************************************************
285 bool CLuaObject::hasKey(const char *key
) const
287 if (!isEnumerable()) throw ELuaNotATable(NLMISC::toString("Trying to access key '%s' on object '%s' of type %s.", key
, getId().c_str(), getTypename()).c_str());
288 CLuaObject value
= operator[](key
);
289 return (!value
.isNil());
292 // *************************************************
293 CLuaObject
CLuaObject::newTable(const char *tableName
)
297 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to create a subtable '%s' on object '%s' of type %s (not a table).", tableName
, getId().c_str(), getTypename()));
298 CLuaStackChecker
lsc(_LuaState
);
300 _LuaState
->push(tableName
);
301 _LuaState
->newTable();
302 _LuaState
->setTable(-3);
304 return at(tableName
); //\TODO nico double copy here ...
307 // *************************************************
308 void CLuaObject::setValue(const char *key
, const CLuaObject
&value
)
312 nlassert(value
.isValid());
313 nlassert(getLuaState() == value
.getLuaState());
314 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%s' on object '%s' of type %s (not a table).", key
, getId().c_str(), getTypename()));
315 CLuaStackChecker
lsc(_LuaState
);
317 _LuaState
->push(key
);
319 _LuaState
->setTable(-3);
323 // *************************************************
324 void CLuaObject::setNil(const char *key
)
328 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value 'nil' at key %s on object '%s' of type %s (not a table).", key
, getId().c_str(), getTypename()));
329 CLuaStackChecker
lsc(_LuaState
);
331 _LuaState
->push(key
);
332 _LuaState
->pushNil();
333 _LuaState
->setTable(-3);
337 // *************************************************
338 void CLuaObject::setValue(const char *key
, const char *value
)
343 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%s' at key %s on object '%s' of type %s (not a table).", value
, key
, getId().c_str(), getTypename()));
344 CLuaStackChecker
lsc(_LuaState
);
346 _LuaState
->push(key
);
347 _LuaState
->push(value
);
348 _LuaState
->setTable(-3);
352 // *************************************************
353 void CLuaObject::setValue(const char *key
, const std::string
&value
)
355 setValue(key
, value
.c_str());
358 // *************************************************
359 void CLuaObject::setValue(const char *key
, bool value
)
363 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%s' at key %s on object '%s' of type %s (not a table).", value
? "true" : "false", key
, getId().c_str(), getTypename()));
364 CLuaStackChecker
lsc(_LuaState
);
366 _LuaState
->push(key
);
367 _LuaState
->push(value
);
368 _LuaState
->setTable(-3);
372 // *************************************************
373 void CLuaObject::setValue(const char *key
, TLuaWrappedFunction value
)
377 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a function value '%p' at key %s on object '%s' of type %s (not a table).", (void *)value
, key
, getId().c_str(), getTypename()));
378 CLuaStackChecker
lsc(_LuaState
);
380 _LuaState
->push(key
);
381 _LuaState
->push(value
);
382 _LuaState
->setTable(-3);
386 // *************************************************
387 void CLuaObject::setValue(const char *key
, double value
)
391 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%lf' at key %s on object '%s' of type %s (not a table).", value
, key
, getId().c_str(), getTypename()));
392 CLuaStackChecker
lsc(_LuaState
);
394 _LuaState
->push(key
);
395 _LuaState
->push(value
);
396 _LuaState
->setTable(-3);
400 // *************************************************
401 void CLuaObject::setValue(const char *key
, uint32 value
)
405 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%u' at key %s on object '%s' of type %s (not a table).", value
, key
, getId().c_str(), getTypename()));
406 CLuaStackChecker
lsc(_LuaState
);
408 _LuaState
->push(key
);
409 _LuaState
->push(value
);
410 _LuaState
->setTable(-3);
414 // *************************************************
415 void CLuaObject::setValue(const char *key
, sint32 value
)
419 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%d' at key %s on object '%s' of type %s (not a table).", value
, key
, getId().c_str(), getTypename()));
420 CLuaStackChecker
lsc(_LuaState
);
422 _LuaState
->push(key
);
423 _LuaState
->push(value
);
424 _LuaState
->setTable(-3);
428 // *************************************************
429 void CLuaObject::setValue(const char *key
, sint64 value
)
433 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%" NL_I64
"d' at key %s on object '%s' of type %s (not a table).", value
, key
, getId().c_str(), getTypename()));
434 CLuaStackChecker
lsc(_LuaState
);
436 _LuaState
->push(key
);
437 _LuaState
->push(value
);
438 _LuaState
->setTable(-3);
442 // *************************************************
443 void CLuaObject::eraseValue(const char *key
)
447 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to erase a value with key '%s' on object '%s' of type %s (not a table).", key
, getId().c_str(), getTypename()));
448 CLuaStackChecker
lsc(_LuaState
);
450 _LuaState
->push(key
);
451 _LuaState
->pushNil();
452 _LuaState
->setTable(-3);
456 // *************************************************
457 bool CLuaObject::callNoThrow(int numArgs
, int numRet
)
462 nlwarning("Calling a non function object (id = %s, type = %s)", getId().c_str(), getTypename());
463 _LuaState
->pop(numArgs
);
467 static volatile bool dumpFunction
= false;
470 CLuaStackRestorer
lsr(_LuaState
, _LuaState
->getTop());
473 lua_getinfo (_LuaState
->getStatePointer(), ">lS", &ar
);
474 nlwarning((std::string(ar
.what
) + ", at line " + NLMISC::toString(ar
.linedefined
) + " in " + std::string(ar
.source
)).c_str());
477 _LuaState
->insert(-1 - numArgs
); // put the function before its arguments
478 int result
= _LuaState
->pcall(numArgs
, numRet
);
484 nlwarning(_LuaState
->toString(-1));
498 // *************************************************
499 bool CLuaObject::callMethodByNameNoThrow(const char *name
, int numArgs
, int numRet
)
502 int initialStackSize
= _LuaState
->getTop();
503 if (!isTable() && !isUserData())
505 nlwarning("Can't call method : object is not a table (id = %s)", getId().c_str());
506 _LuaState
->setTop(std::max(0, initialStackSize
- numArgs
));
509 CLuaObject method
= (*this)[name
];
510 push(); // the 'self' parameter
511 _LuaState
->insert(-1 - numArgs
); // put 'self' before the arguments
512 if (method
.callNoThrow(numArgs
+ 1, numRet
))
516 _LuaState
->setTop(std::max(0, initialStackSize
- numArgs
));
520 /////////////////////
521 // CLuaEnumeration //
522 /////////////////////
524 // *************************************************
525 CLuaEnumeration::CLuaEnumeration(CLuaObject
&table
)
527 nlassert(table
.isEnumerable());
528 CLuaState
*luaState
= table
.getLuaState();
529 CLuaStackChecker
lsc(luaState
);
530 // get pointer to the 'next' function
531 #if LUA_VERSION_NUM >= 502
532 luaState
->pushGlobalTable();
534 luaState
->pushValue(LUA_GLOBALSINDEX
);
536 _NextFunction
= CLuaObject(*luaState
)["next"];
542 if (_NextFunction
.callNoThrow(2, 2))
544 _Value
.pop(*luaState
);
546 _HasNext
= !_Key
.isNil();
547 _Value
.setId(CLuaObject::concatId(table
.getId(), _Key
.toString()));
553 // *************************************************
554 const CLuaObject
&CLuaEnumeration::nextKey() const
560 // *************************************************
561 CLuaObject
&CLuaEnumeration::nextValue()
567 // *************************************************
568 CLuaObject
CLuaObject::getMetaTable() const
571 CLuaStackChecker
lsc(_LuaState
);
573 if (_LuaState
->getMetaTable(-1))
575 _LuaState
->remove(-2);
576 return CLuaObject(*_LuaState
);
579 _LuaState
->pushNil();
580 return CLuaObject(*_LuaState
);
583 // *************************************************
584 bool CLuaObject::setMetaTable(CLuaObject
&metatable
)
587 nlassert(metatable
.isValid());
588 nlassert(this->getLuaState() == metatable
.getLuaState());
589 CLuaStackChecker
lsc(_LuaState
);
592 bool ok
= _LuaState
->setMetaTable(-2);
597 // *************************************************
598 std::string
CLuaObject::toStringRecurse(uint depth
/*=0*/, uint maxDepth
/*= 20*/, std::set
<const void *> *alreadySeen
/*= NULL */) const
600 if (maxDepth
!= 0 && depth
> maxDepth
) return "";
601 const uint INDENT_NUM_BLANK
= 2;
602 std::string
indentStr(depth
* INDENT_NUM_BLANK
, ' ');
604 if (isEnumerable()) // is enumeration possible on that object ?
609 if (alreadySeen
->count(toPointer())) // avoid cyclic graph (infinite recursion else)
611 result
+= indentStr
+"<cycle>";
614 alreadySeen
->insert(toPointer());
616 result
+= indentStr
+ "{\n";
617 CLuaObject
*table
= const_cast<CLuaObject
*>(this);
619 ENUM_LUA_TABLE(*table
, it
)
621 //nlwarning("entering table %s", it.nextKey().toString().c_str());
622 result
+= std::string((depth
+ 1) * INDENT_NUM_BLANK
, ' ') + it
.nextKey().toString() + " = ";
623 if (it
.nextValue().isEnumerable())
625 result
+= "\n" + it
.nextValue().toStringRecurse(depth
+ 1, maxDepth
, alreadySeen
);
629 result
+= it
.nextValue().toStringRecurse();
635 throw NLMISC::Exception("possible infinite loop, aborting enumeration");
638 result
+= indentStr
+ "}";
643 return (indentStr
+ "nil").c_str();
647 return (indentStr
+ "\"" + toString() + "\"").c_str();
649 else if (isFunction())
651 return (indentStr
+ "<function>").c_str();
655 return ((indentStr
+ toString()).c_str());
659 // *************************************************
660 void CLuaObject::dump(uint maxDepth
/*= 20*/, std::set
<const void *> *alreadySeen
/*= NULL */) const
664 std::string str
= toStringRecurse(0, maxDepth
, alreadySeen
);
665 std::vector
<std::string
> res
;
666 NLMISC::explode(str
, std::string("\n"), res
);
667 for(uint k
= 0; k
< res
.size(); ++k
)
669 NLMISC::InfoLog
->forceDisplayRaw((res
[k
] + "\n") .c_str());
672 catch(const std::exception
&e
)
674 //CLuaIHMRyzom::dumpCallStack();
679 // *************************************************
680 std::string
CLuaObject::concatId(const std::string
&left
,const std::string
&right
)
682 if (!right
.empty() && isdigit(right
[0]))
684 if (left
.empty()) return "[" + right
+ "]";
685 return left
+ "[" + right
+ "]";
687 if (left
.empty()) return right
;
688 return left
+ "." + right
;
692 // *************************************************
693 void CLuaEnumeration::next()
696 CLuaState
*luaState
= _Table
.getLuaState();
698 CLuaStackChecker
lsc(luaState
);
702 if (_NextFunction
.callNoThrow(2, 2))
704 _Value
.pop(*luaState
);
706 _HasNext
= !_Key
.isNil();
707 _Value
.setId(_Table
.getId() + "." + _Key
.toString());