diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce60111fa..d0d31984d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1194,6 +1194,7 @@ set (PCH_SOURCES r_data/voxels.cpp r_data/renderstyle.cpp r_data/r_interpolate.cpp + scripting/symbols.cpp scripting/thingdef.cpp scripting/thingdef_data.cpp scripting/thingdef_properties.cpp diff --git a/src/dobject.h b/src/dobject.h index ef7560909..6ad8ded6f 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -603,6 +603,7 @@ static inline void GC::WriteBarrier(DObject *pointed) } } +#include "symbols.h" #include "dobjtype.h" inline bool DObject::IsKindOf (const PClass *base) const diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index c39e316a7..eda59bcc1 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -66,7 +66,6 @@ EXTERN_CVAR(Bool, strictdecorate); // PUBLIC DATA DEFINITIONS ------------------------------------------------- FMemArena FlatpointerArena; // stores the flat pointers because freeing them individually is rather messy. -FNamespaceManager Namespaces; FTypeTable TypeTable; TArray PClass::AllClasses; @@ -2683,68 +2682,6 @@ PPrototype *NewPrototype(const TArray &rettypes, const TArray return static_cast(proto); } -/* PFunction **************************************************************/ - -IMPLEMENT_CLASS(PFunction, false, false) - -//========================================================================== -// -// PFunction :: PropagataMark -// -//========================================================================== - -size_t PFunction::PropagateMark() -{ - for (unsigned i = 0; i < Variants.Size(); ++i) - { - GC::Mark(Variants[i].Proto); - } - return Variants.Size() * sizeof(Variants[0]) + Super::PropagateMark(); -} - -//========================================================================== -// -// PFunction :: AddVariant -// -// Adds a new variant for this function. Does not check if a matching -// variant already exists. -// -//========================================================================== - -unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArray &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 = dyn_cast(proto->ArgumentTypes[0]); - assert(selftypeptr != nullptr); - variant.SelfClass = dyn_cast(selftypeptr->PointedType); - assert(variant.SelfClass != nullptr); - } - else - { - variant.SelfClass = nullptr; - } - - return Variants.Push(variant); -} - /* PClass *****************************************************************/ IMPLEMENT_CLASS(PClass, false, true) @@ -3863,228 +3800,3 @@ CCMD(typetable) DumpTypeTable(); } -// 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, true) - -IMPLEMENT_POINTERS_START(PSymbolType) - IMPLEMENT_POINTER(Type) -IMPLEMENT_POINTERS_END - -IMPLEMENT_CLASS(PSymbolVMFunction, false, false) - -//========================================================================== -// -// -// -//========================================================================== - -PSymbol::~PSymbol() -{ -} - -PSymbolTable::PSymbolTable() -: ParentSymbolTable(nullptr) -{ -} - -PSymbolTable::PSymbolTable(PSymbolTable *parent) -: ParentSymbolTable(parent) -{ -} - -PSymbolTable::~PSymbolTable () -{ - ReleaseSymbols(); -} - -size_t PSymbolTable::MarkSymbols() -{ - size_t count = 0; - MapType::Iterator it(Symbols); - MapType::Pair *pair; - - while (it.NextPair(pair)) - { - GC::Mark(pair->Value); - count++; - } - return count * sizeof(*pair); -} - -void PSymbolTable::ReleaseSymbols() -{ - // The GC will take care of deleting the symbols. We just need to - // clear our references to them. - 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); - return sym; -} - -void PSymbolTable::RemoveSymbol(PSymbol *sym) -{ - auto mysym = Symbols.CheckKey(sym->SymbolName); - if (mysym == nullptr || *mysym != sym) return; - Symbols.Remove(sym->SymbolName); -} - -PSymbol *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; - *symslot = newsym; - return oldsym; - } - // Else, just insert normally and return nullptr since there was no - // symbol to replace. - Symbols.Insert(newsym->SymbolName, newsym); - return nullptr; -} - -IMPLEMENT_CLASS(PNamespace, false, true) - -IMPLEMENT_POINTERS_START(PNamespace) -IMPLEMENT_POINTER(Parent) -IMPLEMENT_POINTERS_END - -PNamespace::PNamespace(int filenum, PNamespace *parent) -{ - Parent = parent; - if (parent) Symbols.SetParentTable(&parent->Symbols); - FileNum = filenum; -} - -size_t PNamespace::PropagateMark() -{ - GC::Mark(Parent); - return Symbols.MarkSymbols() + 1; -} - -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() -{ - 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; -} - -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. - for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) - { - for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext) - { - if (ty->IsKindOf(RUNTIME_CLASS(PStruct))) - { - 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); -} diff --git a/src/dobjtype.h b/src/dobjtype.h index 6fd2f9b1d..c999f3f50 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -39,36 +39,6 @@ enum VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature }; -// Symbol information ------------------------------------------------------- - -class PTypeBase : public DObject -{ - DECLARE_ABSTRACT_CLASS(PTypeBase, DObject) - -public: - virtual FString QualifiedName() const - { - return ""; - } -}; - -class PSymbol : public PTypeBase -{ - DECLARE_ABSTRACT_CLASS(PSymbol, PTypeBase); -public: - virtual ~PSymbol(); - - virtual FString QualifiedName() const - { - return SymbolName.GetChars(); - } - - FName SymbolName; - -protected: - PSymbol(FName name) { SymbolName = name; } -}; - // An action function ------------------------------------------------------- struct FState; @@ -79,100 +49,6 @@ struct VMReturn; class VMFunction; struct FNamespaceManager; -// A VM function ------------------------------------------------------------ - -class PSymbolVMFunction : public PSymbol -{ - DECLARE_CLASS(PSymbolVMFunction, PSymbol); -public: - VMFunction *Function; - - PSymbolVMFunction(FName name) : PSymbol(name) {} - PSymbolVMFunction() : PSymbol(NAME_None) {} -}; - -// A symbol for a type ------------------------------------------------------ - -class PSymbolType : public PSymbol -{ - DECLARE_CLASS(PSymbolType, PSymbol); - HAS_OBJECT_POINTERS; -public: - class PType *Type; - - PSymbolType(FName name, class PType *ty) : PSymbol(name), Type(ty) {} - PSymbolType() : PSymbol(NAME_None) {} -}; - -// A symbol table ----------------------------------------------------------- - -struct PSymbolTable -{ - PSymbolTable(); - PSymbolTable(PSymbolTable *parent); - ~PSymbolTable(); - - size_t MarkSymbols(); - - // Sets the table to use for searches if this one doesn't contain the - // requested symbol. - void SetParentTable (PSymbolTable *parent); - PSymbolTable *GetParentTable() const - { - return ParentSymbolTable; - } - - // Finds a symbol in the table, optionally searching parent tables - // as well. - PSymbol *FindSymbol (FName symname, bool searchparents) const; - - // Like FindSymbol with searchparents set true, but also returns the - // specific symbol table the symbol was found in. - PSymbol *FindSymbolInTable(FName symname, PSymbolTable *&symtable); - - - // Places the symbol in the table and returns a pointer to it or NULL if - // a symbol with the same name is already in the table. This symbol is - // not copied and will be freed when the symbol table is destroyed. - PSymbol *AddSymbol (PSymbol *sym); - - // Similar to AddSymbol but always succeeds. Returns the symbol that used - // to be in the table with this name, if any. - PSymbol *ReplaceSymbol(PSymbol *sym); - - void RemoveSymbol(PSymbol *sym); - - // Frees all symbols from this table. - void ReleaseSymbols(); - - typedef TMap MapType; - - MapType::Iterator GetIterator() - { - return MapType::Iterator(Symbols); - } - -private: - - PSymbolTable *ParentSymbolTable; - MapType Symbols; - - friend class DObject; - friend struct FNamespaceManager; -}; - -// A symbol for a compiler tree node ---------------------------------------- - -class PSymbolTreeNode : public PSymbol -{ - DECLARE_CLASS(PSymbolTreeNode, PSymbol); -public: - struct ZCC_TreeNode *Node; - - PSymbolTreeNode(FName name, struct ZCC_TreeNode *node) : PSymbol(name), Node(node) {} - PSymbolTreeNode() : PSymbol(NAME_None) {} -}; - // Basic information shared by all types ------------------------------------ // Only one copy of a type is ever instantiated at one time. @@ -536,52 +412,6 @@ public: virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; }; -// Struct/class fields ------------------------------------------------------ - -// A PField describes a symbol that takes up physical space in the struct. -class PField : public PSymbol -{ - DECLARE_CLASS(PField, PSymbol); - HAS_OBJECT_POINTERS -public: - PField(FName name, PType *type, DWORD flags = 0, size_t offset = 0, int bitvalue = 0); - - size_t Offset; - PType *Type; - DWORD Flags; - int BitValue; -protected: - PField(); -}; - -// Struct/class fields ------------------------------------------------------ - -// A PField describes a symbol that takes up physical space in the struct. -class PProperty : public PSymbol -{ - DECLARE_CLASS(PProperty, PSymbol); -public: - PProperty(FName name, TArray &variables); - - TArray Variables; - -protected: - PProperty(); -}; - -class PPropFlag : public PSymbol -{ - DECLARE_CLASS(PPropFlag, PSymbol); -public: - PPropFlag(FName name, PField *offset, int bitval); - - PField *Offset; - int bitval; - -protected: - PPropFlag(); -}; - // Compound types ----------------------------------------------------------- class PEnum : public PInt @@ -729,36 +559,6 @@ protected: PPrototype(); }; -// TBD: Should we really support overloading? -class PFunction : public PSymbol -{ - DECLARE_CLASS(PFunction, PSymbol); -public: - struct Variant - { - PPrototype *Proto; - VMFunction *Implementation; - TArray ArgFlags; // Should be the same length as Proto->ArgumentTypes - TArray ArgNames; // we need the names to access them later when the function gets compiled. - uint32_t Flags; - int UseFlags; - PStruct *SelfClass; - }; - TArray Variants; - PStruct *OwningClass = nullptr; - - unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags, int useflags); - int GetImplicitArgs() - { - if (Variants[0].Flags & VARF_Action) return 3; - else if (Variants[0].Flags & VARF_Method) return 1; - return 0; - } - - size_t PropagateMark(); - - PFunction(PStruct *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {} -}; // Meta-info for every class derived from DObject --------------------------- @@ -914,82 +714,6 @@ extern PStateLabel *TypeStateLabel; extern PPointer *TypeNullPtr; extern PPointer *TypeVoidPtr; -// A constant value --------------------------------------------------------- - -class PSymbolConst : public PSymbol -{ - DECLARE_CLASS(PSymbolConst, PSymbol); -public: - PType *ValueType; - - PSymbolConst(FName name, PType *type=NULL) : PSymbol(name), ValueType(type) {} - PSymbolConst() : PSymbol(NAME_None), ValueType(NULL) {} -}; - -// A constant numeric value ------------------------------------------------- - -class PSymbolConstNumeric : public PSymbolConst -{ - DECLARE_CLASS(PSymbolConstNumeric, PSymbolConst); -public: - union - { - int Value; - double Float; - void *Pad; - }; - - PSymbolConstNumeric(FName name, PType *type=NULL) : PSymbolConst(name, type) {} - PSymbolConstNumeric(FName name, PType *type, int val) : PSymbolConst(name, type), Value(val) {} - PSymbolConstNumeric(FName name, PType *type, unsigned int val) : PSymbolConst(name, type), Value((int)val) {} - PSymbolConstNumeric(FName name, PType *type, double val) : PSymbolConst(name, type), Float(val) {} - PSymbolConstNumeric() {} -}; - -// A constant string value -------------------------------------------------- - -class PSymbolConstString : public PSymbolConst -{ - DECLARE_CLASS(PSymbolConstString, PSymbolConst); -public: - FString Str; - - PSymbolConstString(FName name, const FString &str) : PSymbolConst(name, TypeString), Str(str) {} - PSymbolConstString() {} -}; - -// Namespaces -------------------------------------------------- - -class PNamespace : public PTypeBase -{ - DECLARE_CLASS(PNamespace, PTypeBase) - HAS_OBJECT_POINTERS; - -public: - PSymbolTable Symbols; - PNamespace *Parent; - int FileNum; // This is for blocking DECORATE access to later files. - - PNamespace() {} - PNamespace(int filenum, PNamespace *parent); - size_t PropagateMark(); -}; - -struct FNamespaceManager -{ - PNamespace *GlobalNamespace; - TArray AllNamespaces; - - FNamespaceManager(); - PNamespace *NewNamespace(int filenum); - size_t MarkSymbols(); - void ReleaseSymbols(); - int RemoveSymbols(); -}; - -extern FNamespaceManager Namespaces; - - // Enumerations for serializing types in an archive ------------------------- inline bool &DObject::BoolVar(FName field) diff --git a/src/scripting/symbols.cpp b/src/scripting/symbols.cpp new file mode 100644 index 000000000..ba0704433 --- /dev/null +++ b/src/scripting/symbols.cpp @@ -0,0 +1,440 @@ +/* +** 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 +#include "dobject.h" +#include "i_system.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 + + +//========================================================================== +// +// +// +//========================================================================== + +PSymbol::~PSymbol() +{ +} + +//========================================================================== +// +// +// +//========================================================================== + +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 &argflags, TArray &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 = dyn_cast(proto->ArgumentTypes[0]); + assert(selftypeptr != nullptr); + variant.SelfClass = dyn_cast(selftypeptr->PointedType); + 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; +} + +//========================================================================== +// +// +// +//========================================================================== + +PSymbolTable::PSymbolTable() +: ParentSymbolTable(nullptr) +{ +} + +PSymbolTable::PSymbolTable(PSymbolTable *parent) +: ParentSymbolTable(parent) +{ +} + +PSymbolTable::~PSymbolTable () +{ + ReleaseSymbols(); +} + +//========================================================================== +// +// +// +//========================================================================== + +size_t PSymbolTable::MarkSymbols() +{ + size_t count = 0; + MapType::Iterator it(Symbols); + MapType::Pair *pair; + + while (it.NextPair(pair)) + { + GC::Mark(pair->Value); + count++; + } + return count * sizeof(*pair); +} + +//========================================================================== +// +// +// +//========================================================================== + +void PSymbolTable::ReleaseSymbols() +{ + // The GC will take care of deleting the symbols. We just need to + // clear our references to them. + 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); + return sym; +} + +//========================================================================== +// +// +// +//========================================================================== + +void PSymbolTable::RemoveSymbol(PSymbol *sym) +{ + auto mysym = Symbols.CheckKey(sym->SymbolName); + if (mysym == nullptr || *mysym != sym) return; + Symbols.Remove(sym->SymbolName); +} + +//========================================================================== +// +// +// +//========================================================================== + +PSymbol *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; + *symslot = newsym; + return oldsym; + } + // Else, just insert normally and return nullptr since there was no + // symbol to replace. + Symbols.Insert(newsym->SymbolName, newsym); + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +//========================================================================== +// +// +// +//========================================================================== + +PNamespace::PNamespace(int filenum, PNamespace *parent) +{ + Parent = parent; + if (parent) Symbols.SetParentTable(&parent->Symbols); + FileNum = filenum; +} + +//========================================================================== +// +// +// +//========================================================================== + +size_t PNamespace::PropagateMark() +{ + GC::Mark(Parent); + return Symbols.MarkSymbols() + 1; +} + +//========================================================================== +// +// +// +//========================================================================== + +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() +{ + 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->IsKindOf(RUNTIME_CLASS(PStruct))) + { + 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); +} diff --git a/src/scripting/symbols.h b/src/scripting/symbols.h new file mode 100644 index 000000000..52d7ac60a --- /dev/null +++ b/src/scripting/symbols.h @@ -0,0 +1,286 @@ +// Note: This must not be included by anything but dobject.h! +#pragma once + +#ifndef __DOBJECT_H__ +#error You must #include "dobject.h" to get symbols.h +#endif + + +class VMFunction; +class PType; +class PPrototype; +struct ZCC_TreeNode; +class PStruct; + +// Symbol information ------------------------------------------------------- + +class PTypeBase : public DObject +{ + DECLARE_ABSTRACT_CLASS(PTypeBase, DObject) + +public: + virtual FString QualifiedName() const + { + return ""; + } +}; + +class PSymbol : public PTypeBase +{ + DECLARE_ABSTRACT_CLASS(PSymbol, PTypeBase); +public: + virtual ~PSymbol(); + + virtual FString QualifiedName() const + { + return SymbolName.GetChars(); + } + + FName SymbolName; + +protected: + PSymbol(FName name) { SymbolName = name; } +}; + +// A VM function ------------------------------------------------------------ + +class PSymbolVMFunction : public PSymbol +{ + DECLARE_CLASS(PSymbolVMFunction, PSymbol); +public: + VMFunction *Function; + + PSymbolVMFunction(FName name) : PSymbol(name) {} + PSymbolVMFunction() : PSymbol(NAME_None) {} +}; + +// A symbol for a type ------------------------------------------------------ + +class PSymbolType : public PSymbol +{ + DECLARE_CLASS(PSymbolType, PSymbol); +public: + PType *Type; + + PSymbolType(FName name, class PType *ty) : PSymbol(name), Type(ty) {} + PSymbolType() : PSymbol(NAME_None) {} +}; + +// A symbol for a compiler tree node ---------------------------------------- + +class PSymbolTreeNode : public PSymbol +{ + DECLARE_CLASS(PSymbolTreeNode, PSymbol); +public: + struct ZCC_TreeNode *Node; + + PSymbolTreeNode(FName name, struct ZCC_TreeNode *node) : PSymbol(name), Node(node) {} + PSymbolTreeNode() : PSymbol(NAME_None) {} +}; + +// Struct/class fields ------------------------------------------------------ + +// A PField describes a symbol that takes up physical space in the struct. +class PField : public PSymbol +{ + DECLARE_CLASS(PField, PSymbol); + HAS_OBJECT_POINTERS +public: + PField(FName name, PType *type, uint32_t flags = 0, size_t offset = 0, int bitvalue = 0); + + size_t Offset; + PType *Type; + uint32_t Flags; + int BitValue; +protected: + PField(); +}; + +// Properties ------------------------------------------------------ + +// For setting properties in class defaults. +class PProperty : public PSymbol +{ + DECLARE_CLASS(PProperty, PSymbol); +public: + PProperty(FName name, TArray &variables); + + TArray Variables; + +protected: + PProperty(); +}; + +class PPropFlag : public PSymbol +{ + DECLARE_CLASS(PPropFlag, PSymbol); +public: + PPropFlag(FName name, PField *offset, int bitval); + + PField *Offset; + int bitval; + +protected: + PPropFlag(); +}; + +// A constant value --------------------------------------------------------- + +class PSymbolConst : public PSymbol +{ + DECLARE_CLASS(PSymbolConst, PSymbol); +public: + PType *ValueType; + + PSymbolConst(FName name, PType *type=NULL) : PSymbol(name), ValueType(type) {} + PSymbolConst() : PSymbol(NAME_None), ValueType(NULL) {} +}; + +// A constant numeric value ------------------------------------------------- + +class PSymbolConstNumeric : public PSymbolConst +{ + DECLARE_CLASS(PSymbolConstNumeric, PSymbolConst); +public: + union + { + int Value; + double Float; + void *Pad; + }; + + PSymbolConstNumeric(FName name, PType *type=NULL) : PSymbolConst(name, type) {} + PSymbolConstNumeric(FName name, PType *type, int val) : PSymbolConst(name, type), Value(val) {} + PSymbolConstNumeric(FName name, PType *type, unsigned int val) : PSymbolConst(name, type), Value((int)val) {} + PSymbolConstNumeric(FName name, PType *type, double val) : PSymbolConst(name, type), Float(val) {} + PSymbolConstNumeric() {} +}; + +// A constant string value -------------------------------------------------- + +class PSymbolConstString : public PSymbolConst +{ + DECLARE_CLASS(PSymbolConstString, PSymbolConst); +public: + FString Str; + + PSymbolConstString(FName name, const FString &str); + PSymbolConstString() {} +}; + + +// A function for the VM -------------------------------------------------- + +// TBD: Should we really support overloading? +class PFunction : public PSymbol +{ + DECLARE_CLASS(PFunction, PSymbol); +public: + struct Variant + { + PPrototype *Proto; + VMFunction *Implementation; + TArray ArgFlags; // Should be the same length as Proto->ArgumentTypes + TArray ArgNames; // we need the names to access them later when the function gets compiled. + uint32_t Flags; + int UseFlags; + PStruct *SelfClass; + }; + TArray Variants; + PStruct *OwningClass = nullptr; + + unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags, int useflags); + int GetImplicitArgs(); + + PFunction(PStruct *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {} +}; + +// A symbol table ----------------------------------------------------------- + +struct PSymbolTable +{ + PSymbolTable(); + PSymbolTable(PSymbolTable *parent); + ~PSymbolTable(); + + size_t MarkSymbols(); + + // Sets the table to use for searches if this one doesn't contain the + // requested symbol. + void SetParentTable (PSymbolTable *parent); + PSymbolTable *GetParentTable() const + { + return ParentSymbolTable; + } + + // Finds a symbol in the table, optionally searching parent tables + // as well. + PSymbol *FindSymbol (FName symname, bool searchparents) const; + + // Like FindSymbol with searchparents set true, but also returns the + // specific symbol table the symbol was found in. + PSymbol *FindSymbolInTable(FName symname, PSymbolTable *&symtable); + + + // Places the symbol in the table and returns a pointer to it or NULL if + // a symbol with the same name is already in the table. This symbol is + // not copied and will be freed when the symbol table is destroyed. + PSymbol *AddSymbol (PSymbol *sym); + + // Similar to AddSymbol but always succeeds. Returns the symbol that used + // to be in the table with this name, if any. + PSymbol *ReplaceSymbol(PSymbol *sym); + + void RemoveSymbol(PSymbol *sym); + + // Frees all symbols from this table. + void ReleaseSymbols(); + + typedef TMap MapType; + + MapType::Iterator GetIterator() + { + return MapType::Iterator(Symbols); + } + +private: + + PSymbolTable *ParentSymbolTable; + MapType Symbols; + + friend class DObject; + friend struct FNamespaceManager; +}; + +// Namespaces -------------------------------------------------- + +class PNamespace : public PTypeBase +{ + DECLARE_CLASS(PNamespace, PTypeBase) + HAS_OBJECT_POINTERS; + +public: + PSymbolTable Symbols; + PNamespace *Parent; + int FileNum; // This is for blocking DECORATE access to later files. + + PNamespace() {} + PNamespace(int filenum, PNamespace *parent); + size_t PropagateMark(); +}; + +struct FNamespaceManager +{ + PNamespace *GlobalNamespace; + TArray AllNamespaces; + + FNamespaceManager(); + PNamespace *NewNamespace(int filenum); + size_t MarkSymbols(); + void ReleaseSymbols(); + int RemoveSymbols(); +}; + +extern FNamespaceManager Namespaces; + +