mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-01-19 12:07:13 +01:00
Move some of the signal implementation to source file.
Don't combine the Consume and Approve methods. The null value does not count towards returned values anymore.
This commit is contained in:
parent
df12603405
commit
da76d87a93
@ -150,6 +150,780 @@ Object Signal::Fetch(String name)
|
|||||||
return NullObject();
|
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)
|
static SQInteger SqCreateSignal(HSQUIRRELVM vm)
|
||||||
{
|
{
|
||||||
|
@ -347,41 +347,22 @@ private:
|
|||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
*/
|
*/
|
||||||
Signal()
|
Signal();
|
||||||
: m_Head(nullptr), m_Data(), m_Name()
|
|
||||||
{
|
|
||||||
s_FreeSignals.push_back(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Base constructor.
|
* Base constructor.
|
||||||
*/
|
*/
|
||||||
Signal(const CSStr name)
|
Signal(const CSStr name);
|
||||||
: Signal(String(name ? name : _SC("")))
|
|
||||||
{
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Base constructor.
|
* Base constructor.
|
||||||
*/
|
*/
|
||||||
Signal(const String & name)
|
Signal(const String & name);
|
||||||
: Signal(String(name))
|
|
||||||
{
|
|
||||||
/* ... */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Base constructor.
|
* Base constructor.
|
||||||
*/
|
*/
|
||||||
Signal(String && name)
|
Signal(String && name);
|
||||||
: m_Head(nullptr), m_Data(), m_Name(std::move(name))
|
|
||||||
{
|
|
||||||
if (m_Name.empty())
|
|
||||||
{
|
|
||||||
s_FreeSignals.push_back(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -398,15 +379,7 @@ public:
|
|||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Destructor.
|
* Destructor.
|
||||||
*/
|
*/
|
||||||
~Signal()
|
~Signal();
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
if (m_Name.empty())
|
|
||||||
{
|
|
||||||
s_FreeSignals.erase(std::remove(s_FreeSignals.begin(), s_FreeSignals.end(), this),
|
|
||||||
s_FreeSignals.end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Copy assignment operator. (disabled)
|
* Copy assignment operator. (disabled)
|
||||||
@ -469,708 +442,82 @@ public:
|
|||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* The number of slots connected to the signal.
|
* The number of slots connected to the signal.
|
||||||
*/
|
*/
|
||||||
SQInteger Count() const
|
SQInteger 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Clear all slots connected to the signal.
|
* Clear all slots connected to the signal.
|
||||||
*/
|
*/
|
||||||
void Clear()
|
void 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Connect a function with a specific environment to the signal.
|
* Connect a function with a specific environment to the signal.
|
||||||
*/
|
*/
|
||||||
void Connect(Object & env, Function & func)
|
void 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* See whether a function with a specific environment is connected to the signal.
|
* See whether a function with a specific environment is connected to the signal.
|
||||||
*/
|
*/
|
||||||
bool Connected(Object & env, Function & func) const
|
bool 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Disconnect a function with a specific environment from the signal.
|
* Disconnect a function with a specific environment from the signal.
|
||||||
*/
|
*/
|
||||||
void Disconnect(Object & env, Function & func)
|
void 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Disconnect all functions with a specific environment from the signal.
|
* Disconnect all functions with a specific environment from the signal.
|
||||||
*/
|
*/
|
||||||
void DisconnectThis(Object & env)
|
void 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Disconnect all matching functions regardless of the environment from the signal.
|
* Disconnect all matching functions regardless of the environment from the signal.
|
||||||
*/
|
*/
|
||||||
void DisconnectFunc(Function & func)
|
void 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Count all functions with a specific environment from the signal.
|
* Count all functions with a specific environment from the signal.
|
||||||
*/
|
*/
|
||||||
Uint32 CountThis(Object & env) const
|
Uint32 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Count all matching functions regardless of the environment from the signal.
|
* Count all matching functions regardless of the environment from the signal.
|
||||||
*/
|
*/
|
||||||
Uint32 CountFunc(Function & func) const
|
Uint32 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Make sure that specified slot is positioned ahead of all other slots.
|
* Make sure that specified slot is positioned ahead of all other slots.
|
||||||
*/
|
*/
|
||||||
void Head(Object & env, Function & func)
|
void 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Make sure that specified slot is positioned behind of all other slots.
|
* Make sure that specified slot is positioned behind of all other slots.
|
||||||
*/
|
*/
|
||||||
void Tail(Object & env, Function & func)
|
void 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Emit the event to the connected slots.
|
* Emit the event to the connected slots.
|
||||||
*/
|
*/
|
||||||
static SQInteger SqEmit(HSQUIRRELVM vm)
|
static SQInteger 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Emit the event to the connected slots and collect returned values.
|
* Emit the event to the connected slots and collect returned values.
|
||||||
*/
|
*/
|
||||||
static SQInteger SqQuery(HSQUIRRELVM vm)
|
static SQInteger 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
|
||||||
* Actual implementation of the consume method.
|
|
||||||
*/
|
|
||||||
static SQInteger SqConsumeApproveImpl(HSQUIRRELVM vm, const SQBool rval, bool neg)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
// Return value of each slot
|
|
||||||
SQBool ret = !rval;
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
// Obtain the returned value
|
|
||||||
sq_tobool(vm, -1, &ret);
|
|
||||||
// Should we proceed to the next slot or stop here?
|
|
||||||
if (ret == rval)
|
|
||||||
{
|
|
||||||
// The slot satisfied our criteria
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Forward the returned value to the invoker
|
|
||||||
sq_pushbool(vm, neg ? !ret : ret);
|
|
||||||
// Specify that we returned something
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Emit the event to the connected slots and see if they consume it.
|
* Emit the event to the connected slots and see if they consume it.
|
||||||
*/
|
*/
|
||||||
static SQInteger SqConsume(HSQUIRRELVM vm)
|
static SQInteger SqConsume(HSQUIRRELVM vm);
|
||||||
{
|
|
||||||
return SqConsumeApproveImpl(vm, SQTrue, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Emit the event to the connected slots and see if they approve it.
|
* Emit the event to the connected slots and see if they approve it.
|
||||||
*/
|
*/
|
||||||
static SQInteger SqApprove(HSQUIRRELVM vm)
|
static SQInteger SqApprove(HSQUIRRELVM vm);
|
||||||
{
|
|
||||||
return SqConsumeApproveImpl(vm, SQFalse, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------------------------
|
||||||
* Emit the event to the connected slots and see if they return something.
|
* Emit the event to the connected slots and see if they return something.
|
||||||
*/
|
*/
|
||||||
static SQInteger SqRequest(HSQUIRRELVM vm)
|
static SQInteger 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user