mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-02-08 17:32:09 +00:00
Let's use inline checkers in PType instead of constantly having to do clumsy IsKindOf checks etc. Once complete this also means that the types can be taken out of the class hierarchy, freeing up some common names.
601 lines
16 KiB
C++
601 lines
16 KiB
C++
/*
|
|
** symbols.cpp
|
|
** Implements the symbol types and symbol table
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 1998-2016 Randy Heit
|
|
** Copyright 2006-2017 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.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#include <float.h>
|
|
#include "dobject.h"
|
|
#include "i_system.h"
|
|
#include "templates.h"
|
|
#include "serializer.h"
|
|
#include "types.h"
|
|
#include "vm.h"
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
FNamespaceManager Namespaces;
|
|
|
|
// Symbol tables ------------------------------------------------------------
|
|
|
|
IMPLEMENT_CLASS(PTypeBase, true, false);
|
|
IMPLEMENT_CLASS(PSymbol, true, false);
|
|
IMPLEMENT_CLASS(PSymbolConst, false, false);
|
|
IMPLEMENT_CLASS(PSymbolConstNumeric, false, false);
|
|
IMPLEMENT_CLASS(PSymbolConstString, false, false);
|
|
IMPLEMENT_CLASS(PSymbolTreeNode, false, false)
|
|
IMPLEMENT_CLASS(PSymbolType, false, false)
|
|
IMPLEMENT_CLASS(PSymbolVMFunction, false, false)
|
|
IMPLEMENT_CLASS(PFunction, false, false)
|
|
IMPLEMENT_CLASS(PNamespace, false, true)
|
|
|
|
IMPLEMENT_POINTERS_START(PNamespace)
|
|
IMPLEMENT_POINTER(Parent)
|
|
IMPLEMENT_POINTERS_END
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PSymbolConstString::PSymbolConstString(FName name, const FString &str)
|
|
: PSymbolConst(name, TypeString), Str(str)
|
|
{
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PFunction :: AddVariant
|
|
//
|
|
// Adds a new variant for this function. Does not check if a matching
|
|
// variant already exists.
|
|
//
|
|
//==========================================================================
|
|
|
|
unsigned PFunction::AddVariant(PPrototype *proto, TArray<uint32_t> &argflags, TArray<FName> &argnames, VMFunction *impl, int flags, int useflags)
|
|
{
|
|
Variant variant;
|
|
|
|
// I do not think we really want to deal with overloading here...
|
|
assert(Variants.Size() == 0);
|
|
|
|
variant.Flags = flags;
|
|
variant.UseFlags = useflags;
|
|
variant.Proto = proto;
|
|
variant.ArgFlags = std::move(argflags);
|
|
variant.ArgNames = std::move(argnames);
|
|
variant.Implementation = impl;
|
|
if (impl != nullptr) impl->Proto = proto;
|
|
|
|
// SelfClass can differ from OwningClass, but this is variant-dependent.
|
|
// Unlike the owner there can be cases where different variants can have different SelfClasses.
|
|
// (Of course only if this ever gets enabled...)
|
|
if (flags & VARF_Method)
|
|
{
|
|
assert(proto->ArgumentTypes.Size() > 0);
|
|
auto selftypeptr = proto->ArgumentTypes[0]->toPointer();
|
|
assert(selftypeptr != nullptr);
|
|
variant.SelfClass = selftypeptr->PointedType->toContainer();
|
|
assert(variant.SelfClass != nullptr);
|
|
}
|
|
else
|
|
{
|
|
variant.SelfClass = nullptr;
|
|
}
|
|
|
|
return Variants.Push(variant);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
int PFunction::GetImplicitArgs()
|
|
{
|
|
if (Variants[0].Flags & VARF_Action) return 3;
|
|
else if (Variants[0].Flags & VARF_Method) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* PField *****************************************************************/
|
|
|
|
IMPLEMENT_CLASS(PField, false, false)
|
|
|
|
//==========================================================================
|
|
//
|
|
// PField - Default Constructor
|
|
//
|
|
//==========================================================================
|
|
|
|
PField::PField()
|
|
: PSymbol(NAME_None), Offset(0), Type(nullptr), Flags(0)
|
|
{
|
|
}
|
|
|
|
|
|
PField::PField(FName name, PType *type, uint32_t flags, size_t offset, int bitvalue)
|
|
: PSymbol(name), Offset(offset), Type(type), Flags(flags)
|
|
{
|
|
if (bitvalue != 0)
|
|
{
|
|
BitValue = 0;
|
|
unsigned val = bitvalue;
|
|
while ((val >>= 1)) BitValue++;
|
|
|
|
if (type->isInt() && unsigned(BitValue) < 8u * type->Size)
|
|
{
|
|
// map to the single bytes in the actual variable. The internal bit instructions operate on 8 bit values.
|
|
#ifndef __BIG_ENDIAN__
|
|
Offset += BitValue / 8;
|
|
#else
|
|
Offset += type->Size - 1 - BitValue / 8;
|
|
#endif
|
|
BitValue &= 7;
|
|
Type = TypeBool;
|
|
}
|
|
else
|
|
{
|
|
// Just abort. Bit fields should only be defined internally.
|
|
I_Error("Trying to create an invalid bit field element: %s", name.GetChars());
|
|
}
|
|
}
|
|
else BitValue = -1;
|
|
}
|
|
|
|
VersionInfo PField::GetVersion()
|
|
{
|
|
VersionInfo Highest = { 0,0,0 };
|
|
if (!(Flags & VARF_Deprecated)) Highest = mVersion;
|
|
if (Type->mVersion > Highest) Highest = Type->mVersion;
|
|
return Highest;
|
|
}
|
|
|
|
/* PProperty *****************************************************************/
|
|
|
|
IMPLEMENT_CLASS(PProperty, false, false)
|
|
|
|
//==========================================================================
|
|
//
|
|
// PField - Default Constructor
|
|
//
|
|
//==========================================================================
|
|
|
|
PProperty::PProperty()
|
|
: PSymbol(NAME_None)
|
|
{
|
|
}
|
|
|
|
PProperty::PProperty(FName name, TArray<PField *> &fields)
|
|
: PSymbol(name)
|
|
{
|
|
Variables = std::move(fields);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PSymbolTable::PSymbolTable()
|
|
: ParentSymbolTable(nullptr)
|
|
{
|
|
}
|
|
|
|
PSymbolTable::PSymbolTable(PSymbolTable *parent)
|
|
: ParentSymbolTable(parent)
|
|
{
|
|
}
|
|
|
|
PSymbolTable::~PSymbolTable ()
|
|
{
|
|
ReleaseSymbols();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// this must explicitly delete all content because the symbols have
|
|
// been released from the GC.
|
|
//
|
|
//==========================================================================
|
|
|
|
void PSymbolTable::ReleaseSymbols()
|
|
{
|
|
auto it = GetIterator();
|
|
MapType::Pair *pair;
|
|
while (it.NextPair(pair))
|
|
{
|
|
delete pair->Value;
|
|
}
|
|
Symbols.Clear();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void PSymbolTable::SetParentTable (PSymbolTable *parent)
|
|
{
|
|
ParentSymbolTable = parent;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PSymbol *PSymbolTable::FindSymbol (FName symname, bool searchparents) const
|
|
{
|
|
PSymbol * const *value = Symbols.CheckKey(symname);
|
|
if (value == nullptr && searchparents && ParentSymbolTable != nullptr)
|
|
{
|
|
return ParentSymbolTable->FindSymbol(symname, searchparents);
|
|
}
|
|
return value != nullptr ? *value : nullptr;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PSymbol *PSymbolTable::FindSymbolInTable(FName symname, PSymbolTable *&symtable)
|
|
{
|
|
PSymbol * const *value = Symbols.CheckKey(symname);
|
|
if (value == nullptr)
|
|
{
|
|
if (ParentSymbolTable != nullptr)
|
|
{
|
|
return ParentSymbolTable->FindSymbolInTable(symname, symtable);
|
|
}
|
|
symtable = nullptr;
|
|
return nullptr;
|
|
}
|
|
symtable = this;
|
|
return *value;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PSymbol *PSymbolTable::AddSymbol (PSymbol *sym)
|
|
{
|
|
// Symbols that already exist are not inserted.
|
|
if (Symbols.CheckKey(sym->SymbolName) != nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
Symbols.Insert(sym->SymbolName, sym);
|
|
sym->Release(); // no more GC, please!
|
|
return sym;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PField *PSymbolTable::AddField(FName name, PType *type, uint32_t flags, unsigned &Size, unsigned *Align)
|
|
{
|
|
PField *field = new PField(name, type, flags);
|
|
|
|
// The new field is added to the end of this struct, alignment permitting.
|
|
field->Offset = (Size + (type->Align - 1)) & ~(type->Align - 1);
|
|
|
|
// Enlarge this struct to enclose the new field.
|
|
Size = unsigned(field->Offset + type->Size);
|
|
|
|
// This struct's alignment is the same as the largest alignment of any of
|
|
// its fields.
|
|
if (Align != nullptr)
|
|
{
|
|
*Align = MAX(*Align, type->Align);
|
|
}
|
|
|
|
if (AddSymbol(field) == nullptr)
|
|
{ // name is already in use
|
|
field->Destroy();
|
|
return nullptr;
|
|
}
|
|
return field;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PStruct :: AddField
|
|
//
|
|
// Appends a new native field to the struct. Returns either the new field
|
|
// or nullptr if a symbol by that name already exists.
|
|
//
|
|
//==========================================================================
|
|
|
|
PField *PSymbolTable::AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue)
|
|
{
|
|
PField *field = new PField(name, type, flags | VARF_Native | VARF_Transient, address, bitvalue);
|
|
|
|
if (AddSymbol(field) == nullptr)
|
|
{ // name is already in use
|
|
field->Destroy();
|
|
return nullptr;
|
|
}
|
|
return field;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// PClass :: WriteFields
|
|
//
|
|
//==========================================================================
|
|
|
|
void PSymbolTable::WriteFields(FSerializer &ar, const void *addr, const void *def) const
|
|
{
|
|
auto it = MapType::ConstIterator(Symbols);
|
|
MapType::ConstPair *pair;
|
|
|
|
while (it.NextPair(pair))
|
|
{
|
|
const PField *field = dyn_cast<PField>(pair->Value);
|
|
// Skip fields without or with native serialization
|
|
if (field && !(field->Flags & (VARF_Transient | VARF_Meta)))
|
|
{
|
|
// todo: handle defaults in WriteValue
|
|
//auto defp = def == nullptr ? nullptr : (const uint8_t *)def + field->Offset;
|
|
field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const uint8_t *)addr + field->Offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// PClass :: ReadFields
|
|
//
|
|
//==========================================================================
|
|
|
|
bool PSymbolTable::ReadFields(FSerializer &ar, void *addr, const char *TypeName) const
|
|
{
|
|
bool readsomething = false;
|
|
bool foundsomething = false;
|
|
const char *label;
|
|
while ((label = ar.GetKey()))
|
|
{
|
|
foundsomething = true;
|
|
|
|
const PSymbol *sym = FindSymbol(FName(label, true), false);
|
|
if (sym == nullptr)
|
|
{
|
|
DPrintf(DMSG_ERROR, "Cannot find field %s in %s\n",
|
|
label, TypeName);
|
|
}
|
|
else if (!sym->IsKindOf(RUNTIME_CLASS(PField)))
|
|
{
|
|
DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n",
|
|
label, TypeName);
|
|
}
|
|
else if ((static_cast<const PField *>(sym)->Flags & (VARF_Transient | VARF_Meta)))
|
|
{
|
|
DPrintf(DMSG_ERROR, "Symbol %s in %s is not a serializable field\n",
|
|
label, TypeName);
|
|
}
|
|
else
|
|
{
|
|
readsomething |= static_cast<const PField *>(sym)->Type->ReadValue(ar, nullptr,
|
|
(uint8_t *)addr + static_cast<const PField *>(sym)->Offset);
|
|
}
|
|
}
|
|
return readsomething || !foundsomething;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void PSymbolTable::RemoveSymbol(PSymbol *sym)
|
|
{
|
|
auto mysym = Symbols.CheckKey(sym->SymbolName);
|
|
if (mysym == nullptr || *mysym != sym) return;
|
|
Symbols.Remove(sym->SymbolName);
|
|
delete sym;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void PSymbolTable::ReplaceSymbol(PSymbol *newsym)
|
|
{
|
|
// If a symbol with a matching name exists, take its place and return it.
|
|
PSymbol **symslot = Symbols.CheckKey(newsym->SymbolName);
|
|
if (symslot != nullptr)
|
|
{
|
|
PSymbol *oldsym = *symslot;
|
|
delete oldsym;
|
|
*symslot = newsym;
|
|
}
|
|
// Else, just insert normally and return nullptr since there was no
|
|
// symbol to replace.
|
|
newsym->Release(); // no more GC, please!
|
|
Symbols.Insert(newsym->SymbolName, newsym);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PNamespace::PNamespace(int filenum, PNamespace *parent)
|
|
{
|
|
Parent = parent;
|
|
if (parent) Symbols.SetParentTable(&parent->Symbols);
|
|
FileNum = filenum;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
FNamespaceManager::FNamespaceManager()
|
|
{
|
|
GlobalNamespace = nullptr;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
PNamespace *FNamespaceManager::NewNamespace(int filenum)
|
|
{
|
|
PNamespace *parent = nullptr;
|
|
// The parent will be the last namespace with this or a lower filenum.
|
|
// This ensures that DECORATE won't see the symbols of later files.
|
|
for (int i = AllNamespaces.Size() - 1; i >= 0; i--)
|
|
{
|
|
if (AllNamespaces[i]->FileNum <= filenum)
|
|
{
|
|
parent = AllNamespaces[i];
|
|
break;
|
|
}
|
|
}
|
|
auto newns = new PNamespace(filenum, parent);
|
|
AllNamespaces.Push(newns);
|
|
return newns;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
size_t FNamespaceManager::MarkSymbols()
|
|
{
|
|
for (auto ns : AllNamespaces)
|
|
{
|
|
GC::Mark(ns);
|
|
}
|
|
return AllNamespaces.Size();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void FNamespaceManager::ReleaseSymbols()
|
|
{
|
|
RemoveSymbols();
|
|
GlobalNamespace = nullptr;
|
|
AllNamespaces.Clear();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// removes all symbols from the symbol tables.
|
|
// After running the compiler these are not needed anymore.
|
|
// Only the namespaces themselves are kept because the type table references them.
|
|
//
|
|
//==========================================================================
|
|
|
|
int FNamespaceManager::RemoveSymbols()
|
|
{
|
|
int count = 0;
|
|
for (auto ns : AllNamespaces)
|
|
{
|
|
count += ns->Symbols.Symbols.CountUsed();
|
|
ns->Symbols.ReleaseSymbols();
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Clean out all compiler-only data from the symbol tables
|
|
//
|
|
//==========================================================================
|
|
|
|
void RemoveUnusedSymbols()
|
|
{
|
|
// Global symbols are not needed anymore after running the compiler.
|
|
int count = Namespaces.RemoveSymbols();
|
|
|
|
// We do not need any non-field and non-function symbols in structs and classes anymore.
|
|
// struct/class fields and functions are still needed so that the game can access the script data,
|
|
// but all the rest serves no purpose anymore and can be entirely removed.
|
|
for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i)
|
|
{
|
|
for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext)
|
|
{
|
|
if (ty->isContainer())
|
|
{
|
|
auto it = ty->Symbols.GetIterator();
|
|
PSymbolTable::MapType::Pair *pair;
|
|
while (it.NextPair(pair))
|
|
{
|
|
if (!pair->Value->IsKindOf(RUNTIME_CLASS(PField)) && !pair->Value->IsKindOf(RUNTIME_CLASS(PFunction)))
|
|
{
|
|
ty->Symbols.RemoveSymbol(pair->Value);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DPrintf(DMSG_SPAMMY, "%d symbols removed after compilation\n", count);
|
|
}
|