This commit is contained in:
watrabi
2025-10-28 14:05:46 -04:00
parent 977f1ff4b8
commit c93494f795
452 changed files with 47860 additions and 152 deletions
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "Script/Script.h"
#include <boost/optional.hpp>
namespace RBX
{
extern const char* const sCoreScript;
class CoreScript
: public DescribedNonCreatable<CoreScript, BaseScript, sCoreScript, RBX::Reflection::ClassDescriptor::INTERNAL_LOCAL>
{
private:
typedef DescribedNonCreatable<CoreScript, BaseScript, sCoreScript, RBX::Reflection::ClassDescriptor::INTERNAL_LOCAL> Super;
Code code;
public:
CoreScript();
static boost::optional<ProtectedString> fetchSource(const std::string& name);
virtual Code requestCode(ScriptInformationProvider* scriptInfoProvider=NULL);
virtual void extraErrorReporting(lua_State *thread);
protected:
// Instance
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
};
}
+400
View File
@@ -0,0 +1,400 @@
#pragma once
#include "V8Tree/Instance.h"
#include "V8Tree/Service.h"
#include "script/ThreadRef.h"
#include "script/ScriptContext.h"
struct lua_State;
struct lua_Debug;
struct Table;
namespace RBX
{
class Script;
class ModuleScript;
class DataModel;
namespace Scripting
{
class ScriptDebugger;
enum BreakOnErrorMode
{
BreakOnErrorMode_Never = 0,
BreakOnErrorMode_AllExceptions,
BreakOnErrorMode_UnhandledExceptions
};
enum ExecutionMode
{
ExecutionMode_Continue = 0,
ExecutionMode_Break
};
struct ISpecialBreakpoint
{
virtual ~ISpecialBreakpoint() {}
virtual bool hitTest(lua_State* L, lua_Debug *ar) = 0;
lua_State* baseThread;
};
extern const char* const sDebuggerManager;
// Contains all data related to Lua debugging of Scripts
class DebuggerManager
: public DescribedNonCreatable<DebuggerManager, Instance, sDebuggerManager, Reflection::ClassDescriptor::INTERNAL_LOCAL, Security::LocalUser>
{
typedef DescribedNonCreatable<DebuggerManager, Instance, sDebuggerManager, Reflection::ClassDescriptor::INTERNAL_LOCAL, Security::LocalUser> Super;
public:
typedef boost::unordered_map<const Instance*, ScriptDebugger*> Debuggers;
private:
bool enabled;
Debuggers debuggers;
rbx::signals::connection errorSignalConnection;
rbx::signals::connection descendantAddedSignalConnection;
typedef boost::unordered_map<const Instance*, boost::shared_ptr<ScriptDebugger> > UnaddedDebuggers;
UnaddedDebuggers unaddedDebuggers;
typedef boost::unordered_map<const lua_State*, ScriptDebugger*> DebuggersLookup;
DebuggersLookup debuggersLookup;
RBX::DataModel *dataModel;
BreakOnErrorMode breakOnErrorMode;
boost::scoped_ptr<ISpecialBreakpoint> specialBreakpoint;
ExecutionMode executionMode;
std::list<Lua::WeakThreadRef> pausedThreads, resumingPausedThreads, errorThreads;
bool resuming;
bool scriptAutoResume;
public:
DebuggerManager();
~DebuggerManager();
static DebuggerManager& singleton();
void setDataModel(RBX::DataModel *pDataModel);
RBX::DataModel* getDataModel();
void enableDebugging();
bool getEnabled() const { return enabled; }
BreakOnErrorMode getBreakOnErrorMode() const { return breakOnErrorMode; }
void setBreakOnErrorMode(BreakOnErrorMode mode);
static Reflection::Variant readWatchValue(std::string expression, int stackFrame, lua_State* L);
const Debuggers& getDebuggers()
{
return debuggers;
}
shared_ptr<const Instances> getDebuggers_Reflection();
ScriptDebugger* findDebugger(lua_State* L);
ScriptDebugger* findDebugger(Instance* script);
shared_ptr<ScriptDebugger> addDebugger(Instance* script);
shared_ptr<Instance> addDebugger_Reflection(shared_ptr<Instance> script);
void addDebugger(shared_ptr<ScriptDebugger> debugger);
void populateForLookup(lua_State* L, ScriptDebugger* debugger);
void pause();
void resume();
void stepOver();
void stepInto();
void stepOut();
void reset();
void setScriptAutoResume(bool state) { scriptAutoResume = state; }
static void hook(lua_State* L, lua_Debug *ar);
rbx::signal<void(shared_ptr<Instance>)> debuggerAdded;
rbx::signal<void(shared_ptr<Instance>)> debuggerRemoved;
protected:
/*override*/ bool askForbidChild(const Instance* instance) const;
/*override*/ void verifyAddChild(const Instance* newChild) const;
/*override*/ void onChildAdded(Instance* child);
/*override*/ void onChildRemoved(Instance* child);
/*override*/ void onChildChanged(Instance* instance, const PropertyChanged& event);
void addScriptDebugger(Instance* instance);
void onErrorSignal(lua_State* L);
void onHook(lua_State* L, lua_Debug *ar);
void addUnaddedDebuggerForAddedDescendant(shared_ptr<RBX::Instance> instance);
};
class DebuggerBreakpoint;
class DebuggerWatch;
extern const char* const sScriptDebugger;
// Debugs an RBX::Script
class ScriptDebugger
: public DescribedCreatable<ScriptDebugger, Instance, sScriptDebugger, Reflection::ClassDescriptor::PERSISTENT_HIDDEN, Security::LocalUser>
{
public:
typedef boost::unordered_map<int, DebuggerBreakpoint*> Breakpoints;
typedef std::vector<DebuggerWatch*> Watches;
struct PausedThreadData;
typedef boost::unordered_map<long, PausedThreadData> PausedThreads;
private:
typedef DescribedCreatable<ScriptDebugger, Instance, sScriptDebugger, Reflection::ClassDescriptor::PERSISTENT_HIDDEN, Security::LocalUser> Super;
Breakpoints breakpoints;
Watches watches;
boost::scoped_ptr<ISpecialBreakpoint> specialBreakpoint;
shared_ptr<Instance> script;
rbx::signals::scoped_connection scriptStartedConnection;
rbx::signals::scoped_connection scriptStoppedConnection;
rbx::signals::scoped_connection scriptParentChangedConnection;
rbx::signals::scoped_connection scriptClonedConnection;
Lua::WeakThreadRef rootThread; // The root thread of script. Set when the Script starts and reset when it stops
typedef boost::function<void(lua_State* L, lua_Debug *ar)> HookFunction;
HookFunction hookFunction; // used to overload the hook function
Lua::WeakThreadRef pausedThread; // the thread that a breakpoint hit
Lua::WeakThreadRef errorThread; // the thread that has error
lua_Debug *breakpointHookData; // set for a short period of time during the hook when we encounter a breakpoint
void* globalRawScriptPtr;
Table* prevFuncTable;
void* prevRawScriptPtr;
int currentLine; // the current line when a breakpoint is hit
bool ignoreDebuggerBreak; // whether to ignore breakpoint at the current line
long currentThreadID;
PausedThreads pausedThreads;
bool rootThreadResumed;
public:
ScriptDebugger()
:currentLine(0)
,breakpointHookData(NULL)
,globalRawScriptPtr(NULL)
,prevFuncTable(NULL)
,prevRawScriptPtr(NULL)
,ignoreDebuggerBreak(false)
,currentThreadID(0)
,rootThreadResumed(false)
{}
ScriptDebugger(Instance& script);
~ScriptDebugger();
Instance* getScript() const { return script.get(); }
void setScript(Script* value);
void setScript(ModuleScript* value);
std::string getScriptPath() const;
void setScriptPath(std::string scriptPath);
void setIgnoreDebuggerBreak(bool state) { ignoreDebuggerBreak = state; }
DebuggerBreakpoint* findBreakpoint(int line);
shared_ptr<DebuggerBreakpoint> setBreakpoint(int line);
shared_ptr<Instance> setBreakpoint_Reflection(int line);
const Breakpoints& getBreakpoints()
{
return breakpoints;
}
shared_ptr<const Instances> getBreakpoints_Reflection();
shared_ptr<DebuggerWatch> addWatch(std::string expression);
shared_ptr<Instance> addWatch_Reflection(std::string expression);
const Watches& getWatches()
{
return watches;
}
shared_ptr<const Instances> getWatches_Reflection();
Reflection::Variant getWatchValue(DebuggerWatch* watch, int stackFrame = 0);
Reflection::Variant getWatchValue_Reflection(shared_ptr<Instance> watch);
Reflection::Variant getKeyValue(std::string key, int stackFrame);
bool isDebugging() const
{
return !rootThread.empty();
}
bool isPaused() const;
bool hasError() const
{
return !errorThread.empty();
}
int getCurrentLine() const
{
return currentLine;
}
void pause();
void resume();
void resumeTo(int line);
void stepOver();
void stepInto();
void stepOut();
struct FunctionInfo
{
boost::shared_ptr<RBX::Instance> script;
int frame;
std::string name;
std::string what;
std::string namewhat;
std::string short_src;
int currentline;
int linedefined;
int lastlinedefined;
};
typedef std::vector<FunctionInfo> Stack;
Stack getStack();
shared_ptr<const Reflection::ValueArray> getStack_Reflection();
shared_ptr<const Reflection::ValueMap> getLocals(int stackIndex);
shared_ptr<const Reflection::ValueMap> getUpvalues(int stackIndex);
shared_ptr<const Reflection::ValueMap> getGlobals();
void setLocal(std::string name, Reflection::Variant value, int stackFrame = 0);
void setUpvalue(std::string name, Reflection::Variant value, int stackFrame = 0);
void setGlobal(std::string name, Reflection::Variant value);
void handleError(lua_State* L);
void updateHook();
ScriptContext::Result resumeThread(lua_State* L, bool evalLineHookForCurrentLine = false);
bool handleHook(lua_State* L, lua_Debug *ar);
bool onLineHook(lua_State* L, lua_Debug *ar);
void debuggerBreak(lua_State* L, lua_Debug *ar);
struct PausedThreadData
{
int pausedLine;
Lua::WeakThreadRef thread;
bool hasError;
Stack callStack;
std::string errorMessage;
PausedThreadData()
:hasError(false)
,pausedLine(0)
{
}
};
const PausedThreads& getPausedThreads() { return pausedThreads; }
bool isPausedThread(long threadID);
bool isErrorThread(long threadID);
bool isRootThread(long threadID);
bool isRootThreadResumed() { return rootThreadResumed; }
void setCurrentThread(long threadID);
long getCurrentThread() { return currentThreadID; }
rbx::signal<void(int)> encounteredBreak;
rbx::signal<void()> resuming;
rbx::signal<void(shared_ptr<Instance>)> breakpointAdded;
rbx::signal<void(shared_ptr<Instance>)> breakpointRemoved;
rbx::signal<void(shared_ptr<Instance>)> watchAdded;
rbx::signal<void(shared_ptr<Instance>)> watchRemoved;
rbx::signal<void(int, std::string, Stack)> scriptErrorDetected;
protected:
/*override*/ bool askForbidChild(const Instance* instance) const;
/*override*/ void verifySetParent(const Instance* newParent) const;
/*override*/ void verifyAddChild(const Instance* newChild) const;
/*override*/ void onChildAdded(Instance* child);
/*override*/ void onChildRemoved(Instance* child);
private:
void onScriptStarting(lua_State* L);
void onScriptStopped();
void onScriptParentChanged(shared_ptr<RBX::Instance> newParent);
void onScriptCloned(boost::shared_ptr<Instance> clonedScript);
bool shouldBreak(DebuggerBreakpoint* bp, lua_State* L);
bool hasDifferentScriptInstances(lua_State* L);
void handleError(std::string errorMessage, const Stack& stack = Stack());
void setScript(Instance* value);
boost::shared_ptr<ScriptDebugger> createClone(boost::shared_ptr<Instance> clonedScript);
template<class R>
void withPausedThreadHook(lua_State* L, lua_Debug *ar, boost::function<R(lua_State* L, lua_Debug *ar)> f, R& r, shared_ptr<std::string>& error);
// TODO: template specialization for R=void
template<class R>
R withPausedThread(boost::function<R(lua_State* L, lua_Debug *ar)> f);
static shared_ptr<Reflection::ValueMap> readLocals(int stackIndex, lua_State* L);
static shared_ptr<Reflection::ValueMap> readUpvalues(int stackIndex, lua_State* L);
static shared_ptr<Reflection::ValueMap> readGlobals(lua_State* L);
static Stack readStack(lua_State* L);
static RBX::Instance* getScriptForLuaState(lua_State* L);
static void updateRootThread(ScriptDebugger* scriptDebugger, lua_State *L);
static void setLuaHook(ScriptDebugger* scriptDebugger, int hookMask, lua_State *L);
};
extern const char* const sDebuggerBreakpoint;
class DebuggerBreakpoint
: public DescribedCreatable<DebuggerBreakpoint, Instance, sDebuggerBreakpoint, Reflection::ClassDescriptor::PERSISTENT_HIDDEN, Security::LocalUser>
{
bool enabled;
int line;
std::string condition;
public:
DebuggerBreakpoint();
DebuggerBreakpoint(int line);
~DebuggerBreakpoint();
int getLine() const { return line; }
bool isEnabled() const { return enabled; }
const std::string& getCondition() const { return condition; }
static Reflection::BoundProp<bool> prop_Enabled;
static Reflection::BoundProp<std::string> prop_Condition;
protected:
/*override*/ void verifySetParent(const Instance* newParent) const;
/*override*/ bool askForbidChild(const Instance* instance) const { return true; }
/*override*/ void verifyAddChild(const Instance* newChild) const
{
throw std::runtime_error("DebuggerBreakpoint can have no children");
}
private:
void setLine(int line);
static Reflection::BoundProp<int> prop_Line_Data;
};
extern const char* const sDebuggerWatch;
class DebuggerWatch
: public DescribedCreatable<DebuggerWatch, Instance, sDebuggerWatch, Reflection::ClassDescriptor::PERSISTENT, Security::LocalUser>
{
std::string expression;
public:
DebuggerWatch() {}
DebuggerWatch(std::string expression);
const std::string& getCondition() const { return expression; }
void checkExpressionSyntax();
const std::string& getExpression() const { return expression; }
static Reflection::BoundProp<std::string> prop_Expression;
protected:
/*override*/ void verifySetParent(const Instance* newParent) const;
/*override*/ bool askForbidChild(const Instance* instance) const { return true; }
/*override*/ void verifyAddChild(const Instance* newChild) const
{
throw std::runtime_error("DebuggerWatch can have no children");
}
};
}
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "boost/function.hpp"
#include "reflection/Type.h"
struct lua_State;
namespace RBX
{
class BaseScript;
namespace Scripts
{
typedef boost::function<void(shared_ptr<const Reflection::Tuple> results)> SuccessHandler;
typedef boost::function<void(const char* message, const char* callStack, shared_ptr<BaseScript> source, int line)> ErrorHandler;
struct Continuations
{
SuccessHandler successHandler;
ErrorHandler errorHandler;
bool empty() const
{
return successHandler.empty() && errorHandler.empty();
}
};
}
namespace Lua
{
class Continuations
{
public:
Continuations(const Scripts::Continuations& eh);
Continuations() {}
boost::function<void(lua_State*)> success; // called when the thread exits via ScriptContext::resume
boost::function<void(lua_State*)> error; // called when the thread errors via ScriptContext::resume
private:
static void onSuccessHandler(lua_State* thread, Scripts::SuccessHandler handler);
static void onErrorHandler(lua_State* thread, Scripts::ErrorHandler handler);
};
}
}
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include "util/RunStateOwner.h"
namespace RBX
{
class BaseScript;
class ModuleScript;
// Interface for Instances that turn in-game Scripts on and off
// Implementations can add/remove the script from ScriptContext
class RBXInterface IScriptFilter
{
friend class BaseScript;
protected:
// If script should run - pass back the IScriptOwner who should run it, otherwise NULL
virtual bool scriptShouldRun(BaseScript* script) = 0;
};
extern const char *const sRuntimeScriptService;
class RuntimeScriptService
: public DescribedNonCreatable<RuntimeScriptService, Instance, sRuntimeScriptService, Reflection::ClassDescriptor::INTERNAL_LOCAL>
, public Service
{
private:
typedef DescribedNonCreatable<RuntimeScriptService, Instance, sRuntimeScriptService, Reflection::ClassDescriptor::INTERNAL_LOCAL> Super;
public:
RuntimeScriptService():isRunning(false)
{
}
void runScript(BaseScript* script);
void releaseScript(BaseScript* script);
protected:
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
private:
rbx::signals::scoped_connection runTransitionConnection;
std::set<weak_ptr<BaseScript> > pendingScripts; // holds Scripts that are waiting for "Run"
std::set<weak_ptr<BaseScript> > runningScripts;
bool isRunning;
void onRunTransition(RunTransition event)
{
onRunState(event.newState);
}
void onRunState(RunState state);
};
}
+249
View File
@@ -0,0 +1,249 @@
#pragma once
#include "reflection/Function.h"
#include "script/LuaAtomicClasses.h"
#include "script/LuaEnum.h"
#include "script/ThreadRef.h"
#include "script/LuaInstanceBridge.h"
#include "rbx/make_shared.h"
#include "rbx/DenseHash.h"
#include "util/ProtectedString.h"
#include "util/PhysicalProperties.h"
namespace RBX {
// Utility function that expands a variant to a strongly-typed value
template<typename R, typename F>
R withVariantValue(const Reflection::Variant& value, F f)
{
if (value.isType<void>())
return f();
if (value.isType<bool>())
return f(value.cast<bool>());
if (value.isType<int>())
return f(value.cast<int>());
if (value.isType<long>())
return f(value.cast<long>());
if (value.isType<float>())
return f(value.cast<float>());
if (value.isType<double>())
return f(value.cast<double>());
if (value.isType<std::string>())
return f(value.cast<std::string>());
if (value.isType<RBX::ProtectedString>())
return f(value.cast<RBX::ProtectedString>());
if (value.isType< shared_ptr<Instance> >())
return f(value.cast<shared_ptr<Instance> >());
if (const Reflection::EnumDescriptor* desc = Reflection::EnumDescriptor::lookupDescriptor(value.type()))
{
const Reflection::EnumDescriptor::Item* item = desc->lookup(value);
if (item == NULL)
throw RBX::runtime_error("Invalid value for enum %s", desc->name.c_str());
return f(*item);
}
if (value.isType<Lua::WeakFunctionRef>())
return f(value.cast<Lua::WeakFunctionRef>());
if (value.isType<shared_ptr<const Reflection::ValueArray> >())
return f(value.cast<shared_ptr<const Reflection::ValueArray> >());
if (value.isType<shared_ptr<const Reflection::ValueMap> >())
return f(value.cast<shared_ptr<const Reflection::ValueMap> >());
if (value.isType<shared_ptr<const Reflection::ValueTable> >())
return f(value.cast<shared_ptr<const Reflection::ValueTable> >());
if (value.isType<shared_ptr<const Instances> >())
return f(value.cast<shared_ptr<const Instances> >());
if (value.isType<shared_ptr<const Reflection::Tuple> >())
return f(value.cast<shared_ptr<const Reflection::Tuple> >());
if (value.isType< shared_ptr<Lua::GenericFunction> >())
return f(value.cast< shared_ptr<Lua::GenericFunction> >());
if (value.isType< shared_ptr<Lua::GenericAsyncFunction> >())
return f(value.cast< shared_ptr<Lua::GenericAsyncFunction> >());
if (value.isType<G3D::Vector3int16>())
return f(value.cast<G3D::Vector3int16>());
if (value.isType<G3D::Vector2int16>())
return f(value.cast<G3D::Vector2int16>());
if (value.isType<G3D::Vector3>())
return f(value.cast<G3D::Vector3>());
if (value.isType<RBX::Vector2>())
return f(value.cast<G3D::Vector2>());
if (value.isType<G3D::Rect2D>())
return f(value.cast<G3D::Rect2D>());
if (value.isType<PhysicalProperties>())
return f(value.cast<PhysicalProperties>());
if (value.isType<RBX::RbxRay>())
return f(value.cast<RBX::RbxRay>());
if (value.isType<G3D::CoordinateFrame>())
return f(value.cast<G3D::CoordinateFrame>());
if (value.isType<G3D::Color3>())
return f(value.cast<G3D::Color3>());
if (value.isType<BrickColor>())
return f(value.cast<BrickColor>());
if (value.isType<RBX::Region3>())
return f(value.cast<RBX::Region3>());
if( value.isType<RBX::Region3int16>())
return f(value.cast<RBX::Region3int16>());
if (value.isType<UDim>())
return f(value.cast<UDim>());
if (value.isType<UDim2>())
return f(value.cast<UDim2>());
if (value.isType<Faces>())
return f(value.cast<Faces>());
if (value.isType<Axes>())
return f(value.cast<Axes>());
if (value.isType<CellID>())
return f(value.cast<CellID>());
if (value.isType<ContentId>())
return f(value.cast<ContentId>());
if (value.isType<const Reflection::PropertyDescriptor*>())
return f(*value.cast<const Reflection::PropertyDescriptor*>());
if (value.isType<rbx::signals::connection>())
return f(value.cast<rbx::signals::connection>());
if (value.isType<NumberSequence>())
return f(value.cast<NumberSequence>());
if (value.isType<ColorSequence>())
return f(value.cast<ColorSequence>());
if (value.isType<NumberRange>())
return f(value.cast<NumberRange>());
if (value.isType<NumberSequenceKeypoint>())
return f(value.cast<NumberSequenceKeypoint>());
if (value.isType<ColorSequenceKeypoint>())
return f(value.cast<ColorSequenceKeypoint>());
RBXASSERT(0);
return f();
}
namespace Lua {
class LuaArguments : public Reflection::FunctionDescriptor::Arguments
{
typedef DenseHashMap<const void*, bool> TablesCollection;
static bool getRec(lua_State *L, int luaIndex, Reflection::Variant& value, bool treatNilAsMissing, TablesCollection* visitedTables = NULL);
const int offset;
lua_State * const L;
public:
LuaArguments(lua_State *L, int offset):L(L),offset(offset) {}
virtual size_t size() const {
return lua_gettop(L) - 1;
}
// Gets all arguments from the stack and puts them into the ValueArray
static shared_ptr<Reflection::Tuple> getValues(lua_State* L)
{
int argCount = lua_gettop(L);
shared_ptr<Reflection::Tuple> args(rbx::make_shared<Reflection::Tuple>(argCount));
for (int i = 0; i<argCount; ++i)
{
Reflection::Variant& v = args->values.at(i);
bool success = RBX::Lua::LuaArguments::get(L, i+1, v, false);
RBXASSERT(success);
}
return args;
}
static int pushTuple(const Reflection::Tuple& arguments, lua_State* L)
{
return pushValues(arguments.values, L);
}
// pushes all the values from the ValueArray onto the stack
static int pushValues(const Reflection::ValueArray& arguments, lua_State* L)
{
int argCount = 0;
Reflection::ValueArray::const_iterator end = arguments.end();
for (Reflection::ValueArray::const_iterator iter = arguments.begin(); iter != end; ++iter)
{
argCount += push(*iter, L);
}
return argCount;
}
//////////////////////////////////////
// Implemenent virtual functions
//
// Place the value for the requested parameter in "value".
//
// index: 1-based index into the argument list
// value: the value to set. If index >= size(), then value is unchanged
/*implement*/ bool getVariant(int index, Reflection::Variant& value) const {
const int luaIndex = index + offset;
RBXASSERT(luaIndex>0);
return get(L, luaIndex, value, true);
}
/*implement*/ bool getLong(int index, long& value) const
{
// All numbers in Lua are double, so just call the double version of get
double v;
if (getDouble(index, v))
{
value = G3D::iRound(v);
return true;
}
return false;
}
/*implement*/ bool getDouble(int index, double& value) const;
/*implement*/ bool getObject(int index, shared_ptr<Reflection::DescribedBase>& value) const;
/*implement*/ bool getBool(int index, bool& value) const;
/*implement*/ bool getString(int index, std::string& value) const;
/*implement*/ bool getVector3(int index, Vector3& value) const;
/*implement*/ bool getRegion3(int index, Region3& value) const;
/*implement*/ bool getVector3int16(int index, Vector3int16& value) const;
/*implement*/ bool getRegion3int16(int index, Region3int16& value) const;
/*implement*/ bool getRect(int index, Rect2D& value) const;
/*implement*/ bool getPhysicalProperties(int index, PhysicalProperties& value) const;
/*implement*/ bool getEnum(int index, const Reflection::EnumDescriptor& desc, int& value) const;
//
//////////////////////////////////////
// Gets a value from the Lua stack. Returns false if no value is found
static bool get(lua_State *L, int luaIndex, Reflection::Variant& value, bool treatNilAsMissing);
template<class _InIt>
static int pushArray(_InIt _First, _InIt _Last, lua_State * const L) {
lua_createtable(L, _Last - _First, 0);
unsigned int i = 0;
while (_First!=_Last)
{
int count = push(*_First, L);
RBXASSERT(count == 1); // If not 1, then what do we do?
lua_rawseti(L, -2, ++i);
++_First;
}
return 1;
}
static int push(const Reflection::Variant& value, lua_State * const L);
static int pushReturnValue(const Reflection::Variant& value, lua_State * const L);
static shared_ptr<Reflection::Tuple> convertToReturnValues(const Reflection::Variant& value);
};
}}
+402
View File
@@ -0,0 +1,402 @@
#pragma once
#include "Lua/LuaBridge.h"
#include "util/G3DCore.h"
#include "g3d/Color3.h"
#include "g3d/CoordinateFrame.h"
#include "g3d/Vector3.h"
#include "g3d/Vector3int16.h"
#include "RbxG3D/RbxRay.h"
#include "Util/BrickColor.h"
#include "Util/UDim.h"
#include "Util/Region3.h"
#include "Util/Region3int16.h"
#include "Util/Faces.h"
#include "Util/Axes.h"
#include "Util/CellID.h"
#include "util/PhysicalProperties.h"
#include "v8datamodel/NumberSequence.h"
#include "v8datamodel/ColorSequence.h"
#include "v8datamodel/NumberRange.h"
namespace RBX { namespace Lua {
class CoordinateFrameBridge : public Bridge<G3D::CoordinateFrame>
{
friend class Bridge< G3D::CoordinateFrame >;
public:
static void registerClassLibrary (lua_State *L);
static void pushCoordinateFrame(lua_State *L, const G3D::CoordinateFrame& CF)
{
pushNewObject(L, CF);
}
private:
static int newCoordinateFrame(lua_State *L);
static int fromEulerAnglesXYZ(lua_State *L);
static int fromAxisAngle(lua_State *L);
static int on_add(lua_State *L);
static int on_sub(lua_State *L);
static int on_mul(lua_State *L);
static int on_inverse(lua_State *L);
static int on_lerp(lua_State *L);
// Implementation of G3D::CoordinateFrame help functions
static int on_toWorldSpace(lua_State *L);
static int on_toObjectSpace(lua_State *L);
static int on_pointToWorldSpace(lua_State *L);
static int on_pointToObjectSpace(lua_State *L);
static int on_vectorToWorldSpace(lua_State *L);
static int on_vectorToObjectSpace(lua_State *L);
static int on_toEulerAnglesXYZ(lua_State *L);
static int on_components(lua_State *L);
static const luaL_reg classLibrary[];
};
class PhysicalPropertiesBridge : public Bridge<PhysicalProperties>
{
friend class Bridge<PhysicalProperties>;
public:
static void registerClassLibrary (lua_State *L);
static void pushPhysicalProperties(lua_State *L, const PhysicalProperties& v)
{
if (v.getCustomEnabled() == true)
{
pushNewObject(L, v);
}
else
{
lua_pushnil(L);
}
}
private:
static int newPhysicalProperties(lua_State *L);
static const luaL_reg classLibrary[];
};
class Rect2DBridge : public Bridge<G3D::Rect2D>
{
friend class Bridge< G3D::Rect2D >;
public:
static void registerClassLibrary (lua_State *L);
static void pushRect2D(lua_State *L, const G3D::Rect2D& v)
{
pushNewObject(L, v);
}
private:
static int newRect2D(lua_State *L);
static const luaL_reg classLibrary[];
};
class Region3Bridge : public Bridge<RBX::Region3>
{
friend class Bridge< RBX::Region3 >;
public:
static void registerClassLibrary (lua_State *L) ;
static void pushRegion3(lua_State *L, const RBX::Region3& v)
{
pushNewObject(L, v);
}
private:
static int newRegion3(lua_State *L);
static int expandToGrid(lua_State *L);
static const luaL_reg classLibrary[];
};
class Region3int16Bridge : public Bridge<RBX::Region3int16>
{
friend class Bridge< RBX::Region3int16 >;
public:
static void registerClassLibrary (lua_State *L);
static void pushRegion3int16(lua_State *L, const RBX::Region3int16& v)
{
pushNewObject(L, v);
}
private:
static int newRegion3int16(lua_State *L);
static const luaL_reg classLibrary[];
};
class Vector3Bridge : public Bridge<G3D::Vector3>
{
friend class Bridge< G3D::Vector3 >;
public:
static void registerClassLibrary (lua_State *L);
static void pushVector3(lua_State *L, const G3D::Vector3& v)
{
pushNewObject(L, v);
}
private:
static int newVector3(lua_State *L);
static int newVector3FromNormalId(lua_State *L);
static int newVector3FromAxis(lua_State *L);
static int on_add(lua_State *L);
static int on_sub(lua_State *L);
static int on_mul(lua_State *L);
static int on_div(lua_State *L);
static int on_unm(lua_State *L);
static const luaL_reg classLibrary[];
};
class Vector3int16Bridge : public Bridge<G3D::Vector3int16>
{
friend class Bridge< G3D::Vector3int16 >;
public:
static void registerClassLibrary (lua_State *L);
static void pushVector3int16(lua_State *L, const G3D::Vector3int16& v)
{
pushNewObject(L, v);
}
private:
static int newVector3int16(lua_State *L);
static int on_add(lua_State *L);
static int on_sub(lua_State *L);
static int on_mul(lua_State *L);
static int on_div(lua_State *L);
static int on_unm(lua_State *L);
static const luaL_reg classLibrary[];
};
class RbxRayBridge : public Bridge<RBX::RbxRay>
{
friend class Bridge< RBX::RbxRay >;
public:
static void registerClassLibrary (lua_State *L);
static void pushRay(lua_State *L, const RBX::RbxRay& v)
{
pushNewObject(L, v);
}
private:
static int newRbxRay(lua_State *L);
//static int on_add(lua_State *L);
//static int on_sub(lua_State *L);
//static int on_mul(lua_State *L);
//static int on_div(lua_State *L);
//static int on_unm(lua_State *L);
static const luaL_reg classLibrary[];
};
class Vector2Bridge : public Bridge<RBX::Vector2>
{
friend class Bridge< RBX::Vector2 >;
public:
static void registerClassLibrary (lua_State *L);
static void pushVector2(lua_State *L, const RBX::Vector2& v)
{
pushNewObject(L, v);
}
private:
static int newVector2(lua_State *L);
static int on_add(lua_State *L);
static int on_sub(lua_State *L);
static int on_mul(lua_State *L);
static int on_div(lua_State *L);
static int on_unm(lua_State *L);
static const luaL_reg classLibrary[];
};
class Vector2int16Bridge : public Bridge<RBX::Vector2int16>
{
friend class Bridge< RBX::Vector2int16 >;
public:
static void registerClassLibrary (lua_State *L);
static void pushVector2int16(lua_State *L, const RBX::Vector2int16& v)
{
pushNewObject(L, v);
}
private:
static int newVector2int16(lua_State *L);
static int on_add(lua_State *L);
static int on_sub(lua_State *L);
static int on_mul(lua_State *L);
static int on_div(lua_State *L);
static int on_unm(lua_State *L);
static const luaL_reg classLibrary[];
};
class Color3Bridge : public Bridge<G3D::Color3>
{
friend class Bridge< G3D::Color3 >;
public:
static void registerClassLibrary (lua_State *L);
static void pushColor3(lua_State *L, const G3D::Color3& color);
private:
static int newColor3(lua_State *L);
static const luaL_reg classLibrary[];
};
class UDimBridge : public Bridge<RBX::UDim>
{
friend class Bridge< RBX::UDim>;
public:
static void registerClassLibrary (lua_State *L);
static void pushUDim(lua_State *L, const RBX::UDim& v)
{
pushNewObject(L, v);
}
private:
static int newUDim(lua_State *L);
static int on_add(lua_State *L);
static int on_sub(lua_State *L);
static int on_unm(lua_State *L);
static const luaL_reg classLibrary[];
};
class UDim2Bridge : public Bridge<RBX::UDim2>
{
friend class Bridge< RBX::UDim2>;
public:
static void registerClassLibrary (lua_State *L);
private:
static int newUDim2 (lua_State *L);
static int on_add(lua_State *L);
static int on_sub(lua_State *L);
static int on_unm(lua_State *L);
static const luaL_reg classLibrary[];
};
class FacesBridge : public Bridge<RBX::Faces>
{
friend class Bridge< RBX::Faces>;
public:
static void registerClassLibrary (lua_State *L);
private:
static int newFaces (lua_State *L);
static const luaL_reg classLibrary[];
};
class AxesBridge : public Bridge<RBX::Axes>
{
friend class Bridge< RBX::Axes>;
public:
static void registerClassLibrary (lua_State *L);
private:
static int newAxes(lua_State *L);
static const luaL_reg classLibrary[];
};
class BrickColorBridge : public Bridge<RBX::BrickColor>
{
friend class Bridge< RBX::BrickColor >;
public:
static void registerClassLibrary (lua_State *L) ;
private:
static int newBrickColor(lua_State *L);
static int randomBrickColor(lua_State *L);
static int paletteBrickColor(lua_State *L);
static const luaL_reg classLibrary[];
};
// CellID bridge for cluster access
class CellIDBridge : public Bridge<CellID>
{
friend class Bridge< CellID >;
public:
static void registerClassLibrary (lua_State *L) ;
static void pushCellID(lua_State *L, const CellID& v)
{
pushNewObject(L, v);
}
private:
static int newCellID(lua_State *L);
static const luaL_reg classLibrary[];
};
// Number sequence for particle props
class NumberSequenceBridge : public Bridge<NumberSequence>
{
friend class Bridge< NumberSequence >;
public:
static void registerClassLibrary(lua_State* L);
static void pushNumberSequence(lua_State* L, const NumberSequence& v) { pushNewObject(L, v); }
private:
static int newNumberSequence(lua_State* L);
static const luaL_reg classLibrary[];
};
// Number sequence for particle props
class ColorSequenceBridge : public Bridge<ColorSequence>
{
friend class Bridge< ColorSequence >;
public:
static void registerClassLibrary(lua_State* L);
static void pushColorSequence(lua_State* L, const ColorSequence& v) { pushNewObject(L, v); }
private:
static int newColorSequence(lua_State* L);
static const luaL_reg classLibrary[];
};
class NumberSequenceKeypointBridge : public Bridge<NumberSequenceKeypoint>
{
friend class Bridge< NumberSequenceKeypoint >;
public:
static void registerClassLibrary(lua_State* L);
static void pushNumberSequenceKeypoint(lua_State* L, const NumberSequenceKeypoint& v) { pushNewObject(L, v); }
private:
static int newNumberSequenceKeypoint(lua_State* L);
static const luaL_reg classLibrary[];
};
class ColorSequenceKeypointBridge : public Bridge<ColorSequenceKeypoint>
{
friend class Bridge< ColorSequenceKeypoint >;
public:
static void registerClassLibrary(lua_State* L);
static void pushColorSequenceKeypoint(lua_State* L, const ColorSequenceKeypoint& v) { pushNewObject(L, v); }
private:
static int newColorSequenceKeypoint(lua_State* L);
static const luaL_reg classLibrary[];
};
class NumberRangeBridge : public Bridge<NumberRange>
{
friend class Bridge< NumberRange >;
public:
static void registerClassLibrary(lua_State* L);
static void pushNumberRange(lua_State* L, const NumberRange& v) { pushNewObject(L, v); }
private:
static int newNumberRange(lua_State* L);
static const luaL_reg classLibrary[];
};
// Specialization to implement arithmatic operators
template<>
void Bridge<G3D::Vector3int16>::registerClass (lua_State *L);
template<>
void Bridge<G3D::Vector3>::registerClass (lua_State *L);
template<>
void Bridge<RBX::Vector2>::registerClass (lua_State *L);
template<>
void Bridge<G3D::CoordinateFrame>::registerClass (lua_State *L);
// Specialization to implement arithmatic operators
template<>
void Bridge<RBX::UDim>::registerClass (lua_State *L);
// Specialization to implement arithmatic operators
template<>
void Bridge<RBX::UDim2>::registerClass (lua_State *L);
} }
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include "lauxlib.h"
namespace LuaOsExtension
{
extern const luaL_Reg registry[];
}
namespace LuaMathExtension
{
int noise(lua_State* L);
}
namespace LuaDebugExtension
{
extern const luaL_Reg registry[];
}
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include "Lua/LuaBridge.h"
#include "reflection/enumConverter.h"
#include "rbxformat.h"
namespace RBX { namespace Lua {
class AllEnumDescriptors
{
};
typedef const AllEnumDescriptors* AllEnumDescriptorsPtr;
// Represents a Reflection::EnumDescriptor::Item in Lua
class Enums : public SingletonBridge<AllEnumDescriptorsPtr>
{
public:
static void declareAllEnums(lua_State *L);
static bool getValue(lua_State *L, unsigned int index, RBX::Reflection::Variant& value);
};
typedef const Reflection::EnumDescriptor* EnumDescriptorPtr;
// Represents a Reflection::EnumDescriptor::Item in Lua
class Enum : public SingletonBridge<EnumDescriptorPtr>
{
public:
};
typedef const Reflection::EnumDescriptor::Item* EnumDescriptorItemPtr;
// Represents a Reflection::EnumDescriptor::Item in Lua
class EnumItem : public SingletonBridge<EnumDescriptorItemPtr>
{
public:
static EnumDescriptorItemPtr getItem(lua_State *L, unsigned int index) {
return getObject(L, index);
}
static bool getItem(lua_State *L, unsigned int index, EnumDescriptorItemPtr& value) {
return getValue(L, index, value);
}
};
// specialization
template<>
int Bridge< AllEnumDescriptorsPtr, false >::on_tostring(const AllEnumDescriptorsPtr& object, lua_State *L);
// specialization
template<>
int Bridge< EnumDescriptorPtr, false >::on_tostring(const EnumDescriptorPtr& object, lua_State *L);
// specialization
template<>
int Bridge< EnumDescriptorItemPtr, false >::on_tostring(const EnumDescriptorItemPtr& object, lua_State *L);
} }
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "Lua/LuaBridge.h"
#include "V8Tree/Instance.h"
namespace RBX { namespace Lua {
// specialization
template<>
int Bridge< shared_ptr<Instance>, false >::on_tostring(const shared_ptr<Instance>& object, lua_State *L);
class ObjectBridge : public SharedPtrBridge<Instance>
{
friend class SharedPtrBridge<Instance>;
static const luaL_reg classLibrary[];
public:
static int callMemberFunction(lua_State *L);
static int callMemberYieldFunction(lua_State *L);
static void registerInstanceClassLibrary (lua_State *L) {
// Register the "new" function for Instances
luaL_register(L, "Instance", classLibrary);
lua_setreadonly(L, -1, true);
lua_pop(L,1); // Pop table from stack. http://lua-users.org/lists/lua-l/2003-12/msg00139.html
}
static int newInstance(lua_State *L);
static int lockInstance(lua_State *L);
static int unlockInstance(lua_State *L);
static boost::shared_ptr<Instance> getInstance(lua_State *L, unsigned int index)
{
return getPtr(L, index);
}
};
template<>
void Bridge< shared_ptr<Instance>, false >::on_newindex(shared_ptr<Instance>& object, const char* name, lua_State *L);
} }
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include "Lua/LuaBridge.h"
namespace RBX {
namespace Lua {
class Library
{
std::string libraryName;
public:
Library(std::string libraryName)
:libraryName(libraryName)
{};
const std::string& getLibraryName() const { return libraryName; }
bool operator ==(const Library& other) const
{
return this->libraryName == other.libraryName;
}
};
// Represents a Reflection::EnumDescriptor::Item in Lua
class LibraryBridge : public Bridge<Library>
{
public:
static void registerClassLibrary (lua_State *L);
static int find(lua_State *L, const std::string& libraryName);
static void push(lua_State *L, const Library& item);
static void saveLibraryResult(lua_State *L, int results, std::string libraryName);
};
}
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "Util/Memory.h"
#include "boost/pool/object_pool.hpp"
#include "boost/iostreams/filter/gzip.hpp"
namespace RBX
{
class LuaAllocator
{
private:
size_t heapSize;
size_t heapCount;
size_t maxHeapSize;
size_t maxHeapCount;
// memory pools
std::vector<boost::pool<>*> memPools;
public:
LuaAllocator(bool usePool = false);
~LuaAllocator();
static size_t heapLimit; // maximum heap size allowed. 0 == no limit
void clearHeapMax();
void getHeapStats(size_t& heapSize, size_t& heapCount, size_t& maxHeapSize, size_t& maxHeapCount) const;
void getHeapStats(size_t& heapSize, size_t& heapCount) const;
bool hasSpace(const long diff);
virtual void* alloc(void *ptr, size_t osize, size_t nsize);
static void * alloc(void *ud, void *ptr, size_t osize, size_t nsize);
};
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "V8DataModel/GlobalSettings.h"
namespace RBX
{
extern const char *const sLuaSettings;
class LuaSettings
: public GlobalAdvancedSettingsItem<LuaSettings, sLuaSettings>
{
public:
LuaSettings();
int gcPause;
int gcStepMul;
double defaultWaitTime;
double smallestWaitTime;
int gcLimit; //Ideal limit above which we trigger aggressive garbage collection, in average KB per gcFrequency
int gcFrequency; //How many heartbeats between maunal GC steps
bool areScriptStartsReported;
float waitingThreadsBudget; // 0..1 A percentage
};
}
+1 -1
View File
@@ -27,7 +27,7 @@ template<>
int Bridge<EventInstance>::on_index(const EventInstance& object, const char* name, lua_State *L)
{
// The pre-defined "connect()" method
if (strcmp(name, "connect") == 0 || strcmp(name, "Connect") == 0)
if (strcmp(name, "connect")==0)
{
lua_pushcfunction(L, EventBridge::connect);
return 1;
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include "Lua/LuaBridge.h"
#include "reflection/object.h"
#include "Reflection/Event.h"
namespace RBX
{
class Instance;
namespace Lua
{
struct EventInstance
{
const Reflection::EventDescriptor* descriptor;
// We use a weak pointer because references to a Event shouldn't lock the source of the event.
// If the source has been collected, then connecting to the Event will return an empty connection.
weak_ptr<Instance> source;
bool operator== (const EventInstance& other) const
{
if (descriptor != other.descriptor)
return false;
shared_ptr<Instance> l = source.lock();
if (!l)
return false;
shared_ptr<Instance> l2 = other.source.lock();
if (!l2)
return false;
return l == l2;
}
};
// specialization
template<>
int Bridge<EventInstance>::on_tostring(const EventInstance& object, lua_State *L);
class EventBridge : public Bridge<EventInstance>
{
public:
static int connect(lua_State *L);
static int wait(lua_State *L);
};
class SignalConnectionBridge : public Bridge< rbx::signals::connection >
{
friend class Bridge< rbx::signals::connection >;
static int disconnect(lua_State *L);
};
}
}
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#include "Util/AsyncHttpQueue.h"
#include "Util/ContentId.h"
#include "Util/ProtectedString.h"
#include "V8Tree/Instance.h"
namespace RBX
{
class ContentProvider;
extern const char* const sLuaSourceContainer;
class LuaSourceContainer
: public DescribedNonCreatable<LuaSourceContainer, Instance, sLuaSourceContainer>
{
public:
enum RemoteSourceLoadState
{
NotAttemptedToLoad,
Loaded,
FailedToLoad
};
static void loadLinkedScripts(shared_ptr<ContentProvider> cp, Instance* root, AsyncHttpQueue::ResultJob jobType, boost::function<void()> callback);
static void loadLinkedScriptsForInstances(shared_ptr<ContentProvider> cp, Instances& instances, AsyncHttpQueue::ResultJob jobType, boost::function<void()> callback);
static void blockingLoadLinkedScripts(ContentProvider* cp, Instance* root);
static void blockingLoadLinkedScriptsForInstances(ContentProvider* cp, Instances& instances);
static Reflection::RemoteEventDesc<LuaSourceContainer, void()> event_requestLock;
LuaSourceContainer();
const ContentId& getScriptId() const;
void setScriptId(const ContentId& contentId);
const ProtectedString& getCachedRemoteSource() const;
void setCachedRemoteSource(const ProtectedString& value);
int getCachedRemoteSourceLoadState() const;
void setCachedRemoteSourceLoadState(int value);
Instance* getCurrentEditor() const;
void setCurrentEditor(Instance* newEditor);
virtual void fireSourceChanged() {};
rbx::remote_signal<void()> requestLock;
rbx::remote_signal<void(bool)> lockGrantedOrNot;
protected:
virtual void onScriptIdChanged() {}
void processRemoteEvent(const Reflection::EventDescriptor& descriptor, const Reflection::EventArguments& args, const SystemAddress& source) override;
private:
struct LinkedScriptLoadData
{
rbx::atomic<int> scriptCount;
boost::function<void()> callbackWhenDone;
shared_ptr<Instance> context;
AsyncHttpQueue::ResultJob jobType;
boost::mutex scriptApplyResultClosuresMutex;
std::vector<boost::function<void()> > scriptApplyResultClosures;
};
static void linkedSourceCountingVisitor(shared_ptr<Instance> descendant, int* counter);
static void linkedSourceLoadedHandler(weak_ptr<LuaSourceContainer> weakScript, AsyncHttpQueue::RequestResult result,
shared_ptr<const std::string> loadedSource, shared_ptr<LinkedScriptLoadData> metadata);
static void updateScriptInstancesUnderWriteLock(DataModel* dm, shared_ptr<LinkedScriptLoadData> metadata);
static void linkedSourceFetchingVisitor(shared_ptr<Instance> descendant, shared_ptr<ContentProvider> cp,
AsyncHttpQueue::ResultJob jobType, shared_ptr<LinkedScriptLoadData> metadata);
ContentId scriptId;
ProtectedString cachedRemoteSource;
RemoteSourceLoadState cachedRemoteSourceLoadState;
weak_ptr<Instance> currentEditor;
};
}
+185
View File
@@ -0,0 +1,185 @@
#pragma once
#if (defined(_WIN32) || (defined(__APPLE__) && !defined(RBX_PLATFORM_IOS))) && !defined(RBX_STUDIO_BUILD)
#define RBX_SECURE_DOUBLE
#endif
#ifdef _WIN32
#define RBX_ALIGN(s) _declspec(align(s))
#else
#define RBX_ALIGN(s) __attribute__((__aligned__(s)))
#endif
#include <boost/unordered_map.hpp>
#include <string>
#if defined(RBX_SECURE_DOUBLE)
#include <emmintrin.h>
#endif
#ifndef RBX_STUDIO_BUILD
#define LUAVM_SECURE
#endif
// Utilities for shuffling fields and enum values
#define LUAVM_SHUFFLE_COMMA ,
#ifdef LUAVM_SECURE
#define LUAVM_SHUFFLE2(sep,a0,a1) a1 sep a0
#define LUAVM_SHUFFLE3(sep,a0,a1,a2) a1 sep a2 sep a0
#define LUAVM_SHUFFLE4(sep,a0,a1,a2,a3) a3 sep a1 sep a0 sep a2
#define LUAVM_SHUFFLE5(sep,a0,a1,a2,a3,a4) a4 sep a0 sep a2 sep a1 sep a3
#define LUAVM_SHUFFLE6(sep,a0,a1,a2,a3,a4,a5) a3 sep a5 sep a2 sep a0 sep a1 sep a4
#define LUAVM_SHUFFLE7(sep,a0,a1,a2,a3,a4,a5,a6) a2 sep a3 sep a0 sep a4 sep a6 sep a1 sep a5
#define LUAVM_SHUFFLE8(sep,a0,a1,a2,a3,a4,a5,a6,a7) a7 sep a0 sep a5 sep a6 sep a3 sep a1 sep a2 sep a4
#define LUAVM_SHUFFLE9(sep,a0,a1,a2,a3,a4,a5,a6,a7,a8) a2 sep a6 sep a4 sep a7 sep a1 sep a8 sep a0 sep a3 sep a5
#else
#define LUAVM_SHUFFLE2(sep,a0,a1) a0 sep a1
#define LUAVM_SHUFFLE3(sep,a0,a1,a2) a0 sep a1 sep a2
#define LUAVM_SHUFFLE4(sep,a0,a1,a2,a3) a0 sep a1 sep a2 sep a3
#define LUAVM_SHUFFLE5(sep,a0,a1,a2,a3,a4) a0 sep a1 sep a2 sep a3 sep a4
#define LUAVM_SHUFFLE6(sep,a0,a1,a2,a3,a4,a5) a0 sep a1 sep a2 sep a3 sep a4 sep a5
#define LUAVM_SHUFFLE7(sep,a0,a1,a2,a3,a4,a5,a6) a0 sep a1 sep a2 sep a3 sep a4 sep a5 sep a6
#define LUAVM_SHUFFLE8(sep,a0,a1,a2,a3,a4,a5,a6,a7) a0 sep a1 sep a2 sep a3 sep a4 sep a5 sep a6 sep a7
#define LUAVM_SHUFFLE9(sep,a0,a1,a2,a3,a4,a5,a6,a7,a8) a0 sep a1 sep a2 sep a3 sep a4 sep a5 sep a6 sep a7 sep a8
#endif
// Utility class for obfuscating fields of primitive types
// WARNING: this will give incorrect results if T = float.
template <typename T> class LuaVMValue
{
public:
operator const T() const
{
#ifdef LUAVM_SECURE
return (T)((uintptr_t)storage + reinterpret_cast<uintptr_t>(this));
#else
return storage;
#endif
}
void operator=(const T& value)
{
#ifdef LUAVM_SECURE
storage = (T)((uintptr_t)value - reinterpret_cast<uintptr_t>(this));
#else
storage = value;
#endif
}
const T operator->() const
{
return operator const T();
}
private:
T storage;
};
// Encoding/decoding lineinfo
#if defined(LUAVM_SECURE)
#define LUAVM_ENCODELINE(line, pc) ((line) ^ ((pc) << 8))
#define LUAVM_DECODELINE(line, pc) ((line) ^ ((pc) << 8))
#else
#define LUAVM_ENCODELINE(line, pc) (line)
#define LUAVM_DECODELINE(line, pc) (line)
#endif
// Encoding/decoding instructions
#if defined(LUAVM_SECURE)
#define LUAVM_ENCODEINSN(insn, key) ((insn) * key)
#define LUAVM_DECODEINSN(insn, key) ((insn).v * key)
#else
#define LUAVM_ENCODEINSN(insn, key) (insn)
#define LUAVM_DECODEINSN(insn, key) (insn).v
#endif
typedef unsigned int (*RbxOpEncoder)(unsigned int i, int pc, unsigned key);
// Utility class
struct lua_State;
namespace RBX
{
class ProtectedString;
}
// Core scripts have a fixed key
// Don't use these except in LuaVM*.cpp!
// These are defines to make sure they don't end up in an executable by complete accident
#define LUAVM_INTERNAL_CORE_ENCODE_KEY 641
#define LUAVM_INTERNAL_CORE_DECODE_KEY 6700417
// Constants for key values
#define LUAVM_KEY_DUMMY 1
#define LUAVM_KEY_INVALID 0
#define LUAVM_MODKEY_DUMMY 1
namespace LuaVM
{
// Utilities for working with regular scripts
std::string compile(const std::string& source);
std::string compileLegacy(const std::string& source);
int load(lua_State* L, const RBX::ProtectedString& source, const char* chunkname, unsigned int modkey = 1);
unsigned int getKey();
// Utilities for working with core scripts
std::string compileCore(const std::string& source);
unsigned int getKeyCore();
unsigned int getModKeyCore();
// Controls whether replication uses bytecode or source code
bool useSecureReplication();
// Controls whether scripts can be compiled from source code
bool canCompileScripts();
// Gets embedded bytecode for core scripts/libraries
std::string getBytecodeCore(const std::string& name);
//const ref
boost::unordered_map<std::string, std::string> getBytecodeCoreModules();
// Old Encoding Scheme
unsigned int rbxOldEncode(unsigned int i, int pc, unsigned int key);
// Dual-Affine-Xor Encoding
unsigned int rbxDaxEncode(unsigned int i, int pc, unsigned int key);
}
#if defined(RBX_SECURE_DOUBLE)
// Note that users who can find a value can still change magnitude or sign easily.
// sse2+ only
class LuaSecureDouble
{
private:
double storage;
public:
static RBX_ALIGN(16) int luaXorMask[4];
operator const double() const
{
__m128d xmmKey = _mm_load_pd((double*)(luaXorMask));
__m128d xmmData = _mm_load_sd(&storage);
__m128d xmmResult = _mm_xor_pd(xmmData, xmmKey );
return _mm_cvtsd_f64(xmmResult);
}
void operator=(const double& value)
{
__m128d xmmKey = _mm_load_pd((double*)(luaXorMask));
__m128d xmmData = _mm_load_sd(&value);
__m128d xmmResult = _mm_xor_pd(xmmData, xmmKey );
storage = _mm_cvtsd_f64(xmmResult);
}
static void initDouble();
};
#endif
+98
View File
@@ -0,0 +1,98 @@
#pragma once
#include "Reflection/reflection.h"
#include "Script/ThreadRef.h"
#include "Script/LuaSourceContainer.h"
#include "Util/ProtectedString.h"
#include "V8Tree/Instance.h"
#include <boost/intrusive_ptr.hpp>
#include <vector>
namespace RBX
{
extern const char* const sModuleScript;
class ModuleScript
: public DescribedCreatable<ModuleScript, LuaSourceContainer, sModuleScript>
{
public:
static const Reflection::PropDescriptor<ModuleScript, ProtectedString> prop_Source;
enum ScriptSetupState
{
NotRunYet = 0,
Running = 1,
CompletedError = 2,
CompletedSuccess = 3
};
class PerVMState
{
public:
PerVMState();
virtual ~PerVMState();
int getResultRegistryIndex() const;
// Destroy the current result index and replace it with index.
void reassignResultRegistryIndex(int newIndex);
void setRunning(boost::intrusive_ptr<Lua::WeakThreadRef::Node> node);
void setCompletedError();
void setCompletedSuccess(lua_State* globalStateContainingResult, int resultRegistryIndex);
ScriptSetupState getCurrentState() const;
void addYieldedImporter(Lua::WeakThreadRef L);
void getAndClearYieldedImporters(std::vector<Lua::WeakThreadRef>* out);
void cleanupAndResetState();
void resetState();
private:
ScriptSetupState scriptLoadingState;
boost::intrusive_ptr<Lua::WeakThreadRef::Node> node;
lua_State* globalStateContainingResult;
int resultRegistryIndex;
std::vector<Lua::WeakThreadRef> yieldedImporters;
void releaseReferenceIfCompletedSuccessfully();
void releaseScriptNodeIfPresent();
};
ModuleScript();
// Instance
bool askSetParent(const Instance* instance) const override { return true; }
ProtectedString getSource() const;
void setSource(const ProtectedString& newText);
std::string requestHash() const;
PerVMState& vmState(lua_State* vm);
// Try to get rid of this method once new play button is launched.
static void cleanupAndResetState(const weak_ptr<ModuleScript> module);
// Reset the state of the module script without destroying its result index.
void resetState();
void setReloadRequested(bool reload) { reloadRequested = reload; }
bool getReloadRequested() const { return reloadRequested; }
void fireSourceChanged() override;
rbx::signal<void(lua_State*)> starting;
protected:
void onScriptIdChanged() override;
private:
ProtectedString source;
bool reloadRequested;
typedef boost::unordered_map<lua_State*, PerVMState> VMStateMap;
VMStateMap stateMap;
};
} // namespace
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>
struct lua_State;
namespace RBX
{
class DataModel;
class Instance;
namespace ScriptAnalyzer
{
// Don't change codes for the existing warnings - they have a corresponding wiki anchor tag in http://wiki.watrbx.wtf/index.php?title=Script_Analysis
enum WarningCode
{
Warning_Unknown = 0,
Warning_UnknownGlobal = 1,
Warning_DeprecatedGlobal = 2,
Warning_GlobalUsedAsLocal = 3,
Warning_LocalShadow = 4,
Warning_SameLineStatement = 5,
Warning_MultiLineStatement = 6,
Warning_UnknownType = 7,
Warning_DotCall = 8,
Warning_UnknownMember = 9,
Warning_BuiltinGlobalWrite = 10,
Warning_Placeholder = 11,
Warning_Internal
};
struct Position
{
unsigned int line, column;
Position(unsigned int line, unsigned int column)
: line(line)
, column(column)
{
}
};
struct Location
{
Position begin, end;
Location()
: begin(0, 0)
, end(0, 0)
{
}
Location(const Position& begin, const Position& end)
: begin(begin)
, end(end)
{
}
Location(const Position& begin, unsigned int length)
: begin(begin)
, end(begin.line, begin.column + length)
{
}
Location(const Location& begin, const Location& end)
: begin(begin.begin)
, end(end.end)
{
}
};
struct Error
{
Location location;
std::string text;
};
struct Warning
{
WarningCode code;
Location location;
std::string text;
Warning (WarningCode code, Location location, std::string text)
: code(code)
, location(location)
, text(text)
{}
};
struct IntellesenseResult
{
IntellesenseResult()
: name("")
, isLocal(false)
, isFunction(false)
, location(Position(0,0) , Position(0,0))
{}
std::string name;
bool isLocal;
bool isFunction;
Location location;
std::vector<IntellesenseResult> children;
};
struct Result
{
boost::optional<Error> error;
std::vector<Warning> warnings;
std::vector<IntellesenseResult> intellesenseAnalysis;
};
Result analyze(DataModel* dm, shared_ptr<Instance> script, const std::string& code);
};
}
+466
View File
@@ -0,0 +1,466 @@
#pragma once
#include "V8Tree/Service.h"
#include "Util/ProtectedString.h"
#include "util/runstateowner.h"
#include "Script/IScriptFilter.h"
#include "script/ThreadRef.h"
#include "script/ExitHandlers.h"
#include "Security/SecurityContext.h"
#include "Util/AsyncHttpQueue.h"
#include "util/RunningAverage.h"
#include "rbx/RunningAverage.h"
#define BOOST_DATE_TIME_NO_LIB
#include "boost/date_time/posix_time/posix_time.hpp"
struct lua_State;
struct lua_Debug;
LOGGROUP(ScriptContext)
LOGGROUP(ScriptContextRemove)
LOGGROUP(ScriptContextAdd)
LOGGROUP(ScriptContextClose)
namespace RBX
{
class LuaSourceContainer;
class LuaAllocator;
class LibraryService;
class ModuleScript;
class ModelInstance;
namespace Stats
{
class Item;
}
namespace Lua
{
class YieldingThreads;
class WeakFunctionRef;
}
namespace Network
{
class Player;
}
class BaseScript;
class CoreScript;
class ScriptStats;
class LuaStatsItem;
void registerScriptDescriptors();
extern const char* const sScriptContext;
class ScriptContext
: public DescribedCreatable<ScriptContext, Instance, sScriptContext, Reflection::ClassDescriptor::INTERNAL_LOCAL>
, public Service
, public IScriptFilter
{
friend class LuaStatsItem;
friend class GcJob;
friend class WaitingScriptsJob;
public:
static const int hookCount;
struct ScriptStartOptions
{
struct LuaSyntaxError : std::runtime_error
{
LuaSyntaxError(int lineNumber, std::exception& source)
:std::runtime_error(source.what())
,lineNumber(lineNumber)
{
}
int lineNumber;
};
RBX::Security::Identities identity;
Scripts::Continuations continuations;
boost::function<std::string(const std::string&)> filter; // may throw a LuaSyntaxError
ScriptStartOptions():identity(RBX::Security::GameScript_)
{
}
};
private:
typedef DescribedCreatable<ScriptContext, Instance, sScriptContext, Reflection::ClassDescriptor::INTERNAL_LOCAL> Super;
class ScriptImpersonator : public RBX::Security::Impersonator
{
public:
ScriptImpersonator(lua_State *thread);
};
struct GlobalState
{
GlobalState()
: state(0)
, gcCount(0)
{
}
lua_State* state;
RunningAverage<double> gcAllocAvg; // average lua memory allocation per luaGcFrequency in KB
int gcCount;
};
typedef boost::array<GlobalState, RBX::Security::COUNT_VM_Classes> GlobalStates; // separate Lua top-level states
GlobalStates globalStates;
Lua::WeakThreadRef commandLineSandbox;
std::set<BaseScript*> scripts;
RBX::Time nextPendingScripts;
struct ScriptStart
{
shared_ptr<BaseScript> script;
ScriptStartOptions options;
};
std::vector<ScriptStart> pendingScripts; // scripts waiting to be executed
std::vector<ScriptStart> loadingScripts; // scripts waiting to be executed
// An obfuscated pointer to a location near where the object was created.
// copying the object becomes detectable.
// https://en.wikipedia.org/wiki/Feistel_cipher
// the "update" method might be targeted even if it is obfuscated.
struct SecurityAnchor
{
size_t value[2];
FORCEINLINE void update(const void* ptr)
{
#ifdef _WIN32
size_t localValue[2] = {reinterpret_cast<size_t>(ptr), ~reinterpret_cast<size_t>(ptr)};
localValue[0] ^= localValue[1]*RBX_BUILDSEED | 20151112;
localValue[1] ^= localValue[0]*20151112 | RBX_BUILDSEED;
value[0] = localValue[0];
value[1] = localValue[1];
#endif
}
FORCEINLINE bool checkAnchor(const void* ptr) const
{
#ifdef _WIN32
size_t localValue[2] = {value[0], value[1]};
localValue[1] ^= localValue[0]*20151112 | RBX_BUILDSEED;
localValue[0] ^= localValue[1]*RBX_BUILDSEED | 20151112;
return !((reinterpret_cast<size_t>(ptr)+localValue[1])
^ (~reinterpret_cast<size_t>(ptr)+localValue[0]));
#else
return true;
#endif
}
};
SecurityAnchor securityAnchor;
shared_ptr<RunService> runService;
boost::scoped_ptr<Lua::YieldingThreads> yieldEvent; // collects all threads that have yielded, and periodically resumes them
struct WaitingThread
{
Lua::ThreadRef thread;
shared_ptr<const Reflection::Tuple> arguments;
};
rbx::safe_queue<WaitingThread> waitingThreads;
bool robloxPlace;
bool scriptsDisabled; // == don't run the scripts contained in BaseScript objects
bool preventNewConnection;
shared_ptr<LuaStatsItem> statsItem;
bool collectScriptStats;
shared_ptr<ScriptStats> scriptStats;
std::set<weak_ptr<ModuleScript> > loadedModules;
int startScriptReentrancy;
rbx::atomic<int> timedoutCount;
Time::Interval timoutSpan; // The time that is allowed per heartbeat before scripts stop running (0 means no timeouts)
Time timoutTime; // The system time when we should time-out scripts
rbx::atomic<int> timedout; // == scripts should stop running
boost::scoped_ptr<boost::thread> timeoutThread;
boost::mutex timeoutMutex;
volatile bool endTimoutThread;
CEvent checkTimeout;
struct AssetModuleInfo
{
enum State
{
NotFetchedYet = 0,
Fetching,
Fetched,
Failed
};
State state;
std::vector<Lua::WeakThreadRef> yieldedImporters;
shared_ptr<ModuleScript> module;
shared_ptr<ModelInstance> root;
AssetModuleInfo()
: state(NotFetchedYet)
{}
};
typedef boost::unordered_map<int, AssetModuleInfo> LoadedAssetModules;
LoadedAssetModules loadedAssetModules;
Time luaGcStartTime;
RunningAverage<double> avgLuaGcInterval; // in msec
RunningAverage<double> avgLuaGcTime; // in msec
RunningAverageTimeInterval<> resumedThreads;
RunningAverage<> throttlingThreads; // 1 if threads are being deffered
bool statesClosed;
public:
ScriptContext();
virtual ~ScriptContext();
///////////////////////////////////////////////////
// IScriptFilter
/*override*/ virtual bool scriptShouldRun(BaseScript* script);
static void setAdminScriptPath(const std::string& newPath);
//////////////////////////////////////////////////
// Reflection API
static Reflection::BoundProp<bool> propScriptsDisabled;
static Reflection::BoundProp<int> propLuaGcLimit;
static Reflection::BoundProp<int> propLuaGcFrequency;
static Reflection::BoundProp<int> propLuaGcStepSize;
void setTimeout(double seconds);
void setCollectScriptStats(bool);
// Core & Starter Scripts
void addStarterScript(int assetId);
void addCoreScript(int assetId, shared_ptr<Instance> parent, std::string name);
void addCoreScriptLocal(std::string scriptName, shared_ptr<Instance> parent);
// Experimental error signal for catching errors server-side
rbx::signal<void(std::string, std::string, shared_ptr<Instance>)> errorSignal;
// A temporary signals used for diagnostic purposes
rbx::signal<void(shared_ptr<Instance>, std::string, shared_ptr<Instance>)> camelCaseViolation;
rbx::signal<void(lua_State*)> scriptErrorDetected;
////////////////////////////////////////////////
// Configuration
void setRobloxPlace(bool robloxPlace);
void initializeLuaStateSandbox(Lua::WeakThreadRef& threadRef, lua_State* parentState, Security::Identities identity);
void setKeys(unsigned int scriptKey, unsigned int coreScriptModKey);
////////////////////////////////////////////////
// Helpers and utilities
Reflection::Variant evaluateStudioCommandItem(const char* itemToEvaluate, shared_ptr<RBX::LuaSourceContainer> script);
static bool checkSyntax(const std::string& code, int& line, std::string& errorMessage);
static lua_State* getGlobalState(lua_State* thread);
static ScriptContext& getContext(lua_State* thread);
static void printCallStack(lua_State* thread, std::string* output = NULL, bool dontPrint = false);
static std::string extractCallStack(lua_State* thread, shared_ptr<BaseScript>& source, int& line);
// Shutdown helpers
bool shouldPreventNewConnections() { return preventNewConnection; }
void setPreventNewConnections() { preventNewConnection = true; }
void closeStates(bool resettingSimulation); // Closes down all threads
bool haveStatesClosed() { return statesClosed; }
void cleanupModules();
/////////////////////////////////////////////////
// Script Instance API
// Called by IScriptOwner implementers
void addScript(weak_ptr<BaseScript> script, ScriptStartOptions startOptions = ScriptStartOptions()); // checks pointer validity
void removeScript(weak_ptr<BaseScript> script);
size_t numScripts() {return scripts.size();}
bool hasScript(BaseScript* script) {return (scripts.find(script) != scripts.end());}
/////////////////////////////////////////////////
// Calls that make lua run/resume
void executeInNewThread(RBX::Security::Identities identity, const ProtectedString& script, const char* name);
std::auto_ptr<Reflection::Tuple> executeInNewThread(RBX::Security::Identities identity, const ProtectedString& script, const char* name, const Reflection::Tuple& arguments);
void executeInNewThreadWithExtraGlobals(RBX::Security::Identities identity,
const ProtectedString& script, const char* name,
const std::map<std::string, shared_ptr<Instance> >& extraGlobals);
// Calls a function
Reflection::Tuple callInNewThread(Lua::WeakFunctionRef& function, const Reflection::Tuple& arguments);
// Thread-safe call:
void scheduleResume(Lua::ThreadRef thread, shared_ptr<const Reflection::Tuple> arguments);
typedef enum { Success, Yield, Error } Result;
// Resumes the thread. Reports errors and queues yielding threads for later execution
// NOTE: The caller is reponsible for balancing the stack
Result resume(RBX::Lua::ThreadRef thread, int narg);
/////////////////////////////////////////////
// Stats
void scriptResumedFromEvent() { resumedThreads.sample(); }
size_t getThreadCount() const;
shared_ptr<const Reflection::Tuple> getHeapStats(bool clearHighwaterMark);
shared_ptr<const Reflection::Tuple> getScriptStats(); // deprecated. Don't use it anymore
shared_ptr<const Reflection::ValueArray> getScriptStatsNew();
struct ScriptStat
{
std::string hash;
std::string name;
Instances scripts;
double activity;
unsigned int invocationCount;
};
void getScriptStatsTyped(std::vector<ScriptStat>& result);
double getAvgLuaGcTime() { return avgLuaGcTime.value(); }
double getAvgLuaGcInterval() { return avgLuaGcInterval.value(); }
void reloadModuleScript(shared_ptr<ModuleScript> moduleScript);
bool checkSecurityAnchorValid() const
{
return securityAnchor.checkAnchor(&this->securityAnchor);
}
protected:
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
private:
rbx::signals::scoped_connection heartbeatConnection;
boost::scoped_ptr<LuaAllocator> allocator;
shared_ptr<TaskScheduler::Job> gcJob;
shared_ptr<TaskScheduler::Job> waitingScriptsJob;
void onHeartbeat(const Heartbeat& heartbeat);
void stepGc();
void resumeWaitingScripts(Time expirationTime);
static void sandboxThread(lua_State* thread);
static void setThreadIdentityAndSandbox(lua_State* thread, RBX::Security::Identities identity, shared_ptr<BaseScript> script);
static RBX::Security::Identities getThreadIdentity(lua_State* thread);
// Executes a script, throws an std::exception on error
// The script is spawned from the global root thread, but it is "sandboxed" to the extent that global declarations
// don't affect other threads
// If globalStateToExecuteIn is NULL we get the global state to execute in by our current identity, which is the first arg to this function
void executeInNewThread(RBX::Security::Identities identity, const ProtectedString& script, const char* name,
boost::function1<size_t, lua_State*> pushArguments,
boost::function2<void, lua_State*, size_t> readImmediateResults,
Scripts::Continuations continuations,
lua_State* globalStateToExecuteIn = NULL,
const std::map<std::string, shared_ptr<Instance> >* extraGlobals = NULL,
unsigned int modkey = 1);
// Resumes the thread (expects the top of the stack to contain a function)
// Throws an std::exception if the thread throws a error
void resumeWithArgs(Lua::ThreadRef thread, shared_ptr<const Reflection::Tuple> arguments);
void resume(Lua::ThreadRef thread, boost::function1<size_t, lua_State*> pushArguments, boost::function2<void, lua_State*, size_t> readResults);
void onChangedScriptEnabled(const Reflection::PropertyDescriptor&);
void onCheckTimeout();
void onHook(lua_State *L, lua_Debug *ar);
struct ScriptStatInformation
{
ScriptStatInformation()
{}
std::string name;
Instances scripts;
};
std::map<std::string, ScriptStatInformation> scriptHashInfo;
// Functions exposed in the Lua environment:
public:
static void hook(lua_State *L, lua_Debug *ar);
void reportError(lua_State* thread);
lua_State* getGlobalState(RBX::Security::Identities identity);
private:
static int print(lua_State *L);
static int doPrint(lua_State *thread, const MessageType& messageType = MESSAGE_OUTPUT);
static int crash(lua_State *L);
static int tick(lua_State* thread);
static int rbxTime(lua_State* thread);
static int time(lua_State* thread);
static int wait(lua_State* thread);
static int delay(lua_State* thread);
static int ypcall(lua_State* thread);
void on_ypcall_success(Lua::WeakThreadRef caller, lua_State* functor);
void on_ypcall_failure(Lua::WeakThreadRef caller, lua_State* functor);
static int spawn(lua_State* thread);
static int printidentity(lua_State* thread);
static int loadfile(lua_State* thread);
static int loadstring(lua_State* thread);
static int notImplemented(lua_State* thread);
static int dofile(lua_State* thread);
static int settings(lua_State* thread);
static int usersettings(lua_State* thread);
static int pluginmanager(lua_State* thread);
static int debuggermanager(lua_State *thread);
static int loadLibrary(lua_State* L);
static int loadRobloxLibrary(lua_State* L);
static int requireModuleScript(lua_State* L);
static int stats(lua_State* thread);
static int version(lua_State* thread);
static int statsitemvalue(lua_State* thread);
static int requireModuleScriptFromInstance(lua_State* L, shared_ptr<ModuleScript> moduleScript);
static int requireModuleScriptFromAssetId(lua_State* L, int assetId);
static void moduleContentLoaded(AsyncHttpQueue::RequestResult result, shared_ptr<Instances> instances,
ScriptContext& sc, Security::Identities identity, lua_State* globalState, AssetModuleInfo* info);
static void moduleContentLinkedSourcesResolved(shared_ptr<Instances> instances,
shared_ptr<ModuleScript> foundModuleScript, ScriptContext& sc, Security::Identities identity,
lua_State* globalState, AssetModuleInfo* info);
void startRunningModuleScript(Security::Identities identity, lua_State* globalState, shared_ptr<ModuleScript> moduleScript);
static void requireModuleScriptSuccessContinuation(shared_ptr<ModuleScript> moduleScript,
lua_State* threadRunningModuleCode);
static void requireModuleScriptErrorContinuation(shared_ptr<ModuleScript> moduleScript,
lua_State* threadRunningModuleCode);
static void reloadModuleScriptInternal(lua_State* globalState, shared_ptr<ModuleScript> moduleScript);
static void reloadModuleScriptSuccessContinuation(shared_ptr<ModuleScript> moduleScript,
lua_State* reloadThread,
int oldResultRegistryIndex);
static void reloadModuleScriptErrorContinuation(shared_ptr<ModuleScript> moduleScript,
lua_State* reloadThread);
static int warn(lua_State *L);
static void validateThreadAccess(lua_State* L);
static int resumeImpl(lua_State* L, int nargs);
int camelCaseViolationCount;
rbx::signals::connection camelCaseViolationConnection;
void onCamelCaseViolation(shared_ptr<Instance> object, std::string memberName, shared_ptr<Instance> script);
friend class BaseScript;
void disassociateState(BaseScript* script);
bool openState(size_t idx);
void closeState(lua_State* globalState);
void startScript(ScriptStart scriptStart);
static void eraseScript(std::vector<ScriptContext::ScriptStart>& container, BaseScript* script);
void startPendingScripts();
unsigned int coreScriptModKey;
};
#ifdef _DEBUG
class StackBalanceCheck
{
const int oldTop;
lua_State *thread;
bool cancelled;
public:
StackBalanceCheck(lua_State *thread);
~StackBalanceCheck();
void cancel() { cancelled = true; }
};
#define RBXASSERT_BALLANCED_LUA_STACK(L) StackBalanceCheck stackBalanceCheck(L)
#define RBXASSERT_BALLANCED_LUA_STACK2(L) StackBalanceCheck stackBalanceCheck2(L)
#define CANCEL_BALLANCED_LUA_STACK_CHECK() stackBalanceCheck.cancel()
#define CANCEL_BALLANCED_LUA_STACK_CHECK2() stackBalanceCheck2.cancel()
#else
#define RBXASSERT_BALLANCED_LUA_STACK(L) ((void)0)
#define RBXASSERT_BALLANCED_LUA_STACK2(L) ((void)0)
#define CANCEL_BALLANCED_LUA_STACK_CHECK() ((void)0)
#define CANCEL_BALLANCED_LUA_STACK_CHECK2() ((void)0)
#endif
}
+80
View File
@@ -0,0 +1,80 @@
#pragma once
#include "util/runstateowner.h"
#include "g3d/Array.h"
#include "boost/any.hpp"
#include "boost/shared_ptr.hpp"
#include "lua/luabridge.h"
#include "script/threadref.h"
#include <boost/thread/mutex.hpp>
#include <vector>
struct lua_State;
class ThreadInfo;
namespace RBX {
class Instance;
class ScriptContext;
namespace Lua {
class YieldingThreads
{
ScriptContext* context;
struct WaitingThread
{
boost::intrusive_ptr<WeakThreadRef> thread;
RBX::Time waitTime;
RBX::Time resumeTime;
WaitingThread(lua_State *L, RBX::Time::Interval requestedDelay)
:thread(new WeakThreadRef(L)),
waitTime(RBX::Time::now<RBX::Time::Precise>())
{
resumeTime = waitTime + requestedDelay;
}
bool operator <(const WaitingThread& other) const
{
return this->resumeTime > other.resumeTime;
}
};
typedef std::priority_queue< WaitingThread > WaitThreadRefs;
// Lua refs to threads that are waiting on the event
WaitThreadRefs waitingThreads;
public:
YieldingThreads(ScriptContext* context);
// Hooking up consumers:
void queueWaiter(lua_State *L);
void queueWaiter(lua_State *L, LUA_NUMBER delay);
void resume(double wallTime, Time expirationTime, bool& throttling);
std::size_t waiterCount() const;
private:
friend class ScriptContext;
void clearAllSinks();
};
// specialization
template<>
int Bridge<rbx::signals::connection>::on_tostring(const rbx::signals::connection& object, lua_State *L);
template<>
int Bridge<boost::intrusive_ptr<class WeakThreadRef::Node> >::on_tostring(const boost::intrusive_ptr<class WeakThreadRef::Node>& object, lua_State *L);
template<>
int Bridge< shared_ptr<GenericFunction> >::on_tostring(const shared_ptr<GenericFunction>& object, lua_State *L);
template<>
int Bridge< shared_ptr<GenericAsyncFunction> >::on_tostring(const shared_ptr<GenericAsyncFunction>& object, lua_State *L);
} }
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include "rbx/RunningAverage.h"
#include "boost/weak_ptr.hpp"
#include "util/Utilities.h"
#include "V8DataModel/Stats.h"
#include "script/ScriptContext.h"
#include <stack>
#include <map>
namespace RBX
{
class ScriptStats
{
public:
struct StatCollection
{
boost::shared_ptr<ActivityMeter<2> > activity;
boost::shared_ptr<InvocationMeter<2> > invocations;
};
typedef std::map<std::string, StatCollection> ScriptActivityMeterMap;
protected:
ScriptActivityMeterMap scriptActivityMap;
std::stack<std::string> scriptStack;
void stopCollection(const std::string& scriptHash);
void startCollection(const std::string& scriptHash, bool firstTime);
public:
ScriptStats();
void scriptResumeStarted(const std::string& scriptHash);
void scriptResumeStopped(const std::string& scriptHash);
const ScriptActivityMeterMap& getScriptActivityMap() const { return scriptActivityMap; }
};
class LuaStatsItem : public Stats::Item
{
ScriptContext* scriptContext;
Stats::Item* averageGcInterval;
Stats::Item* averageGcTime;
Stats::Item* resumedThreads;
Stats::Item* deferredThreads;
public:
LuaStatsItem(ScriptContext* context) : scriptContext(context)
{
setName("Lua");
}
static shared_ptr<LuaStatsItem> create(ScriptContext* context)
{
shared_ptr<LuaStatsItem> result = Creatable<Instance>::create<LuaStatsItem>(context);
result->init();
return result;
}
void init();
virtual void update();
};
}
+185
View File
@@ -0,0 +1,185 @@
#pragma once
#include "rbx/intrusive_ptr_target.h"
#include "boost/intrusive_ptr.hpp"
#include "rbx/boost.hpp"
#include "rbx/threadsafe.h"
#include "reflection/type.h"
struct lua_State;
using boost::shared_ptr;
LOGGROUP(WeakThreadRef)
namespace RBX {
namespace Lua {
void dumpThreadRefCounts();
// Used internally
namespace detail {
class LiveThreadRef
: public rbx::quick_intrusive_ptr_target<LiveThreadRef>
, public Diagnostics::Countable<LiveThreadRef>
, boost::noncopyable
{
lua_State* L;
int threadId;
friend class WeakThreadRef;
friend class ThreadRef;
public:
// Do not create this. It is an internal class
LiveThreadRef (lua_State* thread);
~LiveThreadRef();
bool empty() const {
return L == NULL;
}
lua_State* thread() const {
return L;
}
};
}
// You get this by calling lock() on WeakThreadRef
class ThreadRef
: public Diagnostics::Countable<ThreadRef>
{
boost::intrusive_ptr<detail::LiveThreadRef> liveThreadRef;
friend class WeakThreadRef;
ThreadRef (detail::LiveThreadRef* liveThreadRef):liveThreadRef(liveThreadRef) {}
public:
ThreadRef() {}
ThreadRef(lua_State* thread)
:liveThreadRef(new detail::LiveThreadRef(thread)) {}
lua_State* get() const {
return liveThreadRef ? liveThreadRef->thread() : NULL;
}
operator lua_State*() const
{
return get();
}
bool empty() const {
return liveThreadRef && liveThreadRef->thread() != NULL;
}
};
// Registers a weak reference to a thread, ensuring that it isn't collected (sometimes)
class WeakThreadRef
: public rbx::quick_intrusive_ptr_target<WeakThreadRef>
, boost::noncopyable
, public Diagnostics::Countable<WeakThreadRef>
{
// TODO: boost::mutex would be safer
typedef rbx::spin_mutex Mutex;
static Mutex sync;
public:
class Node
: public rbx::quick_intrusive_ptr_target<Node>
, boost::noncopyable
{
friend class WeakThreadRef;
WeakThreadRef* first;
public:
Node():first(0) {}
~Node();
static boost::intrusive_ptr<Node> create(lua_State* thread);
static Node* get(lua_State* thread);
// Clear all refs to thread and its children
void eraseAllRefs();
template<class Func>
void forEachRefs(Func func)
{
for (WeakThreadRef* ref = first; ref!=NULL; ref = ref->next)
{
func(ref->lock());
}
}
};
friend class Node;
private:
WeakThreadRef* previous;
WeakThreadRef* next;
boost::intrusive_ptr<detail::LiveThreadRef> liveThreadRef;
void addRef(lua_State* L);
void addToNode();
void removeFromNode();
protected:
Node* node;
virtual void removeRef();
lua_State* thread() const {
return threadDangerous();
}
public:
WeakThreadRef():node(0), previous(0), next(0) {}
WeakThreadRef(lua_State* thread);
WeakThreadRef(const WeakThreadRef& other);
WeakThreadRef& operator=(const WeakThreadRef& other);
virtual ~WeakThreadRef();
bool operator==(const WeakThreadRef& other) const;
bool operator!=(const WeakThreadRef& other) const;
void reset();
bool empty() const {
return liveThreadRef ? liveThreadRef->thread()==0 : true;
}
ThreadRef lock()
{
return ThreadRef(liveThreadRef.get());
}
lua_State* threadDangerous() const {
return liveThreadRef ? liveThreadRef->thread() : NULL;
}
};
// A function that takes any number of arguments and returns a tuple
typedef boost::function<shared_ptr<const Reflection::Tuple>(shared_ptr<const Reflection::Tuple>)> GenericFunction;
class IAsyncResult
{
public:
// This may throw
virtual boost::shared_ptr<const Reflection::Tuple> getValue() = 0;
virtual ~IAsyncResult() {}
};
// A function that takes any number of arguments and returns the result through a callback
typedef boost::function<void(shared_ptr<const Reflection::Tuple>, boost::function<void(IAsyncResult*)>)> GenericAsyncFunction;
class WeakFunctionRef : public WeakThreadRef
{
private:
int functionId;
typedef WeakThreadRef Super;
public:
WeakFunctionRef():functionId(0) {}
WeakFunctionRef(lua_State* thread, int index); // Constructs a FunctionRef from the Lua stack
virtual ~WeakFunctionRef();
// Copy:
WeakFunctionRef(const WeakFunctionRef& other);
WeakFunctionRef& operator=(const WeakFunctionRef& other);
// Query:
bool operator==(const WeakFunctionRef& other) const;
bool operator!=(const WeakFunctionRef& other) const;
friend WeakFunctionRef lua_tofunction(lua_State* L);
friend void lua_pushfunction(lua_State* L, const WeakFunctionRef& function);
protected:
virtual void removeRef();
};
// Operations with Lua
WeakFunctionRef lua_tofunction(lua_State* L, int index);
void lua_pushfunction(lua_State* L, const WeakFunctionRef& function);
void lua_pushfunction(lua_State* L, shared_ptr<GenericFunction> function);
void lua_pushfunction(lua_State* L, shared_ptr<GenericAsyncFunction> function);
} }
+192
View File
@@ -0,0 +1,192 @@
#pragma once
#include "V8Tree/Instance.h"
#include "Script/ThreadRef.h"
#include "Util/ScriptInformationProvider.h"
#include "Util/ProtectedString.h"
#include "script/LuaSourceContainer.h"
#include "rbx/atomic.h"
#include <boost/function.hpp>
#include <boost/flyweight.hpp>
struct lua_State;
namespace RBX
{
class IScriptOwner;
class ScriptContext;
class RuntimeScriptService;
class ScriptInformationProvider;
class ContentProvider;
namespace Network
{
class Player;
}
typedef ContentId ScriptId;
extern const char* const sBaseScript;
class BaseScript
: public DescribedNonCreatable<BaseScript, LuaSourceContainer, sBaseScript>
{
private:
typedef DescribedNonCreatable<BaseScript, LuaSourceContainer, sBaseScript> Super;
public:
class Slot;
// Used for development only. It allows you to load CoreScripts from your local disk
static std::string adminScriptsPath;
static bool hasCoreScriptReplacements();
void restartScript();
protected:
RuntimeScriptService* workspace;
///////////////////////////////////
// Instance
/*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
/*override*/ void onAncestorChanged(const AncestorChanged& event);
/*override*/ void onScriptIdChanged();
private:
static const std::string emptyString;
weak_ptr<RBX::Network::Player> localPlayer;
bool disabled;
bool badLinkedScript;
RuntimeScriptService* computeNewWorkspace();
public:
struct Code
{
bool loaded;
boost::flyweight<ProtectedString> script;
Code()
:loaded(false)
{}
Code(const boost::flyweight<ProtectedString>& script)
:loaded(true)
,script(script)
{}
};
BaseScript();
~BaseScript();
static const Reflection::PropDescriptor<BaseScript, ScriptId> prop_SourceCodeId;
weak_ptr<RBX::Network::Player> getLocalPlayer() { return localPlayer; }
void setLocalPlayer(const shared_ptr<RBX::Network::Player>& localPlayer) { this->localPlayer = localPlayer; }
// Thread management
boost::intrusive_ptr<Lua::WeakThreadRef::Node> threadNode;
rbx::signal<void(lua_State*)> starting;
rbx::signal<void()> stopped;
bool isDisabled() const { return disabled; }
static const Reflection::PropDescriptor<BaseScript, bool> prop_Disabled;
virtual Code requestCode(ScriptInformationProvider* scriptInfoProvider=NULL);
virtual void extraErrorReporting(lua_State *thread) {}
//Properties
bool getDisabled() const { return disabled; }
void setDisabled(bool value);
virtual const std::string& requestHash() const;
};
// A BaseScript is started when a containing IScriptOwner sends it to the ScriptContext service
extern const char* const sScript;
class Script
: public DescribedCreatable<Script, BaseScript, sScript>
{
private:
typedef DescribedCreatable<Script, BaseScript, sScript> Super;
private:
boost::flyweight<ProtectedString> embeddedSource;
std::string embeddedSourceHash;
public:
Script();
~Script();
static const Reflection::PropDescriptor<Script, ProtectedString> prop_EmbeddedSourceCode;
/*override*/ XmlElement* writeXml(const boost::function<bool(Instance*)>& isInScope, RBX::CreatorRole creatorRole)
{
return Super::writeXml(isInScope, creatorRole);
}
/*override*/ bool askSetParent(const Instance* instance) const
{
// Scripts can be anywhere
return true;
}
bool isCodeEmbedded() const { return getScriptId().isNull(); }
/*override*/ Code requestCode(ScriptInformationProvider* scriptInfoProvider=NULL);
/*override*/ const std::string& requestHash() const;
void setEmbeddedCode(const ProtectedString& value);
const boost::flyweight<ProtectedString>& getEmbeddedCode() const;
const ProtectedString& getEmbeddedCodeSafe() const;
/*override*/ int getPersistentDataCost() const;
/*override*/ void fireSourceChanged();
private:
std::string getHash() { return requestHash(); }
static const Reflection::BoundFuncDesc<Script, std::string()> func_GetHash;
};
// Only runs on a local machine if either
// a) Inside a tool which is inside a local character
// b) Inside the local backpack
// Local scripts have the full power of a normal script, but can also interact with the Mouse.
// While they are currently run client side, this is a large security hole that will have to be addressed.
// The plan is to have them execute server side, but with an adapter taking the place of the "Mouse" object and abstracting it per-user
//
// A better name would be GuiScript or UserScript, if we could redo this work.
extern const char* const sLocalScript;
class LocalScript
: public DescribedCreatable<LocalScript, Script, sLocalScript>
{
public:
LocalScript();
~LocalScript() {}
};
class BaseScript::Slot
{
rbx::signals::connection connection;
public:
// A Slot must keep a reference to its connection, because it
// must be capable of disconnecting itself.
void assignConnection(const rbx::signals::connection& connection)
{
this->connection = connection;
}
protected:
Slot()
{
}
~Slot()
{
// TODO: Disconnect here???
}
void disconnect()
{
connection.disconnect();
}
};
}