- moved DObject and core parts of the VM to 'common'.
# Conflicts: # src/common/objects/dobject.h
This commit is contained in:
parent
1a0ace4f88
commit
f8ac9a2662
48 changed files with 403 additions and 349 deletions
111
src/common/objects/__autostart.cpp
Normal file
111
src/common/objects/__autostart.cpp
Normal 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
|
||||
113
src/common/objects/autosegs.h
Normal file
113
src/common/objects/autosegs.h
Normal 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
|
||||
611
src/common/objects/dobject.cpp
Normal file
611
src/common/objects/dobject.cpp
Normal file
|
|
@ -0,0 +1,611 @@
|
|||
/*
|
||||
** 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 "cmdlib.h"
|
||||
#include "actor.h"
|
||||
#include "doomstat.h" // Ideally, DObjects can be used independant of Doom.
|
||||
#include "d_player.h" // See p_user.cpp to find out why this doesn't work.
|
||||
#include "c_dispatch.h"
|
||||
#include "dsectoreffect.h"
|
||||
#include "serializer.h"
|
||||
#include "vm.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "types.h"
|
||||
#include "i_time.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ClassReg DObject::RegistrationInfo =
|
||||
{
|
||||
nullptr, // MyClass
|
||||
"DObject", // Name
|
||||
nullptr, // ParentType
|
||||
nullptr,
|
||||
nullptr, // Pointers
|
||||
&DObject::InPlaceConstructor, // ConstructNative
|
||||
nullptr,
|
||||
sizeof(DObject), // SizeOf
|
||||
};
|
||||
_DECLARE_TI(DObject)
|
||||
|
||||
// This bit is needed in the playsim - but give it a less crappy name.
|
||||
DEFINE_FIELD_BIT(DObject,ObjectFlags, bDestroyed, OF_EuthanizeMe)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD (dumpactors)
|
||||
{
|
||||
const char *const filters[32] =
|
||||
{
|
||||
"0:All", "1:Doom", "2:Heretic", "3:DoomHeretic", "4:Hexen", "5:DoomHexen", "6:Raven", "7:IdRaven",
|
||||
"8:Strife", "9:DoomStrife", "10:HereticStrife", "11:DoomHereticStrife", "12:HexenStrife",
|
||||
"13:DoomHexenStrife", "14:RavenStrife", "15:NotChex", "16:Chex", "17:DoomChex", "18:HereticChex",
|
||||
"19:DoomHereticChex", "20:HexenChex", "21:DoomHexenChex", "22:RavenChex", "23:NotStrife", "24:StrifeChex",
|
||||
"25:DoomStrifeChex", "26:HereticStrifeChex", "27:NotHexen", "28:HexenStrifeChex", "29:NotHeretic",
|
||||
"30:NotDoom", "31:All",
|
||||
};
|
||||
Printf("%u object class types total\nActor\tEd Num\tSpawnID\tFilter\tSource\n", PClass::AllClasses.Size());
|
||||
for (unsigned int i = 0; i < PClass::AllClasses.Size(); i++)
|
||||
{
|
||||
PClass *cls = PClass::AllClasses[i];
|
||||
PClassActor *acls = ValidateActor(cls);
|
||||
if (acls != NULL)
|
||||
{
|
||||
auto ainfo = acls->ActorInfo();
|
||||
Printf("%s\t%i\t%i\t%s\t%s\n",
|
||||
acls->TypeName.GetChars(), ainfo->DoomEdNum,
|
||||
ainfo->SpawnID, filters[ainfo->GameFilter & 31],
|
||||
acls->SourceLumpName.GetChars());
|
||||
}
|
||||
else if (cls != NULL)
|
||||
{
|
||||
Printf("%s\tn/a\tn/a\tn/a\tEngine (not an actor type)\tSource: %s\n", cls->TypeName.GetChars(), cls->SourceLumpName.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Type %i is not an object class\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
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)
|
||||
{
|
||||
IFVIRTUAL(DObject, OnDestroy)
|
||||
{
|
||||
VMValue params[1] = { (DObject*)this };
|
||||
VMCall(func, params, 1, nullptr, 0);
|
||||
}
|
||||
}
|
||||
OnDestroy();
|
||||
ObjectFlags = (ObjectFlags & ~OF_Fixed) | OF_EuthanizeMe;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DObject, Destroy)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DObject);
|
||||
self->Destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This once was the main method for pointer cleanup, but
|
||||
// nowadays its only use is swapping out PlayerPawns.
|
||||
// This requires pointer fixing throughout all objects and a few
|
||||
// global variables, but it only needs to look at pointers that
|
||||
// can point to a player.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DObject::StaticPointerSubstitution (AActor *old, AActor *notOld)
|
||||
{
|
||||
DObject *probe;
|
||||
size_t changed = 0;
|
||||
int i;
|
||||
|
||||
if (old == nullptr) return;
|
||||
|
||||
// This is only allowed to replace players. For everything else the results are undefined.
|
||||
if (!old->IsKindOf(NAME_PlayerPawn) || (notOld != nullptr && !notOld->IsKindOf(NAME_PlayerPawn))) return;
|
||||
|
||||
// Go through all objects.
|
||||
i = 0;DObject *last=0;
|
||||
for (probe = GC::Root; probe != NULL; probe = probe->ObjNext)
|
||||
{
|
||||
i++;
|
||||
changed += probe->PointerSubstitution(old, notOld);
|
||||
last = probe;
|
||||
}
|
||||
|
||||
// Go through players.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i])
|
||||
{
|
||||
AActor *replacement = notOld;
|
||||
auto &p = players[i];
|
||||
|
||||
if (p.mo == old) p.mo = replacement, changed++;
|
||||
if (p.poisoner.pp == old) p.poisoner = replacement, changed++;
|
||||
if (p.attacker.pp == old) p.attacker = replacement, changed++;
|
||||
if (p.camera.pp == old) p.camera = replacement, changed++;
|
||||
if (p.ConversationNPC.pp == old) p.ConversationNPC = replacement, changed++;
|
||||
if (p.ConversationPC == old) p.ConversationPC = replacement, changed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Go through sectors. Only the level this actor belongs to is relevant.
|
||||
for (auto &sec : old->Level->sectors)
|
||||
{
|
||||
if (sec.SoundTarget == old) sec.SoundTarget = notOld;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
476
src/common/objects/dobject.h
Normal file
476
src/common/objects/dobject.h
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
/*
|
||||
** 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 "m_alloc.h"
|
||||
#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);
|
||||
static void StaticPointerSubstitution (AActor *old, AActor *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__
|
||||
664
src/common/objects/dobjgc.cpp
Normal file
664
src/common/objects/dobjgc.cpp
Normal file
|
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
** 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 "b_bot.h"
|
||||
#include "p_local.h"
|
||||
#include "g_game.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "sbar.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "s_sndseq.h"
|
||||
#include "r_data/r_interpolate.h"
|
||||
#include "doomstat.h"
|
||||
#include "po_man.h"
|
||||
#include "r_utility.h"
|
||||
#include "menu/menu.h"
|
||||
#include "intermission/intermission.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "events.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;
|
||||
Mark(StatusBar);
|
||||
M_MarkMenus();
|
||||
Mark(DIntermissionController::CurrentIntermission);
|
||||
Mark(staticEventManager.FirstEventHandler);
|
||||
Mark(staticEventManager.LastEventHandler);
|
||||
for (auto Level : AllLevels())
|
||||
Level->Mark();
|
||||
|
||||
// Mark players.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i])
|
||||
players[i].PropagateMark();
|
||||
}
|
||||
|
||||
// NextToThink must not be freed while thinkers are ticking.
|
||||
Mark(NextToThink);
|
||||
// Mark soft roots.
|
||||
if (SoftRoots != NULL)
|
||||
{
|
||||
DObject **probe = &SoftRoots->ObjNext;
|
||||
while (*probe != NULL)
|
||||
{
|
||||
DObject *soft = *probe;
|
||||
probe = &soft->ObjNext;
|
||||
if ((soft->ObjectFlags & (OF_Rooted | OF_EuthanizeMe)) == OF_Rooted)
|
||||
{
|
||||
Mark(soft);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
254
src/common/objects/dobjgc.h
Normal file
254
src/common/objects/dobjgc.h
Normal 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);
|
||||
}
|
||||
}
|
||||
1029
src/common/objects/dobjtype.cpp
Normal file
1029
src/common/objects/dobjtype.cpp
Normal file
File diff suppressed because it is too large
Load diff
142
src/common/objects/dobjtype.h
Normal file
142
src/common/objects/dobjtype.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#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();
|
||||
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);
|
||||
|
||||
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;
|
||||
TArray<PField *> Fields;
|
||||
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
|
||||
70
src/common/objects/zzautozend.cpp
Normal file
70
src/common/objects/zzautozend.cpp
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue