mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-27 04:00:42 +00:00
- 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:
parent
b971bc2717
commit
c1f7cf1c3a
15 changed files with 3294 additions and 79 deletions
|
@ -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/.+")
|
||||
|
|
111
source/common/objects/__autostart.cpp
Normal file
111
source/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
source/common/objects/autosegs.h
Normal file
113
source/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
|
515
source/common/objects/dobject.cpp
Normal file
515
source/common/objects/dobject.cpp
Normal 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
|
475
source/common/objects/dobject.h
Normal file
475
source/common/objects/dobject.h
Normal 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__
|
621
source/common/objects/dobjgc.cpp
Normal file
621
source/common/objects/dobjgc.cpp
Normal 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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
254
source/common/objects/dobjgc.h
Normal file
254
source/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);
|
||||
}
|
||||
}
|
981
source/common/objects/dobjtype.cpp
Normal file
981
source/common/objects/dobjtype.cpp
Normal 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
|
||||
|
||||
|
146
source/common/objects/dobjtype.h
Normal file
146
source/common/objects/dobjtype.h
Normal 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
|
70
source/common/objects/zzautozend.cpp
Normal file
70
source/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
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
//-------------------------------------------------------------------------
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
Loading…
Reference in a new issue