2020-04-06 19:10:07 +00:00
/*
* * vm . h
* * VM < - > native interface
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright - 2016 Randy Heit
* * Copyright 2016 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
# ifndef VM_H
# define VM_H
# include "autosegs.h"
# include "zstring.h"
# include "vectors.h"
# include "cmdlib.h"
# include "engineerrors.h"
# include "memarena.h"
# include "name.h"
# include "scopebarrier.h"
class DObject ;
union VMOP ;
class VMScriptFunction ;
extern FMemArena ClassDataAllocator ;
# define MAX_RETURNS 8 // Maximum number of results a function called by script code can return
# define MAX_TRY_DEPTH 8 // Maximum number of nested TRYs in a single function
void JitRelease ( ) ;
2020-04-06 19:57:47 +00:00
extern void ( * VM_CastSpriteIDToString ) ( FString * a , unsigned int b ) ;
2020-04-06 19:10:07 +00:00
typedef unsigned char VM_UBYTE ;
typedef signed char VM_SBYTE ;
typedef unsigned short VM_UHALF ;
typedef signed short VM_SHALF ;
typedef unsigned int VM_UWORD ;
typedef signed int VM_SWORD ;
# define VM_EPSILON (1 / 65536.0)
// Register types for VMParam
enum
{
REGT_INT = 0 ,
REGT_FLOAT = 1 ,
REGT_STRING = 2 ,
REGT_POINTER = 3 ,
REGT_TYPE = 3 ,
REGT_KONST = 4 ,
REGT_MULTIREG2 = 8 ,
REGT_MULTIREG3 = 16 , // (e.g. a vector)
REGT_MULTIREG = 24 ,
REGT_ADDROF = 32 , // used with PARAM: pass address of this register
REGT_NIL = 128 // parameter was omitted
} ;
# define RET_FINAL (0x80) // Used with RET and RETI in the destination slot: this is the final return value
enum EVMAbortException
{
X_OTHER ,
X_READ_NIL ,
X_WRITE_NIL ,
X_TOO_MANY_TRIES ,
X_ARRAY_OUT_OF_BOUNDS ,
X_DIVISION_BY_ZERO ,
X_BAD_SELF ,
X_FORMAT_ERROR
} ;
class CVMAbortException : public CEngineError
{
public :
static FString stacktrace ;
CVMAbortException ( EVMAbortException reason , const char * moreinfo , va_list ap ) ;
void MaybePrintMessage ( ) ;
} ;
// This must be a separate function because the VC compiler would otherwise allocate memory on the stack for every separate instance of the exception object that may get thrown.
void ThrowAbortException ( EVMAbortException reason , const char * moreinfo , . . . ) ;
void ThrowAbortException ( VMScriptFunction * sfunc , VMOP * line , EVMAbortException reason , const char * moreinfo , . . . ) ;
void ClearGlobalVMStack ( ) ;
struct VMReturn
{
void * Location ;
VM_UBYTE RegType ; // Same as VMParam RegType, except REGT_KONST is invalid; only used by asserts
void SetInt ( int val )
{
assert ( RegType = = REGT_INT ) ;
* ( int * ) Location = val ;
}
void SetFloat ( double val )
{
assert ( RegType = = REGT_FLOAT ) ;
* ( double * ) Location = val ;
}
void SetVector ( const double val [ 3 ] )
{
assert ( RegType = = ( REGT_FLOAT | REGT_MULTIREG3 ) ) ;
( ( double * ) Location ) [ 0 ] = val [ 0 ] ;
( ( double * ) Location ) [ 1 ] = val [ 1 ] ;
( ( double * ) Location ) [ 2 ] = val [ 2 ] ;
}
void SetVector ( const DVector3 & val )
{
assert ( RegType = = ( REGT_FLOAT | REGT_MULTIREG3 ) ) ;
( ( double * ) Location ) [ 0 ] = val [ 0 ] ;
( ( double * ) Location ) [ 1 ] = val [ 1 ] ;
( ( double * ) Location ) [ 2 ] = val [ 2 ] ;
}
void SetVector2 ( const double val [ 2 ] )
{
assert ( RegType = = ( REGT_FLOAT | REGT_MULTIREG2 ) ) ;
( ( double * ) Location ) [ 0 ] = val [ 0 ] ;
( ( double * ) Location ) [ 1 ] = val [ 1 ] ;
}
void SetVector2 ( const DVector2 & val )
{
assert ( RegType = = ( REGT_FLOAT | REGT_MULTIREG2 ) ) ;
( ( double * ) Location ) [ 0 ] = val [ 0 ] ;
( ( double * ) Location ) [ 1 ] = val [ 1 ] ;
}
void SetString ( const FString & val )
{
assert ( RegType = = REGT_STRING ) ;
* ( FString * ) Location = val ;
}
void SetPointer ( void * val )
{
assert ( RegType = = REGT_POINTER ) ;
* ( void * * ) Location = val ;
}
void SetObject ( DObject * val )
{
assert ( RegType = = REGT_POINTER ) ;
* ( void * * ) Location = val ;
}
void IntAt ( int * loc )
{
Location = loc ;
RegType = REGT_INT ;
}
void FloatAt ( double * loc )
{
Location = loc ;
RegType = REGT_FLOAT ;
}
void Vec2At ( DVector2 * loc )
{
Location = loc ;
RegType = REGT_FLOAT | REGT_MULTIREG2 ;
}
void StringAt ( FString * loc )
{
Location = loc ;
RegType = REGT_STRING ;
}
void PointerAt ( void * * loc )
{
Location = loc ;
RegType = REGT_POINTER ;
}
VMReturn ( ) { }
VMReturn ( int * loc ) { IntAt ( loc ) ; }
VMReturn ( double * loc ) { FloatAt ( loc ) ; }
VMReturn ( DVector2 * loc ) { Vec2At ( loc ) ; }
VMReturn ( FString * loc ) { StringAt ( loc ) ; }
VMReturn ( void * * loc ) { PointerAt ( loc ) ; }
} ;
struct VMRegisters ;
struct TypedVMValue
{
union
{
int i ;
void * a ;
double f ;
struct { int pad [ 3 ] ; VM_UBYTE Type ; } ;
struct { int foo [ 4 ] ; } biggest ;
const FString * sp ;
} ;
const FString & s ( ) const { return * sp ; }
TypedVMValue ( )
{
a = NULL ;
Type = REGT_NIL ;
}
TypedVMValue ( const TypedVMValue & o )
{
biggest = o . biggest ;
}
TypedVMValue ( int v )
{
i = v ;
Type = REGT_INT ;
}
TypedVMValue ( double v )
{
f = v ;
Type = REGT_FLOAT ;
}
TypedVMValue ( const FString * s )
{
sp = s ;
Type = REGT_STRING ;
}
TypedVMValue ( DObject * v )
{
a = v ;
Type = REGT_POINTER ;
}
TypedVMValue ( void * v )
{
a = v ;
Type = REGT_POINTER ;
}
TypedVMValue & operator = ( const TypedVMValue & o )
{
biggest = o . biggest ;
return * this ;
}
TypedVMValue & operator = ( int v )
{
i = v ;
Type = REGT_INT ;
return * this ;
}
TypedVMValue & operator = ( double v )
{
f = v ;
Type = REGT_FLOAT ;
return * this ;
}
TypedVMValue & operator = ( const FString * v )
{
sp = v ;
Type = REGT_STRING ;
return * this ;
}
TypedVMValue & operator = ( DObject * v )
{
a = v ;
Type = REGT_POINTER ;
return * this ;
}
} ;
struct VMValue
{
union
{
int i ;
void * a ;
double f ;
struct { int foo [ 2 ] ; } biggest ;
const FString * sp ;
} ;
const FString & s ( ) const { return * sp ; }
VMValue ( )
{
a = NULL ;
}
VMValue ( const VMValue & o )
{
biggest = o . biggest ;
}
VMValue ( int v )
{
i = v ;
}
VMValue ( double v )
{
f = v ;
}
VMValue ( const char * s ) = delete ;
VMValue ( const FString & s ) = delete ;
VMValue ( const FString * s )
{
sp = s ;
}
VMValue ( void * v )
{
a = v ;
}
VMValue & operator = ( const VMValue & o )
{
biggest = o . biggest ;
return * this ;
}
VMValue & operator = ( const TypedVMValue & o )
{
memcpy ( & biggest , & o . biggest , sizeof ( biggest ) ) ;
return * this ;
}
VMValue & operator = ( int v )
{
i = v ;
return * this ;
}
VMValue & operator = ( double v )
{
f = v ;
return * this ;
}
VMValue & operator = ( const FString * v )
{
sp = v ;
return * this ;
}
VMValue & operator = ( const FString & v ) = delete ;
VMValue & operator = ( const char * v ) = delete ;
VMValue & operator = ( DObject * v )
{
a = v ;
return * this ;
}
int ToInt ( int Type )
{
if ( Type = = REGT_INT )
{
return i ;
}
if ( Type = = REGT_FLOAT )
{
return int ( f ) ;
}
if ( Type = = REGT_STRING )
{
return ( int ) s ( ) . ToLong ( ) ;
}
// FIXME
return 0 ;
}
double ToDouble ( int Type )
{
if ( Type = = REGT_FLOAT )
{
return f ;
}
if ( Type = = REGT_INT )
{
return i ;
}
if ( Type = = REGT_STRING )
{
return s ( ) . ToDouble ( ) ;
}
// FIXME
return 0 ;
}
} ;
class VMFunction
{
public :
bool Unsafe = false ;
uint8_t ImplicitArgs = 0 ; // either 0 for static, 1 for method or 3 for action
int VarFlags = 0 ; // [ZZ] this replaces 5+ bool fields
unsigned VirtualIndex = ~ 0u ;
FName Name ;
const uint8_t * RegTypes = nullptr ;
TArray < TypedVMValue > DefaultArgs ;
FString PrintableName ; // so that the VM can print meaningful info if something in this function goes wrong.
class PPrototype * Proto ;
TArray < uint32_t > ArgFlags ; // Should be the same length as Proto->ArgumentTypes
int ( * ScriptCall ) ( VMFunction * func , VMValue * params , int numparams , VMReturn * ret , int numret ) = nullptr ;
VMFunction ( FName name = NAME_None ) : ImplicitArgs ( 0 ) , Name ( name ) , Proto ( NULL )
{
AllFunctions . Push ( this ) ;
}
virtual ~ VMFunction ( ) { }
void * operator new ( size_t size )
{
return ClassDataAllocator . Alloc ( size ) ;
}
void operator delete ( void * block ) { }
void operator delete [ ] ( void * block ) { }
static void DeleteAll ( )
{
for ( auto f : AllFunctions )
{
f - > ~ VMFunction ( ) ;
}
AllFunctions . Clear ( ) ;
// also release any JIT data
JitRelease ( ) ;
}
static void CreateRegUseInfo ( )
{
for ( auto f : AllFunctions )
{
f - > CreateRegUse ( ) ;
}
}
static TArray < VMFunction * > AllFunctions ;
protected :
void CreateRegUse ( ) ;
} ;
// Use this in the prototype for a native function.
# ifdef NDEBUG
# define VM_ARGS VMValue *param, int numparam, VMReturn *ret, int numret
# define VM_ARGS_NAMES param, numparam, ret, numret
# define VM_INVOKE(param, numparam, ret, numret, reginfo) (param), (numparam), (ret), (numret)
# else
# define VM_ARGS VMValue *param, int numparam, VMReturn *ret, int numret, const uint8_t *reginfo
# define VM_ARGS_NAMES param, numparam, ret, numret, reginfo
# define VM_INVOKE(param, numparam, ret, numret, reginfo) (param), (numparam), (ret), (numret), (reginfo)
# endif
class VMNativeFunction : public VMFunction
{
public :
typedef int ( * NativeCallType ) ( VM_ARGS ) ;
// 8 is VARF_Native. I can't write VARF_Native because of circular references between this and dobject/dobjtype.
VMNativeFunction ( ) : NativeCall ( NULL ) { VarFlags = 8 ; ScriptCall = & VMNativeFunction : : NativeScriptCall ; }
VMNativeFunction ( NativeCallType call ) : NativeCall ( call ) { VarFlags = 8 ; ScriptCall = & VMNativeFunction : : NativeScriptCall ; }
VMNativeFunction ( NativeCallType call , FName name ) : VMFunction ( name ) , NativeCall ( call ) { VarFlags = 8 ; ScriptCall = & VMNativeFunction : : NativeScriptCall ; }
// Return value is the number of results.
NativeCallType NativeCall ;
// Function pointer to a native function to be called directly by the JIT using the platform calling convention
void * DirectNativeCall = nullptr ;
private :
static int NativeScriptCall ( VMFunction * func , VMValue * params , int numparams , VMReturn * ret , int numret ) ;
} ;
int VMCall ( VMFunction * func , VMValue * params , int numparams , VMReturn * results , int numresults /*, VMException **trap = NULL*/ ) ;
int VMCallWithDefaults ( VMFunction * func , TArray < VMValue > & params , VMReturn * results , int numresults /*, VMException **trap = NULL*/ ) ;
inline int VMCallAction ( VMFunction * func , VMValue * params , int numparams , VMReturn * results , int numresults /*, VMException **trap = NULL*/ )
{
return VMCall ( func , params , numparams , results , numresults ) ;
}
// Use these to collect the parameters in a native function.
// variable name <x> at position <p>
void NullParam ( const char * varname ) ;
# ifndef NDEBUG
bool AssertObject ( void * ob ) ;
# endif
# define PARAM_NULLCHECK(ptr, var) (ptr == nullptr? NullParam(#var), ptr : ptr)
// This cannot assert because there is no info for varargs
# define PARAM_VA_POINTER(x) const uint8_t *x = (const uint8_t *)param[numparam-1].a;
// For required parameters.
# define PARAM_INT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); int x = param[p].i;
# define PARAM_UINT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); unsigned x = param[p].i;
# define PARAM_BOOL_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); bool x = !!param[p].i;
# define PARAM_NAME_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FName x = ENamedName(param[p].i);
# define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FSoundID x = param[p].i;
# define PARAM_COLOR_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); PalEntry x; x.d = param[p].i;
# define PARAM_FLOAT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); double x = param[p].f;
# define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); DAngle x = param[p].f;
# define PARAM_STRING_VAL_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_STRING); FString x = param[p].s();
# define PARAM_STRING_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_STRING); const FString &x = param[p].s();
# define PARAM_STATELABEL_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); int x = param[p].i;
# define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass());
# define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass());
# define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type *x = (type *)param[p].a;
# define PARAM_OUTPOINTER_AT(p,x,type) assert((p) < numparam); type *x = (type *)param[p].a;
# define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type x = (type )param[p].a;
# define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER && AssertObject(param[p].a)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type)));
# define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base)));
# define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x);
# define PARAM_OBJECT_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER && (AssertObject(param[p].a))); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type)));
# define PARAM_CLASS_NOT_NULL_AT(p,x,base) assert((p) < numparam); assert(reginfo[p] == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base)));
// The above, but with an automatically increasing position index.
# define PARAM_PROLOGUE int paramnum = -1;
# define PARAM_INT(x) ++paramnum; PARAM_INT_AT(paramnum,x)
# define PARAM_UINT(x) ++paramnum; PARAM_UINT_AT(paramnum,x)
# define PARAM_BOOL(x) ++paramnum; PARAM_BOOL_AT(paramnum,x)
# define PARAM_NAME(x) ++paramnum; PARAM_NAME_AT(paramnum,x)
# define PARAM_SOUND(x) ++paramnum; PARAM_SOUND_AT(paramnum,x)
# define PARAM_COLOR(x) ++paramnum; PARAM_COLOR_AT(paramnum,x)
# define PARAM_FLOAT(x) ++paramnum; PARAM_FLOAT_AT(paramnum,x)
# define PARAM_ANGLE(x) ++paramnum; PARAM_ANGLE_AT(paramnum,x)
# define PARAM_STRING(x) ++paramnum; PARAM_STRING_AT(paramnum,x)
# define PARAM_STRING_VAL(x) ++paramnum; PARAM_STRING_VAL_AT(paramnum,x)
# define PARAM_STATELABEL(x) ++paramnum; PARAM_STATELABEL_AT(paramnum,x)
# define PARAM_STATE(x) ++paramnum; PARAM_STATE_AT(paramnum,x)
# define PARAM_STATE_ACTION(x) ++paramnum; PARAM_STATE_ACTION_AT(paramnum,x)
# define PARAM_POINTER(x,type) ++paramnum; PARAM_POINTER_AT(paramnum,x,type)
# define PARAM_OUTPOINTER(x,type) ++paramnum; PARAM_OUTPOINTER_AT(paramnum,x,type)
# define PARAM_POINTERTYPE(x,type) ++paramnum; PARAM_POINTERTYPE_AT(paramnum,x,type)
# define PARAM_OBJECT(x,type) ++paramnum; PARAM_OBJECT_AT(paramnum,x,type)
# define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base)
# define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base)
# define PARAM_POINTER_NOT_NULL(x,type) ++paramnum; PARAM_POINTER_NOT_NULL_AT(paramnum,x,type)
# define PARAM_OBJECT_NOT_NULL(x,type) ++paramnum; PARAM_OBJECT_NOT_NULL_AT(paramnum,x,type)
# define PARAM_CLASS_NOT_NULL(x,base) ++paramnum; PARAM_CLASS_NOT_NULL_AT(paramnum,x,base)
typedef int ( * actionf_p ) ( VM_ARGS ) ;
struct FieldDesc
{
const char * ClassName ;
const char * FieldName ;
size_t FieldOffset ;
unsigned FieldSize ;
int BitValue ;
} ;
namespace
{
// Traits for the types we are interested in
template < typename T > struct native_is_valid { static const bool value = false ; static const bool retval = false ; } ;
template < typename T > struct native_is_valid < T * > { static const bool value = true ; static const bool retval = true ; } ;
template < typename T > struct native_is_valid < T & > { static const bool value = true ; static const bool retval = true ; } ;
template < > struct native_is_valid < void > { static const bool value = true ; static const bool retval = true ; } ;
template < > struct native_is_valid < int > { static const bool value = true ; static const bool retval = true ; } ;
template < > struct native_is_valid < unsigned int > { static const bool value = true ; static const bool retval = true ; } ;
template < > struct native_is_valid < double > { static const bool value = true ; static const bool retval = true ; } ;
template < > struct native_is_valid < bool > { static const bool value = true ; static const bool retval = false ; } ; // Bool as return does not work!
}
// Compile time validation of direct native functions
struct DirectNativeDesc
{
DirectNativeDesc ( ) = default ;
# define TP(n) typename P##n
# define VP(n) ValidateType<P##n>()
template < typename Ret > DirectNativeDesc ( Ret ( * func ) ( ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; }
template < typename Ret , TP ( 1 ) > DirectNativeDesc ( Ret ( * func ) ( P1 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) , TP ( 8 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; VP ( 8 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) , TP ( 8 ) , TP ( 9 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; VP ( 8 ) ; VP ( 9 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) , TP ( 8 ) , TP ( 9 ) , TP ( 10 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 , P10 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; VP ( 8 ) ; VP ( 9 ) ; VP ( 10 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) , TP ( 8 ) , TP ( 9 ) , TP ( 10 ) , TP ( 11 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 , P10 , P11 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; VP ( 8 ) ; VP ( 9 ) ; VP ( 10 ) ; VP ( 11 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) , TP ( 8 ) , TP ( 9 ) , TP ( 10 ) , TP ( 11 ) , TP ( 12 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 , P10 , P11 , P12 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; VP ( 8 ) ; VP ( 9 ) ; VP ( 10 ) ; VP ( 11 ) ; VP ( 12 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) , TP ( 8 ) , TP ( 9 ) , TP ( 10 ) , TP ( 11 ) , TP ( 12 ) , TP ( 13 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 , P10 , P11 , P12 , P13 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; VP ( 8 ) ; VP ( 9 ) ; VP ( 10 ) ; VP ( 11 ) ; VP ( 12 ) ; VP ( 13 ) ; }
template < typename Ret , TP ( 1 ) , TP ( 2 ) , TP ( 3 ) , TP ( 4 ) , TP ( 5 ) , TP ( 6 ) , TP ( 7 ) , TP ( 8 ) , TP ( 9 ) , TP ( 10 ) , TP ( 11 ) , TP ( 12 ) , TP ( 13 ) , TP ( 14 ) > DirectNativeDesc ( Ret ( * func ) ( P1 , P2 , P3 , P4 , P5 , P6 , P7 , P8 , P9 , P10 , P11 , P12 , P13 , P14 ) ) : Ptr ( reinterpret_cast < void * > ( func ) ) { ValidateRet < Ret > ( ) ; VP ( 1 ) ; VP ( 2 ) ; VP ( 3 ) ; VP ( 4 ) ; VP ( 5 ) ; VP ( 6 ) ; VP ( 7 ) ; VP ( 8 ) ; VP ( 9 ) ; VP ( 10 ) ; VP ( 11 ) ; VP ( 12 ) ; VP ( 13 ) , VP ( 14 ) ; }
# undef TP
# undef VP
template < typename T > void ValidateType ( ) { static_assert ( native_is_valid < T > : : value , " Argument type is not valid as a direct native parameter or return type " ) ; }
template < typename T > void ValidateRet ( ) { static_assert ( native_is_valid < T > : : retval , " Return type is not valid as a direct native parameter or return type " ) ; }
operator void * ( ) const { return Ptr ; }
void * Ptr ;
} ;
struct AFuncDesc
{
const char * ClassName ;
const char * FuncName ;
actionf_p Function ;
VMNativeFunction * * VMPointer ;
DirectNativeDesc DirectNative ;
} ;
# if defined(_MSC_VER)
2020-11-12 07:58:10 +00:00
# pragma section(SECTION_AREG,read)
# pragma section(SECTION_FREG,read)
2020-04-06 19:10:07 +00:00
2020-11-12 07:58:10 +00:00
# define MSVC_ASEG __declspec(allocate(SECTION_AREG))
# define MSVC_FSEG __declspec(allocate(SECTION_FREG))
2020-04-06 19:10:07 +00:00
# define GCC_ASEG
# define GCC_FSEG
# else
# define MSVC_ASEG
# define MSVC_FSEG
# define GCC_ASEG __attribute__((section(SECTION_AREG))) __attribute__((used))
# define GCC_FSEG __attribute__((section(SECTION_FREG))) __attribute__((used))
# endif
// Macros to handle action functions. These are here so that I don't have to
// change every single use in case the parameters change.
# define DEFINE_ACTION_FUNCTION_NATIVE(cls, name, native) \
static int AF_ # # cls # # _ # # name ( VM_ARGS ) ; \
VMNativeFunction * cls # # _ # # name # # _VMPtr ; \
static const AFuncDesc cls # # _ # # name # # _Hook = { # cls , # name , AF_ # # cls # # _ # # name , & cls # # _ # # name # # _VMPtr , native } ; \
extern AFuncDesc const * const cls # # _ # # name # # _HookPtr ; \
MSVC_ASEG AFuncDesc const * const cls # # _ # # name # # _HookPtr GCC_ASEG = & cls # # _ # # name # # _Hook ; \
static int AF_ # # cls # # _ # # name ( VM_ARGS )
# define DEFINE_ACTION_FUNCTION_NATIVE0(cls, name, native) \
static int AF_ # # cls # # _ # # name ( VM_ARGS ) ; \
VMNativeFunction * cls # # _ # # name # # _VMPtr ; \
static const AFuncDesc cls # # _ # # name # # _Hook = { # cls , # name , AF_ # # cls # # _ # # name , & cls # # _ # # name # # _VMPtr } ; \
extern AFuncDesc const * const cls # # _ # # name # # _HookPtr ; \
MSVC_ASEG AFuncDesc const * const cls # # _ # # name # # _HookPtr GCC_ASEG = & cls # # _ # # name # # _Hook ; \
static int AF_ # # cls # # _ # # name ( VM_ARGS )
# define DEFINE_ACTION_FUNCTION(cls, name) \
static int AF_ # # cls # # _ # # name ( VM_ARGS ) ; \
VMNativeFunction * cls # # _ # # name # # _VMPtr ; \
static const AFuncDesc cls # # _ # # name # # _Hook = { # cls , # name , AF_ # # cls # # _ # # name , & cls # # _ # # name # # _VMPtr } ; \
extern AFuncDesc const * const cls # # _ # # name # # _HookPtr ; \
MSVC_ASEG AFuncDesc const * const cls # # _ # # name # # _HookPtr GCC_ASEG = & cls # # _ # # name # # _Hook ; \
static int AF_ # # cls # # _ # # name ( VM_ARGS )
// cls is the scripted class name, icls the internal one (e.g. player_t vs. Player)
# define DEFINE_FIELD_X(cls, icls, name) \
static const FieldDesc VMField_ # # icls # # _ # # name = { " A " # cls , # name , ( unsigned ) myoffsetof ( icls , name ) , ( unsigned ) sizeof ( icls : : name ) , 0 } ; \
extern FieldDesc const * const VMField_ # # icls # # _ # # name # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMField_ # # icls # # _ # # name # # _HookPtr GCC_FSEG = & VMField_ # # icls # # _ # # name ;
// This is for cases where the internal size does not match the part that gets exported.
# define DEFINE_FIELD_UNSIZED(cls, icls, name) \
static const FieldDesc VMField_ # # icls # # _ # # name = { " A " # cls , # name , ( unsigned ) myoffsetof ( icls , name ) , ~ 0u , 0 } ; \
extern FieldDesc const * const VMField_ # # icls # # _ # # name # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMField_ # # icls # # _ # # name # # _HookPtr GCC_FSEG = & VMField_ # # icls # # _ # # name ;
# define DEFINE_FIELD_NAMED_X(cls, icls, name, scriptname) \
static const FieldDesc VMField_ # # cls # # _ # # scriptname = { " A " # cls , # scriptname , ( unsigned ) myoffsetof ( icls , name ) , ( unsigned ) sizeof ( icls : : name ) , 0 } ; \
extern FieldDesc const * const VMField_ # # cls # # _ # # scriptname # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMField_ # # cls # # _ # # scriptname # # _HookPtr GCC_FSEG = & VMField_ # # cls # # _ # # scriptname ;
# define DEFINE_FIELD_X_BIT(cls, icls, name, bitval) \
static const FieldDesc VMField_ # # icls # # _ # # name = { " A " # cls , # name , ( unsigned ) myoffsetof ( icls , name ) , ( unsigned ) sizeof ( icls : : name ) , bitval } ; \
extern FieldDesc const * const VMField_ # # icls # # _ # # name # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMField_ # # icls # # _ # # name # # _HookPtr GCC_FSEG = & VMField_ # # cls # # _ # # name ;
# define DEFINE_FIELD(cls, name) \
static const FieldDesc VMField_ # # cls # # _ # # name = { # cls , # name , ( unsigned ) myoffsetof ( cls , name ) , ( unsigned ) sizeof ( cls : : name ) , 0 } ; \
extern FieldDesc const * const VMField_ # # cls # # _ # # name # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMField_ # # cls # # _ # # name # # _HookPtr GCC_FSEG = & VMField_ # # cls # # _ # # name ;
# define DEFINE_FIELD_NAMED(cls, name, scriptname) \
static const FieldDesc VMField_ # # cls # # _ # # scriptname = { # cls , # scriptname , ( unsigned ) myoffsetof ( cls , name ) , ( unsigned ) sizeof ( cls : : name ) , 0 } ; \
extern FieldDesc const * const VMField_ # # cls # # _ # # scriptname # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMField_ # # cls # # _ # # scriptname # # _HookPtr GCC_FSEG = & VMField_ # # cls # # _ # # scriptname ;
# define DEFINE_FIELD_BIT(cls, name, scriptname, bitval) \
static const FieldDesc VMField_ # # cls # # _ # # scriptname = { # cls , # scriptname , ( unsigned ) myoffsetof ( cls , name ) , ( unsigned ) sizeof ( cls : : name ) , bitval } ; \
extern FieldDesc const * const VMField_ # # cls # # _ # # scriptname # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMField_ # # cls # # _ # # scriptname # # _HookPtr GCC_FSEG = & VMField_ # # cls # # _ # # scriptname ;
# define DEFINE_GLOBAL(name) \
static const FieldDesc VMGlobal_ # # name = { " " , # name , ( size_t ) & name , ( unsigned ) sizeof ( name ) , 0 } ; \
extern FieldDesc const * const VMGlobal_ # # name # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMGlobal_ # # name # # _HookPtr GCC_FSEG = & VMGlobal_ # # name ;
# define DEFINE_GLOBAL_NAMED(iname, name) \
static const FieldDesc VMGlobal_ # # name = { " " , # name , ( size_t ) & iname , ( unsigned ) sizeof ( iname ) , 0 } ; \
extern FieldDesc const * const VMGlobal_ # # name # # _HookPtr ; \
MSVC_FSEG FieldDesc const * const VMGlobal_ # # name # # _HookPtr GCC_FSEG = & VMGlobal_ # # name ;
class AActor ;
# define ACTION_RETURN_STATE(v) do { FState *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state); return 1; } return 0; } while(0)
# define ACTION_RETURN_POINTER(v) do { void *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state); return 1; } return 0; } while(0)
# define ACTION_RETURN_OBJECT(v) do { auto state = v; if (numret > 0) { assert(ret != NULL); ret->SetObject(state); return 1; } return 0; } while(0)
# define ACTION_RETURN_FLOAT(v) do { double u = v; if (numret > 0) { assert(ret != nullptr); ret->SetFloat(u); return 1; } return 0; } while(0)
# define ACTION_RETURN_VEC2(v) do { DVector2 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector2(u); return 1; } return 0; } while(0)
# define ACTION_RETURN_VEC3(v) do { DVector3 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector(u); return 1; } return 0; } while(0)
# define ACTION_RETURN_INT(v) do { int u = v; if (numret > 0) { assert(ret != NULL); ret->SetInt(u); return 1; } return 0; } while(0)
# define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v)
# define ACTION_RETURN_STRING(v) do { FString u = v; if (numret > 0) { assert(ret != NULL); ret->SetString(u); return 1; } return 0; } while(0)
// Checks to see what called the current action function
# define ACTION_CALL_FROM_ACTOR() (stateinfo == nullptr || stateinfo->mStateType == STATE_Actor)
# define ACTION_CALL_FROM_PSPRITE() (self->player && stateinfo != nullptr && stateinfo->mStateType == STATE_Psprite)
# define ACTION_CALL_FROM_INVENTORY() (stateinfo != nullptr && stateinfo->mStateType == STATE_StateChain)
// Standard parameters for all action functions
// self - Actor this action is to operate on (player if a weapon)
// stateowner - Actor this action really belongs to (may be an item)
// callingstate - State this action was called from
# define PARAM_ACTION_PROLOGUE(type) \
PARAM_PROLOGUE ; \
PARAM_OBJECT_NOT_NULL ( self , AActor ) ; \
PARAM_OBJECT ( stateowner , type ) \
PARAM_POINTER ( stateinfo , FStateParamInfo ) \
// Number of action paramaters
# define NAP 3
# define PARAM_SELF_PROLOGUE(type) \
PARAM_PROLOGUE ; \
PARAM_OBJECT_NOT_NULL ( self , type ) ;
// for structs we cannot do a class validation
# define PARAM_SELF_STRUCT_PROLOGUE(type) \
PARAM_PROLOGUE ; \
PARAM_POINTER_NOT_NULL ( self , type ) ;
class PFunction ;
VMFunction * FindVMFunction ( PClass * cls , const char * name ) ;
# define DECLARE_VMFUNC(cls, name) static VMFunction *name; if (name == nullptr) name = FindVMFunction(RUNTIME_CLASS(cls), #name);
FString FStringFormat ( VM_ARGS , int offset = 0 ) ;
# define IFVM(cls, funcname) \
static VMFunction * func = nullptr ; \
if ( func = = nullptr ) { \
PClass : : FindFunction ( & func , # cls , # funcname ) ; \
assert ( func ) ; \
} \
if ( func ! = nullptr )
unsigned GetVirtualIndex ( PClass * cls , const char * funcname ) ;
# define IFVIRTUALPTR(self, cls, funcname) \
static unsigned VIndex = ~ 0u ; \
if ( VIndex = = ~ 0u ) { \
VIndex = GetVirtualIndex ( RUNTIME_CLASS ( cls ) , # funcname ) ; \
assert ( VIndex ! = ~ 0u ) ; \
} \
auto clss = self - > GetClass ( ) ; \
VMFunction * func = clss - > Virtuals . Size ( ) > VIndex ? clss - > Virtuals [ VIndex ] : nullptr ; \
if ( func ! = nullptr )
# define IFVIRTUAL(cls, funcname) IFVIRTUALPTR(this, cls, funcname)
# define IFVIRTUALPTRNAME(self, cls, funcname) \
static unsigned VIndex = ~ 0u ; \
if ( VIndex = = ~ 0u ) { \
VIndex = GetVirtualIndex ( PClass : : FindClass ( cls ) , # funcname ) ; \
assert ( VIndex ! = ~ 0u ) ; \
} \
auto clss = self - > GetClass ( ) ; \
VMFunction * func = clss - > Virtuals . Size ( ) > VIndex ? clss - > Virtuals [ VIndex ] : nullptr ; \
if ( func ! = nullptr )
# endif