qzdoom/src/dobjtype.h

989 lines
28 KiB
C
Raw Normal View History

2016-03-01 15:47:10 +00:00
#ifndef DOBJTYPE_H
#define DOBJTYPE_H
#ifndef __DOBJECT_H__
#error You must #include "dobject.h" to get dobjtype.h
#endif
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
#include "vm.h"
2016-03-01 15:47:10 +00:00
// Variable/parameter/field flags -------------------------------------------
// Making all these different storage types use a common set of flags seems
// like the simplest thing to do.
enum
{
VARF_Optional = (1<<0), // func param is optional
VARF_Method = (1<<1), // func has an implied self parameter
VARF_Action = (1<<2), // func has implied owner and state parameters
VARF_Native = (1<<3), // func is native code/don't auto serialize field
VARF_ReadOnly = (1<<4), // field is read only, do not write to it
VARF_Private = (1<<5), // field is private to containing class
VARF_Protected = (1<<6), // field is only accessible by containing class and children.
VARF_Deprecated = (1<<7), // Deprecated fields should output warnings when used.
VARF_Virtual = (1<<8), // function is virtual
VARF_Final = (1<<9), // Function may not be overridden in subclasses
VARF_In = (1<<10),
VARF_Out = (1<<11),
VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures)
VARF_Static = (1<<13), // static class data (by necessity read only.)
VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code.
VARF_Override = (1<<15), // overrides a virtual function from the parent class.
VARF_Ref = (1<<16), // argument is passed by reference.
};
2016-03-01 15:47:10 +00:00
// Symbol information -------------------------------------------------------
class PTypeBase : public DObject
2016-03-01 15:47:10 +00:00
{
DECLARE_ABSTRACT_CLASS(PTypeBase, DObject)
public:
virtual FString QualifiedName() const
{
return "";
}
};
class PSymbol : public PTypeBase
{
DECLARE_ABSTRACT_CLASS(PSymbol, PTypeBase);
2016-03-01 15:47:10 +00:00
public:
virtual ~PSymbol();
virtual FString QualifiedName() const
{
return SymbolName.GetChars();
}
2016-03-01 15:47:10 +00:00
FName SymbolName;
protected:
PSymbol(FName name) { SymbolName = name; }
};
// An action function -------------------------------------------------------
struct FState;
struct StateCallData;
class VMFrameStack;
struct VMValue;
struct VMReturn;
class VMFunction;
// A VM function ------------------------------------------------------------
class PSymbolVMFunction : public PSymbol
{
DECLARE_CLASS(PSymbolVMFunction, PSymbol);
HAS_OBJECT_POINTERS;
public:
VMFunction *Function;
PSymbolVMFunction(FName name) : PSymbol(name) {}
PSymbolVMFunction() : PSymbol(NAME_None) {}
};
// A symbol for a type ------------------------------------------------------
class PSymbolType : public PSymbol
{
DECLARE_CLASS(PSymbolType, PSymbol);
HAS_OBJECT_POINTERS;
public:
class PType *Type;
PSymbolType(FName name, class PType *ty) : PSymbol(name), Type(ty) {}
PSymbolType() : PSymbol(NAME_None) {}
};
// A symbol table -----------------------------------------------------------
struct PSymbolTable
{
PSymbolTable();
PSymbolTable(PSymbolTable *parent);
~PSymbolTable();
size_t MarkSymbols();
// Sets the table to use for searches if this one doesn't contain the
// requested symbol.
void SetParentTable (PSymbolTable *parent);
PSymbolTable *GetParentTable() const
{
return ParentSymbolTable;
}
2016-03-01 15:47:10 +00:00
// Finds a symbol in the table, optionally searching parent tables
// as well.
PSymbol *FindSymbol (FName symname, bool searchparents) const;
// Like FindSymbol with searchparents set true, but also returns the
// specific symbol table the symbol was found in.
PSymbol *FindSymbolInTable(FName symname, PSymbolTable *&symtable);
2016-03-01 15:47:10 +00:00
// Places the symbol in the table and returns a pointer to it or NULL if
// a symbol with the same name is already in the table. This symbol is
// not copied and will be freed when the symbol table is destroyed.
PSymbol *AddSymbol (PSymbol *sym);
// Similar to AddSymbol but always succeeds. Returns the symbol that used
// to be in the table with this name, if any.
PSymbol *ReplaceSymbol(PSymbol *sym);
2016-03-01 15:47:10 +00:00
// Frees all symbols from this table.
void ReleaseSymbols();
typedef TMap<FName, PSymbol *> MapType;
MapType::Iterator GetIterator()
{
return MapType::Iterator(Symbols);
}
2016-03-01 15:47:10 +00:00
private:
PSymbolTable *ParentSymbolTable;
MapType Symbols;
friend class DObject;
};
// A symbol for a compiler tree node ----------------------------------------
class PSymbolTreeNode : public PSymbol
{
DECLARE_CLASS(PSymbolTreeNode, PSymbol);
public:
struct ZCC_TreeNode *Node;
PSymbolTreeNode(FName name, struct ZCC_TreeNode *node) : PSymbol(name), Node(node) {}
PSymbolTreeNode() : PSymbol(NAME_None) {}
};
2016-03-01 15:47:10 +00:00
extern PSymbolTable GlobalSymbols;
// Basic information shared by all types ------------------------------------
// Only one copy of a type is ever instantiated at one time.
// - Enums, classes, and structs are defined by their names and outer classes.
// - Pointers are uniquely defined by the type they point at.
// - ClassPointers are also defined by their class restriction.
// - Arrays are defined by their element type and count.
// - DynArrays are defined by their element type.
// - Maps are defined by their key and value types.
// - Prototypes are defined by the argument and return types.
// - Functions are defined by their names and outer objects.
// In table form:
// Outer Name Type Type2 Count
// Enum * *
// Class * *
// Struct * *
// Function * *
// Pointer *
// ClassPointer + *
// Array * *
// DynArray *
// Map * *
// Prototype *+ *+
struct ZCC_ExprConstant;
class PClassType;
class PType : public PTypeBase
2016-03-01 15:47:10 +00:00
{
//DECLARE_ABSTRACT_CLASS_WITH_META(PType, DObject, PClassType);
// We need to unravel the _WITH_META macro, since PClassType isn't defined yet,
// and we can't define it until we've defined PClass. But we can't define that
// without defining PType.
DECLARE_ABSTRACT_CLASS(PType, PTypeBase)
2016-03-01 15:47:10 +00:00
HAS_OBJECT_POINTERS;
protected:
enum { MetaClassNum = CLASSREG_PClassType };
2016-03-01 15:47:10 +00:00
public:
typedef PClassType MetaClass;
MetaClass *GetClass() const;
struct Conversion
{
Conversion(PType *target, void (*convert)(ZCC_ExprConstant *, class FSharedStringArena &))
: TargetType(target), ConvertConstant(convert) {}
PType *TargetType;
void (*ConvertConstant)(ZCC_ExprConstant *val, class FSharedStringArena &strdump);
};
unsigned int Size; // this type's size
unsigned int Align; // this type's preferred alignment
PType *HashNext; // next type in this type table
PSymbolTable Symbols;
bool MemberOnly = false; // type may only be used as a struct/class member but not as a local variable or function argument.
FString mDescriptiveName;
BYTE loadOp, storeOp, moveOp, RegType, RegCount;
2016-03-01 15:47:10 +00:00
PType(unsigned int size = 1, unsigned int align = 1);
2016-03-01 15:47:10 +00:00
virtual ~PType();
- 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
virtual bool isNumeric() { return false; }
2016-03-01 15:47:10 +00:00
bool AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &));
int FindConversion(PType *target, const Conversion **slots, int numslots);
// Writes the value of a variable of this type at (addr) to an archive, preceded by
// a tag indicating its type. The tag is there so that variable types can be changed
// without completely breaking savegames, provided that the change isn't between
// totally unrelated types.
virtual void WriteValue(FSerializer &ar, const char *key,const void *addr) const;
// Returns true if the stored value was compatible. False otherwise.
// If the value was incompatible, then the memory at *addr is unchanged.
virtual bool ReadValue(FSerializer &ar, const char *key,void *addr) const;
// Sets the default value for this type at (base + offset)
// If the default value is binary 0, then this function doesn't need
// to do anything, because PClass::Extend() takes care of that.
//
// The stroffs array is so that types that need special initialization
// and destruction (e.g. strings) can add their offsets to it for special
// initialization when the object is created and destruction when the
// object is destroyed.
virtual void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL) const;
virtual void SetPointer(void *base, unsigned offset, TArray<size_t> *ptrofs = NULL) const;
// Initialize the value, if needed (e.g. strings)
virtual void InitializeValue(void *addr, const void *def) const;
// Destroy the value, if needed (e.g. strings)
virtual void DestroyValue(void *addr) const;
2016-03-01 15:47:10 +00:00
// Sets the value of a variable of this type at (addr)
virtual void SetValue(void *addr, int val);
virtual void SetValue(void *addr, double val);
2016-03-01 15:47:10 +00:00
// Gets the value of a variable of this type at (addr)
virtual int GetValueInt(void *addr) const;
2016-03-30 03:24:59 +00:00
virtual double GetValueFloat(void *addr) const;
2016-03-01 15:47:10 +00:00
// Gets the opcode to store from a register to memory
int GetStoreOp() const
{
return storeOp;
}
2016-03-01 15:47:10 +00:00
// Gets the opcode to load from memory to a register
int GetLoadOp() const
{
return loadOp;
}
2016-03-01 15:47:10 +00:00
// Gets the opcode to move from register to another register
int GetMoveOp() const
{
return moveOp;
}
2016-03-01 15:47:10 +00:00
// Gets the register type for this type
int GetRegType() const
{
return RegType;
}
2016-03-01 15:47:10 +00:00
int GetRegCount() const
{
return RegCount;
}
2016-03-01 15:47:10 +00:00
// Returns true if this type matches the two identifiers. Referring to the
// above table, any type is identified by at most two characteristics. Each
// type that implements this function will cast these to the appropriate type.
// It is up to the caller to make sure they are the correct types. There is
// only one prototype for this function in order to simplify type table
// management.
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
// Get the type IDs used by IsMatch
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
const char *DescriptiveName() const;
2016-03-01 15:47:10 +00:00
size_t PropagateMark();
static void StaticInit();
private:
// Stuff for type conversion searches
class VisitQueue
{
public:
VisitQueue() : In(0), Out(0) {}
void Push(PType *type);
PType *Pop();
bool IsEmpty() { return In == Out; }
private:
// This is a fixed-sized ring buffer.
PType *Queue[64];
int In, Out;
void Advance(int &ptr)
{
ptr = (ptr + 1) & (countof(Queue) - 1);
}
};
class VisitedNodeSet
{
public:
VisitedNodeSet() { memset(Buckets, 0, sizeof(Buckets)); }
void Insert(PType *node);
bool Check(const PType *node);
private:
PType *Buckets[32];
size_t Hash(const PType *type) { return size_t(type) >> 4; }
};
TArray<Conversion> Conversions;
PType *PredType;
PType *VisitNext;
short PredConv;
short Distance;
void MarkPred(PType *pred, int conv, int dist)
{
PredType = pred;
PredConv = conv;
Distance = dist;
}
void FillConversionPath(const Conversion **slots);
};
// Not-really-a-type types --------------------------------------------------
class PErrorType : public PType
{
DECLARE_CLASS(PErrorType, PType);
public:
PErrorType() : PType(0, 1) {}
};
class PVoidType : public PType
{
DECLARE_CLASS(PVoidType, PType);
public:
PVoidType() : PType(0, 1) {}
};
// Some categorization typing -----------------------------------------------
class PBasicType : public PType
{
DECLARE_ABSTRACT_CLASS(PBasicType, PType);
public:
PBasicType();
PBasicType(unsigned int size, unsigned int align);
};
class PCompoundType : public PType
{
DECLARE_ABSTRACT_CLASS(PCompoundType, PType);
};
class PNamedType : public PCompoundType
{
DECLARE_ABSTRACT_CLASS(PNamedType, PCompoundType);
HAS_OBJECT_POINTERS;
public:
PTypeBase *Outer; // object this type is contained within
2016-03-01 15:47:10 +00:00
FName TypeName; // this type's name
PNamedType() : Outer(NULL) {
mDescriptiveName = "NamedType";
}
PNamedType(FName name, PTypeBase *outer) : Outer(outer), TypeName(name) {
mDescriptiveName = name.GetChars();
}
2016-03-01 15:47:10 +00:00
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
virtual FString QualifiedName() const;
2016-03-01 15:47:10 +00:00
};
// Basic types --------------------------------------------------------------
class PInt : public PBasicType
{
DECLARE_CLASS(PInt, PBasicType);
public:
- 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
PInt(unsigned int size, bool unsign, bool compatible = true);
2016-03-01 15:47:10 +00:00
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
2016-03-01 15:47:10 +00:00
virtual void SetValue(void *addr, int val);
virtual void SetValue(void *addr, double val);
2016-03-01 15:47:10 +00:00
virtual int GetValueInt(void *addr) const;
2016-03-30 03:24:59 +00:00
virtual double GetValueFloat(void *addr) const;
- 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
virtual bool isNumeric() override { return IntCompatible; }
2016-03-01 15:47:10 +00:00
bool Unsigned;
- 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
bool IntCompatible;
2016-03-01 15:47:10 +00:00
protected:
PInt();
void SetOps();
2016-03-01 15:47:10 +00:00
};
class PBool : public PInt
{
DECLARE_CLASS(PBool, PInt);
public:
PBool();
};
class PFloat : public PBasicType
{
DECLARE_CLASS(PFloat, PBasicType);
public:
PFloat(unsigned int size);
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
2016-03-01 15:47:10 +00:00
virtual void SetValue(void *addr, int val);
virtual void SetValue(void *addr, double val);
2016-03-01 15:47:10 +00:00
virtual int GetValueInt(void *addr) const;
2016-03-30 03:24:59 +00:00
virtual double GetValueFloat(void *addr) const;
- 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
virtual bool isNumeric() override { return true; }
2016-03-01 15:47:10 +00:00
protected:
PFloat();
void SetOps();
2016-03-01 15:47:10 +00:00
private:
struct SymbolInitF
{
ENamedName Name;
double Value;
};
struct SymbolInitI
{
ENamedName Name;
int Value;
};
void SetSingleSymbols();
void SetDoubleSymbols();
void SetSymbols(const SymbolInitF *syminit, size_t count);
void SetSymbols(const SymbolInitI *syminit, size_t count);
};
class PString : public PBasicType
{
DECLARE_CLASS(PString, PBasicType);
public:
PString();
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL) const override;
void InitializeValue(void *addr, const void *def) const override;
void DestroyValue(void *addr) const override;
2016-03-01 15:47:10 +00:00
};
// Variations of integer types ----------------------------------------------
class PName : public PInt
{
DECLARE_CLASS(PName, PInt);
public:
PName();
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
2016-03-01 15:47:10 +00:00
};
class PSound : public PInt
{
DECLARE_CLASS(PSound, PInt);
public:
PSound();
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
2016-03-01 15:47:10 +00:00
};
class PColor : public PInt
{
DECLARE_CLASS(PColor, PInt);
public:
PColor();
};
- 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
class PStateLabel : public PInt
{
DECLARE_CLASS(PStateLabel, PInt);
public:
PStateLabel();
};
2016-03-01 15:47:10 +00:00
// Pointers -----------------------------------------------------------------
class PStatePointer : public PBasicType
{
DECLARE_CLASS(PStatePointer, PBasicType);
public:
PStatePointer();
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
2016-03-01 15:47:10 +00:00
};
class PPointer : public PBasicType
{
DECLARE_CLASS(PPointer, PBasicType);
HAS_OBJECT_POINTERS;
public:
PPointer();
PPointer(PType *pointsat, bool isconst = false);
2016-03-01 15:47:10 +00:00
PType *PointedType;
bool IsConst;
2016-03-01 15:47:10 +00:00
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override;
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
2016-03-01 15:47:10 +00:00
protected:
void SetOps();
2016-03-01 15:47:10 +00:00
};
class PClassPointer : public PPointer
{
DECLARE_CLASS(PClassPointer, PPointer);
HAS_OBJECT_POINTERS;
public:
PClassPointer(class PClass *restrict);
class PClass *ClassRestriction;
// this is only here to block PPointer's implementation
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override {}
2016-03-01 15:47:10 +00:00
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
protected:
PClassPointer();
};
// Struct/class fields ------------------------------------------------------
// A PField describes a symbol that takes up physical space in the struct.
class PField : public PSymbol
{
DECLARE_CLASS(PField, PSymbol);
HAS_OBJECT_POINTERS
public:
PField(FName name, PType *type, DWORD flags = 0, size_t offset = 0, int bitvalue = -1);
2016-03-01 15:47:10 +00:00
size_t Offset;
2016-03-01 15:47:10 +00:00
PType *Type;
DWORD Flags;
int BitValue;
2016-03-01 15:47:10 +00:00
protected:
PField();
};
// Compound types -----------------------------------------------------------
class PEnum : public PNamedType
{
DECLARE_CLASS(PEnum, PNamedType);
HAS_OBJECT_POINTERS;
public:
PEnum(FName name, PTypeBase *outer);
2016-03-01 15:47:10 +00:00
PType *ValueType;
TMap<FName, int> Values;
protected:
PEnum();
};
class PArray : public PCompoundType
{
DECLARE_CLASS(PArray, PCompoundType);
HAS_OBJECT_POINTERS;
public:
PArray(PType *etype, unsigned int ecount);
PType *ElementType;
unsigned int ElementCount;
unsigned int ElementSize;
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special) const override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *special) const override;
2016-03-01 15:47:10 +00:00
protected:
PArray();
};
class PDynArray : public PCompoundType
{
DECLARE_CLASS(PDynArray, PCompoundType);
HAS_OBJECT_POINTERS;
public:
PDynArray(PType *etype);
PType *ElementType;
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
protected:
PDynArray();
};
class PMap : public PCompoundType
{
DECLARE_CLASS(PMap, PCompoundType);
HAS_OBJECT_POINTERS;
public:
PMap(PType *keytype, PType *valtype);
PType *KeyType;
PType *ValueType;
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
protected:
PMap();
};
class PStruct : public PNamedType
{
DECLARE_CLASS(PStruct, PNamedType);
public:
PStruct(FName name, PTypeBase *outer);
2016-03-01 15:47:10 +00:00
TArray<PField *> Fields;
bool HasNativeFields;
2016-03-01 15:47:10 +00:00
virtual PField *AddField(FName name, PType *type, DWORD flags=0);
virtual PField *AddNativeField(FName name, PType *type, size_t address, DWORD flags = 0, int bitvalue = -1);
2016-03-01 15:47:10 +00:00
size_t PropagateMark();
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *specials) const override;
void SetPointer(void *base, unsigned offset, TArray<size_t> *specials) const override;
static void WriteFields(FSerializer &ar, const void *addr, const TArray<PField *> &fields);
bool ReadFields(FSerializer &ar, void *addr) const;
2016-03-01 15:47:10 +00:00
protected:
PStruct();
};
class PPrototype : public PCompoundType
{
DECLARE_CLASS(PPrototype, PCompoundType);
public:
PPrototype(const TArray<PType *> &rettypes, const TArray<PType *> &argtypes);
TArray<PType *> ArgumentTypes;
TArray<PType *> ReturnTypes;
size_t PropagateMark();
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
protected:
PPrototype();
};
// TBD: Should we really support overloading?
class PFunction : public PSymbol
{
DECLARE_CLASS(PFunction, PSymbol);
public:
struct Variant
{
PPrototype *Proto;
2016-03-01 15:47:10 +00:00
VMFunction *Implementation;
TArray<DWORD> ArgFlags; // Should be the same length as Proto->ArgumentTypes
TArray<FName> ArgNames; // we need the names to access them later when the function gets compiled.
uint32_t Flags;
int UseFlags;
PClass *SelfClass;
2016-03-01 15:47:10 +00:00
};
TArray<Variant> Variants;
PClass *OwningClass = nullptr;
2016-03-01 15:47:10 +00:00
unsigned AddVariant(PPrototype *proto, TArray<DWORD> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags, int useflags);
int GetImplicitArgs()
{
if (Variants[0].Flags & VARF_Action) return 3;
else if (Variants[0].Flags & VARF_Method) return 1;
return 0;
}
2016-03-01 15:47:10 +00:00
size_t PropagateMark();
PFunction(PClass *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {}
2016-03-01 15:47:10 +00:00
};
// Meta-info for every class derived from DObject ---------------------------
enum
{
TentativeClass = UINT_MAX,
};
class PClassClass;
class PClass : public PStruct
{
DECLARE_CLASS(PClass, PStruct);
HAS_OBJECT_POINTERS;
protected:
// We unravel _WITH_META here just as we did for PType.
enum { MetaClassNum = CLASSREG_PClassClass };
TArray<FTypeAndOffset> SpecialInits;
void Derive(PClass *newclass, FName name);
void InitializeSpecials(void *addr) const;
void SetSuper();
2016-03-01 15:47:10 +00:00
public:
typedef PClassClass MetaClass;
MetaClass *GetClass() const;
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
void WriteAllFields(FSerializer &ar, const void *addr) const;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
bool ReadAllFields(FSerializer &ar, void *addr) const;
void InitializeDefaults();
int FindVirtualIndex(FName name, PPrototype *proto);
virtual void DeriveData(PClass *newclass);
2016-03-01 15:47:10 +00:00
static void StaticInit();
static void StaticShutdown();
static void StaticBootstrap();
// Per-class information -------------------------------------
PClass *ParentClass; // the class this class derives from
PClass *VMExported; // this is here to allow script classes to override native virtual functions
2016-03-01 15:47:10 +00:00
const size_t *Pointers; // object pointers defined by this class *only*
const size_t *FlatPointers; // object pointers defined by this class and all its superclasses; not initialized by default
BYTE *Defaults;
bool bRuntimeClass; // class was defined at run-time, not compile-time
bool bExported; // This type has been declared in a script
bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility
TArray<VMFunction*> Virtuals; // virtual function table
2016-03-01 15:47:10 +00:00
void (*ConstructNative)(void *);
// The rest are all functions and static data ----------------
PClass();
~PClass();
void InsertIntoHash();
DObject *CreateNew() const;
PClass *CreateDerivedClass(FName name, unsigned int size);
PField *AddField(FName name, PType *type, DWORD flags=0) override;
2016-03-01 15:47:10 +00:00
void InitializeActorInfo();
void BuildFlatPointers();
void DestroySpecials(void *addr) const;
2016-03-01 15:47:10 +00:00
const PClass *NativeClass() const;
// Returns true if this type is an ancestor of (or same as) the passed type.
bool IsAncestorOf(const PClass *ti) const
{
while (ti)
{
if (this == ti)
return true;
ti = ti->ParentClass;
}
return false;
}
inline bool IsDescendantOf(const PClass *ti) const
{
return ti->IsAncestorOf(this);
}
// Find a type, given its name.
const PClass *FindParentClass(FName name) const;
PClass *FindParentClass(FName name) { return const_cast<PClass *>(const_cast<const PClass *>(this)->FindParentClass(name)); }
2016-03-01 15:47:10 +00:00
static PClass *FindClass(const char *name) { return FindClass(FName(name, true)); }
static PClass *FindClass(const FString &name) { return FindClass(FName(name, true)); }
static PClass *FindClass(ENamedName name) { return FindClass(FName(name)); }
static PClass *FindClass(FName name);
static PClassActor *FindActor(const char *name) { return FindActor(FName(name, true)); }
static PClassActor *FindActor(const FString &name) { return FindActor(FName(name, true)); }
static PClassActor *FindActor(ENamedName name) { return FindActor(FName(name)); }
static PClassActor *FindActor(FName name);
PClass *FindClassTentative(FName name);
2016-03-01 15:47:10 +00:00
static TArray<PClass *> AllClasses;
static bool bShutdown;
};
class PClassType : public PClass
{
DECLARE_CLASS(PClassType, PClass);
protected:
public:
PClassType();
virtual void DeriveData(PClass *newclass);
2016-03-01 15:47:10 +00:00
PClass *TypeTableType; // The type to use for hashing into the type table
};
inline PType::MetaClass *PType::GetClass() const
{
return static_cast<MetaClass *>(DObject::GetClass());
}
class PClassClass : public PClassType
{
DECLARE_CLASS(PClassClass, PClassType);
public:
PClassClass();
};
inline PClass::MetaClass *PClass::GetClass() const
{
return static_cast<MetaClass *>(DObject::GetClass());
}
// Type tables --------------------------------------------------------------
struct FTypeTable
{
enum { HASH_SIZE = 1021 };
PType *TypeHash[HASH_SIZE];
PType *FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, size_t *bucketnum);
void ReplaceType(PType *newtype, PType *oldtype, size_t bucket);
void AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t parm2, size_t bucket);
void AddType(PType *type);
void Mark();
void Clear();
static size_t Hash(const PClass *p1, intptr_t p2, intptr_t p3);
};
extern FTypeTable TypeTable;
// Returns a type from the TypeTable. Will create one if it isn't present.
PMap *NewMap(PType *keytype, PType *valuetype);
PArray *NewArray(PType *type, unsigned int count);
PDynArray *NewDynArray(PType *type);
PPointer *NewPointer(PType *type, bool isconst = false);
2016-03-01 15:47:10 +00:00
PClassPointer *NewClassPointer(PClass *restrict);
PEnum *NewEnum(FName name, PTypeBase *outer);
PStruct *NewStruct(FName name, PTypeBase *outer);
2016-03-01 15:47:10 +00:00
PPrototype *NewPrototype(const TArray<PType *> &rettypes, const TArray<PType *> &argtypes);
// Built-in types -----------------------------------------------------------
extern PErrorType *TypeError;
extern PVoidType *TypeVoid;
extern PInt *TypeSInt8, *TypeUInt8;
extern PInt *TypeSInt16, *TypeUInt16;
extern PInt *TypeSInt32, *TypeUInt32;
extern PBool *TypeBool;
extern PFloat *TypeFloat32, *TypeFloat64;
extern PString *TypeString;
extern PName *TypeName;
extern PSound *TypeSound;
extern PColor *TypeColor;
extern PStruct *TypeVector2;
extern PStruct *TypeVector3;
2016-03-01 15:47:10 +00:00
extern PStatePointer *TypeState;
- 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
extern PStateLabel *TypeStateLabel;
extern PPointer *TypeNullPtr;
2016-03-01 15:47:10 +00:00
// A constant value ---------------------------------------------------------
class PSymbolConst : public PSymbol
{
DECLARE_CLASS(PSymbolConst, PSymbol);
public:
PType *ValueType;
PSymbolConst(FName name, PType *type=NULL) : PSymbol(name), ValueType(type) {}
PSymbolConst() : PSymbol(NAME_None), ValueType(NULL) {}
};
// A constant numeric value -------------------------------------------------
class PSymbolConstNumeric : public PSymbolConst
{
DECLARE_CLASS(PSymbolConstNumeric, PSymbolConst);
public:
union
{
int Value;
double Float;
void *Pad;
};
PSymbolConstNumeric(FName name, PType *type=NULL) : PSymbolConst(name, type) {}
PSymbolConstNumeric(FName name, PType *type, int val) : PSymbolConst(name, type), Value(val) {}
PSymbolConstNumeric(FName name, PType *type, unsigned int val) : PSymbolConst(name, type), Value((int)val) {}
PSymbolConstNumeric(FName name, PType *type, double val) : PSymbolConst(name, type), Float(val) {}
PSymbolConstNumeric() {}
};
// A constant string value --------------------------------------------------
class PSymbolConstString : public PSymbolConst
{
DECLARE_CLASS(PSymbolConstString, PSymbolConst);
public:
FString Str;
PSymbolConstString(FName name, FString &str) : PSymbolConst(name, TypeString), Str(str) {}
PSymbolConstString() {}
};
void ReleaseGlobalSymbols();
// Enumerations for serializing types in an archive -------------------------
enum ETypeVal : BYTE
{
VAL_Int8,
VAL_UInt8,
VAL_Int16,
VAL_UInt16,
VAL_Int32,
VAL_UInt32,
VAL_Int64,
VAL_UInt64,
VAL_Zero,
VAL_One,
VAL_Float32,
VAL_Float64,
VAL_String,
VAL_Name,
VAL_Struct,
VAL_Array,
VAL_Object,
VAL_State,
VAL_Class,
};
2016-03-01 15:47:10 +00:00
#endif