gzdoom-gles/src/scripting/vm/vm.h

772 lines
28 KiB
C
Raw Normal View History

/*
** 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.
**---------------------------------------------------------------------------
**
*/
2016-03-01 15:47:10 +00:00
#ifndef VM_H
#define VM_H
#include "autosegs.h"
#include "zstring.h"
#include "vectors.h"
#include "cmdlib.h"
#include "doomerrors.h"
#include "memarena.h"
#include "scripting/backend/scopebarrier.h"
2016-03-01 15:47:10 +00:00
2017-02-18 20:18:23 +00:00
class DObject;
union VMOP;
class VMScriptFunction;
extern FMemArena ClassDataAllocator;
2016-03-01 15:47:10 +00:00
#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
2018-11-25 21:10:34 +00:00
void JitRelease();
2016-03-01 15:47:10 +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)
2016-03-01 15:47:10 +00:00
// 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,
2016-03-01 15:47:10 +00:00
REGT_ADDROF = 32, // used with PARAM: pass address of this register
REGT_NIL = 128 // parameter was omitted
2016-03-01 15:47:10 +00:00
};
#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,
2017-01-13 19:44:34 +00:00
X_FORMAT_ERROR
};
class CVMAbortException : public CDoomError
{
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, ...);
2016-03-01 15:47:10 +00:00
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));
2016-03-01 15:47:10 +00:00
((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];
}
2016-03-01 15:47:10 +00:00
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;
}
2016-03-01 15:47:10 +00:00
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;
}
2016-03-01 15:47:10 +00:00
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); }
2016-03-01 15:47:10 +00:00
};
struct VMRegisters;
struct TypedVMValue
2016-03-01 15:47:10 +00:00
{
union
{
int i;
void *a;
2016-03-01 15:47:10 +00:00
double f;
struct { int pad[3]; VM_UBYTE Type; };
struct { int foo[4]; } biggest;
const FString *sp;
2016-03-01 15:47:10 +00:00
};
const FString &s() const { return *sp; }
2016-03-01 15:47:10 +00:00
TypedVMValue()
2016-03-01 15:47:10 +00:00
{
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;
}
2016-03-01 15:47:10 +00:00
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)
2016-03-01 15:47:10 +00:00
{
sp = s;
2016-03-01 15:47:10 +00:00
}
VMValue(DObject *v)
{
a = v;
}
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)
2016-03-01 15:47:10 +00:00
{
if (Type == REGT_INT)
{
return i;
}
if (Type == REGT_FLOAT)
{
return int(f);
}
if (Type == REGT_STRING)
{
return s().ToLong();
}
// FIXME
return 0;
}
double ToDouble(int Type)
2016-03-01 15:47:10 +00:00
{
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();
2018-11-25 21:10:34 +00:00
// 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
2016-03-01 15:47:10 +00:00
class VMNativeFunction : public VMFunction
{
public:
typedef int (*NativeCallType)(VM_ARGS);
2016-03-01 15:47:10 +00:00
// 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; }
2016-03-01 15:47:10 +00:00
// 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);
2016-03-01 15:47:10 +00:00
};
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*/);
2016-03-01 15:47:10 +00:00
inline int VMCallAction(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults/*, VMException **trap = NULL*/)
{
2018-11-16 17:50:34 +00:00
return VMCall(func, params, numparams, results, numresults);
}
2016-03-01 15:47:10 +00:00
// 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)
2016-03-01 15:47:10 +00:00
// 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;
2016-03-01 15:47:10 +00:00
// 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_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)));
2016-03-01 15:47:10 +00:00
2016-03-01 15:47:10 +00:00
// 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)
2016-03-01 15:47:10 +00:00
#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)
2016-03-01 15:47:10 +00:00
#define PARAM_STRING(x) ++paramnum; PARAM_STRING_AT(paramnum,x)
#define PARAM_STRING_VAL(x) ++paramnum; PARAM_STRING_VAL_AT(paramnum,x)
2016-03-01 15:47:10 +00:00
#define PARAM_STATE(x) ++paramnum; PARAM_STATE_AT(paramnum,x)
- fixed: State labels were resolved in the calling function's context instead of the called function one's. This could cause problems with functions that take states as parameters but use them to set them internally instead of passing them through the A_Jump interface back to the caller, like A_Chase or A_LookEx. This required some quite significant refactoring because the entire state resolution logic had been baked into the compiler which turned out to be a major maintenance problem. Fixed this by adding a new builtin type 'statelabel'. This is an opaque identifier representing a state, with the actual data either directly encoded into the number for single label state or an index into a state information table. The state resolution is now the task of the called function as it should always have remained. Note, that this required giving back the 'action' qualifier to most state jumping functions. - refactored most A_Jump checkers to a two stage setup with a pure checker that returns a boolean and a scripted A_Jump wrapper, for some simpler checks the checker function was entirely omitted and calculated inline in the A_Jump function. It is strongly recommended to use the boolean checkers unless using an inline function invocation in a state as they lead to vastly clearer code and offer more flexibility. - let Min() and Max() use the OP_MIN and OP_MAX opcodes. Although these were present, these function were implemented using some grossly inefficient branching tests. - the DECORATE 'state' cast kludge will now actually call ResolveState because a state label is not a state and needs conversion.
2016-11-14 13:12:27 +00:00
#define PARAM_STATE_ACTION(x) ++paramnum; PARAM_STATE_ACTION_AT(paramnum,x)
2016-03-01 15:47:10 +00:00
#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)
2016-03-01 15:47:10 +00:00
#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)
2016-03-01 15:47:10 +00:00
typedef int(*actionf_p)(VM_ARGS);
struct FieldDesc
{
const char *ClassName;
const char *FieldName;
size_t FieldOffset;
unsigned FieldSize;
int BitValue;
};
// 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)) { ValidateType<Ret>(); }
template<typename Ret, TP(1)> DirectNativeDesc(Ret(*func)(P1)) : Ptr(reinterpret_cast<void*>(func)) { ValidateType<Ret>(); VP(1); }
template<typename Ret, TP(1), TP(2)> DirectNativeDesc(Ret(*func)(P1,P2)) : Ptr(reinterpret_cast<void*>(func)) { ValidateType<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)) { ValidateType<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)) { ValidateType<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)) { ValidateType<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)) { ValidateType<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)) { ValidateType<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)) { ValidateType<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)) { ValidateType<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)) { ValidateType<Ret>(); VP(1); VP(2); VP(3); VP(4); VP(5); VP(6); VP(7); VP(8); VP(9); VP(10); }
#undef TP
#undef VP
template<typename T> void ValidateType() { static_assert(is_valid<T>::value, "Argument type is not valid as a direct native parameter or return type"); }
// Traits for the types we are interested in
template<typename T> struct is_valid { static const bool value = false; };
template<typename T> struct is_valid<T*> { static const bool value = true; };
template<typename T> struct is_valid<T&> { static const bool value = true; };
template<> struct is_valid<void> { static const bool value = true; };
template<> struct is_valid<int> { static const bool value = true; };
template<> struct is_valid<double> { static const bool value = true; };
operator void *() const { return Ptr; }
void *Ptr = nullptr;
};
struct AFuncDesc
{
const char *ClassName;
const char *FuncName;
actionf_p Function;
VMNativeFunction **VMPointer;
DirectNativeDesc DirectNative;
};
#if defined(_MSC_VER)
#pragma section(".areg$u",read)
#pragma section(".freg$u",read)
#define MSVC_ASEG __declspec(allocate(".areg$u"))
#define MSVC_FSEG __declspec(allocate(".freg$u"))
#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(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)
2016-03-01 15:47:10 +00:00
#endif