- added DObject as a preparation for the ZScript compiler. Currently large parts are disabled because the backing features are not present yet.

# Conflicts:
#	source/CMakeLists.txt
This commit is contained in:
Christoph Oelckers 2020-04-06 16:10:54 +02:00
parent b971bc2717
commit c1f7cf1c3a
15 changed files with 3294 additions and 79 deletions

View file

@ -606,6 +606,7 @@ file( GLOB HEADER_FILES
common/console/*.h
common/utility/*.h
common/engine/*.h
common/objects/*.h
common/filesystem/*.h
common/textures/*.h
common/thirdparty/*.h
@ -771,6 +772,9 @@ set (PCH_SOURCES
common/engine/sc_man.cpp
common/engine/palettecontainer.cpp
common/engine/stringtable.cpp
common/objects/dobject.cpp
common/objects/dobjgc.cpp
common/objects/dobjtype.cpp
core/utility/stats.cpp
@ -839,7 +843,7 @@ use_precompiled_header(".")
add_executable( ${PROJECT_NAME} WIN32 MACOSX_BUNDLE
${HEADER_FILES}
${NOT_COMPILED_SOURCE_FILES}
#__autostart.cpp
common/objects/__autostart.cpp
${SYSTEM_SOURCES}
${FASTMATH_SOURCES}
${PCH_SOURCES}
@ -865,6 +869,7 @@ add_executable( ${PROJECT_NAME} WIN32 MACOSX_BUNDLE
common/thirdparty/math/tan.c
common/thirdparty/math/tanh.c
common/thirdparty/math/fastsin.cpp
common/objects/zzautozend.cpp
)
#set_source_files_properties( ${FASTMATH_SOURCES} PROPERTIES COMPILE_FLAGS ${DEM_FASTMATH_FLAG} )
@ -1034,6 +1039,8 @@ source_group("Common\\Audio\\Music" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_
source_group("Common\\Console" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/console/.+")
source_group("Common\\Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/utility/.+")
source_group("Common\\Engine" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/engine/.+")
source_group("Common\\Objects" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/objects/.+")
source_group("Common\\Fonts" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/fonts/.+")
source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+")
source_group("Common\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/.+")
source_group("Common\\Third Party" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/.+")

View file

@ -0,0 +1,111 @@
/*
** autostart.cpp
** This file contains the heads of lists stored in special data segments
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
** The particular scheme used here was chosen because it's small.
**
** An alternative that will work with any C++ compiler is to use static
** classes to build these lists at run time. Under Visual C++, doing things
** that way can require a lot of extra space, which is why I'm doing things
** this way.
**
** In the case of PClass lists (section creg), I orginally used the
** constructor to do just that, and the code for that still exists if you
** compile with something other than Visual C++ or GCC.
*/
#include "autosegs.h"
#if defined(_MSC_VER)
// The various reg sections are used to group pointers spread across multiple
// source files into cohesive arrays in the final executable. We don't
// actually care about these sections themselves and merge them all into
// a single section during the final link. (.rdata is the standard section
// for initialized read-only data.)
#pragma comment(linker, "/merge:.areg=.rdata /merge:.creg=.rdata /merge:.freg=.rdata")
#pragma comment(linker, "/merge:.greg=.rdata /merge:.yreg=.rdata")
#pragma section(".areg$a",read)
__declspec(allocate(".areg$a")) void *const ARegHead = 0;
#pragma section(".creg$a",read)
__declspec(allocate(".creg$a")) void *const CRegHead = 0;
#pragma section(".freg$a",read)
__declspec(allocate(".freg$a")) void *const FRegHead = 0;
#pragma section(".greg$a",read)
__declspec(allocate(".greg$a")) void *const GRegHead = 0;
#pragma section(".yreg$a",read)
__declspec(allocate(".yreg$a")) void *const YRegHead = 0;
// We want visual styles support under XP
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
#elif defined(__GNUC__)
#include "doomtype.h"
// I don't know of an easy way to merge sections together with the GNU linker,
// so GCC users will see all of these sections appear in the final executable.
// (There are linker scripts, but that apparently involves extracting the
// default script from ld and then modifying it.)
void *const ARegHead __attribute__((section(SECTION_AREG))) = 0;
void *const CRegHead __attribute__((section(SECTION_CREG))) = 0;
void *const FRegHead __attribute__((section(SECTION_FREG))) = 0;
void *const GRegHead __attribute__((section(SECTION_GREG))) = 0;
void *const YRegHead __attribute__((section(SECTION_YREG))) = 0;
#else
#error Please fix autostart.cpp for your compiler
#endif

View file

@ -0,0 +1,113 @@
/*
** autosegs.h
** Arrays built at link-time
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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 AUTOSEGS_H
#define AUTOSEGS_H
#if defined(__clang__)
#if defined(__has_feature) && __has_feature(address_sanitizer)
#define NO_SANITIZE __attribute__((no_sanitize("address")))
#else
#define NO_SANITIZE
#endif
#else
#define NO_SANITIZE
#endif
#define REGMARKER(x) (x)
typedef void * const REGINFO;
typedef void * NCREGINFO;
// List of Action functons
extern REGINFO ARegHead;
extern REGINFO ARegTail;
// List of TypeInfos
extern REGINFO CRegHead;
extern REGINFO CRegTail;
// List of class fields
extern REGINFO FRegHead;
extern REGINFO FRegTail;
// List of properties
extern REGINFO GRegHead;
extern REGINFO GRegTail;
// List of MAPINFO map options
extern REGINFO YRegHead;
extern REGINFO YRegTail;
class FAutoSegIterator
{
public:
FAutoSegIterator(REGINFO &head, REGINFO &tail)
{
// Weirdness. Mingw's linker puts these together backwards.
if (&head <= &tail)
{
Head = &head;
Tail = &tail;
}
else
{
Head = &tail;
Tail = &head;
}
Probe = Head;
}
NCREGINFO operator*() const NO_SANITIZE
{
return *Probe;
}
FAutoSegIterator &operator++() NO_SANITIZE
{
do
{
++Probe;
} while (*Probe == 0 && Probe < Tail);
return *this;
}
void Reset()
{
Probe = Head;
}
protected:
REGINFO *Probe;
REGINFO *Head;
REGINFO *Tail;
};
#endif

View file

@ -0,0 +1,515 @@
/*
** dobject.cpp
** Implements the base class DObject, which most other classes derive from
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
*/
#include <stdlib.h>
#include "dobject.h"
#include "cmdlib.h"
#include "c_dispatch.h"
#include "serializer.h"
#include "i_time.h"
#include "printf.h"
//==========================================================================
//
//
//
//==========================================================================
ClassReg DObject::RegistrationInfo =
{
nullptr, // MyClass
"DObject", // Name
nullptr, // ParentType
nullptr,
nullptr, // Pointers
&DObject::InPlaceConstructor, // ConstructNative
nullptr,
sizeof(DObject), // SizeOf
};
_DECLARE_TI(DObject)
//==========================================================================
//
//
//
//==========================================================================
CCMD (dumpclasses)
{
// This is by no means speed-optimized. But it's an informational console
// command that will be executed infrequently, so I don't mind.
struct DumpInfo
{
const PClass *Type;
DumpInfo *Next;
DumpInfo *Children;
static DumpInfo *FindType (DumpInfo *root, const PClass *type)
{
if (root == NULL)
{
return root;
}
if (root->Type == type)
{
return root;
}
if (root->Next != NULL)
{
return FindType (root->Next, type);
}
if (root->Children != NULL)
{
return FindType (root->Children, type);
}
return NULL;
}
static DumpInfo *AddType (DumpInfo **root, const PClass *type)
{
DumpInfo *info, *parentInfo;
if (*root == NULL)
{
info = new DumpInfo;
info->Type = type;
info->Next = NULL;
info->Children = *root;
*root = info;
return info;
}
if (type->ParentClass == (*root)->Type)
{
parentInfo = *root;
}
else if (type == (*root)->Type)
{
return *root;
}
else
{
parentInfo = FindType (*root, type->ParentClass);
if (parentInfo == NULL)
{
parentInfo = AddType (root, type->ParentClass);
}
}
// Has this type already been added?
for (info = parentInfo->Children; info != NULL; info = info->Next)
{
if (info->Type == type)
{
return info;
}
}
info = new DumpInfo;
info->Type = type;
info->Next = parentInfo->Children;
info->Children = NULL;
parentInfo->Children = info;
return info;
}
static void PrintTree (DumpInfo *root, int level)
{
Printf ("%*c%s\n", level, ' ', root->Type->TypeName.GetChars());
if (root->Children != NULL)
{
PrintTree (root->Children, level + 2);
}
if (root->Next != NULL)
{
PrintTree (root->Next, level);
}
}
static void FreeTree (DumpInfo *root)
{
if (root->Children != NULL)
{
FreeTree (root->Children);
}
if (root->Next != NULL)
{
FreeTree (root->Next);
}
delete root;
}
};
unsigned int i;
int shown, omitted;
DumpInfo *tree = NULL;
const PClass *root = NULL;
if (argv.argc() > 1)
{
root = PClass::FindClass (argv[1]);
if (root == NULL)
{
Printf ("Class '%s' not found\n", argv[1]);
return;
}
}
shown = omitted = 0;
DumpInfo::AddType (&tree, root != NULL ? root : RUNTIME_CLASS(DObject));
for (i = 0; i < PClass::AllClasses.Size(); i++)
{
PClass *cls = PClass::AllClasses[i];
if (root == NULL || cls == root || cls->IsDescendantOf(root))
{
DumpInfo::AddType (&tree, cls);
// Printf (" %s\n", PClass::m_Types[i]->Name + 1);
shown++;
}
else
{
omitted++;
}
}
DumpInfo::PrintTree (tree, 2);
DumpInfo::FreeTree (tree);
Printf ("%d classes shown, %d omitted\n", shown, omitted);
}
//==========================================================================
//
//
//
//==========================================================================
void DObject::InPlaceConstructor (void *mem)
{
new ((EInPlace *)mem) DObject;
}
DObject::DObject ()
: Class(0), ObjectFlags(0)
{
ObjectFlags = GC::CurrentWhite & OF_WhiteBits;
ObjNext = GC::Root;
GCNext = nullptr;
GC::Root = this;
}
DObject::DObject (PClass *inClass)
: Class(inClass), ObjectFlags(0)
{
ObjectFlags = GC::CurrentWhite & OF_WhiteBits;
ObjNext = GC::Root;
GCNext = nullptr;
GC::Root = this;
}
//==========================================================================
//
//
//
//==========================================================================
DObject::~DObject ()
{
if (!PClass::bShutdown)
{
PClass *type = GetClass();
if (!(ObjectFlags & OF_Cleanup) && !PClass::bShutdown)
{
if (!(ObjectFlags & (OF_YesReallyDelete|OF_Released)))
{
Printf("Warning: '%s' is freed outside the GC process.\n",
type != NULL ? type->TypeName.GetChars() : "==some object==");
}
if (!(ObjectFlags & OF_Released))
{
// Find all pointers that reference this object and NULL them.
Release();
}
}
if (nullptr != type)
{
type->DestroySpecials(this);
}
}
}
void DObject::Release()
{
DObject **probe;
// Unlink this object from the GC list.
for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext))
{
if (*probe == this)
{
*probe = ObjNext;
if (&ObjNext == GC::SweepPos)
{
GC::SweepPos = probe;
}
break;
}
}
// If it's gray, also unlink it from the gray list.
if (this->IsGray())
{
for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext))
{
if (*probe == this)
{
*probe = GCNext;
break;
}
}
}
ObjNext = nullptr;
GCNext = nullptr;
ObjectFlags |= OF_Released;
}
//==========================================================================
//
//
//
//==========================================================================
void DObject:: Destroy ()
{
// We cannot call the VM during shutdown because all the needed data has been or is in the process of being deleted.
if (PClass::bVMOperational)
{
#if 0
IFVIRTUAL(DObject, OnDestroy)
{
VMValue params[1] = { (DObject*)this };
VMCall(func, params, 1, nullptr, 0);
}
#endif
}
OnDestroy();
ObjectFlags = (ObjectFlags & ~OF_Fixed) | OF_EuthanizeMe;
}
#if 0
DEFINE_ACTION_FUNCTION(DObject, Destroy)
{
PARAM_SELF_PROLOGUE(DObject);
self->Destroy();
return 0;
}
#endif
//==========================================================================
//
//
//
//==========================================================================
size_t DObject::PropagateMark()
{
const PClass *info = GetClass();
if (!PClass::bShutdown)
{
const size_t *offsets = info->FlatPointers;
if (offsets == NULL)
{
const_cast<PClass *>(info)->BuildFlatPointers();
offsets = info->FlatPointers;
}
while (*offsets != ~(size_t)0)
{
GC::Mark((DObject **)((uint8_t *)this + *offsets));
offsets++;
}
offsets = info->ArrayPointers;
if (offsets == NULL)
{
const_cast<PClass *>(info)->BuildArrayPointers();
offsets = info->ArrayPointers;
}
while (*offsets != ~(size_t)0)
{
auto aray = (TArray<DObject*>*)((uint8_t *)this + *offsets);
for (auto &p : *aray)
{
GC::Mark(&p);
}
offsets++;
}
return info->Size;
}
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
size_t DObject::PointerSubstitution (DObject *old, DObject *notOld)
{
const PClass *info = GetClass();
const size_t *offsets = info->FlatPointers;
size_t changed = 0;
if (offsets == NULL)
{
const_cast<PClass *>(info)->BuildFlatPointers();
offsets = info->FlatPointers;
}
while (*offsets != ~(size_t)0)
{
if (*(DObject **)((uint8_t *)this + *offsets) == old)
{
*(DObject **)((uint8_t *)this + *offsets) = notOld;
changed++;
}
offsets++;
}
offsets = info->ArrayPointers;
if (offsets == NULL)
{
const_cast<PClass *>(info)->BuildArrayPointers();
offsets = info->ArrayPointers;
}
while (*offsets != ~(size_t)0)
{
auto aray = (TArray<DObject*>*)((uint8_t *)this + *offsets);
for (auto &p : *aray)
{
if (p == old)
{
p = notOld;
changed++;
}
}
offsets++;
}
return changed;
}
//==========================================================================
//
//
//
//==========================================================================
void DObject::SerializeUserVars(FSerializer &arc)
{
if (arc.isWriting())
{
// Write all fields that aren't serialized by native code.
GetClass()->WriteAllFields(arc, this);
}
else
{
GetClass()->ReadAllFields(arc, this);
}
}
//==========================================================================
//
//
//
//==========================================================================
void DObject::Serialize(FSerializer &arc)
{
const auto SerializeFlag = [&](const char *const name, const EObjectFlags flag)
{
int value = ObjectFlags & flag;
int defaultvalue = 0;
arc(name, value, defaultvalue);
if (arc.isReading())
{
ObjectFlags |= value;
}
};
SerializeFlag("justspawned", OF_JustSpawned);
SerializeFlag("spawned", OF_Spawned);
ObjectFlags |= OF_SerialSuccess;
}
void DObject::CheckIfSerialized () const
{
if (!(ObjectFlags & OF_SerialSuccess))
{
I_Error (
"BUG: %s::Serialize\n"
"(or one of its superclasses) needs to call\n"
"Super::Serialize\n",
StaticType()->TypeName.GetChars());
}
}
#if 0
DEFINE_ACTION_FUNCTION(DObject, MSTime)
{
ACTION_RETURN_INT((uint32_t)I_msTime());
}
void *DObject::ScriptVar(FName field, PType *type)
{
auto cls = GetClass();
auto sym = dyn_cast<PField>(cls->FindSymbol(field, true));
if (sym && (sym->Type == type || type == nullptr))
{
if (!(sym->Flags & VARF_Meta))
{
return (((char*)this) + sym->Offset);
}
else
{
return (cls->Meta + sym->Offset);
}
}
// This is only for internal use so I_Error is fine.
I_Error("Variable %s not found in %s\n", field.GetChars(), cls->TypeName.GetChars());
return nullptr;
}
#endif

View file

@ -0,0 +1,475 @@
/*
** dobject.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2008 Randy Heit
** 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 __DOBJECT_H__
#define __DOBJECT_H__
#include <stdlib.h>
#include <type_traits>
#include "vectors.h"
#include "name.h"
#include "palentry.h"
#include "textureid.h"
class PClass;
class PType;
class FSerializer;
class FSoundID;
class DObject;
/*
class DConsoleCommand;
class DConsoleAlias;
class DSeqNode;
class DSeqActorNode;
class DSeqPolyNode;
class DSeqSectorNode;
class DThinker;
class AActor;
class DPolyAction;
class DMovePoly;
class DPolyDoor;
class DRotatePoly;
class DPusher;
class DScroller;
class DSectorEffect;
class DLighting;
class DFireFlicker;
class DFlicker;
class DGlow;
class DGlow2;
class DLightFlash;
class DPhased;
class DStrobe;
class DMover;
class DElevator;
class DMovingCeiling;
class DCeiling;
class DDoor;
class DMovingFloor;
class DFloor;
class DFloorWaggle;
class DPlat;
class DPillar;
*/
class PClassActor;
#define RUNTIME_CLASS_CASTLESS(cls) (cls::RegistrationInfo.MyClass) // Passed a native class name, returns a PClass representing that class
#define RUNTIME_CLASS(cls) ((typename cls::MetaClass *)RUNTIME_CLASS_CASTLESS(cls)) // Like above, but returns the true type of the meta object
#define NATIVE_TYPE(object) (object->StaticType()) // Passed an object, returns the type of the C++ class representing the object
// Enumerations for the meta classes created by ClassReg::RegisterClass()
struct ClassReg
{
PClass *MyClass;
const char *Name;
ClassReg *ParentType;
ClassReg *_VMExport;
const size_t *Pointers;
void (*ConstructNative)(void *);
void(*InitNatives)();
unsigned int SizeOf;
PClass *RegisterClass();
void SetupClass(PClass *cls);
};
enum EInPlace { EC_InPlace };
#define DECLARE_ABSTRACT_CLASS(cls,parent) \
public: \
virtual PClass *StaticType() const; \
static ClassReg RegistrationInfo, * const RegistrationInfoPtr; \
typedef parent Super; \
private: \
typedef cls ThisClass;
#define DECLARE_ABSTRACT_CLASS_WITH_META(cls,parent,meta) \
DECLARE_ABSTRACT_CLASS(cls,parent) \
public: \
typedef meta MetaClass; \
MetaClass *GetClass() const { return static_cast<MetaClass *>(DObject::GetClass()); }
#define DECLARE_CLASS(cls,parent) \
DECLARE_ABSTRACT_CLASS(cls,parent) \
private: static void InPlaceConstructor (void *mem);
#define DECLARE_CLASS_WITH_META(cls,parent,meta) \
DECLARE_ABSTRACT_CLASS_WITH_META(cls,parent,meta) \
private: static void InPlaceConstructor (void *mem);
#define HAS_OBJECT_POINTERS \
static const size_t PointerOffsets[];
#if defined(_MSC_VER)
# pragma section(".creg$u",read)
# define _DECLARE_TI(cls) __declspec(allocate(".creg$u")) ClassReg * const cls::RegistrationInfoPtr = &cls::RegistrationInfo;
#else
# define _DECLARE_TI(cls) ClassReg * const cls::RegistrationInfoPtr __attribute__((section(SECTION_CREG))) = &cls::RegistrationInfo;
#endif
#define _IMP_PCLASS(cls, ptrs, create) \
ClassReg cls::RegistrationInfo = {\
nullptr, \
#cls, \
&cls::Super::RegistrationInfo, \
nullptr, \
ptrs, \
create, \
nullptr, \
sizeof(cls) }; \
_DECLARE_TI(cls) \
PClass *cls::StaticType() const { return RegistrationInfo.MyClass; }
#define IMPLEMENT_CLASS(cls, isabstract, ptrs) \
_X_CONSTRUCTOR_##isabstract(cls) \
_IMP_PCLASS(cls, _X_POINTERS_##ptrs(cls), _X_ABSTRACT_##isabstract(cls))
// Taking the address of a field in an object at address > 0 instead of
// address 0 keeps GCC from complaining about possible misuse of offsetof.
// Using 8 to avoid unaligned pointer use.
#define IMPLEMENT_POINTERS_START(cls) const size_t cls::PointerOffsets[] = {
#define IMPLEMENT_POINTER(field) ((size_t)&((ThisClass*)8)->field) - 8,
#define IMPLEMENT_POINTERS_END ~(size_t)0 };
// Possible arguments for the IMPLEMENT_CLASS macro
#define _X_POINTERS_true(cls) cls::PointerOffsets
#define _X_POINTERS_false(cls) nullptr
#define _X_FIELDS_true(cls) nullptr
#define _X_FIELDS_false(cls) nullptr
#define _X_CONSTRUCTOR_true(cls)
#define _X_CONSTRUCTOR_false(cls) void cls::InPlaceConstructor(void *mem) { new((EInPlace *)mem) cls; }
#define _X_ABSTRACT_true(cls) nullptr
#define _X_ABSTRACT_false(cls) cls::InPlaceConstructor
#define _X_VMEXPORT_true(cls) nullptr
#define _X_VMEXPORT_false(cls) nullptr
#include "dobjgc.h"
class AActor;
class DObject
{
public:
virtual PClass *StaticType() const { return RegistrationInfo.MyClass; }
static ClassReg RegistrationInfo, * const RegistrationInfoPtr;
static void InPlaceConstructor (void *mem);
typedef PClass MetaClass;
private:
typedef DObject ThisClass;
protected:
// Per-instance variables. There are four.
#ifndef NDEBUG
public:
enum
{
MAGIC_ID = 0x1337cafe
};
uint32_t MagicID = MAGIC_ID; // only used by the VM for checking native function parameter types.
#endif
private:
PClass *Class; // This object's type
public:
DObject *ObjNext; // Keep track of all allocated objects
DObject *GCNext; // Next object in this collection list
uint32_t ObjectFlags; // Flags for this object
void *ScriptVar(FName field, PType *type);
protected:
public:
DObject ();
DObject (PClass *inClass);
virtual ~DObject ();
inline bool IsKindOf (const PClass *base) const;
inline bool IsKindOf(FName base) const;
inline bool IsA (const PClass *type) const;
void SerializeUserVars(FSerializer &arc);
virtual void Serialize(FSerializer &arc);
// Releases the object from the GC, letting the caller care of any maintenance.
void Release();
// For catching Serialize functions in derived classes
// that don't call their base class.
void CheckIfSerialized () const;
virtual void OnDestroy() {}
void Destroy();
// Add other types as needed.
inline bool &BoolVar(FName field);
inline int &IntVar(FName field);
inline FTextureID &TextureIDVar(FName field);
inline FSoundID &SoundVar(FName field);
inline PalEntry &ColorVar(FName field);
inline FName &NameVar(FName field);
inline double &FloatVar(FName field);
inline DAngle &AngleVar(FName field);
inline FString &StringVar(FName field);
template<class T> T*& PointerVar(FName field);
// This is only needed for swapping out PlayerPawns and absolutely nothing else!
virtual size_t PointerSubstitution (DObject *old, DObject *notOld);
PClass *GetClass() const
{
assert(Class != nullptr);
return Class;
}
void SetClass (PClass *inClass)
{
Class = inClass;
}
private:
struct nonew
{
};
void *operator new(size_t len, nonew&)
{
GC::AllocBytes += len;
return M_Malloc(len);
}
public:
void operator delete (void *mem, nonew&)
{
GC::AllocBytes -= _msize(mem);
M_Free(mem);
}
void operator delete (void *mem)
{
M_Free(mem);
}
// GC fiddling
// An object is white if either white bit is set.
bool IsWhite() const
{
return !!(ObjectFlags & OF_WhiteBits);
}
bool IsBlack() const
{
return !!(ObjectFlags & OF_Black);
}
// An object is gray if it isn't white or black.
bool IsGray() const
{
return !(ObjectFlags & OF_MarkBits);
}
// An object is dead if it's the other white.
bool IsDead() const
{
return !!(ObjectFlags & GC::OtherWhite() & OF_WhiteBits);
}
void ChangeWhite()
{
ObjectFlags ^= OF_WhiteBits;
}
void MakeWhite()
{
ObjectFlags = (ObjectFlags & ~OF_MarkBits) | (GC::CurrentWhite & OF_WhiteBits);
}
void White2Gray()
{
ObjectFlags &= ~OF_WhiteBits;
}
void Black2Gray()
{
ObjectFlags &= ~OF_Black;
}
void Gray2Black()
{
ObjectFlags |= OF_Black;
}
// Marks all objects pointed to by this one. Returns the (approximate)
// amount of memory used by this object.
virtual size_t PropagateMark();
protected:
// This form of placement new and delete is for use *only* by PClass's
// CreateNew() method. Do not use them for some other purpose.
void *operator new(size_t, EInPlace *mem)
{
return (void *)mem;
}
void operator delete (void *mem, EInPlace *)
{
M_Free (mem);
}
template<typename T, typename... Args>
friend T* Create(Args&&... args);
friend class JitCompiler;
};
// This is the only method aside from calling CreateNew that should be used for creating DObjects
// to ensure that the Class pointer is always set.
template<typename T, typename... Args>
T* Create(Args&&... args)
{
DObject::nonew nono;
T *object = new(nono) T(std::forward<Args>(args)...);
if (object != nullptr)
{
object->SetClass(RUNTIME_CLASS(T));
assert(object->GetClass() != nullptr); // beware of objects that get created before the type system is up.
}
return object;
}
// When you write to a pointer to an Object, you must call this for
// proper bookkeeping in case the Object holding this pointer has
// already been processed by the GC.
static inline void GC::WriteBarrier(DObject *pointing, DObject *pointed)
{
if (pointed != NULL && pointed->IsWhite() && pointing->IsBlack())
{
Barrier(pointing, pointed);
}
}
static inline void GC::WriteBarrier(DObject *pointed)
{
if (pointed != NULL && State == GCS_Propagate && pointed->IsWhite())
{
Barrier(NULL, pointed);
}
}
#include "memarena.h"
extern FMemArena ClassDataAllocator;
//#include "symbols.h"
#include "dobjtype.h"
inline bool DObject::IsKindOf (const PClass *base) const
{
return base->IsAncestorOf (GetClass ());
}
inline bool DObject::IsKindOf(FName base) const
{
return GetClass()->IsDescendantOf(base);
}
inline bool DObject::IsA (const PClass *type) const
{
return (type == GetClass());
}
template<class T> T *dyn_cast(DObject *p)
{
if (p != NULL && p->IsKindOf(RUNTIME_CLASS_CASTLESS(T)))
{
return static_cast<T *>(p);
}
return NULL;
}
template<class T> const T *dyn_cast(const DObject *p)
{
return dyn_cast<T>(const_cast<DObject *>(p));
}
inline bool &DObject::BoolVar(FName field)
{
return *(bool*)ScriptVar(field, nullptr);
}
inline int &DObject::IntVar(FName field)
{
return *(int*)ScriptVar(field, nullptr);
}
inline FTextureID &DObject::TextureIDVar(FName field)
{
return *(FTextureID*)ScriptVar(field, nullptr);
}
inline FSoundID &DObject::SoundVar(FName field)
{
return *(FSoundID*)ScriptVar(field, nullptr);
}
inline PalEntry &DObject::ColorVar(FName field)
{
return *(PalEntry*)ScriptVar(field, nullptr);
}
inline FName &DObject::NameVar(FName field)
{
return *(FName*)ScriptVar(field, nullptr);
}
inline double &DObject::FloatVar(FName field)
{
return *(double*)ScriptVar(field, nullptr);
}
inline DAngle &DObject::AngleVar(FName field)
{
return *(DAngle*)ScriptVar(field, nullptr);
}
template<class T>
inline T *&DObject::PointerVar(FName field)
{
return *(T**)ScriptVar(field, nullptr); // pointer check is more tricky and for the handful of uses in the DECORATE parser not worth the hassle.
}
#endif //__DOBJECT_H__

View file

@ -0,0 +1,621 @@
/*
** dobjgc.cpp
** The garbage collector. Based largely on Lua's.
**
**---------------------------------------------------------------------------
** Copyright 2008 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
*/
/******************************************************************************
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
// HEADER FILES ------------------------------------------------------------
#include "dobject.h"
#include "templates.h"
#include "c_dispatch.h"
#include "menu/menu.h"
#include "stats.h"
// MACROS ------------------------------------------------------------------
/*
@@ DEFAULT_GCPAUSE defines the default pause between garbage-collector cycles
@* as a percentage.
** CHANGE it if you want the GC to run faster or slower (higher values
** mean larger pauses which mean slower collection.) You can also change
** this value dynamically.
*/
#define DEFAULT_GCPAUSE 150 // 150% (wait for memory to increase by half before next GC)
/*
@@ DEFAULT_GCMUL defines the default speed of garbage collection relative to
@* memory allocation as a percentage.
** CHANGE it if you want to change the granularity of the garbage
** collection. (Higher values mean coarser collections. 0 represents
** infinity, where each step performs a full collection.) You can also
** change this value dynamically.
*/
#define DEFAULT_GCMUL 400 // GC runs 'quadruple the speed' of memory allocation
// Number of sectors to mark for each step.
#define GCSTEPSIZE 1024u
#define GCSWEEPMAX 40
#define GCSWEEPCOST 10
#define GCFINALIZECOST 100
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
//extern DThinker *NextToThink;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
namespace GC
{
size_t AllocBytes;
size_t Threshold;
size_t Estimate;
DObject *Gray;
DObject *Root;
DObject *SoftRoots;
DObject **SweepPos;
uint32_t CurrentWhite = OF_White0 | OF_Fixed;
EGCState State = GCS_Pause;
int Pause = DEFAULT_GCPAUSE;
int StepMul = DEFAULT_GCMUL;
int StepCount;
size_t Dept;
bool FinalGC;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// CODE --------------------------------------------------------------------
//==========================================================================
//
// SetThreshold
//
// Sets the new threshold after a collection is finished.
//
//==========================================================================
void SetThreshold()
{
Threshold = (Estimate / 100) * Pause;
}
//==========================================================================
//
// PropagateMark
//
// Marks the top-most gray object black and marks all objects it points to
// gray.
//
//==========================================================================
size_t PropagateMark()
{
DObject *obj = Gray;
assert(obj->IsGray());
obj->Gray2Black();
Gray = obj->GCNext;
return !(obj->ObjectFlags & OF_EuthanizeMe) ? obj->PropagateMark() :
obj->GetClass()->Size;
}
//==========================================================================
//
// SweepList
//
// Runs a limited sweep on a list, returning the location where to resume
// the sweep at next time. (FIXME: Horrible Engrish in this description.)
//
//==========================================================================
static DObject **SweepList(DObject **p, size_t count, size_t *finalize_count)
{
DObject *curr;
int deadmask = OtherWhite();
size_t finalized = 0;
while ((curr = *p) != NULL && count-- > 0)
{
if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead?
{
assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed));
curr->MakeWhite(); // make it white (for next cycle)
p = &curr->ObjNext;
}
else // must erase 'curr'
{
assert(curr->IsDead());
*p = curr->ObjNext;
if (!(curr->ObjectFlags & OF_EuthanizeMe))
{ // The object must be destroyed before it can be finalized.
// Note that thinkers must already have been destroyed. If they get here without
// having been destroyed first, it means they somehow became unattached from the
// thinker lists. If I don't maintain the invariant that all live thinkers must
// be in a thinker list, then I need to add write barriers for every time a
// thinker pointer is changed. This seems easier and perfectly reasonable, since
// a live thinker that isn't on a thinker list isn't much of a thinker.
// However, this can happen during deletion of the thinker list while cleaning up
// from a savegame error so we can't assume that any thinker that gets here is an error.
curr->Destroy();
}
curr->ObjectFlags |= OF_Cleanup;
delete curr;
finalized++;
}
}
if (finalize_count != NULL)
{
*finalize_count = finalized;
}
return p;
}
//==========================================================================
//
// Mark
//
// Mark a single object gray.
//
//==========================================================================
void Mark(DObject **obj)
{
DObject *lobj = *obj;
//assert(lobj == nullptr || !(lobj->ObjectFlags & OF_Released));
if (lobj != nullptr && !(lobj->ObjectFlags & OF_Released))
{
if (lobj->ObjectFlags & OF_EuthanizeMe)
{
*obj = (DObject *)NULL;
}
else if (lobj->IsWhite())
{
lobj->White2Gray();
lobj->GCNext = Gray;
Gray = lobj;
}
}
}
//==========================================================================
//
// MarkArray
//
// Mark an array of objects gray.
//
//==========================================================================
void MarkArray(DObject **obj, size_t count)
{
for (size_t i = 0; i < count; ++i)
{
Mark(obj[i]);
}
}
//==========================================================================
//
// MarkRoot
//
// Mark the root set of objects.
//
//==========================================================================
static void MarkRoot()
{
int i;
Gray = NULL;
// Time to propagate the marks.
State = GCS_Propagate;
StepCount = 0;
}
//==========================================================================
//
// Atomic
//
// If there were any propagations that needed to be done atomicly, they
// would go here. It also sets things up for the sweep state.
//
//==========================================================================
static void Atomic()
{
// Flip current white
CurrentWhite = OtherWhite();
SweepPos = &Root;
State = GCS_Sweep;
Estimate = AllocBytes;
}
//==========================================================================
//
// SingleStep
//
// Performs one step of the collector.
//
//==========================================================================
static size_t SingleStep()
{
switch (State)
{
case GCS_Pause:
MarkRoot(); // Start a new collection
return 0;
case GCS_Propagate:
if (Gray != NULL)
{
return PropagateMark();
}
else
{ // no more gray objects
Atomic(); // finish mark phase
return 0;
}
case GCS_Sweep: {
size_t old = AllocBytes;
size_t finalize_count;
SweepPos = SweepList(SweepPos, GCSWEEPMAX, &finalize_count);
if (*SweepPos == NULL)
{ // Nothing more to sweep?
State = GCS_Finalize;
}
//assert(old >= AllocBytes);
Estimate -= MAX<size_t>(0, old - AllocBytes);
return (GCSWEEPMAX - finalize_count) * GCSWEEPCOST + finalize_count * GCFINALIZECOST;
}
case GCS_Finalize:
State = GCS_Pause; // end collection
Dept = 0;
return 0;
default:
assert(0);
return 0;
}
}
//==========================================================================
//
// Step
//
// Performs enough single steps to cover GCSTEPSIZE * StepMul% bytes of
// memory.
//
//==========================================================================
void Step()
{
size_t lim = (GCSTEPSIZE/100) * StepMul;
size_t olim;
if (lim == 0)
{
lim = (~(size_t)0) / 2; // no limit
}
Dept += AllocBytes - Threshold;
do
{
olim = lim;
lim -= SingleStep();
} while (olim > lim && State != GCS_Pause);
if (State != GCS_Pause)
{
if (Dept < GCSTEPSIZE)
{
Threshold = AllocBytes + GCSTEPSIZE; // - lim/StepMul
}
else
{
Dept -= GCSTEPSIZE;
Threshold = AllocBytes;
}
}
else
{
assert(AllocBytes >= Estimate);
SetThreshold();
}
StepCount++;
}
//==========================================================================
//
// FullGC
//
// Collects everything in one fell swoop.
//
//==========================================================================
void FullGC()
{
if (State <= GCS_Propagate)
{
// Reset sweep mark to sweep all elements (returning them to white)
SweepPos = &Root;
// Reset other collector lists
Gray = NULL;
State = GCS_Sweep;
}
// Finish any pending sweep phase
while (State != GCS_Finalize)
{
SingleStep();
}
MarkRoot();
while (State != GCS_Pause)
{
SingleStep();
}
SetThreshold();
}
//==========================================================================
//
// Barrier
//
// Implements a write barrier to maintain the invariant that a black node
// never points to a white node by making the node pointed at gray.
//
//==========================================================================
void Barrier(DObject *pointing, DObject *pointed)
{
assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead()));
assert(pointed->IsWhite() && !pointed->IsDead());
assert(State != GCS_Finalize && State != GCS_Pause);
assert(!(pointed->ObjectFlags & OF_Released)); // if a released object gets here, something must be wrong.
if (pointed->ObjectFlags & OF_Released) return; // don't do anything with non-GC'd objects.
// The invariant only needs to be maintained in the propagate state.
if (State == GCS_Propagate)
{
pointed->White2Gray();
pointed->GCNext = Gray;
Gray = pointed;
}
// In other states, we can mark the pointing object white so this
// barrier won't be triggered again, saving a few cycles in the future.
else if (pointing != NULL)
{
pointing->MakeWhite();
}
}
void DelSoftRootHead()
{
if (SoftRoots != NULL)
{
// Don't let the destructor print a warning message
SoftRoots->ObjectFlags |= OF_YesReallyDelete;
delete SoftRoots;
}
SoftRoots = NULL;
}
//==========================================================================
//
// AddSoftRoot
//
// Marks an object as a soft root. A soft root behaves exactly like a root
// in MarkRoot, except it can be added at run-time.
//
//==========================================================================
void AddSoftRoot(DObject *obj)
{
DObject **probe;
// Are there any soft roots yet?
if (SoftRoots == NULL)
{
// Create a new object to root the soft roots off of, and stick
// it at the end of the object list, so we know that anything
// before it is not a soft root.
SoftRoots = Create<DObject>();
SoftRoots->ObjectFlags |= OF_Fixed;
probe = &Root;
while (*probe != NULL)
{
probe = &(*probe)->ObjNext;
}
Root = SoftRoots->ObjNext;
SoftRoots->ObjNext = NULL;
*probe = SoftRoots;
}
// Mark this object as rooted and move it after the SoftRoots marker.
probe = &Root;
while (*probe != NULL && *probe != obj)
{
probe = &(*probe)->ObjNext;
}
*probe = (*probe)->ObjNext;
obj->ObjNext = SoftRoots->ObjNext;
SoftRoots->ObjNext = obj;
obj->ObjectFlags |= OF_Rooted;
WriteBarrier(obj);
}
//==========================================================================
//
// DelSoftRoot
//
// Unroots an object so that it must be reachable or it will get collected.
//
//==========================================================================
void DelSoftRoot(DObject *obj)
{
DObject **probe;
if (!(obj->ObjectFlags & OF_Rooted))
{ // Not rooted, so nothing to do.
return;
}
obj->ObjectFlags &= ~OF_Rooted;
// Move object out of the soft roots part of the list.
probe = &SoftRoots;
while (*probe != NULL && *probe != obj)
{
probe = &(*probe)->ObjNext;
}
if (*probe == obj)
{
*probe = obj->ObjNext;
obj->ObjNext = Root;
Root = obj;
}
}
}
//==========================================================================
//
// STAT gc
//
// Provides information about the current garbage collector state.
//
//==========================================================================
ADD_STAT(gc)
{
static const char *StateStrings[] = {
" Pause ",
"Propagate",
" Sweep ",
"Finalize " };
FString out;
out.Format("[%s] Alloc:%6zuK Thresh:%6zuK Est:%6zuK Steps: %d",
StateStrings[GC::State],
(GC::AllocBytes + 1023) >> 10,
(GC::Threshold + 1023) >> 10,
(GC::Estimate + 1023) >> 10,
GC::StepCount);
if (GC::State != GC::GCS_Pause)
{
out.AppendFormat(" %zuK", (GC::Dept + 1023) >> 10);
}
return out;
}
//==========================================================================
//
// CCMD gc
//
// Controls various aspects of the collector.
//
//==========================================================================
CCMD(gc)
{
if (argv.argc() == 1)
{
Printf ("Usage: gc stop|now|full|count|pause [size]|stepmul [size]\n");
return;
}
if (stricmp(argv[1], "stop") == 0)
{
GC::Threshold = ~(size_t)0 - 2;
}
else if (stricmp(argv[1], "now") == 0)
{
GC::Threshold = GC::AllocBytes;
}
else if (stricmp(argv[1], "full") == 0)
{
GC::FullGC();
}
else if (stricmp(argv[1], "count") == 0)
{
int cnt = 0;
for (DObject *obj = GC::Root; obj; obj = obj->ObjNext, cnt++);
Printf("%d active objects counted\n", cnt);
}
else if (stricmp(argv[1], "pause") == 0)
{
if (argv.argc() == 2)
{
Printf ("Current GC pause is %d\n", GC::Pause);
}
else
{
GC::Pause = MAX(1,atoi(argv[2]));
}
}
else if (stricmp(argv[1], "stepmul") == 0)
{
if (argv.argc() == 2)
{
Printf ("Current GC stepmul is %d\n", GC::StepMul);
}
else
{
GC::StepMul = MAX(100, atoi(argv[2]));
}
}
}

View file

@ -0,0 +1,254 @@
#pragma once
#include <stdint.h>
#include "tarray.h"
class DObject;
class FSerializer;
enum EObjectFlags
{
// GC flags
OF_White0 = 1 << 0, // Object is white (type 0)
OF_White1 = 1 << 1, // Object is white (type 1)
OF_Black = 1 << 2, // Object is black
OF_Fixed = 1 << 3, // Object is fixed (should not be collected)
OF_Rooted = 1 << 4, // Object is soft-rooted
OF_EuthanizeMe = 1 << 5, // Object wants to die
OF_Cleanup = 1 << 6, // Object is now being deleted by the collector
OF_YesReallyDelete = 1 << 7, // Object is being deleted outside the collector, and this is okay, so don't print a warning
OF_WhiteBits = OF_White0 | OF_White1,
OF_MarkBits = OF_WhiteBits | OF_Black,
// Other flags
OF_JustSpawned = 1 << 8, // Thinker was spawned this tic
OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls
OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list
OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk)
OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning)
OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function
};
template<class T> class TObjPtr;
namespace GC
{
enum EGCState
{
GCS_Pause,
GCS_Propagate,
GCS_Sweep,
GCS_Finalize
};
// Number of bytes currently allocated through M_Malloc/M_Realloc.
extern size_t AllocBytes;
// Amount of memory to allocate before triggering a collection.
extern size_t Threshold;
// List of gray objects.
extern DObject *Gray;
// List of every object.
extern DObject *Root;
// Current white value for potentially-live objects.
extern uint32_t CurrentWhite;
// Current collector state.
extern EGCState State;
// Position of GC sweep in the list of objects.
extern DObject **SweepPos;
// Size of GC pause.
extern int Pause;
// Size of GC steps.
extern int StepMul;
// Is this the final collection just before exit?
extern bool FinalGC;
// Current white value for known-dead objects.
static inline uint32_t OtherWhite()
{
return CurrentWhite ^ OF_WhiteBits;
}
// Frees all objects, whether they're dead or not.
void FreeAll();
// Does one collection step.
void Step();
// Does a complete collection.
void FullGC();
// Handles the grunt work for a write barrier.
void Barrier(DObject *pointing, DObject *pointed);
// Handles a write barrier.
static inline void WriteBarrier(DObject *pointing, DObject *pointed);
// Handles a write barrier for a pointer that isn't inside an object.
static inline void WriteBarrier(DObject *pointed);
// Handles a read barrier.
template<class T> inline T *ReadBarrier(T *&obj)
{
if (obj == NULL || !(obj->ObjectFlags & OF_EuthanizeMe))
{
return obj;
}
return obj = NULL;
}
// Check if it's time to collect, and do a collection step if it is.
static inline void CheckGC()
{
if (AllocBytes >= Threshold)
Step();
}
// Forces a collection to start now.
static inline void StartCollection()
{
Threshold = AllocBytes;
}
// Marks a white object gray. If the object wants to die, the pointer
// is NULLed instead.
void Mark(DObject **obj);
// Marks an array of objects.
void MarkArray(DObject **objs, size_t count);
// For cleanup
void DelSoftRootHead();
// Soft-roots an object.
void AddSoftRoot(DObject *obj);
// Unroots an object.
void DelSoftRoot(DObject *obj);
template<class T> void Mark(T *&obj)
{
union
{
T *t;
DObject *o;
};
o = obj;
Mark(&o);
obj = t;
}
template<class T> void Mark(TObjPtr<T> &obj);
template<class T> void MarkArray(T **obj, size_t count)
{
MarkArray((DObject **)(obj), count);
}
template<class T> void MarkArray(TArray<T> &arr)
{
MarkArray(&arr[0], arr.Size());
}
}
// A template class to help with handling read barriers. It does not
// handle write barriers, because those can be handled more efficiently
// with knowledge of the object that holds the pointer.
template<class T>
class TObjPtr
{
union
{
T pp;
DObject *o;
};
public:
TObjPtr() = default;
TObjPtr(const TObjPtr<T> &q) = default;
TObjPtr(T q) throw()
: pp(q)
{
}
T operator=(T q)
{
pp = q;
return *this;
}
T operator=(std::nullptr_t nul)
{
o = nullptr;
return *this;
}
// To allow NULL, too.
T operator=(const int val)
{
assert(val == 0);
o = nullptr;
return *this;
}
// To allow NULL, too. In Clang NULL is a long.
T operator=(const long val)
{
assert(val == 0);
o = nullptr;
return *this;
}
T Get() throw()
{
return GC::ReadBarrier(pp);
}
operator T() throw()
{
return GC::ReadBarrier(pp);
}
T &operator*()
{
T q = GC::ReadBarrier(pp);
assert(q != NULL);
return *q;
}
T operator->() throw()
{
return GC::ReadBarrier(pp);
}
bool operator!=(T u) throw()
{
return GC::ReadBarrier(o) != u;
}
bool operator==(T u) throw()
{
return GC::ReadBarrier(o) == u;
}
template<class U> friend inline void GC::Mark(TObjPtr<U> &obj);
template<class U> friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr<U> &value, TObjPtr<U> *);
template<class U> friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr<U> &value, U *);
friend class DObject;
};
// Use barrier_cast instead of static_cast when you need to cast
// the contents of a TObjPtr to a related type.
template<class T,class U> inline T barrier_cast(TObjPtr<U> &o)
{
return static_cast<T>(static_cast<U>(o));
}
namespace GC
{
template<class T> inline void Mark(TObjPtr<T> &obj)
{
GC::Mark(&obj.o);
}
}

View file

@ -0,0 +1,981 @@
/*
** dobjtype.cpp
** Implements the type information class
**
**---------------------------------------------------------------------------
** Copyright 1998-2016 Randy Heit
** Copyright 2005-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.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include <limits>
#include "dobject.h"
#include "serializer.h"
#include "autosegs.h"
#include "v_text.h"
#include "c_cvars.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
EXTERN_CVAR(Bool, strictdecorate);
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FMemArena ClassDataAllocator(32768); // use this for all static class data that can be released in bulk when the type system is shut down.
TArray<PClass *> PClass::AllClasses;
TMap<FName, PClass*> PClass::ClassMap;
TArray<VMFunction**> PClass::FunctionPtrList;
bool PClass::bShutdown;
bool PClass::bVMOperational;
// Originally this was just a bogus pointer, but with the VM performing a read barrier on every object pointer write
// that does not work anymore. WP_NOCHANGE needs to point to a vaild object to work as intended.
// This Object does not need to be garbage collected, though, but it needs to provide the proper structure so that the
// GC can process it.
AActor *WP_NOCHANGE;
//DEFINE_GLOBAL(WP_NOCHANGE);
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// A harmless non-nullptr FlatPointer for classes without pointers.
static const size_t TheEnd = ~(size_t)0;
//==========================================================================
//
// PClass :: WriteValue
//
// Similar to PStruct's version, except it also needs to traverse parent
// classes.
//
//==========================================================================
static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *addr)
{
#if 0
if (type != nullptr)
{
RecurseWriteFields(type->ParentClass, ar, addr);
// Don't write this part if it has no non-transient variables
for (unsigned i = 0; i < type->Fields.Size(); ++i)
{
if (!(type->Fields[i]->Flags & (VARF_Transient|VARF_Meta)))
{
// Tag this section with the class it came from in case
// a more-derived class has variables that shadow a less-
// derived class. Whether or not that is a language feature
// that will actually be allowed remains to be seen.
FString key;
key.Format("class:%s", type->TypeName.GetChars());
if (ar.BeginObject(key.GetChars()))
{
type->VMType->Symbols.WriteFields(ar, addr);
ar.EndObject();
}
break;
}
}
}
#endif
}
// Same as WriteValue, but does not create a new object in the serializer
// This is so that user variables do not contain unnecessary subblocks.
void PClass::WriteAllFields(FSerializer &ar, const void *addr) const
{
RecurseWriteFields(this, ar, addr);
}
//==========================================================================
//
// PClass :: ReadAllFields
//
//==========================================================================
bool PClass::ReadAllFields(FSerializer &ar, void *addr) const
{
bool readsomething = false;
bool foundsomething = false;
#if 0
const char *key;
key = ar.GetKey();
if (strcmp(key, "classtype"))
{
// this does not represent a DObject
Printf(TEXTCOLOR_RED "trying to read user variables but got a non-object (first key is '%s')\n", key);
ar.mErrors++;
return false;
}
while ((key = ar.GetKey()))
{
if (strncmp(key, "class:", 6))
{
// We have read all user variable blocks.
break;
}
foundsomething = true;
PClass *type = PClass::FindClass(key + 6);
if (type != nullptr)
{
// Only read it if the type is related to this one.
if (IsDescendantOf(type))
{
if (ar.BeginObject(nullptr))
{
readsomething |= type->VMType->Symbols.ReadFields(ar, addr, type->TypeName.GetChars());
ar.EndObject();
}
}
else
{
DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n",
type->TypeName.GetChars(), TypeName.GetChars());
}
}
else
{
DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n",
key+6, TypeName.GetChars());
}
}
#endif
return readsomething || !foundsomething;
}
//==========================================================================
//
// cregcmp
//
// Sorter to keep built-in types in a deterministic order. (Needed?)
//
//==========================================================================
static int cregcmp (const void *a, const void *b) NO_SANITIZE
{
const PClass *class1 = *(const PClass **)a;
const PClass *class2 = *(const PClass **)b;
return strcmp(class1->TypeName.GetChars(), class2->TypeName.GetChars());
}
//==========================================================================
//
// PClass :: StaticInit STATIC
//
// Creates class metadata for all built-in types.
//
//==========================================================================
void PClass::StaticInit ()
{
#if 0
Namespaces.GlobalNamespace = Namespaces.NewNamespace(0);
#endif
FAutoSegIterator probe(CRegHead, CRegTail);
while (*++probe != nullptr)
{
((ClassReg *)*probe)->RegisterClass ();
}
probe.Reset();
#if 0
for(auto cls : AllClasses)
{
if (cls->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
PClassActor::AllActorClasses.Push(static_cast<PClassActor*>(cls));
}
}
#endif
// Keep built-in classes in consistant order. I did this before, though
// I'm not sure if this is really necessary to maintain any sort of sync.
qsort(&AllClasses[0], AllClasses.Size(), sizeof(AllClasses[0]), cregcmp);
#if 0
// WP_NOCHANGE must point to a valid object, although it does not need to be a weapon.
// A simple DObject is enough to give the GC the ability to deal with it, if subjected to it.
WP_NOCHANGE = (AActor*)Create<DObject>();
WP_NOCHANGE->Release();
#endif
}
//==========================================================================
//
// PClass :: StaticShutdown STATIC
//
// Frees all static class data.
//
//==========================================================================
void PClass::StaticShutdown ()
{
#if 0
if (WP_NOCHANGE != nullptr)
{
delete WP_NOCHANGE;
}
#endif
// delete all variables containing pointers to script functions.
for (auto p : FunctionPtrList)
{
*p = nullptr;
}
//ScriptUtil::Clear();
FunctionPtrList.Clear();
//VMFunction::DeleteAll();
// Make a full garbage collection here so that all destroyed but uncollected higher level objects
// that still exist are properly taken down before the low level data is deleted.
GC::FullGC();
// From this point onward no scripts may be called anymore because the data needed by the VM is getting deleted now.
// This flags DObject::Destroy not to call any scripted OnDestroy methods anymore.
bVMOperational = false;
#if 0
for (auto &p : players)
{
p.PendingWeapon = nullptr;
}
Namespaces.ReleaseSymbols();
#endif
// This must be done in two steps because the native classes are not ordered by inheritance,
// so all meta data must be gone before deleting the actual class objects.
for (auto cls : AllClasses) if (cls->Meta != nullptr) cls->DestroyMeta(cls->Meta);
for (auto cls : AllClasses) delete cls;
// Unless something went wrong, anything left here should be class and type objects only, which do not own any scripts.
bShutdown = true;
//TypeTable.Clear();
ClassDataAllocator.FreeAllBlocks();
AllClasses.Clear();
//PClassActor::AllActorClasses.Clear();
ClassMap.Clear();
FAutoSegIterator probe(CRegHead, CRegTail);
while (*++probe != nullptr)
{
auto cr = ((ClassReg *)*probe);
cr->MyClass = nullptr;
}
}
//==========================================================================
//
// PClass Constructor
//
//==========================================================================
PClass::PClass()
{
PClass::AllClasses.Push(this);
}
//==========================================================================
//
// PClass Destructor
//
//==========================================================================
PClass::~PClass()
{
if (Defaults != nullptr)
{
M_Free(Defaults);
Defaults = nullptr;
}
if (Meta != nullptr)
{
M_Free(Meta);
Meta = nullptr;
}
}
//==========================================================================
//
// ClassReg :: RegisterClass
//
// Create metadata describing the built-in class this struct is intended
// for.
//
//==========================================================================
PClass *ClassReg::RegisterClass()
{
// Skip classes that have already been registered
if (MyClass != nullptr)
{
return MyClass;
}
// Add type to list
PClass *cls = new PClass;
SetupClass(cls);
cls->InsertIntoHash(true);
if (ParentType != nullptr)
{
cls->ParentClass = ParentType->RegisterClass();
}
return cls;
}
//==========================================================================
//
// ClassReg :: SetupClass
//
// Copies the class-defining parameters from a ClassReg to the Class object
// created for it.
//
//==========================================================================
void ClassReg::SetupClass(PClass *cls)
{
assert(MyClass == nullptr);
MyClass = cls;
cls->TypeName = FName(Name+1);
cls->Size = SizeOf;
cls->Pointers = Pointers;
cls->ConstructNative = ConstructNative;
}
//==========================================================================
//
// PClass :: InsertIntoHash
//
// Add class to the type table.
//
//==========================================================================
void PClass::InsertIntoHash (bool native)
{
auto k = ClassMap.CheckKey(TypeName);
if (k != nullptr)
{ // This type has already been inserted
I_Error("Tried to register class '%s' more than once.\n", TypeName.GetChars());
}
else
{
ClassMap[TypeName] = this;
}
#if 0
if (!native && IsDescendantOf(RUNTIME_CLASS(AActor)))
{
PClassActor::AllActorClasses.Push(static_cast<PClassActor*>(this));
}
#endif
}
//==========================================================================
//
// PClass :: FindParentClass
//
// Finds a parent class that matches the given name, including itself.
//
//==========================================================================
const PClass *PClass::FindParentClass(FName name) const
{
for (const PClass *type = this; type != nullptr; type = type->ParentClass)
{
if (type->TypeName == name)
{
return type;
}
}
return nullptr;
}
//==========================================================================
//
// PClass :: FindClass
//
// Find a type, passed the name as a name.
//
//==========================================================================
PClass *PClass::FindClass (FName zaname)
{
if (zaname == NAME_None)
{
return nullptr;
}
auto k = ClassMap.CheckKey(zaname);
return k ? *k : nullptr;
}
//==========================================================================
//
// PClass :: CreateNew
//
// Create a new object that this class represents
//
//==========================================================================
DObject *PClass::CreateNew()
{
uint8_t *mem = (uint8_t *)M_Malloc (Size);
assert (mem != nullptr);
// Set this object's defaults before constructing it.
if (Defaults != nullptr)
memcpy (mem, Defaults, Size);
else
memset (mem, 0, Size);
if (ConstructNative == nullptr)
{
M_Free(mem);
I_Error("Attempt to instantiate abstract class %s.", TypeName.GetChars());
}
ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this));
InitializeSpecials(mem, Defaults, &PClass::SpecialInits);
return (DObject *)mem;
}
//==========================================================================
//
// PClass :: InitializeSpecials
//
// Initialize special fields (e.g. strings) of a newly-created instance.
//
//==========================================================================
void PClass::InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits)
{
// Once we reach a native class, we can stop going up the family tree,
// since native classes handle initialization natively.
if ((!bRuntimeClass && Inits == &PClass::SpecialInits) || ParentClass == nullptr)
{
return;
}
ParentClass->InitializeSpecials(addr, defaults, Inits);
#if 0
for (auto tao : (this->*Inits))
{
tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second);
}
#endif
}
//==========================================================================
//
// PClass :: DestroySpecials
//
// Destroy special fields (e.g. strings) of an instance that is about to be
// deleted.
//
//==========================================================================
void PClass::DestroySpecials(void *addr)
{
if (!bRuntimeClass)
{
return;
}
assert(ParentClass != nullptr);
ParentClass->DestroySpecials(addr);
#if 0
for (auto tao : SpecialInits)
{
tao.first->DestroyValue((uint8_t *)addr + tao.second);
}
#endif
}
//==========================================================================
//
// PClass :: DestroyMeta
//
// Same for meta data
//
//==========================================================================
void PClass::DestroyMeta(void *addr)
{
if (ParentClass != nullptr) ParentClass->DestroyMeta(addr);
#if 0
for (auto tao : MetaInits)
{
tao.first->DestroyValue((uint8_t *)addr + tao.second);
}
#endif
}
//==========================================================================
//
// PClass :: Derive
//
// Copies inheritable values into the derived class and other miscellaneous setup.
//
//==========================================================================
void PClass::Derive(PClass *newclass, FName name)
{
newclass->bRuntimeClass = true;
newclass->ParentClass = this;
newclass->ConstructNative = ConstructNative;
newclass->TypeName = name;
newclass->MetaSize = MetaSize;
}
//==========================================================================
//
// PClassActor :: InitializeNativeDefaults
//
//==========================================================================
void PClass::InitializeDefaults()
{
}
//==========================================================================
//
// PClass :: CreateDerivedClass
//
// Create a new class based on an existing class
//
//==========================================================================
PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
{
assert(size >= Size);
PClass *type;
bool notnew;
const PClass *existclass = FindClass(name);
if (existclass != nullptr)
{
// This is a placeholder so fill it in
if (existclass->Size == TentativeClass)
{
type = const_cast<PClass*>(existclass);
if (!IsDescendantOf(type->ParentClass))
{
I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars());
}
DPrintf(DMSG_SPAMMY, "Defining placeholder class %s\n", name.GetChars());
notnew = true;
}
else
{
// a different class with the same name already exists. Let the calling code deal with this.
return nullptr;
}
}
else
{
type = new PClass;
notnew = false;
}
type->TypeName = name;
type->bRuntimeClass = true;
Derive(type, name);
type->Size = size;
if (size != TentativeClass)
{
#if 0
NewClassType(type);
#endif
type->InitializeDefaults();
type->Virtuals = Virtuals;
}
else
type->bOptional = false;
if (!notnew)
{
type->InsertIntoHash(false);
}
return type;
}
#if 0
//==========================================================================
//
// PClass :: AddField
//
//==========================================================================
PField *PClass::AddField(FName name, PType *type, uint32_t flags)
{
PField *field;
if (!(flags & VARF_Meta))
{
unsigned oldsize = Size;
field = VMType->Symbols.AddField(name, type, flags, Size);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Defaults != nullptr)
{
Defaults = (uint8_t *)M_Realloc(Defaults, Size);
memset(Defaults + oldsize, 0, Size - oldsize);
}
}
else
{
// Same as above, but a different data storage.
unsigned oldsize = MetaSize;
field = VMType->Symbols.AddField(name, type, flags, MetaSize);
if (field != nullptr && !(flags & VARF_Native) && Meta != nullptr)
{
Meta = (uint8_t *)M_Realloc(Meta, MetaSize);
memset(Meta + oldsize, 0, MetaSize - oldsize);
}
}
if (field != nullptr) Fields.Push(field);
return field;
}
#endif
//==========================================================================
//
// PClass :: FindClassTentative
//
// Like FindClass but creates a placeholder if no class is found.
// This will be filled in when the actual class is constructed.
//
//==========================================================================
PClass *PClass::FindClassTentative(FName name)
{
if (name == NAME_None)
{
return nullptr;
}
PClass *found = FindClass(name);
if (found != nullptr) return found;
PClass *type = new PClass;
DPrintf(DMSG_SPAMMY, "Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars());
Derive(type, name);
type->Size = TentativeClass;
type->InsertIntoHash(false);
return type;
}
//==========================================================================
//
// PClass :: FindVirtualIndex
//
// Compares a prototype with the existing list of virtual functions
// and returns an index if something matching is found.
//
//==========================================================================
#if 0
int PClass::FindVirtualIndex(FName name, PFunction::Variant *variant, PFunction *parentfunc)
{
auto proto = variant->Proto;
for (unsigned i = 0; i < Virtuals.Size(); i++)
{
if (Virtuals[i]->Name == name)
{
auto vproto = Virtuals[i]->Proto;
if (vproto->ReturnTypes.Size() != proto->ReturnTypes.Size() ||
vproto->ArgumentTypes.Size() < proto->ArgumentTypes.Size())
{
continue; // number of parameters does not match, so it's incompatible
}
bool fail = false;
// The first argument is self and will mismatch so just skip it.
for (unsigned a = 1; a < proto->ArgumentTypes.Size(); a++)
{
if (proto->ArgumentTypes[a] != vproto->ArgumentTypes[a])
{
fail = true;
break;
}
}
if (fail) continue;
for (unsigned a = 0; a < proto->ReturnTypes.Size(); a++)
{
if (proto->ReturnTypes[a] != vproto->ReturnTypes[a])
{
fail = true;
break;
}
}
if (!fail)
{
if (vproto->ArgumentTypes.Size() > proto->ArgumentTypes.Size() && parentfunc)
{
// Check if the difference between both functions is only some optional arguments.
for (unsigned a = proto->ArgumentTypes.Size(); a < vproto->ArgumentTypes.Size(); a++)
{
if (!(parentfunc->Variants[0].ArgFlags[a] & VARF_Optional)) return -1;
}
// Todo: extend the prototype
for (unsigned a = proto->ArgumentTypes.Size(); a < vproto->ArgumentTypes.Size(); a++)
{
proto->ArgumentTypes.Push(vproto->ArgumentTypes[a]);
variant->ArgFlags.Push(parentfunc->Variants[0].ArgFlags[a]);
variant->ArgNames.Push(NAME_None);
}
}
return i;
}
}
}
return -1;
}
PSymbol *PClass::FindSymbol(FName symname, bool searchparents) const
{
if (VMType == nullptr) return nullptr;
return VMType->Symbols.FindSymbol(symname, searchparents);
}
#endif
//==========================================================================
//
// PClass :: BuildFlatPointers
//
// Create the FlatPointers array, if it doesn't exist already.
// It comprises all the Pointers from superclasses plus this class's own
// Pointers. If this class does not define any new Pointers, then
// FlatPointers will be set to the same array as the super class.
//
//==========================================================================
void PClass::BuildFlatPointers ()
{
if (FlatPointers != nullptr)
{ // Already built: Do nothing.
return;
}
else if (ParentClass == nullptr)
{ // No parent (i.e. DObject: FlatPointers is the same as Pointers.
if (Pointers == nullptr)
{ // No pointers: Make FlatPointers a harmless non-nullptr.
FlatPointers = &TheEnd;
}
else
{
FlatPointers = Pointers;
}
}
else
{
ParentClass->BuildFlatPointers ();
TArray<size_t> ScriptPointers;
#if 0
// Collect all pointers in scripted fields. These are not part of the Pointers list.
for (auto field : Fields)
{
if (!(field->Flags & VARF_Native))
{
field->Type->SetPointer(Defaults, unsigned(field->Offset), &ScriptPointers);
}
}
#endif
if (Pointers == nullptr && ScriptPointers.Size() == 0)
{ // No new pointers: Just use the same FlatPointers as the parent.
FlatPointers = ParentClass->FlatPointers;
}
else
{ // New pointers: Create a new FlatPointers array and add them.
int numPointers, numSuperPointers;
if (Pointers != nullptr)
{
// Count pointers defined by this class.
for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++)
{
}
}
else numPointers = 0;
// Count pointers defined by superclasses.
for (numSuperPointers = 0; ParentClass->FlatPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++)
{ }
// Concatenate them into a new array
size_t *flat = (size_t*)ClassDataAllocator.Alloc(sizeof(size_t) * (numPointers + numSuperPointers + ScriptPointers.Size() + 1));
if (numSuperPointers > 0)
{
memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers);
}
if (numPointers > 0)
{
memcpy(flat + numSuperPointers, Pointers, sizeof(size_t)*numPointers);
}
if (ScriptPointers.Size() > 0)
{
memcpy(flat + numSuperPointers + numPointers, &ScriptPointers[0], sizeof(size_t) * ScriptPointers.Size());
}
flat[numSuperPointers + numPointers + ScriptPointers.Size()] = ~(size_t)0;
FlatPointers = flat;
}
}
}
//==========================================================================
//
// PClass :: BuildArrayPointers
//
// same as above, but creates a list to dynamic object arrays
//
//==========================================================================
void PClass::BuildArrayPointers()
{
if (ArrayPointers != nullptr)
{ // Already built: Do nothing.
return;
}
else if (ParentClass == nullptr)
{ // No parent (i.e. DObject: FlatPointers is the same as Pointers.
ArrayPointers = &TheEnd;
}
else
{
ParentClass->BuildArrayPointers();
TArray<size_t> ScriptPointers;
// Collect all arrays to pointers in scripted fields.
#if 0
for (auto field : Fields)
{
if (!(field->Flags & VARF_Native))
{
field->Type->SetPointerArray(Defaults, unsigned(field->Offset), &ScriptPointers);
}
}
#endif
if (ScriptPointers.Size() == 0)
{ // No new pointers: Just use the same ArrayPointers as the parent.
ArrayPointers = ParentClass->ArrayPointers;
}
else
{ // New pointers: Create a new FlatPointers array and add them.
int numSuperPointers;
// Count pointers defined by superclasses.
for (numSuperPointers = 0; ParentClass->ArrayPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++)
{
}
// Concatenate them into a new array
size_t *flat = (size_t*)ClassDataAllocator.Alloc(sizeof(size_t) * (numSuperPointers + ScriptPointers.Size() + 1));
if (numSuperPointers > 0)
{
memcpy(flat, ParentClass->ArrayPointers, sizeof(size_t)*numSuperPointers);
}
if (ScriptPointers.Size() > 0)
{
memcpy(flat + numSuperPointers, &ScriptPointers[0], sizeof(size_t) * ScriptPointers.Size());
}
flat[numSuperPointers + ScriptPointers.Size()] = ~(size_t)0;
ArrayPointers = flat;
}
}
}
//==========================================================================
//
// PClass :: NativeClass
//
// Finds the native type underlying this class.
//
//==========================================================================
const PClass *PClass::NativeClass() const
{
const PClass *cls = this;
while (cls && cls->bRuntimeClass)
cls = cls->ParentClass;
return cls;
}
#if 0
VMFunction *PClass::FindFunction(FName clsname, FName funcname)
{
auto cls = PClass::FindClass(clsname);
if (!cls) return nullptr;
auto func = dyn_cast<PFunction>(cls->FindSymbol(funcname, true));
if (!func) return nullptr;
return func->Variants[0].Implementation;
}
void PClass::FindFunction(VMFunction **pptr, FName clsname, FName funcname)
{
auto cls = PClass::FindClass(clsname);
if (!cls) return;
auto func = dyn_cast<PFunction>(cls->FindSymbol(funcname, true));
if (!func) return;
*pptr = func->Variants[0].Implementation;
FunctionPtrList.Push(pptr);
}
unsigned GetVirtualIndex(PClass *cls, const char *funcname)
{
// Look up the virtual function index in the defining class because this may have gotten overloaded in subclasses with something different than a virtual override.
auto sym = dyn_cast<PFunction>(cls->FindSymbol(funcname, false));
assert(sym != nullptr);
auto VIndex = sym->Variants[0].Implementation->VirtualIndex;
return VIndex;
}
#endif

View file

@ -0,0 +1,146 @@
#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;
#if 0
// This is intentionally not in vm.h so that this file remains free of DObject pollution.
class VMException : public DObject
{
DECLARE_CLASS(VMException, DObject);
};
#endif
// An action function -------------------------------------------------------
struct FState;
struct StateCallData;
class VMFrameStack;
struct VMValue;
struct VMReturn;
class VMFunction;
class PClassType;
struct FNamespaceManager;
enum
{
TentativeClass = UINT_MAX,
};
class PClass
{
protected:
void Derive(PClass *newclass, FName name);
void InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits);
void SetSuper();
public:
void WriteAllFields(FSerializer &ar, const void *addr) const;
bool ReadAllFields(FSerializer &ar, void *addr) const;
void InitializeDefaults();
#if 0
int FindVirtualIndex(FName name, PFunction::Variant *variant, PFunction *parentfunc);
PSymbol *FindSymbol(FName symname, bool searchparents) const;
PField *AddField(FName name, PType *type, uint32_t flags);
#endif
static void StaticInit();
static void StaticShutdown();
// Per-class information -------------------------------------
PClass *ParentClass = nullptr; // the class this class derives from
const size_t *Pointers = nullptr; // object pointers defined by this class *only*
const size_t *FlatPointers = nullptr; // object pointers defined by this class and all its superclasses; not initialized by default
const size_t *ArrayPointers = nullptr; // dynamic arrays containing object pointers.
uint8_t *Defaults = nullptr;
uint8_t *Meta = nullptr; // Per-class static script data
unsigned Size = sizeof(DObject);
unsigned MetaSize = 0;
FName TypeName = NAME_None;
FName SourceLumpName = NAME_None;
bool bRuntimeClass = false; // class was defined at run-time, not compile-time
bool bDecorateClass = false; // may be subject to some idiosyncracies due to DECORATE backwards compatibility
bool bAbstract = false;
bool bOptional = false;
TArray<VMFunction*> Virtuals; // virtual function table
TArray<FTypeAndOffset> MetaInits;
TArray<FTypeAndOffset> SpecialInits;
#if 0
TArray<PField *> Fields;
#endif
PClassType *VMType = nullptr;
void (*ConstructNative)(void *);
// The rest are all functions and static data ----------------
PClass();
~PClass();
void InsertIntoHash(bool native);
DObject *CreateNew();
PClass *CreateDerivedClass(FName name, unsigned int size);
void InitializeActorInfo();
void BuildFlatPointers();
void BuildArrayPointers();
void DestroySpecials(void *addr);
void DestroyMeta(void *addr);
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);
}
inline bool IsDescendantOf(FName ti) const
{
auto me = this;
while (me)
{
if (me->TypeName == ti)
return true;
me = me->ParentClass;
}
return false;
}
// 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)); }
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);
static VMFunction *FindFunction(FName cls, FName func);
static void FindFunction(VMFunction **pptr, FName cls, FName func);
PClass *FindClassTentative(FName name);
static TMap<FName, PClass*> ClassMap;
static TArray<PClass *> AllClasses;
static TArray<VMFunction**> FunctionPtrList;
static bool bShutdown;
static bool bVMOperational;
};
#endif

View file

@ -0,0 +1,70 @@
/*
** autozend.cpp
** This file contains the tails of lists stored in special data segments
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
** See autostart.cpp for an explanation of why I do things like this.
*/
#include "autosegs.h"
#if defined(_MSC_VER)
#pragma section(".areg$z",read)
__declspec(allocate(".areg$z")) void *const ARegTail = 0;
#pragma section(".creg$z",read)
__declspec(allocate(".creg$z")) void *const CRegTail = 0;
#pragma section(".freg$z",read)
__declspec(allocate(".freg$z")) void *const FRegTail = 0;
#pragma section(".greg$z",read)
__declspec(allocate(".greg$z")) void *const GRegTail = 0;
#pragma section(".yreg$z",read)
__declspec(allocate(".yreg$z")) void *const YRegTail = 0;
#elif defined(__GNUC__)
#include "doomtype.h"
void *const ARegTail __attribute__((section(SECTION_AREG))) = 0;
void *const CRegTail __attribute__((section(SECTION_CREG))) = 0;
void *const FRegTail __attribute__((section(SECTION_FREG))) = 0;
void *const GRegTail __attribute__((section(SECTION_GREG))) = 0;
void *const YRegTail __attribute__((section(SECTION_YREG))) = 0;
#else
#error Please fix autozend.cpp for your compiler
#endif

View file

@ -18,7 +18,6 @@ set( PCH_SOURCES
src/light.cpp
src/lighting.cpp
src/lion.cpp
src/main.cpp
src/map.cpp
src/menu.cpp
src/mono.cpp
@ -28,7 +27,6 @@ set( PCH_SOURCES
src/network.cpp
src/object.cpp
src/osdcmds.cpp
src/paul.cpp
src/player.cpp
src/queen.cpp
src/ra.cpp
@ -47,9 +45,7 @@ set( PCH_SOURCES
src/sound.cpp
src/spider.cpp
src/status.cpp
src/stream.cpp
src/switch.cpp
src/text2.cpp
src/timer.cpp
src/trigdat.cpp
src/view.cpp

View file

@ -1,17 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------

View file

@ -1,19 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "paul.h"

View file

@ -1,19 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "stream.h"

View file

@ -1,19 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "text2.h"