mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 08:47:17 +01:00
1058 lines
30 KiB
C++
1058 lines
30 KiB
C++
// ------------------------------------------------------------------------------------------------
|
|
#include "Misc/Signal.hpp"
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
namespace SqMod {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQMODE_DECL_TYPENAME(Typename, _SC("SqSignalWrapper"))
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::SignalContainer Signal::s_Signals;
|
|
Signal::FreeSignals Signal::s_FreeSignals;
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Terminate()
|
|
{
|
|
// Terminate named signals
|
|
for (const auto & s : s_Signals)
|
|
{
|
|
// Clear slots
|
|
s.second.mPtr->Clear();
|
|
// Release the name
|
|
s.second.mPtr->m_Name.clear();
|
|
// Release whatever is in the user data
|
|
s.second.mPtr->m_Data.Release();
|
|
}
|
|
// Finally clear the container itself
|
|
s_Signals.clear();
|
|
// Create a copy so we don't invalidate iterators when destructor removes the instances
|
|
FreeSignals fsig(s_FreeSignals);
|
|
// Terminate anonymous signals
|
|
for (const auto & s : fsig)
|
|
{
|
|
// Clear slots
|
|
s->Clear();
|
|
// Release whatever is in the user data
|
|
s->m_Data.Release();
|
|
}
|
|
// Finally clear the container itself
|
|
s_FreeSignals.clear();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Object Signal::Create()
|
|
{
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Create the signal instance
|
|
DeleteGuard< Signal > dg(new Signal());
|
|
// Attempt to create the signal instance
|
|
ClassType< Signal >::PushInstance(DefaultVM::Get(), dg.Get());
|
|
// This is now managed by the script
|
|
dg.Release();
|
|
// Return the created signal
|
|
return Var< Object >(DefaultVM::Get(), -1).value;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Object Signal::Create(String name)
|
|
{
|
|
// Validate the signal name
|
|
if (name.empty())
|
|
{
|
|
STHROWF("Empty signal names are not allowed");
|
|
}
|
|
// Compute the hash of the specified name
|
|
const std::size_t hash = std::hash< String >{}(name);
|
|
// See if the signal already exists
|
|
for (const auto & e : s_Signals)
|
|
{
|
|
if (e.first == hash)
|
|
{
|
|
return e.second.mObj; // Found a match so let's return it
|
|
}
|
|
}
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Create the signal instance
|
|
DeleteGuard< Signal > dg(new Signal(std::move(name)));
|
|
// Grab the signal instance pointer
|
|
Signal * ptr = dg.Get();
|
|
// Attempt to create the signal instance
|
|
ClassType< Signal >::PushInstance(DefaultVM::Get(), ptr);
|
|
// This is now managed by the script
|
|
dg.Release();
|
|
// Grab a reference to the instance created on the stack
|
|
s_Signals.emplace_back(hash, SignalReference(ptr, Var< Object >(DefaultVM::Get(), -1).value));
|
|
// Return the created signal
|
|
return s_Signals.back().second.mObj;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Remove(String name)
|
|
{
|
|
// Validate the signal name
|
|
if (name.empty())
|
|
{
|
|
STHROWF("Empty signal names are not allowed");
|
|
}
|
|
// Compute the hash of the specified name
|
|
const std::size_t hash = std::hash< String >{}(name);
|
|
// Iterator to the existing signal, if any
|
|
SignalContainer::const_iterator itr = s_Signals.cbegin();
|
|
// Search for a signal with this name
|
|
for (; itr != s_Signals.cend(); ++itr)
|
|
{
|
|
if (itr->first == hash)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// Did we find anything?
|
|
if (itr != s_Signals.cend())
|
|
{
|
|
// Clear the name
|
|
itr->second.mPtr->m_Name.clear();
|
|
// Put it on the free list
|
|
s_FreeSignals.push_back(itr->second.mPtr);
|
|
// Finally, remove it from the named list
|
|
s_Signals.erase(itr);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Object Signal::Fetch(String name)
|
|
{
|
|
// Validate the signal name
|
|
if (name.empty())
|
|
{
|
|
STHROWF("Empty signal names are not allowed");
|
|
}
|
|
// Compute the hash of the specified name
|
|
const std::size_t hash = std::hash< String >{}(name);
|
|
// Search for a signal with this name
|
|
for (const auto & e : s_Signals)
|
|
{
|
|
if (e.first == hash)
|
|
{
|
|
return e.second.mObj; // Found a match so let's return it
|
|
}
|
|
}
|
|
// No such signal exists
|
|
STHROWF("Unknown signal named (%s)", name.c_str());
|
|
// SHOULD NOT REACH THIS POINT!
|
|
return NullObject();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::Signal()
|
|
: m_Head(nullptr), m_Data(), m_Name()
|
|
{
|
|
s_FreeSignals.push_back(this);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::Signal(const CSStr name)
|
|
: Signal(String(name ? name : _SC("")))
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::Signal(const String & name)
|
|
: Signal(String(name))
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::Signal(String && name)
|
|
: m_Head(nullptr), m_Data(), m_Name(std::move(name))
|
|
{
|
|
if (m_Name.empty())
|
|
{
|
|
s_FreeSignals.push_back(this);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Signal::~Signal()
|
|
{
|
|
Clear();
|
|
if (m_Name.empty())
|
|
{
|
|
s_FreeSignals.erase(std::remove(s_FreeSignals.begin(), s_FreeSignals.end(), this),
|
|
s_FreeSignals.end());
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::Count() const
|
|
{
|
|
// Don't attempt to count anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Final count
|
|
Uint32 count = 0;
|
|
// Walk down the chain and count all nodes
|
|
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
|
{
|
|
++count;
|
|
}
|
|
// Return the count
|
|
return count;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Clear()
|
|
{
|
|
// Don't attempt to clear anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Walk down the chain and delete all nodes
|
|
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Delete the node instance
|
|
delete node;
|
|
}
|
|
// Reset the head
|
|
m_Head = nullptr;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Connect(Object & env, Function & func)
|
|
{
|
|
// Don't attempt to search anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
m_Head = new Slot(env, func, nullptr);
|
|
// We're done here
|
|
return;
|
|
}
|
|
|
|
const Slot slot{env, func};
|
|
// Don't attempt to search anything if there's only one element
|
|
if (m_Head->mNext == nullptr)
|
|
{
|
|
// Is it already inserted?
|
|
if (*m_Head != slot)
|
|
{
|
|
m_Head = new Slot(env, func, m_Head);
|
|
}
|
|
// We're done here
|
|
return;
|
|
}
|
|
|
|
// Walk down the chain and find an already matching node
|
|
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
|
{
|
|
if (*node == slot)
|
|
{
|
|
return; // Already connected
|
|
}
|
|
}
|
|
// Connect now
|
|
m_Head = new Slot(env, func, m_Head);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Signal::Connected(Object & env, Function & func) const
|
|
{
|
|
// Don't attempt to search anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const Slot slot{env, func};
|
|
// Walk down the chain and find a matching node
|
|
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
|
{
|
|
if (*node == slot)
|
|
{
|
|
return true; // Found it
|
|
}
|
|
}
|
|
// No such slot exists
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Disconnect(Object & env, Function & func)
|
|
{
|
|
// Don't attempt to search anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const Slot slot{env, func};
|
|
// Walk down the chain and remove the matching nodes
|
|
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Is this our node?
|
|
if (*node == slot)
|
|
{
|
|
// Is this the head?
|
|
if (node == m_Head)
|
|
{
|
|
m_Head = next; // Move the head to the next one
|
|
}
|
|
// Detach it from the chain
|
|
node->Detach();
|
|
// Delete the node instance
|
|
delete node;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::DisconnectThis(Object & env)
|
|
{
|
|
// Don't attempt to search anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const SQHash hash = Slot{env, NullFunction()}.mEnvHash;
|
|
// Walk down the chain and remove the matching nodes
|
|
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Is this our node?
|
|
if (node->mEnvHash == hash)
|
|
{
|
|
// Is this the head?
|
|
if (node == m_Head)
|
|
{
|
|
m_Head = next; // Move the head to the next one
|
|
}
|
|
// Detach it from the chain
|
|
node->Detach();
|
|
// Delete the node instance
|
|
delete node;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::DisconnectFunc(Function & func)
|
|
{
|
|
// Don't attempt to search anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const SQHash hash = Slot{NullObject(), func}.mFuncHash;
|
|
// Walk down the chain and remove the matching nodes
|
|
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Is this our node?
|
|
if (node->mFuncHash == hash)
|
|
{
|
|
// Is this the head?
|
|
if (node == m_Head)
|
|
{
|
|
m_Head = next; // Move the head to the next one
|
|
}
|
|
// Detach it from the chain
|
|
node->Detach();
|
|
// Delete the node instance
|
|
delete node;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Uint32 Signal::CountThis(Object & env) const
|
|
{
|
|
// Don't attempt to count anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const SQHash hash = Slot{env, NullFunction()}.mEnvHash;
|
|
// Final count
|
|
Uint32 count = 0;
|
|
// Walk down the chain and count the matching nodes
|
|
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
|
{
|
|
// Is this our node?
|
|
if (node->mEnvHash == hash)
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
// Return the count
|
|
return count;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Uint32 Signal::CountFunc(Function & func) const
|
|
{
|
|
// Don't attempt to count anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const SQHash hash = Slot{NullObject(), func}.mFuncHash;
|
|
// Final count
|
|
Uint32 count = 0;
|
|
// Walk down the chain and count the matching nodes
|
|
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
|
{
|
|
// Is this our node?
|
|
if (node->mFuncHash == hash)
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
// Return the count
|
|
return count;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Head(Object & env, Function & func)
|
|
{
|
|
// Don't attempt to search anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
m_Head = new Slot(env, func, nullptr);
|
|
// We're done here
|
|
return;
|
|
}
|
|
|
|
const Slot slot{env, func};
|
|
// Don't attempt to search anything if there's only one element
|
|
if (m_Head->mNext == nullptr)
|
|
{
|
|
// Is it already inserted?
|
|
if (*m_Head != slot)
|
|
{
|
|
m_Head = new Slot(env, func, m_Head);
|
|
}
|
|
// We're done here
|
|
return;
|
|
}
|
|
|
|
// Grab the head node
|
|
Slot * node = m_Head;
|
|
// Walk down the chain and find a matching node
|
|
for (; node != nullptr; node = node->mNext)
|
|
{
|
|
if (*node == slot)
|
|
{
|
|
break; // Found it
|
|
}
|
|
}
|
|
// Have we found anything?
|
|
if (node == nullptr)
|
|
{
|
|
m_Head = new Slot(env, func, m_Head); // Lead everyone
|
|
}
|
|
// Is it the head already?
|
|
else if (m_Head != node)
|
|
{
|
|
node->AttachNext(m_Head);
|
|
// We're the head now
|
|
m_Head = node;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Signal::Tail(Object & env, Function & func)
|
|
{
|
|
// Don't attempt to search anything if there's no head
|
|
if (m_Head == nullptr)
|
|
{
|
|
m_Head = new Slot(env, func, nullptr);
|
|
// We're done here
|
|
return;
|
|
}
|
|
|
|
const Slot slot{env, func};
|
|
// Don't attempt to search anything if there's only one element
|
|
if (m_Head->mNext == nullptr)
|
|
{
|
|
// Is it already inserted?
|
|
if (*m_Head != slot)
|
|
{
|
|
m_Head->mNext = new Slot(env, func, nullptr);
|
|
// Link with the head
|
|
m_Head->mNext->mPrev = m_Head;
|
|
}
|
|
// We're done here
|
|
return;
|
|
}
|
|
|
|
// Grab the head node
|
|
Slot * node = m_Head, * prev = nullptr;
|
|
// Walk down the chain and find a matching node
|
|
for (; node != nullptr; prev = node, node = node->mNext)
|
|
{
|
|
if (*node == slot)
|
|
{
|
|
break; // Found it
|
|
}
|
|
}
|
|
// Have we found anything?
|
|
if (node == nullptr)
|
|
{
|
|
// Create the slot now
|
|
node = new Slot(env, func, nullptr);
|
|
}
|
|
else
|
|
{
|
|
// Walk down the chain until the end
|
|
while (prev->mNext != nullptr)
|
|
{
|
|
prev = prev->mNext;
|
|
}
|
|
// Finally, detach the node from it's current position
|
|
node->Detach();
|
|
}
|
|
// Knowing 'prev' points to last element
|
|
node->AttachPrev(prev);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqEmit(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// The signal instance
|
|
Signal * signal = nullptr;
|
|
// Attempt to extract the signal instance
|
|
try
|
|
{
|
|
signal = Var< Signal * >(vm, 1).value;
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
return sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Walk down the chain and trigger slots
|
|
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Remember the current stack size
|
|
const StackGuard sg(vm);
|
|
// Push the callback object
|
|
sq_pushobject(vm, node->mFuncRef);
|
|
// Is there an explicit environment?
|
|
if (sq_isnull(node->mEnvRef))
|
|
{
|
|
sq_pushroottable(vm);
|
|
}
|
|
else
|
|
{
|
|
sq_pushobject(vm, node->mEnvRef);
|
|
}
|
|
// Are there any parameters to forward?
|
|
if (top > 1)
|
|
{
|
|
for (SQInteger i = 2; i <= top; ++i)
|
|
{
|
|
sq_push(vm, i);
|
|
}
|
|
}
|
|
// Make the function call and store the result
|
|
const SQRESULT res = sq_call(vm, top, false, ErrorHandling::IsEnabled());
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
}
|
|
// Specify that we don't return anything
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqQuery(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// Do we have the collector environment?
|
|
if (top <= 1)
|
|
{
|
|
return sq_throwerror(vm, "Missing collector environment");
|
|
}
|
|
// Do we have the collector function?
|
|
else if (top <= 2)
|
|
{
|
|
return sq_throwerror(vm, "Missing collector callback");
|
|
}
|
|
// The signal instance
|
|
Signal * signal = nullptr;
|
|
// Attempt to extract the signal instance and collector
|
|
try
|
|
{
|
|
signal = Var< Signal * >(vm, 1).value;
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
return sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// The collector
|
|
HSQOBJECT cenv, cfunc;
|
|
// Grab the collector environment
|
|
SQRESULT res = sq_getstackobj(vm, 2, &cenv);
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Was there a valid environment?
|
|
else if (sq_isnull(cenv))
|
|
{
|
|
// Remember the current stack size
|
|
const StackGuard sg(vm);
|
|
// Default to the root table
|
|
sq_pushroottable(vm);
|
|
// Try to grab the collector environment again
|
|
SQRESULT res = sq_getstackobj(vm, -1, &cenv);
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
}
|
|
// Grab the collector function
|
|
res = sq_getstackobj(vm, 3, &cfunc);
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Some dummy checks to make sure the collector is a callable object
|
|
else if (!sq_isfunction(cfunc) && !sq_isclosure(cfunc) && !sq_isnativeclosure(cfunc))
|
|
{
|
|
return sq_throwerror(vm, "Invalid collector");
|
|
}
|
|
// Walk down the chain and trigger slots
|
|
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Remember the current stack size
|
|
const StackGuard sg(vm);
|
|
// Push the callback object
|
|
sq_pushobject(vm, node->mFuncRef);
|
|
// Is there an explicit environment?
|
|
if (sq_isnull(node->mEnvRef))
|
|
{
|
|
sq_pushroottable(vm);
|
|
}
|
|
else
|
|
{
|
|
sq_pushobject(vm, node->mEnvRef);
|
|
}
|
|
// Are there any parameters to forward?
|
|
if (top > 3)
|
|
{
|
|
for (SQInteger i = 4; i <= top; ++i)
|
|
{
|
|
sq_push(vm, i);
|
|
}
|
|
}
|
|
// Make the function call and store the result
|
|
res = sq_call(vm, top - 2, true, ErrorHandling::IsEnabled());
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Push the collector onto the stack
|
|
sq_pushobject(vm, cfunc);
|
|
sq_pushobject(vm, cenv);
|
|
// Push the returned value
|
|
sq_push(vm, -3);
|
|
// Make the function call and store the result
|
|
res = sq_call(vm, 2, false, ErrorHandling::IsEnabled());
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
}
|
|
// Specify that we don't return anything
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqConsume(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// The signal instance
|
|
Signal * signal = nullptr;
|
|
// Attempt to extract the signal instance
|
|
try
|
|
{
|
|
signal = Var< Signal * >(vm, 1).value;
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
return sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Default to not consumed
|
|
SQBool ret = SQFalse;
|
|
// Walk down the chain and trigger slots
|
|
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Remember the current stack size
|
|
const StackGuard sg(vm);
|
|
// Push the callback object
|
|
sq_pushobject(vm, node->mFuncRef);
|
|
// Is there an explicit environment?
|
|
if (sq_isnull(node->mEnvRef))
|
|
{
|
|
sq_pushroottable(vm);
|
|
}
|
|
else
|
|
{
|
|
sq_pushobject(vm, node->mEnvRef);
|
|
}
|
|
// Are there any parameters to forward?
|
|
if (top > 1)
|
|
{
|
|
for (SQInteger i = 2; i <= top; ++i)
|
|
{
|
|
sq_push(vm, i);
|
|
}
|
|
}
|
|
// Make the function call and store the result
|
|
const SQRESULT res = sq_call(vm, top, true, ErrorHandling::IsEnabled());
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Is the returned value not null?
|
|
else if (sq_gettype(vm, -1) != OT_NULL)
|
|
{
|
|
// Obtain the returned value
|
|
sq_tobool(vm, -1, &ret);
|
|
// Should we proceed to the next slot or stop here?
|
|
if (ret == SQTrue)
|
|
{
|
|
break; // The slot consumed the signal
|
|
}
|
|
}
|
|
}
|
|
// Forward the returned value to the invoker
|
|
sq_pushbool(vm, ret);
|
|
// Specify that we returned something
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqApprove(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// The signal instance
|
|
Signal * signal = nullptr;
|
|
// Attempt to extract the signal instance
|
|
try
|
|
{
|
|
signal = Var< Signal * >(vm, 1).value;
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
return sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Default to approved
|
|
SQBool ret = SQTrue;
|
|
// Walk down the chain and trigger slots
|
|
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Remember the current stack size
|
|
const StackGuard sg(vm);
|
|
// Push the callback object
|
|
sq_pushobject(vm, node->mFuncRef);
|
|
// Is there an explicit environment?
|
|
if (sq_isnull(node->mEnvRef))
|
|
{
|
|
sq_pushroottable(vm);
|
|
}
|
|
else
|
|
{
|
|
sq_pushobject(vm, node->mEnvRef);
|
|
}
|
|
// Are there any parameters to forward?
|
|
if (top > 1)
|
|
{
|
|
for (SQInteger i = 2; i <= top; ++i)
|
|
{
|
|
sq_push(vm, i);
|
|
}
|
|
}
|
|
// Make the function call and store the result
|
|
const SQRESULT res = sq_call(vm, top, true, ErrorHandling::IsEnabled());
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Is the returned value not null?
|
|
else if (sq_gettype(vm, -1) != OT_NULL)
|
|
{
|
|
// Obtain the returned value
|
|
sq_tobool(vm, -1, &ret);
|
|
// Should we proceed to the next slot or stop here?
|
|
if (ret == SQFalse)
|
|
{
|
|
break; // The slot did not approve the signal
|
|
}
|
|
}
|
|
}
|
|
// Forward the returned value to the invoker
|
|
sq_pushbool(vm, ret);
|
|
// Specify that we returned something
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Signal::SqRequest(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// The signal instance
|
|
Signal * signal = nullptr;
|
|
// Attempt to extract the signal instance
|
|
try
|
|
{
|
|
signal = Var< Signal * >(vm, 1).value;
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// Do we have a valid signal instance?
|
|
if (!signal)
|
|
{
|
|
return sq_throwerror(vm, "Invalid signal instance");
|
|
}
|
|
// Walk down the chain and trigger slots
|
|
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
|
{
|
|
// Grab the next node upfront
|
|
next = node->mNext;
|
|
// Remember the current stack size
|
|
const StackGuard sg(vm);
|
|
// Push the callback object
|
|
sq_pushobject(vm, node->mFuncRef);
|
|
// Is there an explicit environment?
|
|
if (sq_isnull(node->mEnvRef))
|
|
{
|
|
sq_pushroottable(vm);
|
|
}
|
|
else
|
|
{
|
|
sq_pushobject(vm, node->mEnvRef);
|
|
}
|
|
// Are there any parameters to forward?
|
|
if (top > 1)
|
|
{
|
|
for (SQInteger i = 2; i <= top; ++i)
|
|
{
|
|
sq_push(vm, i);
|
|
}
|
|
}
|
|
// Make the function call and store the result
|
|
const SQRESULT res = sq_call(vm, top, true, ErrorHandling::IsEnabled());
|
|
// Validate the result
|
|
if (SQ_FAILED(res))
|
|
{
|
|
return res; // Propagate the error
|
|
}
|
|
// Is the returned value not null?
|
|
else if (sq_gettype(vm, -1) != OT_NULL)
|
|
{
|
|
// Push back the returned value
|
|
sq_push(vm, -1);
|
|
// Specify that we returned something
|
|
return 1;
|
|
}
|
|
}
|
|
// Specify that we returned nothing
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static SQInteger SqCreateSignal(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// Was the signal name specified?
|
|
if (top <= 1)
|
|
{
|
|
sq_pushobject(DefaultVM::Get(), Signal::Create());
|
|
}
|
|
else
|
|
{
|
|
// Attempt to generate the string value
|
|
StackStrF val(vm, 2);
|
|
// Have we failed to retrieve the string?
|
|
if (SQ_FAILED(val.mRes))
|
|
{
|
|
return val.mRes; // Propagate the error!
|
|
}
|
|
// Create the signal instance and push it on the stack
|
|
try
|
|
{
|
|
sq_pushobject(DefaultVM::Get(), Signal::Create(String(val.mPtr, val.mLen)));
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
}
|
|
// We have an argument on the stack
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static SQInteger SqRemoveSignal(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// Was the signal name specified?
|
|
if (top <= 1)
|
|
{
|
|
return sq_throwerror(vm, "Missing signal name");
|
|
}
|
|
// Attempt to generate the string value
|
|
StackStrF val(vm, 2);
|
|
// Have we failed to retrieve the string?
|
|
if (SQ_FAILED(val.mRes))
|
|
{
|
|
return val.mRes; // Propagate the error!
|
|
}
|
|
// Create the signal instance and push it on the stack
|
|
try
|
|
{
|
|
Signal::Remove(String(val.mPtr, val.mLen));
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// We have an argument on the stack
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static SQInteger SqFetchSignal(HSQUIRRELVM vm)
|
|
{
|
|
const Int32 top = sq_gettop(vm);
|
|
// Was the signal name specified?
|
|
if (top <= 1)
|
|
{
|
|
return sq_throwerror(vm, "Missing signal name");
|
|
}
|
|
// Attempt to generate the string value
|
|
StackStrF val(vm, 2);
|
|
// Have we failed to retrieve the string?
|
|
if (SQ_FAILED(val.mRes))
|
|
{
|
|
return val.mRes; // Propagate the error!
|
|
}
|
|
// Create the signal instance and push it on the stack
|
|
try
|
|
{
|
|
sq_pushobject(DefaultVM::Get(), Signal::Fetch(String(val.mPtr, val.mLen)));
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
return sq_throwerror(vm, e.what());
|
|
}
|
|
// We have an argument on the stack
|
|
return 1;
|
|
}
|
|
|
|
// ================================================================================================
|
|
void Register_Signal(HSQUIRRELVM vm)
|
|
{
|
|
RootTable(vm).Bind(Typename::Str,
|
|
Class< Signal, NoConstructor< Signal > >(vm, Typename::Str)
|
|
// Meta-methods
|
|
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
|
|
.Func(_SC("_tostring"), &Signal::ToString)
|
|
// Core Properties
|
|
.Prop(_SC("Data"), &Signal::GetData, &Signal::SetData)
|
|
.Prop(_SC("Slots"), &Signal::Count)
|
|
// Core Methods
|
|
.Func(_SC("Clear"), &Signal::Clear)
|
|
.Func(_SC("Connect"), &Signal::Connect)
|
|
.Func(_SC("Connected"), &Signal::Connected)
|
|
.Func(_SC("Disconnect"), &Signal::Disconnect)
|
|
.Func(_SC("EliminateThis"), &Signal::DisconnectThis)
|
|
.Func(_SC("EliminateFunc"), &Signal::DisconnectFunc)
|
|
.Func(_SC("CountThis"), &Signal::CountThis)
|
|
.Func(_SC("CountFunc"), &Signal::CountFunc)
|
|
.Func(_SC("Head"), &Signal::Head)
|
|
.Func(_SC("Tail"), &Signal::Tail)
|
|
// Squirrel Functions
|
|
.SquirrelFunc(_SC("Emit"), &Signal::SqEmit)
|
|
.SquirrelFunc(_SC("Broadcast"), &Signal::SqEmit)
|
|
.SquirrelFunc(_SC("Query"), &Signal::SqQuery)
|
|
.SquirrelFunc(_SC("Consume"), &Signal::SqConsume)
|
|
.SquirrelFunc(_SC("Approve"), &Signal::SqApprove)
|
|
.SquirrelFunc(_SC("Request"), &Signal::SqRequest)
|
|
);
|
|
|
|
RootTable(vm)
|
|
.SquirrelFunc(_SC("SqSignal"), &SqFetchSignal)
|
|
.SquirrelFunc(_SC("SqCreateSignal"), &SqCreateSignal)
|
|
.SquirrelFunc(_SC("SqRemoveSignal"), &SqRemoveSignal);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Forward the call to terminate the signals.
|
|
*/
|
|
void TerminateSignals()
|
|
{
|
|
Signal::Terminate();
|
|
}
|
|
|
|
} // Namespace:: SqMod
|