Skip to content

Commit 7efec77

Browse files
committed
Optimize squirrel function_stub() Vector/QAngle return value
Instead of temporary allocating dynamic memory, which subsequently is copied into another dynamically allocated piece of memory, provide a temporary buffer on the stack. The script value inserted into the VM still needs to allocate dynamically though... A few sites are adapted to not create a ScriptVariant_t from a temporary Vector to avoid dynamic allocation there too. cf. #321
1 parent f2b2e28 commit 7efec77

File tree

5 files changed

+141
-89
lines changed

5 files changed

+141
-89
lines changed

sp/src/game/server/hl2/proto_sniper.cpp

+9-6
Original file line numberDiff line numberDiff line change
@@ -2645,16 +2645,19 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget )
26452645
{
26462646
// By default, aim for the center
26472647
Vector vecTarget = pTarget->WorldSpaceCenter();
2648+
const Vector vecBulletOrigin = GetBulletOrigin();
26482649

26492650
#ifdef MAPBASE_VSCRIPT
2650-
if (m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope(m_ScriptScope))
2651+
if ( m_ScriptScope.IsInitialized() && g_Hook_GetActualShootPosition.CanRunInScope( m_ScriptScope ) )
26512652
{
26522653
ScriptVariant_t functionReturn;
2653-
ScriptVariant_t args[] = { GetBulletOrigin(), ToHScript( pTarget ) };
2654-
if (g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args ))
2654+
ScriptVariant_t args[] = { vecBulletOrigin, ToHScript( pTarget ) };
2655+
if ( g_Hook_GetActualShootPosition.Call( m_ScriptScope, &functionReturn, args ) )
26552656
{
2656-
if (functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f)
2657+
if ( functionReturn.m_type == FIELD_VECTOR && functionReturn.m_pVector->LengthSqr() != 0.0f )
2658+
{
26572659
return *functionReturn.m_pVector;
2660+
}
26582661
}
26592662
}
26602663
#endif
@@ -2682,12 +2685,12 @@ Vector CProtoSniper::DesiredBodyTarget( CBaseEntity *pTarget )
26822685
{
26832686
if( flTimeSinceLastMiss > 0.0f && flTimeSinceLastMiss < 4.0f && hl2_episodic.GetBool() )
26842687
{
2685-
vecTarget = pTarget->BodyTarget( GetBulletOrigin(), false );
2688+
vecTarget = pTarget->BodyTarget( vecBulletOrigin, false );
26862689
}
26872690
else
26882691
{
26892692
// Shoot zombies in the headcrab
2690-
vecTarget = pTarget->HeadTarget( GetBulletOrigin() );
2693+
vecTarget = pTarget->HeadTarget( vecBulletOrigin );
26912694
}
26922695
}
26932696
else if( pTarget->Classify() == CLASS_ANTLION )

sp/src/game/shared/mapbase/vscript_consts_shared.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,8 @@ void RegisterSharedScriptConstants()
346346
ScriptRegisterConstant( g_pScriptVM, ROPE_NO_GRAVITY, "Disable gravity on this rope. (for use in rope flags)" );
347347
ScriptRegisterConstant( g_pScriptVM, ROPE_NUMFLAGS, "The number of rope flags recognized by the game." );
348348

349-
ScriptRegisterConstantNamed( g_pScriptVM, Vector( ROPE_GRAVITY ), "ROPE_GRAVITY", "Default rope gravity vector." );
349+
static Vector vecRopeGravity( ROPE_GRAVITY );
350+
ScriptRegisterConstantNamed( g_pScriptVM, vecRopeGravity, "ROPE_GRAVITY", "Default rope gravity vector." );
350351

351352
//
352353
// Sounds

sp/src/public/vscript/ivscript.h

+33-71
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ enum ScriptFuncBindingFlags_t
300300
SF_MEMBER_FUNC = 0x01,
301301
};
302302

303-
typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn );
303+
union ScriptVariantTemporaryStorage_t;
304+
typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage );
304305

305306
struct ScriptFunctionBinding_t
306307
{
@@ -389,93 +390,44 @@ struct ScriptVariant_t
389390
ScriptVariant_t( bool val ) : m_flags( 0 ), m_type( FIELD_BOOLEAN ) { m_bool = val; }
390391
ScriptVariant_t( HSCRIPT val ) : m_flags( 0 ), m_type( FIELD_HSCRIPT ) { m_hScript = val; }
391392

392-
ScriptVariant_t( const Vector &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR )
393-
{
394-
if ( !bCopy )
395-
{
396-
m_pVector = &val;
397-
}
398-
else
399-
{
400-
m_pVector = (Vector*)malloc( sizeof( Vector ) );
401-
new ( (Vector*)m_pVector ) Vector( val );
402-
m_flags |= SV_FREE;
403-
}
404-
}
405-
ScriptVariant_t( const Vector *val, bool bCopy = false ) : ScriptVariant_t( *val ) { }
393+
ScriptVariant_t( const Vector &val ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { m_pVector = &val; }
394+
ScriptVariant_t( const Vector *val ) : ScriptVariant_t( *val ) { }
406395

407-
ScriptVariant_t( const char *val , bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_CSTRING )
408-
{
409-
if ( !bCopy )
410-
{
411-
m_pszString = val;
412-
}
413-
else
414-
{
415-
m_pszString = strdup( val );
416-
m_flags |= SV_FREE;
417-
}
418-
}
396+
ScriptVariant_t( const char *val ) : m_flags( 0 ), m_type( FIELD_CSTRING ) { m_pszString = val; }
419397

420398
#ifdef MAPBASE_VSCRIPT
421-
ScriptVariant_t( const QAngle &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR )
422-
{
423-
if ( !bCopy )
424-
{
425-
m_pAngle = &val;
426-
}
427-
else
428-
{
429-
m_pAngle = (QAngle*)malloc( sizeof( QAngle ) );
430-
new ( (QAngle*)m_pAngle ) QAngle( val );
431-
m_flags |= SV_FREE;
432-
}
433-
}
434-
ScriptVariant_t( const QAngle *val, bool bCopy = false ) : ScriptVariant_t( *val ) { }
399+
ScriptVariant_t( const QAngle &val ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { m_pAngle = &val; }
400+
ScriptVariant_t( const QAngle *val ) : ScriptVariant_t( *val ) { }
435401

436-
ScriptVariant_t( Vector &&val ) : ScriptVariant_t( val, true ) { }
437-
ScriptVariant_t( QAngle &&val ) : ScriptVariant_t( val, true ) { }
402+
ScriptVariant_t( Vector &&val ) = delete;
403+
ScriptVariant_t( QAngle &&val ) = delete;
438404
#endif
439405

440406
bool IsNull() const { return (m_type == FIELD_VOID ); }
441407

442408
operator int() const { Assert( m_type == FIELD_INTEGER ); return m_int; }
443409
operator float() const { Assert( m_type == FIELD_FLOAT ); return m_float; }
444410
operator const char *() const { Assert( m_type == FIELD_CSTRING ); return ( m_pszString ) ? m_pszString : ""; }
445-
operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); static Vector vecNull(0, 0, 0); return (m_pVector) ? *m_pVector : vecNull; }
411+
operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); return (m_pVector) ? *m_pVector : vec3_origin; }
446412
operator char() const { Assert( m_type == FIELD_CHARACTER ); return m_char; }
447413
operator bool() const { Assert( m_type == FIELD_BOOLEAN ); return m_bool; }
448414
operator HSCRIPT() const { Assert( m_type == FIELD_HSCRIPT ); return m_hScript; }
449415
#ifdef MAPBASE_VSCRIPT
450-
operator const QAngle &() const { Assert( m_type == FIELD_VECTOR ); static QAngle vecNull(0, 0, 0); return (m_pAngle) ? *m_pAngle : vecNull; }
416+
operator const QAngle &() const { Assert( m_type == FIELD_VECTOR ); return (m_pAngle) ? *m_pAngle : vec3_angle; }
451417
#endif
452418

453-
void operator=( int i ) { m_type = FIELD_INTEGER; m_int = i; }
454-
void operator=( float f ) { m_type = FIELD_FLOAT; m_float = f; }
455-
void operator=( double f ) { m_type = FIELD_FLOAT; m_float = (float)f; }
456-
void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_pVector = &vec; }
457-
void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_pVector = vec; }
458-
void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_pszString = psz; }
459-
void operator=( char c ) { m_type = FIELD_CHARACTER; m_char = c; }
460-
void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_bool = b; }
461-
void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_hScript = h; }
419+
void operator=( int i ) { m_type = FIELD_INTEGER; m_flags = 0; m_int = i; }
420+
void operator=( float f ) { m_type = FIELD_FLOAT; m_flags = 0; m_float = f; }
421+
void operator=( double f ) { m_type = FIELD_FLOAT; m_flags = 0; m_float = (float)f; }
422+
void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pVector = &vec; }
423+
void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pVector = vec; }
424+
void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_flags = 0; m_pszString = psz; }
425+
void operator=( char c ) { m_type = FIELD_CHARACTER; m_flags = 0; m_char = c; }
426+
void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_flags = 0; m_bool = b; }
427+
void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_flags = 0; m_hScript = h; }
462428
#ifdef MAPBASE_VSCRIPT
463-
void operator=( const QAngle &vec ) { m_type = FIELD_VECTOR; m_pAngle = &vec; }
464-
void operator=( const QAngle *vec ) { m_type = FIELD_VECTOR; m_pAngle = vec; }
465-
466-
void operator=( Vector &&vec )
467-
{
468-
m_pVector = (Vector*)malloc( sizeof( Vector ) );
469-
new ( (Vector*)m_pVector ) Vector( vec );
470-
m_flags |= SV_FREE;
471-
}
472-
473-
void operator=( QAngle &&ang )
474-
{
475-
m_pAngle = (QAngle*)malloc( sizeof( QAngle ) );
476-
new ( (QAngle*)m_pAngle ) Vector( ang );
477-
m_flags |= SV_FREE;
478-
}
429+
void operator=( const QAngle &vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pAngle = &vec; }
430+
void operator=( const QAngle *vec ) { m_type = FIELD_VECTOR; m_flags = 0; m_pAngle = vec; }
479431
#endif
480432

481433
void Free()
@@ -649,6 +601,16 @@ inline void ScriptVariant_t::EmplaceAllocedVector( const Vector &vec )
649601

650602
#define SCRIPT_VARIANT_NULL ScriptVariant_t()
651603

604+
union ScriptVariantTemporaryStorage_t
605+
{
606+
// members must be initialized via placement-new
607+
ScriptVariantTemporaryStorage_t() { }
608+
609+
// members must have trivial destructor, since no destructor will be invoked
610+
Vector m_vec;
611+
QAngle m_ang;
612+
};
613+
652614
#ifdef MAPBASE_VSCRIPT
653615
//---------------------------------------------------------
654616
struct ScriptConstantBinding_t
@@ -741,7 +703,7 @@ static inline int ToConstantVariant(int value)
741703
// This is used for registering variants (particularly vectors) not tied to existing variables.
742704
// The principal difference is that m_data is initted with bCopy set to true.
743705
#define ScriptRegisterConstantFromTemp( pVM, constant, description ) ScriptRegisterConstantFromTempNamed( pVM, constant, #constant, description )
744-
#define ScriptRegisterConstantFromTempNamed( pVM, constant, scriptName, description ) do { static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ScriptVariant_t( constant, true ); pVM->RegisterConstant( &binding ); } while (0)
706+
#define ScriptRegisterConstantFromTempNamed( pVM, constant, scriptName, description ) do { static const auto constantStorage = constant; static ScriptConstantBinding_t binding; binding.m_pszScriptName = scriptName; binding.m_pszDescription = description; binding.m_data = ScriptVariant_t( constantStorage ); pVM->RegisterConstant( &binding ); } while (0)
745707

746708
//-----------------------------------------------------------------------------
747709
//

sp/src/public/vscript/vscript_templates.h

+94-10
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
326326
class CNonMemberScriptBinding##N \
327327
{ \
328328
public: \
329-
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
330-
{ \
329+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
330+
{ \
331331
Assert( nArguments == N ); \
332332
Assert( pReturn ); \
333333
Assert( !pContext ); \
@@ -337,15 +337,15 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
337337
return false; \
338338
} \
339339
*pReturn = ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \
340-
return true; \
341-
} \
340+
return true; \
341+
} \
342342
}; \
343343
\
344344
template <typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
345345
class CNonMemberScriptBinding##N<FUNC_TYPE, void FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
346346
{ \
347347
public: \
348-
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
348+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
349349
{ \
350350
Assert( nArguments == N ); \
351351
Assert( !pReturn ); \
@@ -360,12 +360,52 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
360360
} \
361361
}; \
362362
\
363+
template <typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
364+
class CNonMemberScriptBinding##N<FUNC_TYPE, Vector FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
365+
{ \
366+
public: \
367+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
368+
{ \
369+
Assert( nArguments == N ); \
370+
Assert( pReturn ); \
371+
Assert( !pContext ); \
372+
\
373+
if ( nArguments != N || !pReturn || pContext ) \
374+
{ \
375+
return false; \
376+
} \
377+
new ( &temporaryReturnStorage.m_vec ) Vector( ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ) ); \
378+
*pReturn = temporaryReturnStorage.m_vec; \
379+
return true; \
380+
} \
381+
}; \
382+
\
383+
template <typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
384+
class CNonMemberScriptBinding##N<FUNC_TYPE, QAngle FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
385+
{ \
386+
public: \
387+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
388+
{ \
389+
Assert( nArguments == N ); \
390+
Assert( pReturn ); \
391+
Assert( !pContext ); \
392+
\
393+
if ( nArguments != N || !pReturn || pContext ) \
394+
{ \
395+
return false; \
396+
} \
397+
new ( &temporaryReturnStorage.m_ang ) QAngle( ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ) ); \
398+
*pReturn = temporaryReturnStorage.m_ang; \
399+
return true; \
400+
} \
401+
}; \
402+
\
363403
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
364404
class CMemberScriptBinding##N \
365405
{ \
366406
public: \
367-
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
368-
{ \
407+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
408+
{ \
369409
Assert( nArguments == N ); \
370410
Assert( pReturn ); \
371411
Assert( pContext ); \
@@ -375,15 +415,15 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
375415
return false; \
376416
} \
377417
*pReturn = (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid<FUNC_TYPE>(pFunction))( SCRIPT_BINDING_ARGS_##N ); \
378-
return true; \
379-
} \
418+
return true; \
419+
} \
380420
}; \
381421
\
382422
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
383423
class CMemberScriptBinding##N<OBJECT_TYPE_PTR, FUNC_TYPE, void FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
384424
{ \
385425
public: \
386-
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \
426+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
387427
{ \
388428
Assert( nArguments == N ); \
389429
Assert( !pReturn ); \
@@ -398,6 +438,46 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
398438
} \
399439
}; \
400440
\
441+
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
442+
class CMemberScriptBinding##N<OBJECT_TYPE_PTR, FUNC_TYPE, Vector FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
443+
{ \
444+
public: \
445+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
446+
{ \
447+
Assert( nArguments == N ); \
448+
Assert( pReturn ); \
449+
Assert( pContext ); \
450+
\
451+
if ( nArguments != N || !pReturn || !pContext ) \
452+
{ \
453+
return false; \
454+
} \
455+
new ( &temporaryReturnStorage.m_vec ) Vector( (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid<FUNC_TYPE>(pFunction))( SCRIPT_BINDING_ARGS_##N ) ); \
456+
*pReturn = temporaryReturnStorage.m_vec; \
457+
return true; \
458+
} \
459+
}; \
460+
\
461+
template <class OBJECT_TYPE_PTR, typename FUNC_TYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
462+
class CMemberScriptBinding##N<OBJECT_TYPE_PTR, FUNC_TYPE, QAngle FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N> \
463+
{ \
464+
public: \
465+
static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn, ScriptVariantTemporaryStorage_t &temporaryReturnStorage ) \
466+
{ \
467+
Assert( nArguments == N ); \
468+
Assert( pReturn ); \
469+
Assert( pContext ); \
470+
\
471+
if ( nArguments != N || !pReturn || !pContext ) \
472+
{ \
473+
return false; \
474+
} \
475+
new ( &temporaryReturnStorage.m_ang ) QAngle( (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid<FUNC_TYPE>(pFunction))( SCRIPT_BINDING_ARGS_##N ) ); \
476+
*pReturn = temporaryReturnStorage.m_ang; \
477+
return true; \
478+
} \
479+
}; \
480+
\
401481
template <typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N> \
402482
inline ScriptBindingFunc_t ScriptCreateBinding(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \
403483
{ \
@@ -419,7 +499,11 @@ inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p )
419499
return &CMemberScriptBinding##N<OBJECT_TYPE_PTR, Func_t, FUNCTION_RETTYPE FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_##N>::Call; \
420500
}
421501

502+
//note: no memory is actually allocated in the functions that get defined,
503+
// it merely uses placement-new for which we need to disable this
504+
#include "tier0/memdbgoff.h"
422505
FUNC_GENERATE_ALL( DEFINE_SCRIPT_BINDINGS );
506+
#include "tier0/memdbgon.h"
423507

424508
//-----------------------------------------------------------------------------
425509
//

sp/src/vscript/vscript_squirrel.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1399,14 +1399,15 @@ SQInteger function_stub(HSQUIRRELVM vm)
13991399
}
14001400

14011401
ScriptVariant_t retval;
1402+
ScriptVariantTemporaryStorage_t retval_storage;
14021403

14031404
SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm);
14041405
assert(pSquirrelVM);
14051406

14061407
sq_resetobject(&pSquirrelVM->lastError_);
14071408

14081409
(*pFunc->m_pfnBinding)(pFunc->m_pFunction, instance, params.Base(), nargs,
1409-
pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &retval);
1410+
pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &retval, retval_storage);
14101411

14111412
if (!sq_isnull(pSquirrelVM->lastError_))
14121413
{
@@ -1417,6 +1418,7 @@ SQInteger function_stub(HSQUIRRELVM vm)
14171418

14181419
PushVariant(vm, retval);
14191420

1421+
Assert(!(retval.m_flags & SV_FREE));
14201422
retval.Free();
14211423

14221424
return pFunc->m_desc.m_ReturnType != FIELD_VOID;

0 commit comments

Comments
 (0)