Files
2025-09-18 17:55:52 -04:00

587 lines
13 KiB
C++

#include "stdafx.h"
#include "script/LuaArguments.h"
#include "util/ProtectedString.h"
DYNAMIC_FASTFLAGVARIABLE(LuaCrashOnIncorrectTables, false)
namespace RBX { namespace Lua {
bool LuaArguments::getString(int index, std::string& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
switch (lua_type(L, index))
{
case LUA_TSTRING:
{
size_t len;
const char* s = lua_tolstring(L, index, &len);
value.assign(s, len);
}
return true;
default:
return false;
}
}
bool LuaArguments::getVector3int16(int index, Vector3int16& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
return Bridge<RBX::Vector3int16>::getValue(L, index, value);
}
bool LuaArguments::getRegion3int16(int index, Region3int16& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
return Bridge<RBX::Region3int16>::getValue(L, index, value);
}
bool LuaArguments::getVector3(int index, Vector3& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
return Bridge<RBX::Vector3>::getValue(L, index, value);
}
bool LuaArguments::getRegion3(int index, Region3& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
return Bridge<RBX::Region3>::getValue(L, index, value);
}
bool LuaArguments::getRect(int index, Rect2D& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
return Bridge<RBX::Rect2D>::getValue(L, index, value);
}
bool LuaArguments::getPhysicalProperties(int index, PhysicalProperties& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
return Bridge<PhysicalProperties>::getValue(L, index, value);
}
bool LuaArguments::getObject(int index, shared_ptr<Reflection::DescribedBase>& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
switch (lua_type(L, index))
{
case LUA_TNIL:
value = shared_ptr<Instance>();
return true;
case LUA_TUSERDATA:
{
if (ObjectBridge::getPtr(L, index, value))
return true;
else
return false;
}
default:
return false;
}
}
bool LuaArguments::getDouble(int index, double& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
switch (lua_type(L, index))
{
case LUA_TNUMBER:
value = lua_tonumber(L, index);
return true;
default:
return false;
}
}
bool LuaArguments::getBool(int index, bool& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
switch (lua_type(L, index))
{
case LUA_TBOOLEAN:
value = lua_toboolean(L, index) ? true : false;
return true;
default:
// TODO: Convert NUMBER to bool?
return false;
}
}
bool LuaArguments::getEnum(int index, const Reflection::EnumDescriptor& desc, int& value) const
{
index += offset;
if (index > lua_gettop(L))
return false;
switch (lua_type(L, index))
{
case LUA_TUSERDATA:
EnumDescriptorItemPtr item;
if (EnumItem::getItem(L, index, item))
{
if (item->owner != desc)
return false;
value = item->value;
return true;
}
return false;
case LUA_TNUMBER:
value = lua_tonumber(L, index);
return desc.isValue( value );
default:
return false;
}
}
// Gets a value from the Lua stack. Returns false if no value is found
bool LuaArguments::get(lua_State *L, int luaIndex, Reflection::Variant& value, bool treatNilAsMissing)
{
TablesCollection localTables(NULL);
return getRec(L, luaIndex, value, treatNilAsMissing, &localTables);
}
bool LuaArguments::getRec(lua_State *L, int luaIndex, Reflection::Variant& value, bool treatNilAsMissing, TablesCollection* visitedTables)
{
if (luaIndex > lua_gettop(L))
return false;
switch (lua_type(L, luaIndex))
{
case LUA_TNUMBER: // ArgHelper has a special case for this
value = lua_tonumber(L, luaIndex);
return true;
case LUA_TBOOLEAN: // ArgHelper has a special case for this
value = lua_toboolean(L, luaIndex) ? true : false;
return true;
case LUA_TSTRING: // ArgHelper has a special case for this
{
size_t len;
const char* s = lua_tolstring(L, luaIndex, &len);
value = std::string(s, len);
}
return true;
case LUA_TTABLE:
{
// mark table as visited
const void* tablePtr = 0;
if (visitedTables)
{
tablePtr = lua_topointer(L, luaIndex);
if (tablePtr)
{
bool& visited = (*visitedTables)[tablePtr];
if (visited)
throw std::runtime_error("tables cannot be cyclic");
visited = true;
}
}
const int n = lua_objlen(L, luaIndex); /* get size of table */
if (n > 0)
{
// Create a new collection so that we can populate it
shared_ptr<Reflection::ValueArray> values(rbx::make_shared<Reflection::ValueArray>(n));
// Now populate the collection with values from the Lua *array* (recursive call to get)
for (int i=1; i<=n; i++)
{
Reflection::Variant& v = (*values)[i-1];
lua_rawgeti(L, luaIndex, i); /* push t[i] */
getRec(L, -1, v, false, visitedTables);
lua_pop(L, 1);
}
// Assign the values object to our ValueArray
value = shared_ptr<const RBX::Reflection::ValueArray>(values);
}
else
{
// Create a new collection so that we can populate it
shared_ptr<Reflection::ValueTable> values;
// Convert the stack index to a positive value:
int tableIndex = luaIndex>0 ? luaIndex : lua_gettop(L) + luaIndex + 1;
// Traverse the *general table*
// See http://www.luafaq.org/#T7.1
lua_pushnil(L); /* first key */
while (lua_next(L, tableIndex) != 0)
{
/* uses 'key' (at index -2) and 'value' (at index -1) */
std::string key;
switch (lua_type(L, -2))
{
case LUA_TSTRING:
key = lua_tostring(L, -2);
break;
default:
throw std::runtime_error("keys must be strings");
}
Reflection::Variant v;
getRec(L, -1, v, false, visitedTables);
if (!values)
values = rbx::make_shared<Reflection::ValueTable>();
(*values)[key] = v;
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(L, 1);
}
if (values)
value = shared_ptr<const RBX::Reflection::ValueTable>(values);
else
// Assume an empty Lua table is an empty ValueArray (they can auto-convert later)
value = rbx::make_shared<const Reflection::ValueArray>();
// reset visited status
if (visitedTables && tablePtr)
(*visitedTables)[tablePtr] = false;
}
}
return true;
case LUA_TNIL:
// See http://lua-users.org/wiki/TrailingNilParameters
if (treatNilAsMissing)
return false;
value = Reflection::Variant();
return true;
case LUA_TFUNCTION:
value = lua_tofunction(L, luaIndex);
return true;
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
default:
// Treat as void (nil)
value = Reflection::Variant();
return true;
case LUA_TUSERDATA:
{
// TODO: Support GenericFunctionBridge
if (Enums::getValue(L, luaIndex, value)) // ArgHelper has a special case for this
return true;
if (ObjectBridge::getPtr(L, luaIndex, value)) // ArgHelper has a special case for this
return true;
if (CoordinateFrameBridge::getValue(L, luaIndex, value))
return true;
if (Region3Bridge::getValue(L, luaIndex, value)) // ArgHelper has a special case for this
return true;
if (Region3int16Bridge::getValue(L, luaIndex, value)) // ArgHelper has a special case for this
return true;
if (Vector3int16Bridge::getValue(L, luaIndex, value)) // ArgHelper has a special case for this
return true;
if (Vector2int16Bridge::getValue(L, luaIndex, value)) // ArgHelper has a special case for this
return true;
if (Vector3Bridge::getValue(L, luaIndex, value)) // ArgHelper has a special case for this
return true;
if (Rect2DBridge::getValue(L, luaIndex, value))
return true;
if (Vector2Bridge::getValue(L, luaIndex, value))
return true;
if (RbxRayBridge::getValue(L, luaIndex, value))
return true;
if (Color3Bridge::getValue(L, luaIndex, value))
return true;
if (BrickColorBridge::getValue(L, luaIndex, value))
return true;
if (UDimBridge::getValue(L, luaIndex, value))
return true;
if (UDim2Bridge::getValue(L, luaIndex, value))
return true;
if (FacesBridge::getValue(L, luaIndex, value))
return true;
if (AxesBridge::getValue(L, luaIndex, value))
return true;
if (CellIDBridge::getValue(L, luaIndex, value))
return true;
if (PhysicalPropertiesBridge::getValue(L, luaIndex, value))
return true;
if (NumberSequenceBridge::getValue(L, luaIndex, value))
return true;
if (ColorSequenceBridge::getValue(L, luaIndex, value))
return true;
if (NumberRangeBridge::getValue(L, luaIndex, value))
return true;
if (NumberSequenceKeypointBridge::getValue(L, luaIndex, value))
return true;
if (ColorSequenceKeypointBridge::getValue(L, luaIndex, value))
return true;
// Treat as void (nil)
value = Reflection::Variant();
}
return true; // TODO: Shouldn't we return false?
}
}
class ArgumentPusher
{
lua_State * const L;
public:
ArgumentPusher(lua_State * const L)
:L(L)
{
}
int operator()(void)
{
lua_pushnil(L);
return 1;
}
int operator()(bool value)
{
lua_pushboolean(L, value);
return 1;
}
template<class T>
int operator()(T value, typename boost::enable_if<boost::is_arithmetic<T> >::type* dummy = 0)
{
lua_pushnumber(L, value);
return 1;
}
int operator()(const std::string& value)
{
lua_pushstring(L, value);
return 1;
}
int operator()(const RBX::ProtectedString& value)
{
lua_pushstring(L, value.getSource());
return 1;
}
int operator()(const shared_ptr<Instance>& value)
{
ObjectBridge::push(L, value);
return 1;
}
int operator()(const Reflection::EnumDescriptor::Item& value)
{
EnumItem::push(L, &value);
return 1;
}
int operator()(shared_ptr<const Reflection::Tuple> tuple)
{
// "unpack" the values of the array to the stack
if (!tuple)
return 0; // null is equivalent to 0 items (optimization)
int count = 0;
Reflection::ValueArray::const_iterator iter = tuple->values.begin();
Reflection::ValueArray::const_iterator end = tuple->values.end();
while (iter!=end)
{
count += LuaArguments::push(*iter, L);
++iter;
}
return count;
}
int operator()(const Lua::WeakFunctionRef& value)
{
lua_pushfunction(L, value);
return 1;
}
int operator()(shared_ptr<const Reflection::ValueTable> valueMap)
{
if (!valueMap)
{
// return an empty table (never return nil)
lua_createtable(L, 0, 0);
return 1;
}
Reflection::ValueTable::const_iterator _First = valueMap->begin();
Reflection::ValueTable::const_iterator _Last = valueMap->end();
lua_createtable(L, 0, valueMap->size());
while (_First!=_Last)
{
lua_pushstring(L, _First->first);
int top = lua_gettop(L);
LuaArguments::push(_First->second, L);
if (DFFlag::LuaCrashOnIncorrectTables)
{
if (lua_gettop(L) != top + 1)
{
static std::string k = _First->first;
static Reflection::Variant v = _First->second;
RBXCRASH();
}
}
lua_settable(L, -3);
++_First;
}
return 1;
}
int operator()(shared_ptr<const Reflection::ValueArray> values)
{
if (!values)
{
// return an empty table (never return nil)
lua_createtable(L, 0, 0);
return 1;
}
return LuaArguments::pushArray(values->begin(), values->end(), L);
}
int operator()(shared_ptr<const Reflection::ValueMap> valueMap)
{
if (!valueMap)
{
// return an empty table (never return nil)
lua_createtable(L, 0, 0);
return 1;
}
Reflection::ValueMap::const_iterator _First = valueMap->begin();
Reflection::ValueMap::const_iterator _Last = valueMap->end();
lua_createtable(L, 0, valueMap->size());
while (_First!=_Last)
{
RBXASSERT(!_First->first.empty());
lua_pushstring(L, _First->first);
LuaArguments::push(_First->second, L);
lua_settable(L, -3);
++_First;
}
return 1;
}
int operator()(shared_ptr<const Instances> values)
{
if (values)
return LuaArguments::pushArray(values->begin(), values->end(), L);
else
{
// return an empty table (never return nil)
lua_createtable(L, 0, 0);
return 1;
}
}
int operator()(shared_ptr<Lua::GenericFunction> value)
{
lua_pushfunction(L, value);
return 1;
}
int operator()(shared_ptr<Lua::GenericAsyncFunction> value)
{
lua_pushfunction(L, value);
return 1;
}
int operator()(const ContentId& value)
{
lua_pushstring(L, value.toString());
return 1;
}
int operator()(const Reflection::PropertyDescriptor& value)
{
lua_pushstring(L,value.name.toString());
return 1;
}
template<class T>
int operator()(const T& value, typename boost::disable_if<boost::is_arithmetic<T> >::type* dummy = 0)
{
Bridge<T>::pushNewObject(L, value);
return 1;
}
};
int LuaArguments::push(const Reflection::Variant& value, lua_State * const L)
{
ArgumentPusher pusher(L);
return withVariantValue<int>(value, pusher);
}
int LuaArguments::pushReturnValue(const Reflection::Variant& value, lua_State * const L)
{
return value.isVoid() ? 0 : push(value, L);
}
shared_ptr<Reflection::Tuple> LuaArguments::convertToReturnValues(const Reflection::Variant& value)
{
shared_ptr<Reflection::Tuple> result;
if (value.isVoid())
{
result.reset(new Reflection::Tuple(0));
}
else
{
result.reset(new Reflection::Tuple(1));
result->values[0] = value;
}
return result;
}
}}