1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-14 19:57:17 +01:00
SqMod/include/sqrat/sqratFunction.h

336 lines
12 KiB
C
Raw Normal View History

2015-11-01 09:06:54 +01:00
//
// sqratFunction: Squirrel Function Wrapper
//
//
// Copyright (c) 2009 Brandon Jones
// Copyirght 2011 Li-Cheng (Andy) Tai
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
#if !defined(_SCRAT_SQFUNC_H_)
#define _SCRAT_SQFUNC_H_
2016-02-23 16:48:30 +01:00
#ifdef SQMOD_PLUGIN_API
#include <SqAPI.h>
2016-02-23 16:48:30 +01:00
#else
#include <squirrelex.h>
2016-02-23 16:48:30 +01:00
#endif // SQMOD_PLUGIN_API
2015-11-01 09:06:54 +01:00
#include "sqratObject.h"
namespace Sqrat {
2018-07-31 16:41:46 +02:00
// Represents a function in Squirrel
struct Function {
2015-11-01 09:06:54 +01:00
friend class TableBase;
friend class Table;
friend class ArrayBase;
friend struct Var<Function>;
HSQOBJECT mEnv, mObj;
2015-11-01 09:06:54 +01:00
2018-07-31 16:41:46 +02:00
// Default constructor (null)
Function() {
sq_resetobject(&mEnv);
sq_resetobject(&mObj);
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Copy constructor
Function(const Function& sf) : mEnv(sf.mEnv), mObj(sf.mObj) {
sq_addref(DefaultVM::Get_(), &mEnv);
sq_addref(DefaultVM::Get_(), &mObj);
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Move constructor
Function(Function&& sf) : mEnv(sf.mEnv), mObj(sf.mObj) {
sq_resetobject(&sf.GetEnv());
sq_resetobject(&sf.GetFunc());
}
2018-07-31 16:41:46 +02:00
// Constructs a Function from a slot in an Object
Function(const Object& e, const SQChar* slot) : mEnv(e.GetObject()) {
sq_addref(DefaultVM::Get_(), &mEnv);
2015-11-01 09:06:54 +01:00
Object so = e.GetSlot(slot);
mObj = so.GetObject();
sq_addref(DefaultVM::Get_(), &mObj);
2015-11-01 09:06:54 +01:00
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQObjectType value_type = so.GetType();
2018-07-31 16:41:46 +02:00
if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE) {
2015-11-01 09:06:54 +01:00
// Note that classes can also be considered functions in Squirrel
2018-07-31 16:41:46 +02:00
SQTHROW(DefaultVM::Get_(), _SC("function not found in slot"));
2015-11-01 09:06:54 +01:00
}
#endif
}
2018-07-31 16:41:46 +02:00
// Constructs a Function from a value off the stack at the specified index
// Assumes the Function environment is at index 1.
Function(HSQUIRRELVM vm, SQInteger idx) {
sq_getstackobj(vm, 1, &mEnv);
sq_getstackobj(vm, idx, &mObj);
sq_addref(vm, &mEnv);
sq_addref(vm, &mObj);
2018-07-31 16:41:46 +02:00
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQObjectType value_type = sq_gettype(vm, idx);
if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE) {
SQTHROW(vm, FormatTypeError(vm, idx, _SC("closure")));
}
#endif
}
2019-08-15 16:48:24 +02:00
// Constructs a Function from a value off the stack at the specified index pointed by `idx2`
// Assumes the Function environment is at index pointed by `idx1`
// If `idx1` is null, it defaults to root table
// If `idx1` is an object, it will be used as the environment
// If `idx1` is actually a function/closure then `idx2` is ignored
Function(HSQUIRRELVM vm, SQInteger idx1, SQInteger idx2) : Function() {
Assign(vm, idx1, idx2);
}
2018-07-31 16:41:46 +02:00
// Constructs a Function from two Squirrel objects (one is the environment object and the other is the function object)
Function(HSQUIRRELVM vm, HSQOBJECT e, HSQOBJECT o) : mEnv(e), mObj(o) {
sq_addref(vm, &mEnv);
sq_addref(vm, &mObj);
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Destructor
2015-11-01 09:06:54 +01:00
~Function() {
Release();
}
2018-07-31 16:41:46 +02:00
// Copy Assignment operator
Function& operator=(const Function& sf) {
2015-11-01 09:06:54 +01:00
Release();
mEnv = sf.mEnv;
mObj = sf.mObj;
2018-07-31 16:41:46 +02:00
if (!sf.IsNull()) {
sq_addref(DefaultVM::Get_(), &mEnv);
sq_addref(DefaultVM::Get_(), &mObj);
2018-07-31 16:41:46 +02:00
}
2015-11-01 09:06:54 +01:00
return *this;
}
2018-07-31 16:41:46 +02:00
// Move Assignment operator
Function& operator=(Function&& sf) {
Release();
mEnv = sf.mEnv;
mObj = sf.mObj;
sq_resetobject(&sf.GetEnv());
sq_resetobject(&sf.GetFunc());
return *this;
}
2018-07-31 16:41:46 +02:00
// Checks whether the Function is null
bool IsNull() const {
return sq_isnull(mObj);
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Gets the Squirrel environment object for this Function (copy)
HSQOBJECT GetEnv() const {
return mEnv;
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Gets the Squirrel environment object for this Function (reference)
HSQOBJECT& GetEnv() {
return mEnv;
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Gets the Squirrel function object for this Function (copy)
HSQOBJECT GetFunc() const {
return mObj;
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Gets the Squirrel function object for this Function (reference)
HSQOBJECT& GetFunc() {
return mObj;
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Gets the Squirrel VM for this Function
HSQUIRRELVM GetVM() const {
2018-07-31 16:41:46 +02:00
return DefaultVM::Get_();
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Sets the Function to null (removing its references to underlying Squirrel objects)
void Release() {
2015-11-01 09:06:54 +01:00
if(!IsNull()) {
sq_release(DefaultVM::Get_(), &mEnv);
sq_release(DefaultVM::Get_(), &mObj);
sq_resetobject(&mEnv);
sq_resetobject(&mObj);
2015-11-01 09:06:54 +01:00
}
}
2018-07-31 16:41:46 +02:00
// Sets the Function to null (removing its references to underlying Squirrel objects)
// This doesn't call release if the reference count is 1.
// Workaround for some weird squirrel behavior that generates an assert in debug mode.
void ReleaseGently() {
2015-11-01 09:06:54 +01:00
if(!IsNull()) {
sq_release(DefaultVM::Get_(), &mEnv);
if (sq_getrefcount(DefaultVM::Get_(), &mObj) > 1) {
sq_release(DefaultVM::Get_(), &mObj);
2015-11-01 09:06:54 +01:00
}
sq_resetobject(&mEnv);
sq_resetobject(&mObj);
2015-11-01 09:06:54 +01:00
}
}
2019-08-15 16:48:24 +02:00
// Assigns a Function from a value off the stack at the specified index pointed by `idx2`
// Assumes the Function environment is at index pointed by `idx1`
// If `idx1` is null, it defaults to root table
// If `idx1` is an object, it will be used as the environment
// If `idx1` is actually a function/closure then `idx2` is ignored
bool Assign(HSQUIRRELVM vm, SQInteger idx1, SQInteger idx2) {
2019-08-15 16:48:24 +02:00
// Release current callback, if any
Release();
// Tells if the current environment was used
bool cenv = false;
2019-08-15 16:48:24 +02:00
// Retrieve the environment type
const SQObjectType ty1 = sq_gettype(vm, idx1);
// Should we use the root table as the environment?
if (ty1 == OT_NULL) {
sq_pushroottable(vm); // Push it on the stack
sq_getstackobj(vm, -1, &mEnv); // Grab it
sq_poptop(vm); // Clean up the stack
// Should we use current environment?
} else if (ty1 & (_RT_CLOSURE | _RT_NATIVECLOSURE)) {
sq_getstackobj(vm, 1, &mEnv); // `this` as environment
idx2 = idx1; // There is no explicit environment given
cenv = true;
2019-08-15 16:48:24 +02:00
// Is there a specific environment?
} else if (ty1 & (_RT_TABLE | _RT_CLASS | _RT_INSTANCE)) {
sq_getstackobj(vm, idx1, &mEnv); // Grab the given environment
} else {
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQTHROW(vm, FormatTypeError(vm, idx1, _SC("object")));
#else
return cenv;
2019-08-15 16:48:24 +02:00
#endif
}
// Retrieve the callback type
const SQObjectType ty2 = sq_gettype(vm, idx2);
// Is the callback is valid?
if (ty2 & (_RT_CLOSURE | _RT_NATIVECLOSURE)) {
sq_getstackobj(vm, idx2, &mObj);
// Reference the environment and function
sq_addref(vm, &mEnv);
sq_addref(vm, &mObj);
} else {
sq_resetobject(&mEnv); // Discard obtained environment
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQTHROW(vm, FormatTypeError(vm, idx2, _SC("closure")));
#endif
}
// Return whether current environment was used
return cenv;
2019-08-15 16:48:24 +02:00
}
2018-07-31 16:41:46 +02:00
// Runs the Function and returns its value as a SharedPtr
template<class R, class... Args> SharedPtr<R> Evaluate(Args &&... args) {
static constexpr unsigned ARGC = sizeof...(Args) + 1; // + environment
2018-07-31 16:41:46 +02:00
HSQUIRRELVM vm = DefaultVM::Get_();
2015-11-01 09:06:54 +01:00
SQInteger top = sq_gettop(vm);
2018-07-31 16:41:46 +02:00
// Push the environment followed by the function
sq_pushobject(vm, mObj);
sq_pushobject(vm, mEnv);
2018-07-31 16:41:46 +02:00
// Validate the funtion parameter count
2015-11-01 09:06:54 +01:00
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQInteger nparams;
SQInteger nfreevars;
if (SQ_SUCCEEDED(sq_getclosureinfo(vm, -2, &nparams, &nfreevars)) && (nparams != ARGC)) {
2015-11-01 09:06:54 +01:00
sq_pop(vm, 2);
SQTHROW(vm, _SC("wrong number of parameters"));
return SharedPtr<R>();
}
#endif
2018-07-31 16:41:46 +02:00
// Push the arguments
PushVars(vm, std::forward<Args>(args)...);
2015-11-01 09:06:54 +01:00
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQRESULT result = sq_call(vm, ARGC, true, ErrorHandling::IsEnabled());
2015-11-01 09:06:54 +01:00
//handle an error: pop the stack and throw the exception
if (SQ_FAILED(result)) {
sq_settop(vm, top);
SQTHROW(vm, LastErrorString(vm));
return SharedPtr<R>();
}
#else
sq_call(vm, ARGC, true, ErrorHandling::IsEnabled());
2015-11-01 09:06:54 +01:00
#endif
SharedPtr<R> ret = Var<SharedPtr<R> >(vm, -1).value;
sq_settop(vm, top);
return ret;
}
2018-07-31 16:41:46 +02:00
// Runs the Function
template< class... Args >
void Execute(Args &&... args) {
static constexpr unsigned ARGC = sizeof...(Args) + 1; // + environment
2018-07-31 16:41:46 +02:00
HSQUIRRELVM vm = DefaultVM::Get_();
2015-11-01 09:06:54 +01:00
SQInteger top = sq_gettop(vm);
2018-07-31 16:41:46 +02:00
// Push the environment followed by the function
sq_pushobject(vm, mObj);
sq_pushobject(vm, mEnv);
2018-07-31 16:41:46 +02:00
// Validate the funtion parameter count
2015-11-01 09:06:54 +01:00
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQInteger nparams;
SQInteger nfreevars;
if (SQ_SUCCEEDED(sq_getclosureinfo(vm, -2, &nparams, &nfreevars)) && (nparams != ARGC)) {
2015-11-01 09:06:54 +01:00
sq_pop(vm, 2);
SQTHROW(vm, _SC("wrong number of parameters"));
return;
2015-11-01 09:06:54 +01:00
}
#endif
2018-07-31 16:41:46 +02:00
PushVars(vm, std::forward<Args>(args)...);
2015-11-01 09:06:54 +01:00
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQRESULT result = sq_call(vm, ARGC, false, ErrorHandling::IsEnabled());
2015-11-01 09:06:54 +01:00
sq_settop(vm, top);
//handle an error: throw the exception
2015-11-01 09:06:54 +01:00
if (SQ_FAILED(result)) {
SQTHROW(vm, LastErrorString(vm));
return;
2015-11-01 09:06:54 +01:00
}
#else
sq_call(vm, ARGC, false, ErrorHandling::IsEnabled());
2015-11-01 09:06:54 +01:00
sq_settop(vm, top);
#endif
}
2018-07-31 16:41:46 +02:00
// Runs the Function
template< class... Args > void operator() (Args &&... args) {
Execute(std::forward<Args>(args)...);
2015-11-01 09:06:54 +01:00
}
};
2018-07-31 16:41:46 +02:00
// Used to get and push Function instances to and from the stack as references (functions are always references in Squirrel)
template<> struct Var<Function> {
/// The actual value of get operations
Function value;
// Attempts to get the value off the stack at idx as a Function
// Assumes the Function environment is at index 1.
Var(HSQUIRRELVM vm, SQInteger idx) : value(vm, idx) {
2015-11-01 09:06:54 +01:00
}
2018-07-31 16:41:46 +02:00
// Called by Sqrat::PushVar to put a Function on the stack
static void push(HSQUIRRELVM vm, const Function& value) {
2015-11-01 09:06:54 +01:00
sq_pushobject(vm, value.GetFunc());
}
};
2018-07-31 16:41:46 +02:00
// Used to get and push Function instances to and from the stack as references (functions are always references in Squirrel)
template<> struct Var<Function&> : public Var<Function> {
Var(HSQUIRRELVM vm, SQInteger idx) : Var<Function>(vm, idx)
{
}
};
// Used to get and push Function instances to and from the stack as references (functions are always references in Squirrel)
template<> struct Var<const Function&> : public Var<Function> {
Var(HSQUIRRELVM vm, SQInteger idx) : Var<Function>(vm, idx)
{
}
};
2015-11-01 09:06:54 +01:00
}
#endif