diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dcabeb2a0..a6dcfd766 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1215,6 +1215,7 @@ set (PCH_SOURCES r_data/renderstyle.cpp r_data/r_interpolate.cpp scripting/symbols.cpp + scripting/types.cpp scripting/thingdef.cpp scripting/thingdef_data.cpp scripting/thingdef_properties.cpp diff --git a/src/actor.h b/src/actor.h index 631d854d0..666ca67f8 100644 --- a/src/actor.h +++ b/src/actor.h @@ -33,7 +33,6 @@ // States are tied to finite states are tied to animation frames. #include "info.h" -#include #include "doomdef.h" #include "textures/textures.h" #include "r_data/renderstyle.h" @@ -612,6 +611,12 @@ public: return (AActor *)(this->GetClass()->Defaults); } + FActorInfo *GetInfo() const + { + return ((PClassActor*)GetClass())->ActorInfo(); + } + + FDropItem *GetDropItems() const; // Return true if the monster should use a missile attack, false for melee @@ -1566,6 +1571,17 @@ template inline T *Spawn() // for inventory items we do not need coordi return static_cast(AActor::StaticSpawn(RUNTIME_TEMPLATE_CLASS(T), DVector3(0, 0, 0), NO_REPLACE)); } +inline PClassActor *PClass::FindActor(FName name) +{ + auto cls = FindClass(name); + return cls && cls->IsDescendantOf(RUNTIME_CLASS(AActor)) ? static_cast(cls) : nullptr; +} + +inline PClassActor *ValidateActor(PClass *cls) +{ + return cls && cls->IsDescendantOf(RUNTIME_CLASS(AActor)) ? static_cast(cls) : nullptr; +} + void PrintMiscActorInfo(AActor * query); AActor *P_LinePickActor(AActor *t1, DAngle angle, double distance, DAngle pitch, ActorFlags actorMask, uint32_t wallMask); diff --git a/src/am_map.cpp b/src/am_map.cpp index 31961831e..5c39857dc 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -734,8 +734,6 @@ static int grid = 0; bool automapactive = false; -DEFINE_GLOBAL(automapactive); - // location of window on screen static int f_x; static int f_y; diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 637494333..5fc891fb4 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -14,6 +14,7 @@ #include "d_net.h" #include "serializer.h" #include "d_player.h" +#include "vm.h" IMPLEMENT_CLASS(DBot, false, true) diff --git a/src/b_game.cpp b/src/b_game.cpp index 1c4230d56..ea80030ff 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -62,6 +62,7 @@ Everything that is changed is marked (maybe commented) with "Added by MC" #include "d_player.h" #include "doomerrors.h" #include "events.h" +#include "vm.h" static FRandom pr_botspawn ("BotSpawn"); diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 540c77f7b..76c552db2 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -46,6 +46,7 @@ #include "w_wad.h" #include "templates.h" #include "dobject.h" +#include "vm.h" #include #include diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 4d40ce66d..b45210053 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -1147,34 +1147,6 @@ CCMD(currentpos) } } -//----------------------------------------------------------------------------- -// -// -// -//----------------------------------------------------------------------------- -CCMD(vmengine) -{ - if (argv.argc() == 2) - { - if (stricmp(argv[1], "default") == 0) - { - VMSelectEngine(VMEngine_Default); - return; - } - else if (stricmp(argv[1], "checked") == 0) - { - VMSelectEngine(VMEngine_Checked); - return; - } - else if (stricmp(argv[1], "unchecked") == 0) - { - VMSelectEngine(VMEngine_Unchecked); - return; - } - } - Printf("Usage: vmengine \n"); -} - //----------------------------------------------------------------------------- // // Print secret info (submitted by Karl Murks) diff --git a/src/c_console.cpp b/src/c_console.cpp index 0d37198f3..f401ed0e5 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -68,6 +68,7 @@ #include "gstrings.h" #include "c_consolebuffer.h" #include "g_levellocals.h" +#include "vm.h" FString FStringFormat(VM_ARGS); // extern from thingdef_data.cpp diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index f223444e4..6b0b62da7 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -52,6 +52,7 @@ #include "v_video.h" #include "colormatcher.h" #include "menu/menu.h" +#include "vm.h" struct FLatchedValue { diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index ad32fcca9..464ed4495 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -55,6 +55,7 @@ #include "d_main.h" #include "serializer.h" #include "menu/menu.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 411564851..dcb42a2bb 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -74,6 +74,7 @@ #include "info.h" #include "v_text.h" #include "backend/vmbuilder.h" +#include "types.h" // [SO] Just the way Randy said to do it :) // [RH] Made this CVAR_SERVERINFO @@ -791,7 +792,7 @@ void SetDehParams(FState *state, int codepointer) // Let's identify the codepointer we're dealing with. PFunction *sym; - sym = dyn_cast(RUNTIME_CLASS(AWeapon)->Symbols.FindSymbol(FName(MBFCodePointers[codepointer].name), true)); + sym = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(FName(MBFCodePointers[codepointer].name), true)); if (sym == NULL) return; if (codepointer < 0 || (unsigned)codepointer >= countof(MBFCodePointerFactories)) @@ -876,7 +877,7 @@ static int PatchThing (int thingy) else { info = GetDefaultByType (type); - ednum = &type->DoomEdNum; + ednum = &type->ActorInfo()->DoomEdNum; } } } @@ -1984,7 +1985,7 @@ static int PatchMisc (int dummy) player->health = deh.StartHealth; // Hm... I'm not sure that this is the right way to change this info... - FDropItem *di = PClass::FindActor(NAME_DoomPlayer)->DropItems; + FDropItem *di = PClass::FindActor(NAME_DoomPlayer)->ActorInfo()->DropItems; while (di != NULL) { if (di->Name == NAME_Clip) @@ -2118,7 +2119,7 @@ static int PatchCodePtrs (int dummy) // This skips the action table and goes directly to the internal symbol table // DEH compatible functions are easy to recognize. - PFunction *sym = dyn_cast(RUNTIME_CLASS(AWeapon)->Symbols.FindSymbol(symname, true)); + PFunction *sym = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(symname, true)); if (sym == NULL) { Printf(TEXTCOLOR_RED "Frame %d: Unknown code pointer '%s'\n", frame, Line2); @@ -2744,7 +2745,7 @@ static bool LoadDehSupp () // or AActor so this will find all of them. FString name = "A_"; name << sc.String; - PFunction *sym = dyn_cast(RUNTIME_CLASS(AWeapon)->Symbols.FindSymbol(name, true)); + PFunction *sym = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(name, true)); if (sym == NULL) { sc.ScriptError("Unknown code pointer '%s'", sc.String); @@ -2827,7 +2828,7 @@ static bool LoadDehSupp () { sc.ScriptError ("Can't find type %s", sc.String); } - else if (!type->IsKindOf(RUNTIME_CLASS(PClassActor))) + else if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) { sc.ScriptError ("%s is not an actor", sc.String); } @@ -2843,7 +2844,7 @@ static bool LoadDehSupp () sc.MustGetStringName(","); sc.MustGetNumber(); - if (s.State == NULL || s.State + sc.Number > actortype->OwnedStates + actortype->NumOwnedStates) + if (s.State == NULL || !actortype->OwnsState(s.State + sc.Number)) { sc.ScriptError("Invalid state range in '%s'", type->TypeName.GetChars()); } @@ -3034,7 +3035,8 @@ void FinishDehPatch () subclass = static_cast(dehtype->CreateDerivedClass(typeNameBuilder, dehtype->Size)); } while (subclass == nullptr); - + NewClassType(subclass); // This needs a VM type to work as intended. + AActor *defaults2 = GetDefaultByType (subclass); memcpy ((void *)defaults2, (void *)defaults1, sizeof(AActor)); @@ -3045,21 +3047,21 @@ void FinishDehPatch () if (!type->IsDescendantOf(RUNTIME_CLASS(AInventory))) { // If this is a hacked non-inventory item we must also copy AInventory's special states - statedef.AddStateDefines(RUNTIME_CLASS(AInventory)->StateList); + statedef.AddStateDefines(RUNTIME_CLASS(AInventory)->GetStateLabels()); } statedef.InstallStates(subclass, defaults2); // Use the DECORATE replacement feature to redirect all spawns // of the original class to the new one. - PClassActor *old_replacement = type->Replacement; + PClassActor *old_replacement = type->ActorInfo()->Replacement; - type->Replacement = subclass; - subclass->Replacee = type; + type->ActorInfo()->Replacement = subclass; + subclass->ActorInfo()->Replacee = type; // If this actor was already replaced by another actor, copy that // replacement over to this item. if (old_replacement != NULL) { - subclass->Replacement = old_replacement; + subclass->ActorInfo()->Replacement = old_replacement; } DPrintf (DMSG_NOTIFY, "%s replaces %s\n", subclass->TypeName.GetChars(), type->TypeName.GetChars()); @@ -3116,7 +3118,7 @@ void FinishDehPatch () { if (AmmoPerAttacks[j].ptr == nullptr) { - auto p = dyn_cast(RUNTIME_CLASS(AWeapon)->Symbols.FindSymbol(AmmoPerAttacks[j].func, true)); + auto p = dyn_cast(RUNTIME_CLASS(AWeapon)->FindSymbol(AmmoPerAttacks[j].func, true)); if (p != nullptr) AmmoPerAttacks[j].ptr = p->Variants[0].Implementation; } if (state->ActionFunc == AmmoPerAttacks[j].ptr) @@ -3152,7 +3154,7 @@ DEFINE_ACTION_FUNCTION(ADehackedPickup, DetermineType) int lex = memcmp (DehSpriteMappings[mid].Sprite, sprites[self->sprite].name, 4); if (lex == 0) { - ACTION_RETURN_OBJECT(PClass::FindActor(DehSpriteMappings[mid].ClassName)); + ACTION_RETURN_POINTER(PClass::FindActor(DehSpriteMappings[mid].ClassName)); } else if (lex < 0) { @@ -3163,6 +3165,6 @@ DEFINE_ACTION_FUNCTION(ADehackedPickup, DetermineType) max = mid - 1; } } - ACTION_RETURN_OBJECT(nullptr); + ACTION_RETURN_POINTER(nullptr); } diff --git a/src/d_main.cpp b/src/d_main.cpp index d6edd7230..b03ca3e50 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -112,6 +112,8 @@ #include "g_levellocals.h" #include "events.h" #include "r_utility.h" +#include "vm.h" +#include "types.h" EXTERN_CVAR(Bool, hud_althud) void DrawHUD(); diff --git a/src/d_net.cpp b/src/d_net.cpp index 0af1bd9c1..12449111a 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2592,7 +2592,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player) char *classname = ReadString(stream); int removecount = 0; PClassActor *cls = PClass::FindActor(classname); - if (cls != NULL && cls->IsKindOf(RUNTIME_CLASS(PClassActor))) + if (cls != NULL && cls->IsDescendantOf(RUNTIME_CLASS(AActor))) { removecount = RemoveClass(cls); const PClass *cls_rep = cls->GetReplacement(); diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 70413496c..a476e4b8a 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -57,6 +57,7 @@ #include "templates.h" #include "cmdlib.h" #include "serializer.h" +#include "vm.h" static FRandom pr_pickteam ("PickRandomTeam"); @@ -158,7 +159,7 @@ int D_PlayerClassToInt (const char *classname) { auto type = PlayerClasses[i].Type; - if (type->DisplayName.IsNotEmpty() && stricmp(type->DisplayName, classname) == 0) + if (type->GetDisplayName().IsNotEmpty() && stricmp(type->GetDisplayName(), classname) == 0) { return i; } @@ -737,7 +738,7 @@ void D_WriteUserInfoStrings (int pnum, uint8_t **stream, bool compact) case NAME_PlayerClass: *stream += sprintf(*((char **)stream), "\\%s", info->GetPlayerClassNum() == -1 ? "Random" : - D_EscapeUserInfo(info->GetPlayerClassType()->DisplayName.GetChars()).GetChars()); + D_EscapeUserInfo(info->GetPlayerClassType()->GetDisplayName().GetChars()).GetChars()); break; case NAME_Skin: @@ -915,7 +916,7 @@ void WriteUserInfo(FSerializer &arc, userinfo_t &info) case NAME_PlayerClass: i = info.GetPlayerClassNum(); - string = (i == -1 ? "Random" : PlayerClasses[i].Type->DisplayName.GetChars()); + string = (i == -1 ? "Random" : PlayerClasses[i].Type->GetDisplayName().GetChars()); break; default: @@ -1001,7 +1002,7 @@ CCMD (playerinfo) Printf("%20s: %s (%d)\n", "Skin", Skins[ui->GetSkin()].Name.GetChars(), ui->GetSkin()); Printf("%20s: %s (%d)\n", "Gender", GenderNames[ui->GetGender()], ui->GetGender()); Printf("%20s: %s (%d)\n", "PlayerClass", - ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->DisplayName.GetChars(), + ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->GetDisplayName().GetChars(), ui->GetPlayerClassNum()); // Print generic info diff --git a/src/dobject.cpp b/src/dobject.cpp index 456305f21..687e76f85 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -48,8 +48,9 @@ #include "a_sharedglobal.h" #include "dsectoreffect.h" #include "serializer.h" -#include "virtual.h" +#include "vm.h" #include "g_levellocals.h" +#include "types.h" //========================================================================== // @@ -91,21 +92,22 @@ CCMD (dumpactors) "25:DoomStrifeChex", "26:HereticStrifeChex", "27:NotHexen", "28:HexenStrifeChex", "29:NotHeretic", "30:NotDoom", "31:All", }; - Printf("%i object class types total\nActor\tEd Num\tSpawnID\tFilter\tSource\n", PClass::AllClasses.Size()); + Printf("%u object class types total\nActor\tEd Num\tSpawnID\tFilter\tSource\n", PClass::AllClasses.Size()); for (unsigned int i = 0; i < PClass::AllClasses.Size(); i++) { PClass *cls = PClass::AllClasses[i]; - PClassActor *acls = dyn_cast(cls); + PClassActor *acls = ValidateActor(cls); if (acls != NULL) { + auto ainfo = acls->ActorInfo(); Printf("%s\t%i\t%i\t%s\t%s\n", - acls->TypeName.GetChars(), acls->DoomEdNum, - acls->SpawnID, filters[acls->GameFilter & 31], + acls->TypeName.GetChars(), ainfo->DoomEdNum, + ainfo->SpawnID, filters[ainfo->GameFilter & 31], acls->SourceLumpName.GetChars()); } else if (cls != NULL) { - Printf("%s\tn/a\tn/a\tn/a\tEngine (not an actor type)\n", cls->TypeName.GetChars()); + Printf("%s\tn/a\tn/a\tn/a\tEngine (not an actor type)\tSource: %s\n", cls->TypeName.GetChars(), cls->SourceLumpName.GetChars()); } else { @@ -371,7 +373,7 @@ void DObject:: Destroy () IFVIRTUAL(DObject, OnDestroy) { VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0); + VMCall(func, params, 1, nullptr, 0); } } OnDestroy(); @@ -624,7 +626,7 @@ DEFINE_ACTION_FUNCTION(DObject, MSTime) void *DObject::ScriptVar(FName field, PType *type) { auto cls = GetClass(); - auto sym = dyn_cast(cls->Symbols.FindSymbol(field, true)); + auto sym = dyn_cast(cls->FindSymbol(field, true)); if (sym && (sym->Type == type || type == nullptr)) { if (!(sym->Flags & VARF_Meta)) diff --git a/src/dobject.h b/src/dobject.h index 18a6f14b5..b4db0386a 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -247,13 +247,13 @@ public: void Destroy(); // Add other types as needed. - bool &BoolVar(FName field); - int &IntVar(FName field); - FSoundID &SoundVar(FName field); - PalEntry &ColorVar(FName field); - FName &NameVar(FName field); - double &FloatVar(FName field); - FString &StringVar(FName field); + inline bool &BoolVar(FName field); + inline int &IntVar(FName field); + inline FSoundID &SoundVar(FName field); + inline PalEntry &ColorVar(FName field); + inline FName &NameVar(FName field); + inline double &FloatVar(FName field); + inline FString &StringVar(FName field); template T*& PointerVar(FName field); // If you need to replace one object with another and want to @@ -409,4 +409,40 @@ template const T *dyn_cast(const DObject *p) return dyn_cast(const_cast(p)); } +inline bool &DObject::BoolVar(FName field) +{ + return *(bool*)ScriptVar(field, nullptr); +} + +inline int &DObject::IntVar(FName field) +{ + return *(int*)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); +} + +template +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__ diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index b4fd9835d..d7703de1c 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -49,6 +49,8 @@ #include "doomerrors.h" #include "fragglescript/t_fs.h" #include "a_keys.h" +#include "vm.h" +#include "types.h" // MACROS ------------------------------------------------------------------ @@ -66,2618 +68,19 @@ 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. -FTypeTable TypeTable; TArray PClass::AllClasses; +TMap PClass::ClassMap; TArray PClass::FunctionPtrList; bool PClass::bShutdown; bool PClass::bVMOperational; -PErrorType *TypeError; -PErrorType *TypeAuto; -PVoidType *TypeVoid; -PInt *TypeSInt8, *TypeUInt8; -PInt *TypeSInt16, *TypeUInt16; -PInt *TypeSInt32, *TypeUInt32; -PBool *TypeBool; -PFloat *TypeFloat32, *TypeFloat64; -PString *TypeString; -PName *TypeName; -PSound *TypeSound; -PColor *TypeColor; -PTextureID *TypeTextureID; -PSpriteID *TypeSpriteID; -PStatePointer *TypeState; -PPointer *TypeFont; -PStateLabel *TypeStateLabel; -PStruct *TypeVector2; -PStruct *TypeVector3; -PStruct *TypeColorStruct; -PStruct *TypeStringStruct; -PPointer *TypeNullPtr; -PPointer *TypeVoidPtr; + // PRIVATE DATA DEFINITIONS ------------------------------------------------ // A harmless non-nullptr FlatPointer for classes without pointers. static const size_t TheEnd = ~(size_t)0; -// CODE -------------------------------------------------------------------- - -IMPLEMENT_CLASS(PErrorType, false, false) -IMPLEMENT_CLASS(PVoidType, false, false) - -void DumpTypeTable() -{ - int used = 0; - int min = INT_MAX; - int max = 0; - int all = 0; - int lens[10] = {0}; - for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) - { - int len = 0; - Printf("%4zu:", i); - for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext) - { - Printf(" -> %s", ty->DescriptiveName()); - len++; - all++; - } - if (len != 0) - { - used++; - if (len < min) - min = len; - if (len > max) - max = len; - } - if (len < (int)countof(lens)) - { - lens[len]++; - } - Printf("\n"); - } - Printf("Used buckets: %d/%lu (%.2f%%) for %d entries\n", used, countof(TypeTable.TypeHash), double(used)/countof(TypeTable.TypeHash)*100, all); - Printf("Min bucket size: %d\n", min); - Printf("Max bucket size: %d\n", max); - Printf("Avg bucket size: %.2f\n", double(all) / used); - int j,k; - for (k = countof(lens)-1; k > 0; --k) - if (lens[k]) - break; - for (j = 0; j <= k; ++j) - Printf("Buckets of len %d: %d (%.2f%%)\n", j, lens[j], j!=0?double(lens[j])/used*100:-1.0); -} - -/* PType ******************************************************************/ - -IMPLEMENT_CLASS(PType, true, false) - -//========================================================================== -// -// PType Parameterized Constructor -// -//========================================================================== - -PType::PType(unsigned int size, unsigned int align) -: Size(size), Align(align), HashNext(nullptr) -{ - mDescriptiveName = "Type"; - loadOp = OP_NOP; - storeOp = OP_NOP; - moveOp = OP_NOP; - RegType = REGT_NIL; - RegCount = 1; -} - -//========================================================================== -// -// PType Destructor -// -//========================================================================== - -PType::~PType() -{ -} - -//========================================================================== -// -// PType :: WriteValue -// -//========================================================================== - -void PType::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - assert(0 && "Cannot write value for this type"); -} - -//========================================================================== -// -// PType :: ReadValue -// -//========================================================================== - -bool PType::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - assert(0 && "Cannot read value for this type"); - return false; -} - -//========================================================================== -// -// PType :: SetDefaultValue -// -//========================================================================== - -void PType::SetDefaultValue(void *base, unsigned offset, TArray *stroffs) const -{ -} - -//========================================================================== -// -// PType :: SetDefaultValue -// -//========================================================================== - -void PType::SetPointer(void *base, unsigned offset, TArray *stroffs) const -{ -} - -void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs) const -{ -} - -//========================================================================== -// -// PType :: InitializeValue -// -//========================================================================== - -void PType::InitializeValue(void *addr, const void *def) const -{ -} - -//========================================================================== -// -// PType :: DestroyValue -// -//========================================================================== - -void PType::DestroyValue(void *addr) const -{ -} - -//========================================================================== -// -// PType :: SetValue -// -//========================================================================== - -void PType::SetValue(void *addr, int val) -{ - assert(0 && "Cannot set int value for this type"); -} - -void PType::SetValue(void *addr, double val) -{ - assert(0 && "Cannot set float value for this type"); -} - -//========================================================================== -// -// PType :: GetValue -// -//========================================================================== - -int PType::GetValueInt(void *addr) const -{ - assert(0 && "Cannot get value for this type"); - return 0; -} - -double PType::GetValueFloat(void *addr) const -{ - assert(0 && "Cannot get value for this type"); - return 0; -} - -//========================================================================== -// -// PType :: IsMatch -// -//========================================================================== - -bool PType::IsMatch(intptr_t id1, intptr_t id2) const -{ - return false; -} - -//========================================================================== -// -// PType :: GetTypeIDs -// -//========================================================================== - -void PType::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = 0; - id2 = 0; -} - -//========================================================================== -// -// PType :: GetTypeIDs -// -//========================================================================== - -const char *PType::DescriptiveName() const -{ - return mDescriptiveName.GetChars(); -} - -//========================================================================== -// -// PType :: StaticInit STATIC -// -//========================================================================== - -void PType::StaticInit() -{ - // Create types and add them type the type table. - TypeTable.AddType(TypeError = new PErrorType); - TypeTable.AddType(TypeAuto = new PErrorType(2)); - TypeTable.AddType(TypeVoid = new PVoidType); - TypeTable.AddType(TypeSInt8 = new PInt(1, false)); - TypeTable.AddType(TypeUInt8 = new PInt(1, true)); - TypeTable.AddType(TypeSInt16 = new PInt(2, false)); - TypeTable.AddType(TypeUInt16 = new PInt(2, true)); - TypeTable.AddType(TypeSInt32 = new PInt(4, false)); - TypeTable.AddType(TypeUInt32 = new PInt(4, true)); - TypeTable.AddType(TypeBool = new PBool); - TypeTable.AddType(TypeFloat32 = new PFloat(4)); - TypeTable.AddType(TypeFloat64 = new PFloat(8)); - TypeTable.AddType(TypeString = new PString); - TypeTable.AddType(TypeName = new PName); - TypeTable.AddType(TypeSound = new PSound); - TypeTable.AddType(TypeColor = new PColor); - TypeTable.AddType(TypeState = new PStatePointer); - TypeTable.AddType(TypeStateLabel = new PStateLabel); - TypeTable.AddType(TypeNullPtr = new PPointer); - TypeTable.AddType(TypeSpriteID = new PSpriteID); - TypeTable.AddType(TypeTextureID = new PTextureID); - - TypeVoidPtr = NewPointer(TypeVoid, false); - TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value. - TypeStringStruct = NewNativeStruct("Stringstruct", nullptr); - TypeFont = NewPointer(NewNativeStruct("Font", nullptr)); -#ifdef __BIG_ENDIAN__ - TypeColorStruct->AddField(NAME_a, TypeUInt8); - TypeColorStruct->AddField(NAME_r, TypeUInt8); - TypeColorStruct->AddField(NAME_g, TypeUInt8); - TypeColorStruct->AddField(NAME_b, TypeUInt8); -#else - TypeColorStruct->AddField(NAME_b, TypeUInt8); - TypeColorStruct->AddField(NAME_g, TypeUInt8); - TypeColorStruct->AddField(NAME_r, TypeUInt8); - TypeColorStruct->AddField(NAME_a, TypeUInt8); -#endif - - TypeVector2 = new PStruct(NAME_Vector2, nullptr); - TypeVector2->AddField(NAME_X, TypeFloat64); - TypeVector2->AddField(NAME_Y, TypeFloat64); - TypeTable.AddType(TypeVector2); - TypeVector2->loadOp = OP_LV2; - TypeVector2->storeOp = OP_SV2; - TypeVector2->moveOp = OP_MOVEV2; - TypeVector2->RegType = REGT_FLOAT; - TypeVector2->RegCount = 2; - - TypeVector3 = new PStruct(NAME_Vector3, nullptr); - TypeVector3->AddField(NAME_X, TypeFloat64); - TypeVector3->AddField(NAME_Y, TypeFloat64); - TypeVector3->AddField(NAME_Z, TypeFloat64); - // allow accessing xy as a vector2. This is not supposed to be serialized so it's marked transient - TypeVector3->Symbols.AddSymbol(new PField(NAME_XY, TypeVector2, VARF_Transient, 0)); - TypeTable.AddType(TypeVector3); - TypeVector3->loadOp = OP_LV3; - TypeVector3->storeOp = OP_SV3; - TypeVector3->moveOp = OP_MOVEV3; - TypeVector3->RegType = REGT_FLOAT; - TypeVector3->RegCount = 3; - - - - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_sByte, TypeSInt8)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Byte, TypeUInt8)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Short, TypeSInt16)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uShort, TypeUInt16)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Int, TypeSInt32)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uInt, TypeUInt32)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Bool, TypeBool)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float, TypeFloat64)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Double, TypeFloat64)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float32, TypeFloat32)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float64, TypeFloat64)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_String, TypeString)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Name, TypeName)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Sound, TypeSound)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Color, TypeColor)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_State, TypeState)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector2, TypeVector2)); - Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector3, TypeVector3)); -} - - -/* PBasicType *************************************************************/ - -IMPLEMENT_CLASS(PBasicType, true, false) - -//========================================================================== -// -// PBasicType Default Constructor -// -//========================================================================== - -PBasicType::PBasicType() -{ -} - -//========================================================================== -// -// PBasicType Parameterized Constructor -// -//========================================================================== - -PBasicType::PBasicType(unsigned int size, unsigned int align) -: PType(size, align) -{ - mDescriptiveName = "BasicType"; -} - -/* PCompoundType **********************************************************/ - -IMPLEMENT_CLASS(PCompoundType, true, false) - -/* PNamedType *************************************************************/ - -IMPLEMENT_CLASS(PNamedType, true, false) - -//========================================================================== -// -// PNamedType :: IsMatch -// -//========================================================================== - -bool PNamedType::IsMatch(intptr_t id1, intptr_t id2) const -{ - const DObject *outer = (const DObject *)id1; - FName name = (ENamedName)(intptr_t)id2; - - return Outer == outer && TypeName == name; -} - -//========================================================================== -// -// PNamedType :: GetTypeIDs -// -//========================================================================== - -void PNamedType::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = (intptr_t)Outer; - id2 = TypeName; -} - -/* PInt *******************************************************************/ - -IMPLEMENT_CLASS(PInt, false, false) - -//========================================================================== -// -// PInt Default Constructor -// -//========================================================================== - -PInt::PInt() -: PBasicType(4, 4), Unsigned(false), IntCompatible(true) -{ - mDescriptiveName = "SInt32"; - Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, -0x7FFFFFFF - 1)); - Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, 0x7FFFFFFF)); - SetOps(); -} - -//========================================================================== -// -// PInt Parameterized Constructor -// -//========================================================================== - -PInt::PInt(unsigned int size, bool unsign, bool compatible) -: PBasicType(size, size), Unsigned(unsign), IntCompatible(compatible) -{ - mDescriptiveName.Format("%cInt%d", unsign? 'U':'S', size); - - MemberOnly = (size < 4); - if (!unsign) - { - int maxval = (1u << ((8 * size) - 1)) - 1; // compute as unsigned to prevent overflow before -1 - int minval = -maxval - 1; - Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, minval)); - Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, maxval)); - } - else - { - Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, 0u)); - Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, (1u << ((8 * size) - 1)))); - } - SetOps(); -} - -void PInt::SetOps() -{ - moveOp = OP_MOVE; - RegType = REGT_INT; - if (Size == 4) - { - storeOp = OP_SW; - loadOp = OP_LW; - } - else if (Size == 1) - { - storeOp = OP_SB; - loadOp = Unsigned ? OP_LBU : OP_LB; - } - else if (Size == 2) - { - storeOp = OP_SH; - loadOp = Unsigned ? OP_LHU : OP_LH; - } - else - { - assert(0 && "Unhandled integer size"); - storeOp = OP_NOP; - } -} - -//========================================================================== -// -// PInt :: WriteValue -// -//========================================================================== - -void PInt::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - if (Size == 8 && Unsigned) - { - // this is a special case that cannot be represented by an int64_t. - uint64_t val = *(uint64_t*)addr; - ar(key, val); - } - else - { - int64_t val; - switch (Size) - { - case 1: - val = Unsigned ? *(uint8_t*)addr : *(int8_t*)addr; - break; - - case 2: - val = Unsigned ? *(uint16_t*)addr : *(int16_t*)addr; - break; - - case 4: - val = Unsigned ? *(uint32_t*)addr : *(int32_t*)addr; - break; - - case 8: - val = *(int64_t*)addr; - break; - - default: - return; // something invalid - } - ar(key, val); - } -} - -//========================================================================== -// -// PInt :: ReadValue -// -//========================================================================== - -bool PInt::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - NumericValue val; - - ar(key, val); - if (val.type == NumericValue::NM_invalid) return false; // not found or usable - if (val.type == NumericValue::NM_float) val.signedval = (int64_t)val.floatval; - - // No need to check the unsigned state here. Downcasting to smaller types will yield the same result for both. - switch (Size) - { - case 1: - *(uint8_t*)addr = (uint8_t)val.signedval; - break; - - case 2: - *(uint16_t*)addr = (uint16_t)val.signedval; - break; - - case 4: - *(uint32_t*)addr = (uint32_t)val.signedval; - break; - - case 8: - *(uint64_t*)addr = (uint64_t)val.signedval; - break; - - default: - return false; // something invalid - } - - return true; -} - -//========================================================================== -// -// PInt :: SetValue -// -//========================================================================== - -void PInt::SetValue(void *addr, int val) -{ - assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); - if (Size == 4) - { - *(int *)addr = val; - } - else if (Size == 1) - { - *(uint8_t *)addr = val; - } - else if (Size == 2) - { - *(uint16_t *)addr = val; - } - else if (Size == 8) - { - *(uint64_t *)addr = val; - } - else - { - assert(0 && "Unhandled integer size"); - } -} - -void PInt::SetValue(void *addr, double val) -{ - SetValue(addr, (int)val); -} - -//========================================================================== -// -// PInt :: GetValueInt -// -//========================================================================== - -int PInt::GetValueInt(void *addr) const -{ - assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); - if (Size == 4) - { - return *(int *)addr; - } - else if (Size == 1) - { - return Unsigned ? *(uint8_t *)addr : *(int8_t *)addr; - } - else if (Size == 2) - { - return Unsigned ? *(uint16_t *)addr : *(int16_t *)addr; - } - else if (Size == 8) - { // truncated output - return (int)*(uint64_t *)addr; - } - else - { - assert(0 && "Unhandled integer size"); - return 0; - } -} - -//========================================================================== -// -// PInt :: GetValueFloat -// -//========================================================================== - -double PInt::GetValueFloat(void *addr) const -{ - return GetValueInt(addr); -} - -//========================================================================== -// -// PInt :: GetStoreOp -// -//========================================================================== - -/* PBool ******************************************************************/ - -IMPLEMENT_CLASS(PBool, false, false) - -//========================================================================== -// -// PInt :: SetValue -// -//========================================================================== - -void PBool::SetValue(void *addr, int val) -{ - *(bool*)addr = !!val; -} - -void PBool::SetValue(void *addr, double val) -{ - *(bool*)addr = val != 0.; -} - -int PBool::GetValueInt(void *addr) const -{ - return *(bool *)addr; -} - -double PBool::GetValueFloat(void *addr) const -{ - return *(bool *)addr; -} - -//========================================================================== -// -// PBool Default Constructor -// -//========================================================================== - -PBool::PBool() -: PInt(sizeof(bool), true) -{ - mDescriptiveName = "Bool"; - MemberOnly = false; -} - -/* PFloat *****************************************************************/ - -IMPLEMENT_CLASS(PFloat, false, false) - -//========================================================================== -// -// PFloat Default Constructor -// -//========================================================================== - -PFloat::PFloat() -: PBasicType(8, 8) -{ - mDescriptiveName = "Float"; - SetDoubleSymbols(); - SetOps(); -} - -//========================================================================== -// -// PFloat Parameterized Constructor -// -//========================================================================== - -PFloat::PFloat(unsigned int size) -: PBasicType(size, size) -{ - mDescriptiveName.Format("Float%d", size); - if (size == 8) - { -#ifdef __i386__ - // According to System V i386 ABI alignment of double type is 4 - // GCC and Clang for 32-bit Intel targets follow this requirement - // However GCC has -malign-double option to enable 8-byte alignment - // So calculation of the actual alignment is needed - struct AlignmentCheck { uint8_t i; double d; }; - Align = static_cast(offsetof(AlignmentCheck, d)); -#endif // __i386__ - - SetDoubleSymbols(); - } - else - { - assert(size == 4); - MemberOnly = true; - SetSingleSymbols(); - } - SetOps(); -} - -//========================================================================== -// -// PFloat :: SetDoubleSymbols -// -// Setup constant values for 64-bit floats. -// -//========================================================================== - -void PFloat::SetDoubleSymbols() -{ - static const SymbolInitF symf[] = - { - { NAME_Min_Normal, DBL_MIN }, - { NAME_Max, DBL_MAX }, - { NAME_Epsilon, DBL_EPSILON }, - { NAME_NaN, std::numeric_limits::quiet_NaN() }, - { NAME_Infinity, std::numeric_limits::infinity() }, - { NAME_Min_Denormal, std::numeric_limits::denorm_min() } - }; - static const SymbolInitI symi[] = - { - { NAME_Dig, DBL_DIG }, - { NAME_Min_Exp, DBL_MIN_EXP }, - { NAME_Max_Exp, DBL_MAX_EXP }, - { NAME_Mant_Dig, DBL_MANT_DIG }, - { NAME_Min_10_Exp, DBL_MIN_10_EXP }, - { NAME_Max_10_Exp, DBL_MAX_10_EXP } - }; - SetSymbols(symf, countof(symf)); - SetSymbols(symi, countof(symi)); -} - -//========================================================================== -// -// PFloat :: SetSingleSymbols -// -// Setup constant values for 32-bit floats. -// -//========================================================================== - -void PFloat::SetSingleSymbols() -{ - static const SymbolInitF symf[] = - { - { NAME_Min_Normal, FLT_MIN }, - { NAME_Max, FLT_MAX }, - { NAME_Epsilon, FLT_EPSILON }, - { NAME_NaN, std::numeric_limits::quiet_NaN() }, - { NAME_Infinity, std::numeric_limits::infinity() }, - { NAME_Min_Denormal, std::numeric_limits::denorm_min() } - }; - static const SymbolInitI symi[] = - { - { NAME_Dig, FLT_DIG }, - { NAME_Min_Exp, FLT_MIN_EXP }, - { NAME_Max_Exp, FLT_MAX_EXP }, - { NAME_Mant_Dig, FLT_MANT_DIG }, - { NAME_Min_10_Exp, FLT_MIN_10_EXP }, - { NAME_Max_10_Exp, FLT_MAX_10_EXP } - }; - SetSymbols(symf, countof(symf)); - SetSymbols(symi, countof(symi)); -} - -//========================================================================== -// -// PFloat :: SetSymbols -// -//========================================================================== - -void PFloat::SetSymbols(const PFloat::SymbolInitF *sym, size_t count) -{ - for (size_t i = 0; i < count; ++i) - { - Symbols.AddSymbol(new PSymbolConstNumeric(sym[i].Name, this, sym[i].Value)); - } -} - -void PFloat::SetSymbols(const PFloat::SymbolInitI *sym, size_t count) -{ - for (size_t i = 0; i < count; ++i) - { - Symbols.AddSymbol(new PSymbolConstNumeric(sym[i].Name, this, sym[i].Value)); - } -} - -//========================================================================== -// -// PFloat :: WriteValue -// -//========================================================================== - -void PFloat::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - if (Size == 8) - { - ar(key, *(double*)addr); - } - else - { - ar(key, *(float*)addr); - } -} - -//========================================================================== -// -// PFloat :: ReadValue -// -//========================================================================== - -bool PFloat::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - NumericValue val; - - ar(key, val); - if (val.type == NumericValue::NM_invalid) return false; // not found or usable - else if (val.type == NumericValue::NM_signed) val.floatval = (double)val.signedval; - else if (val.type == NumericValue::NM_unsigned) val.floatval = (double)val.unsignedval; - - if (Size == 8) - { - *(double*)addr = val.floatval; - } - else - { - *(float*)addr = (float)val.floatval; - } - return true; -} - -//========================================================================== -// -// PFloat :: SetValue -// -//========================================================================== - -void PFloat::SetValue(void *addr, int val) -{ - return SetValue(addr, (double)val); -} - -void PFloat::SetValue(void *addr, double val) -{ - assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); - if (Size == 4) - { - *(float *)addr = (float)val; - } - else - { - assert(Size == 8); - *(double *)addr = val; - } -} - -//========================================================================== -// -// PFloat :: GetValueInt -// -//========================================================================== - -int PFloat::GetValueInt(void *addr) const -{ - return xs_ToInt(GetValueFloat(addr)); -} - -//========================================================================== -// -// PFloat :: GetValueFloat -// -//========================================================================== - -double PFloat::GetValueFloat(void *addr) const -{ - assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); - if (Size == 4) - { - return *(float *)addr; - } - else - { - assert(Size == 8); - return *(double *)addr; - } -} - -//========================================================================== -// -// PFloat :: GetStoreOp -// -//========================================================================== - -void PFloat::SetOps() -{ - if (Size == 4) - { - storeOp = OP_SSP; - loadOp = OP_LSP; - } - else - { - assert(Size == 8); - storeOp = OP_SDP; - loadOp = OP_LDP; - } - moveOp = OP_MOVEF; - RegType = REGT_FLOAT; -} - -/* PString ****************************************************************/ - -IMPLEMENT_CLASS(PString, false, false) - -//========================================================================== -// -// PString Default Constructor -// -//========================================================================== - -PString::PString() -: PBasicType(sizeof(FString), alignof(FString)) -{ - mDescriptiveName = "String"; - storeOp = OP_SS; - loadOp = OP_LS; - moveOp = OP_MOVES; - RegType = REGT_STRING; - -} - -//========================================================================== -// -// PString :: WriteValue -// -//========================================================================== - -void PString::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - ar(key, *(FString*)addr); -} - -//========================================================================== -// -// PString :: ReadValue -// -//========================================================================== - -bool PString::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - const char *cptr; - ar.StringPtr(key, cptr); - if (cptr == nullptr) - { - return false; - } - else - { - *(FString*)addr = cptr; - return true; - } -} - -//========================================================================== -// -// PString :: SetDefaultValue -// -//========================================================================== - -void PString::SetDefaultValue(void *base, unsigned offset, TArray *special) const -{ - if (base != nullptr) new((uint8_t *)base + offset) FString; - if (special != nullptr) - { - special->Push(std::make_pair(this, offset)); - } -} - -//========================================================================== -// -// PString :: InitializeValue -// -//========================================================================== - -void PString::InitializeValue(void *addr, const void *def) const -{ - if (def != nullptr) - { - new(addr) FString(*(FString *)def); - } - else - { - new(addr) FString; - } -} - -//========================================================================== -// -// PString :: DestroyValue -// -//========================================================================== - -void PString::DestroyValue(void *addr) const -{ - ((FString *)addr)->~FString(); -} - -/* PName ******************************************************************/ - -IMPLEMENT_CLASS(PName, false, false) - -//========================================================================== -// -// PName Default Constructor -// -//========================================================================== - -PName::PName() -: PInt(sizeof(FName), true, false) -{ - mDescriptiveName = "Name"; - assert(sizeof(FName) == alignof(FName)); -} - -//========================================================================== -// -// PName :: WriteValue -// -//========================================================================== - -void PName::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - const char *cptr = ((const FName*)addr)->GetChars(); - ar.StringPtr(key, cptr); -} - -//========================================================================== -// -// PName :: ReadValue -// -//========================================================================== - -bool PName::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - const char *cptr; - ar.StringPtr(key, cptr); - if (cptr == nullptr) - { - return false; - } - else - { - *(FName*)addr = FName(cptr); - return true; - } -} - -/* PSpriteID ******************************************************************/ - -IMPLEMENT_CLASS(PSpriteID, false, false) - -//========================================================================== -// -// PName Default Constructor -// -//========================================================================== - -PSpriteID::PSpriteID() - : PInt(sizeof(int), true, true) -{ - mDescriptiveName = "SpriteID"; -} - -//========================================================================== -// -// PName :: WriteValue -// -//========================================================================== - -void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) const -{ - int32_t val = *(int*)addr; - ar.Sprite(key, val, nullptr); -} - -//========================================================================== -// -// PName :: ReadValue -// -//========================================================================== - -bool PSpriteID::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - int32_t val; - ar.Sprite(key, val, nullptr); - *(int*)addr = val; - return true; -} - -/* PTextureID ******************************************************************/ - -IMPLEMENT_CLASS(PTextureID, false, false) - -//========================================================================== -// -// PTextureID Default Constructor -// -//========================================================================== - -PTextureID::PTextureID() - : PInt(sizeof(FTextureID), true, false) -{ - mDescriptiveName = "TextureID"; - assert(sizeof(FTextureID) == alignof(FTextureID)); -} - -//========================================================================== -// -// PTextureID :: WriteValue -// -//========================================================================== - -void PTextureID::WriteValue(FSerializer &ar, const char *key, const void *addr) const -{ - FTextureID val = *(FTextureID*)addr; - ar(key, val); -} - -//========================================================================== -// -// PTextureID :: ReadValue -// -//========================================================================== - -bool PTextureID::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - FTextureID val; - ar(key, val); - *(FTextureID*)addr = val; - return true; -} - -/* PSound *****************************************************************/ - -IMPLEMENT_CLASS(PSound, false, false) - -//========================================================================== -// -// PSound Default Constructor -// -//========================================================================== - -PSound::PSound() -: PInt(sizeof(FSoundID), true) -{ - mDescriptiveName = "Sound"; - assert(sizeof(FSoundID) == alignof(FSoundID)); -} - -//========================================================================== -// -// PSound :: WriteValue -// -//========================================================================== - -void PSound::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - const char *cptr = *(const FSoundID *)addr; - ar.StringPtr(key, cptr); -} - -//========================================================================== -// -// PSound :: ReadValue -// -//========================================================================== - -bool PSound::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - const char *cptr; - ar.StringPtr(key, cptr); - if (cptr == nullptr) - { - return false; - } - else - { - *(FSoundID *)addr = FSoundID(cptr); - return true; - } -} - -/* PColor *****************************************************************/ - -IMPLEMENT_CLASS(PColor, false, false) - -//========================================================================== -// -// PColor Default Constructor -// -//========================================================================== - -PColor::PColor() -: PInt(sizeof(PalEntry), true) -{ - mDescriptiveName = "Color"; - assert(sizeof(PalEntry) == alignof(PalEntry)); -} - -/* PStateLabel *****************************************************************/ - -IMPLEMENT_CLASS(PStateLabel, false, false) - -//========================================================================== -// -// PStateLabel Default Constructor -// -//========================================================================== - -PStateLabel::PStateLabel() - : PInt(sizeof(int), false, false) -{ - mDescriptiveName = "StateLabel"; -} - -/* PPointer ***************************************************************/ - -IMPLEMENT_CLASS(PPointer, false, false) - -//========================================================================== -// -// PPointer - Default Constructor -// -//========================================================================== - -PPointer::PPointer() -: PBasicType(sizeof(void *), alignof(void *)), PointedType(nullptr), IsConst(false) -{ - mDescriptiveName = "NullPointer"; - SetOps(); -} - -//========================================================================== -// -// PPointer - Parameterized Constructor -// -//========================================================================== - -PPointer::PPointer(PType *pointsat, bool isconst) -: PBasicType(sizeof(void *), alignof(void *)), PointedType(pointsat), IsConst(isconst) -{ - mDescriptiveName.Format("Pointer<%s%s>", pointsat->DescriptiveName(), isconst? "readonly " : ""); - mVersion = pointsat->mVersion; - SetOps(); -} - -//========================================================================== -// -// PPointer :: GetStoreOp -// -//========================================================================== - -void PPointer::SetOps() -{ - loadOp = (PointedType && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) ? OP_LO : OP_LP; - // Non-destroyed thinkers are always guaranteed to be linked into the thinker chain so we don't need the write barrier for them. - storeOp = (loadOp == OP_LO && !static_cast(PointedType)->IsDescendantOf(RUNTIME_CLASS(DThinker))) ? OP_SO : OP_SP; - moveOp = OP_MOVEA; - RegType = REGT_POINTER; -} - -//========================================================================== -// -// PPointer :: IsMatch -// -//========================================================================== - -bool PPointer::IsMatch(intptr_t id1, intptr_t id2) const -{ - assert(id2 == 0 || id2 == 1); - PType *pointat = (PType *)id1; - - return pointat == PointedType && (!!id2) == IsConst; -} - -//========================================================================== -// -// PPointer :: GetTypeIDs -// -//========================================================================== - -void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = (intptr_t)PointedType; - id2 = 0; -} - -//========================================================================== -// -// PPointer :: SetPointer -// -//========================================================================== - -void PPointer::SetPointer(void *base, unsigned offset, TArray *special) const -{ - if (PointedType != nullptr && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) - { - // Add to the list of pointers for this class. - special->Push(offset); - } -} - -//========================================================================== -// -// PPointer :: WriteValue -// -//========================================================================== - -void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) - { - auto pt = static_cast(PointedType); - - if (pt->IsDescendantOf(RUNTIME_CLASS(PClass))) - { - ar(key, *(PClass **)addr); - } - else - { - ar(key, *(DObject **)addr); - } - } - else if (writer != nullptr) - { - writer(ar, key, addr); - } - else - { - I_Error("Attempt to save pointer to unhandled type %s", PointedType->DescriptiveName()); - } -} - -//========================================================================== -// -// PPointer :: ReadValue -// -//========================================================================== - -bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) - { - auto pt = static_cast(PointedType); - bool res = true; - - if (pt->IsDescendantOf(RUNTIME_CLASS(PClass))) - { - ::Serialize(ar, key, *(PClass **)addr, (PClass**)nullptr); - } - else - { - ::Serialize(ar, key, *(DObject **)addr, nullptr, &res); - } - return res; - } - else if (reader != nullptr) - { - return reader(ar, key, addr); - } - return false; -} - -//========================================================================== -// -// NewPointer -// -// Returns a PPointer to an object of the specified type -// -//========================================================================== - -PPointer *NewPointer(PType *type, bool isconst) -{ - size_t bucket; - PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &bucket); - if (ptype == nullptr) - { - ptype = new PPointer(type, isconst); - TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket); - } - return static_cast(ptype); -} - -/* PStatePointer **********************************************************/ - -IMPLEMENT_CLASS(PStatePointer, false, false) - -//========================================================================== -// -// PStatePointer Default Constructor -// -//========================================================================== - -PStatePointer::PStatePointer() -{ - mDescriptiveName = "Pointer"; - PointedType = NewNativeStruct(NAME_State, nullptr); - IsConst = true; -} - -//========================================================================== -// -// PStatePointer :: WriteValue -// -//========================================================================== - -void PStatePointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const -{ - ar(key, *(FState **)addr); -} - -//========================================================================== -// -// PStatePointer :: ReadValue -// -//========================================================================== - -bool PStatePointer::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - bool res = false; - ::Serialize(ar, key, *(FState **)addr, nullptr, &res); - return res; -} - - - -/* PClassPointer **********************************************************/ - -IMPLEMENT_CLASS(PClassPointer,false, false) - -//========================================================================== -// -// PClassPointer - Parameterized Constructor -// -//========================================================================== - -PClassPointer::PClassPointer(PClass *restrict) -: PPointer(RUNTIME_CLASS(PClass)), ClassRestriction(restrict) -{ - if (restrict) mDescriptiveName.Format("ClassPointer<%s>", restrict->TypeName.GetChars()); - else mDescriptiveName = "ClassPointer"; - // class pointers do not need write barriers because all classes are stored in the global type table and won't get collected. - // This means we can use the cheapoer non-barriered opcodes here. - loadOp = OP_LOS; - storeOp = OP_SP; - mVersion = restrict->mVersion; -} - -//========================================================================== -// -// PClassPointer - isCompatible -// -//========================================================================== - -bool PClassPointer::isCompatible(PType *type) -{ - auto other = dyn_cast(type); - return (other != nullptr && other->ClassRestriction->IsDescendantOf(ClassRestriction)); -} - -//========================================================================== -// -// PClassPointer :: SetPointer -// -//========================================================================== - -void PClassPointer::SetPointer(void *base, unsigned offset, TArray *special) const -{ - // Class pointers do not get added to FlatPointers because they are released from the GC. -} - -//========================================================================== -// -// PClassPointer :: IsMatch -// -//========================================================================== - -bool PClassPointer::IsMatch(intptr_t id1, intptr_t id2) const -{ - const PType *pointat = (const PType *)id1; - const PClass *classat = (const PClass *)id2; - - assert(pointat->IsKindOf(RUNTIME_CLASS(PClass))); - return classat == ClassRestriction; -} - -//========================================================================== -// -// PClassPointer :: GetTypeIDs -// -//========================================================================== - -void PClassPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - assert(PointedType == RUNTIME_CLASS(PClass)); - id1 = (intptr_t)PointedType; - id2 = (intptr_t)ClassRestriction; -} - -//========================================================================== -// -// NewClassPointer -// -// Returns a PClassPointer for the restricted type. -// -//========================================================================== - -PClassPointer *NewClassPointer(PClass *restrict) -{ - size_t bucket; - PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PClassPointer), (intptr_t)RUNTIME_CLASS(PClass), (intptr_t)restrict, &bucket); - if (ptype == nullptr) - { - ptype = new PClassPointer(restrict); - TypeTable.AddType(ptype, RUNTIME_CLASS(PClassPointer), (intptr_t)RUNTIME_CLASS(PClass), (intptr_t)restrict, bucket); - } - return static_cast(ptype); -} - -/* PEnum ******************************************************************/ - -IMPLEMENT_CLASS(PEnum, false, false) - -//========================================================================== -// -// PEnum - Default Constructor -// -//========================================================================== - -PEnum::PEnum() -: PInt(4, false) -{ - mDescriptiveName = "Enum"; -} - -//========================================================================== -// -// PEnum - Parameterized Constructor -// -//========================================================================== - -PEnum::PEnum(FName name, PTypeBase *outer) -: PInt(4, false) -{ - EnumName = name; - Outer = outer; - mDescriptiveName.Format("Enum<%s>", name.GetChars()); -} - -//========================================================================== -// -// NewEnum -// -// Returns a PEnum for the given name and container, making sure not to -// create duplicates. -// -//========================================================================== - -PEnum *NewEnum(FName name, PTypeBase *outer) -{ - size_t bucket; - if (outer == nullptr) outer = Namespaces.GlobalNamespace; - PType *etype = TypeTable.FindType(RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, &bucket); - if (etype == nullptr) - { - etype = new PEnum(name, outer); - TypeTable.AddType(etype, RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, bucket); - } - return static_cast(etype); -} - -/* PArray *****************************************************************/ - -IMPLEMENT_CLASS(PArray, false, false) - -//========================================================================== -// -// PArray - Default Constructor -// -//========================================================================== - -PArray::PArray() -: ElementType(nullptr), ElementCount(0) -{ - mDescriptiveName = "Array"; -} - -//========================================================================== -// -// PArray - Parameterized Constructor -// -//========================================================================== - -PArray::PArray(PType *etype, unsigned int ecount) -: ElementType(etype), ElementCount(ecount) -{ - mDescriptiveName.Format("Array<%s>[%d]", etype->DescriptiveName(), ecount); - - Align = etype->Align; - // Since we are concatenating elements together, the element size should - // also be padded to the nearest alignment. - ElementSize = (etype->Size + (etype->Align - 1)) & ~(etype->Align - 1); - Size = ElementSize * ecount; -} - -//========================================================================== -// -// PArray :: IsMatch -// -//========================================================================== - -bool PArray::IsMatch(intptr_t id1, intptr_t id2) const -{ - const PType *elemtype = (const PType *)id1; - unsigned int count = (unsigned int)(intptr_t)id2; - - return elemtype == ElementType && count == ElementCount; -} - -//========================================================================== -// -// PArray :: GetTypeIDs -// -//========================================================================== - -void PArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = (intptr_t)ElementType; - id2 = ElementCount; -} - -//========================================================================== -// -// PArray :: WriteValue -// -//========================================================================== - -void PArray::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - if (ar.BeginArray(key)) - { - const uint8_t *addrb = (const uint8_t *)addr; - for (unsigned i = 0; i < ElementCount; ++i) - { - ElementType->WriteValue(ar, nullptr, addrb); - addrb += ElementSize; - } - ar.EndArray(); - } -} - -//========================================================================== -// -// PArray :: ReadValue -// -//========================================================================== - -bool PArray::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - if (ar.BeginArray(key)) - { - bool readsomething = false; - unsigned count = ar.ArraySize(); - unsigned loop = MIN(count, ElementCount); - uint8_t *addrb = (uint8_t *)addr; - for(unsigned i=0;iReadValue(ar, nullptr, addrb); - addrb += ElementSize; - } - if (loop < count) - { - DPrintf(DMSG_WARNING, "Array on disk (%u) is bigger than in memory (%u)\n", - count, ElementCount); - } - ar.EndArray(); - return readsomething; - } - return false; -} - -//========================================================================== -// -// PArray :: SetDefaultValue -// -//========================================================================== - -void PArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const -{ - for (unsigned i = 0; i < ElementCount; ++i) - { - ElementType->SetDefaultValue(base, offset + i*ElementSize, special); - } -} - -//========================================================================== -// -// PArray :: SetDefaultValue -// -//========================================================================== - -void PArray::SetPointer(void *base, unsigned offset, TArray *special) const -{ - for (unsigned i = 0; i < ElementCount; ++i) - { - ElementType->SetPointer(base, offset + i*ElementSize, special); - } -} - -//========================================================================== -// -// NewArray -// -// Returns a PArray for the given type and size, making sure not to create -// duplicates. -// -//========================================================================== - -PArray *NewArray(PType *type, unsigned int count) -{ - size_t bucket; - PType *atype = TypeTable.FindType(RUNTIME_CLASS(PArray), (intptr_t)type, count, &bucket); - if (atype == nullptr) - { - atype = new PArray(type, count); - TypeTable.AddType(atype, RUNTIME_CLASS(PArray), (intptr_t)type, count, bucket); - } - return (PArray *)atype; -} - -/* PArray *****************************************************************/ - -IMPLEMENT_CLASS(PStaticArray, false, false) - -//========================================================================== -// -// PArray - Default Constructor -// -//========================================================================== - -PStaticArray::PStaticArray() -{ - mDescriptiveName = "ResizableArray"; -} - -//========================================================================== -// -// PArray - Parameterized Constructor -// -//========================================================================== - -PStaticArray::PStaticArray(PType *etype) - : PArray(etype, 0) -{ - mDescriptiveName.Format("ResizableArray<%s>", etype->DescriptiveName()); -} - -//========================================================================== -// -// PArray :: IsMatch -// -//========================================================================== - -bool PStaticArray::IsMatch(intptr_t id1, intptr_t id2) const -{ - const PType *elemtype = (const PType *)id1; - unsigned int count = (unsigned int)(intptr_t)id2; - - return elemtype == ElementType && count == 0; -} - -//========================================================================== -// -// PArray :: GetTypeIDs -// -//========================================================================== - -void PStaticArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = (intptr_t)ElementType; - id2 = 0; -} - -//========================================================================== -// -// NewStaticArray -// -// Returns a PArray for the given type and size, making sure not to create -// duplicates. -// -//========================================================================== - -PStaticArray *NewStaticArray(PType *type) -{ - size_t bucket; - PType *atype = TypeTable.FindType(RUNTIME_CLASS(PStaticArray), (intptr_t)type, 0, &bucket); - if (atype == nullptr) - { - atype = new PStaticArray(type); - TypeTable.AddType(atype, RUNTIME_CLASS(PStaticArray), (intptr_t)type, 0, bucket); - } - return (PStaticArray *)atype; -} - -/* PDynArray **************************************************************/ - -IMPLEMENT_CLASS(PDynArray, false, false) - -//========================================================================== -// -// PDynArray - Default Constructor -// -//========================================================================== - -PDynArray::PDynArray() -: ElementType(nullptr) -{ - mDescriptiveName = "DynArray"; - Size = sizeof(FArray); - Align = alignof(FArray); -} - -//========================================================================== -// -// PDynArray - Parameterized Constructor -// -//========================================================================== - -PDynArray::PDynArray(PType *etype,PStruct *backing) -: ElementType(etype), BackingType(backing) -{ - mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName()); - Size = sizeof(FArray); - Align = alignof(FArray); -} - -//========================================================================== -// -// PDynArray :: IsMatch -// -//========================================================================== - -bool PDynArray::IsMatch(intptr_t id1, intptr_t id2) const -{ - assert(id2 == 0); - const PType *elemtype = (const PType *)id1; - - return elemtype == ElementType; -} - -//========================================================================== -// -// PDynArray :: GetTypeIDs -// -//========================================================================== - -void PDynArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = (intptr_t)ElementType; - id2 = 0; -} - -//========================================================================== -// -// PDynArray :: InitializeValue -// -//========================================================================== - -void PDynArray::InitializeValue(void *addr, const void *deff) const -{ - const FArray *def = (const FArray*)deff; - FArray *aray = (FArray*)addr; - - if (def == nullptr || def->Count == 0) - { - // Empty arrays do not need construction. - *aray = { nullptr, 0, 0 }; - } - else if (ElementType->GetRegType() != REGT_STRING) - { - // These are just integral values which can be done without any constructor hackery. - size_t blocksize = ElementType->Size * def->Count; - aray->Array = M_Malloc(blocksize); - memcpy(aray->Array, def->Array, blocksize); - aray->Most = aray->Count = def->Count; - } - else - { - // non-empty string arrays require explicit construction. - new(addr) TArray(*(TArray*)def); - } -} - -//========================================================================== -// -// PDynArray :: DestroyValue -// -//========================================================================== - -void PDynArray::DestroyValue(void *addr) const -{ - FArray *aray = (FArray*)addr; - - if (aray->Array != nullptr) - { - if (ElementType->GetRegType() != REGT_STRING) - { - M_Free(aray->Array); - } - else - { - // Damn those cursed strings again. :( - ((TArray*)addr)->~TArray(); - } - } - aray->Count = aray->Most = 0; - aray->Array = nullptr; -} - -//========================================================================== -// -// PDynArray :: SetDefaultValue -// -//========================================================================== - -void PDynArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const -{ - if (base != nullptr) memset((char*)base + offset, 0, sizeof(FArray)); // same as constructing an empty array. - if (special != nullptr) - { - special->Push(std::make_pair(this, offset)); - } -} - -//========================================================================== -// -// PDynArray :: SetPointer -// -//========================================================================== - -void PDynArray::SetPointerArray(void *base, unsigned offset, TArray *special) const -{ - if (ElementType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ElementType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && static_cast(ElementType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass))) - { - // Add to the list of pointer arrays for this class. - special->Push(offset); - } -} - -//========================================================================== -// -// PDynArray :: WriteValue -// -//========================================================================== - -void PDynArray::WriteValue(FSerializer &ar, const char *key, const void *addr) const -{ - FArray *aray = (FArray*)addr; - if (aray->Count > 0) - { - if (ar.BeginArray(key)) - { - const uint8_t *addrb = (const uint8_t *)aray->Array; - for (unsigned i = 0; i < aray->Count; ++i) - { - ElementType->WriteValue(ar, nullptr, addrb); - addrb += ElementType->Size; - } - ar.EndArray(); - } - } -} - -//========================================================================== -// -// PDynArray :: ReadValue -// -//========================================================================== - -bool PDynArray::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - FArray *aray = (FArray*)addr; - DestroyValue(addr); // note that even after calling this we still got a validly constructed empty array. - - if (ar.BeginArray(key)) - { - bool readsomething = false; - unsigned count = ar.ArraySize(); - - size_t blocksize = ElementType->Size * count; - aray->Array = M_Malloc(blocksize); - memset(aray->Array, 0, blocksize); - aray->Most = aray->Count = count; - - uint8_t *addrb = (uint8_t *)aray->Array; - for (unsigned i = 0; iGetRegType() == REGT_STRING) new(addrb) FString; - readsomething |= ElementType->ReadValue(ar, nullptr, addrb); - addrb += ElementType->Size; - } - ar.EndArray(); - return readsomething; - } - return false; -} - -//========================================================================== -// -// NewDynArray -// -// Creates a new DynArray of the given type, making sure not to create a -// duplicate. -// -//========================================================================== - -PDynArray *NewDynArray(PType *type) -{ - size_t bucket; - PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket); - if (atype == nullptr) - { - FString backingname; - - switch (type->GetRegType()) - { - case REGT_INT: - backingname.Format("DynArray_I%d", type->Size * 8); - break; - - case REGT_FLOAT: - backingname.Format("DynArray_F%d", type->Size * 8); - break; - - case REGT_STRING: - backingname = "DynArray_String"; - break; - - case REGT_POINTER: - backingname = "DynArray_Ptr"; - break; - - default: - I_Error("Unsupported dynamic array requested"); - break; - } - - auto backing = NewNativeStruct(backingname, nullptr); - atype = new PDynArray(type, backing); - TypeTable.AddType(atype, RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, bucket); - } - return (PDynArray *)atype; -} - -/* PMap *******************************************************************/ - -IMPLEMENT_CLASS(PMap, false, false) - -//========================================================================== -// -// PMap - Default Constructor -// -//========================================================================== - -PMap::PMap() -: KeyType(nullptr), ValueType(nullptr) -{ - mDescriptiveName = "Map"; - Size = sizeof(FMap); - Align = alignof(FMap); -} - -//========================================================================== -// -// PMap - Parameterized Constructor -// -//========================================================================== - -PMap::PMap(PType *keytype, PType *valtype) -: KeyType(keytype), ValueType(valtype) -{ - mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName()); - Size = sizeof(FMap); - Align = alignof(FMap); -} - -//========================================================================== -// -// PMap :: IsMatch -// -//========================================================================== - -bool PMap::IsMatch(intptr_t id1, intptr_t id2) const -{ - const PType *keyty = (const PType *)id1; - const PType *valty = (const PType *)id2; - - return keyty == KeyType && valty == ValueType; -} - -//========================================================================== -// -// PMap :: GetTypeIDs -// -//========================================================================== - -void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = (intptr_t)KeyType; - id2 = (intptr_t)ValueType; -} - -//========================================================================== -// -// NewMap -// -// Returns a PMap for the given key and value types, ensuring not to create -// duplicates. -// -//========================================================================== - -PMap *NewMap(PType *keytype, PType *valuetype) -{ - size_t bucket; - PType *maptype = TypeTable.FindType(RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, &bucket); - if (maptype == nullptr) - { - maptype = new PMap(keytype, valuetype); - TypeTable.AddType(maptype, RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, bucket); - } - return (PMap *)maptype; -} - -/* PStruct ****************************************************************/ - -IMPLEMENT_CLASS(PStruct, false, false) - -//========================================================================== -// -// PStruct - Default Constructor -// -//========================================================================== - -PStruct::PStruct() -{ - mDescriptiveName = "Struct"; - Size = 0; -} - -//========================================================================== -// -// PStruct - Parameterized Constructor -// -//========================================================================== - -PStruct::PStruct(FName name, PTypeBase *outer) -: PNamedType(name, outer) -{ - mDescriptiveName.Format("Struct<%s>", name.GetChars()); - Size = 0; - HasNativeFields = false; -} - -//========================================================================== -// -// PStruct :: SetDefaultValue -// -//========================================================================== - -void PStruct::SetDefaultValue(void *base, unsigned offset, TArray *special) const -{ - for (const PField *field : Fields) - { - if (!(field->Flags & VARF_Transient)) - { - field->Type->SetDefaultValue(base, unsigned(offset + field->Offset), special); - } - } -} - -//========================================================================== -// -// PStruct :: SetPointer -// -//========================================================================== - -void PStruct::SetPointer(void *base, unsigned offset, TArray *special) const -{ - for (const PField *field : Fields) - { - if (!(field->Flags & VARF_Transient)) - { - field->Type->SetPointer(base, unsigned(offset + field->Offset), special); - } - } -} - -//========================================================================== -// -// PStruct :: WriteValue -// -//========================================================================== - -void PStruct::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - if (ar.BeginObject(key)) - { - WriteFields(ar, addr, Fields); - ar.EndObject(); - } -} - -//========================================================================== -// -// PStruct :: ReadValue -// -//========================================================================== - -bool PStruct::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - if (ar.BeginObject(key)) - { - bool ret = ReadFields(ar, addr); - ar.EndObject(); - return ret; - } - return false; -} - -//========================================================================== -// -// PStruct :: WriteFields STATIC -// -//========================================================================== - -void PStruct::WriteFields(FSerializer &ar, const void *addr, const TArray &fields) -{ - for (unsigned i = 0; i < fields.Size(); ++i) - { - const PField *field = fields[i]; - // Skip fields without or with native serialization - if (!(field->Flags & (VARF_Transient|VARF_Meta))) - { - field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const uint8_t *)addr + field->Offset); - } - } -} - -//========================================================================== -// -// PStruct :: ReadFields -// -//========================================================================== - -bool PStruct::ReadFields(FSerializer &ar, void *addr) const -{ - bool readsomething = false; - bool foundsomething = false; - const char *label; - while ((label = ar.GetKey())) - { - foundsomething = true; - - const PSymbol *sym = Symbols.FindSymbol(FName(label, true), true); - if (sym == nullptr) - { - DPrintf(DMSG_ERROR, "Cannot find field %s in %s\n", - label, TypeName.GetChars()); - } - else if (!sym->IsKindOf(RUNTIME_CLASS(PField))) - { - DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n", - label, TypeName.GetChars()); - } - else if ((static_cast(sym)->Flags & (VARF_Transient | VARF_Meta))) - { - DPrintf(DMSG_ERROR, "Symbol %s in %s is not a serializable field\n", - label, TypeName.GetChars()); - } - else - { - readsomething |= static_cast(sym)->Type->ReadValue(ar, nullptr, - (uint8_t *)addr + static_cast(sym)->Offset); - } - } - return readsomething || !foundsomething; -} - -//========================================================================== -// -// PStruct :: AddField -// -// Appends a new field to the end of a struct. Returns either the new field -// or nullptr if a symbol by that name already exists. -// -//========================================================================== - -PField *PStruct::AddField(FName name, PType *type, uint32_t flags) -{ - 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. - Align = MAX(Align, type->Align); - - if (Symbols.AddSymbol(field) == nullptr) - { // name is already in use - field->Destroy(); - return nullptr; - } - Fields.Push(field); - - 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 *PStruct::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 (Symbols.AddSymbol(field) == nullptr) - { // name is already in use - field->Destroy(); - return nullptr; - } - Fields.Push(field); - HasNativeFields = true; - return field; -} - -//========================================================================== -// -// NewStruct -// Returns a PStruct for the given name and container, making sure not to -// create duplicates. -// -//========================================================================== - -PStruct *NewStruct(FName name, PTypeBase *outer) -{ - size_t bucket; - if (outer == nullptr) outer = Namespaces.GlobalNamespace; - PType *stype = TypeTable.FindType(RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, &bucket); - if (stype == nullptr) - { - stype = new PStruct(name, outer); - TypeTable.AddType(stype, RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, bucket); - } - return static_cast(stype); -} - -/* PNativeStruct ****************************************************************/ - -IMPLEMENT_CLASS(PNativeStruct, false, false) - -//========================================================================== -// -// PNativeStruct - Parameterized Constructor -// -//========================================================================== - -PNativeStruct::PNativeStruct(FName name, PTypeBase *outer) - : PStruct(name, outer) -{ - mDescriptiveName.Format("NativeStruct<%s>", name.GetChars()); - Size = 0; - HasNativeFields = true; -} - -//========================================================================== -// -// NewNativeStruct -// Returns a PNativeStruct for the given name and container, making sure not to -// create duplicates. -// -//========================================================================== - -PNativeStruct *NewNativeStruct(FName name, PTypeBase *outer) -{ - size_t bucket; - if (outer == nullptr) outer = Namespaces.GlobalNamespace; - PType *stype = TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, &bucket); - if (stype == nullptr) - { - stype = new PNativeStruct(name, outer); - TypeTable.AddType(stype, RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, bucket); - } - return static_cast(stype); -} - -/* 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->IsA(RUNTIME_CLASS(PInt)) && 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 &fields) - : PSymbol(name) -{ - Variables = std::move(fields); -} - -/* PPrototype *************************************************************/ - -IMPLEMENT_CLASS(PPrototype, false, false) - -//========================================================================== -// -// PPrototype - Default Constructor -// -//========================================================================== - -PPrototype::PPrototype() -{ -} - -//========================================================================== -// -// PPrototype - Parameterized Constructor -// -//========================================================================== - -PPrototype::PPrototype(const TArray &rettypes, const TArray &argtypes) -: ArgumentTypes(argtypes), ReturnTypes(rettypes) -{ -} - -//========================================================================== -// -// PPrototype :: IsMatch -// -//========================================================================== - -bool PPrototype::IsMatch(intptr_t id1, intptr_t id2) const -{ - const TArray *args = (const TArray *)id1; - const TArray *rets = (const TArray *)id2; - - return *args == ArgumentTypes && *rets == ReturnTypes; -} - -//========================================================================== -// -// PPrototype :: GetTypeIDs -// -//========================================================================== - -void PPrototype::GetTypeIDs(intptr_t &id1, intptr_t &id2) const -{ - id1 = (intptr_t)&ArgumentTypes; - id2 = (intptr_t)&ReturnTypes; -} - -//========================================================================== -// -// PPrototype :: PropagateMark -// -//========================================================================== - -size_t PPrototype::PropagateMark() -{ - GC::MarkArray(ArgumentTypes); - GC::MarkArray(ReturnTypes); - return (ArgumentTypes.Size() + ReturnTypes.Size()) * sizeof(void*) + - Super::PropagateMark(); -} - -//========================================================================== -// -// NewPrototype -// -// Returns a PPrototype for the given return and argument types, making sure -// not to create duplicates. -// -//========================================================================== - -PPrototype *NewPrototype(const TArray &rettypes, const TArray &argtypes) -{ - size_t bucket; - PType *proto = TypeTable.FindType(RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, &bucket); - if (proto == nullptr) - { - proto = new PPrototype(rettypes, argtypes); - TypeTable.AddType(proto, RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, bucket); - } - return static_cast(proto); -} - -/* PClass *****************************************************************/ - -IMPLEMENT_CLASS(PClass, false, false) - //========================================================================== // // PClass :: WriteValue @@ -2705,7 +108,7 @@ static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void * key.Format("class:%s", type->TypeName.GetChars()); if (ar.BeginObject(key.GetChars())) { - PStruct::WriteFields(ar, addr, type->Fields); + type->VMType->Symbols.WriteFields(ar, addr); ar.EndObject(); } break; @@ -2714,15 +117,6 @@ static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void * } } -void PClass::WriteValue(FSerializer &ar, const char *key,const void *addr) const -{ - if (ar.BeginObject(key)) - { - RecurseWriteFields(this, ar, addr); - ar.EndObject(); - } -} - // 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 @@ -2732,21 +126,10 @@ void PClass::WriteAllFields(FSerializer &ar, const void *addr) const //========================================================================== // -// PClass :: ReadValue +// PClass :: ReadAllFields // //========================================================================== -bool PClass::ReadValue(FSerializer &ar, const char *key, void *addr) const -{ - if (ar.BeginObject(key)) - { - bool ret = ReadAllFields(ar, addr); - ar.EndObject(); - return ret; - } - return true; -} - bool PClass::ReadAllFields(FSerializer &ar, void *addr) const { bool readsomething = false; @@ -2772,19 +155,11 @@ bool PClass::ReadAllFields(FSerializer &ar, void *addr) const if (type != nullptr) { // Only read it if the type is related to this one. - const PClass *parent; - for (parent = this; parent != nullptr; parent = parent->ParentClass) - { - if (parent == type) - { - break; - } - } - if (parent != nullptr) + if (IsDescendantOf(type)) { if (ar.BeginObject(nullptr)) { - readsomething |= type->ReadFields(ar, addr); + readsomething |= type->VMType->Symbols.ReadFields(ar, addr, type->TypeName.GetChars()); ar.EndObject(); } } @@ -2844,15 +219,6 @@ void PClass::StaticInit () // I'm not sure if this is really necessary to maintain any sort of sync. qsort(&AllClasses[0], AllClasses.Size(), sizeof(AllClasses[0]), cregcmp); - // Set all symbol table relations here so that they are valid right from the start. - for (auto c : AllClasses) - { - if (c->ParentClass != nullptr) - { - c->Symbols.SetParentTable(&c->ParentClass->Symbols); - } - } - } //========================================================================== @@ -2888,6 +254,10 @@ void PClass::StaticShutdown () p.PendingWeapon = nullptr; } + // 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) 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(); @@ -2914,16 +284,6 @@ void PClass::StaticShutdown () void PClass::StaticBootstrap() { - PClass *cls = new PClass; - PClass::RegistrationInfo.SetupClass(cls); - - // The PClassClass constructor initialized these to nullptr, because the - // PClass metadata had not been created yet. Now it has, so we know what - // they should be and can insert them into the type table successfully. - cls->InsertIntoHash(); - - // Create parent objects before we go so that these definitions are complete. - cls->ParentClass = PClass::RegistrationInfo.ParentType->RegisterClass(); } //========================================================================== @@ -2934,20 +294,6 @@ void PClass::StaticBootstrap() PClass::PClass() { - Size = sizeof(DObject); - ParentClass = nullptr; - Pointers = nullptr; - FlatPointers = nullptr; - ArrayPointers = nullptr; - HashNext = nullptr; - Defaults = nullptr; - bRuntimeClass = false; - bExported = false; - bDecorateClass = false; - ConstructNative = nullptr; - Meta = nullptr; - mDescriptiveName = "Class"; - PClass::AllClasses.Push(this); } @@ -2982,12 +328,6 @@ PClass::~PClass() PClass *ClassReg::RegisterClass() { - static ClassReg *const metaclasses[] = - { - &PClass::RegistrationInfo, - &PClassActor::RegistrationInfo, - }; - // Skip classes that have already been registered if (MyClass != nullptr) { @@ -2995,18 +335,7 @@ PClass *ClassReg::RegisterClass() } // Add type to list - PClass *cls; - - if (MetaClassNum >= countof(metaclasses)) - { - assert(0 && "Class registry has an invalid meta class identifier"); - } - - if (metaclasses[MetaClassNum]->MyClass == nullptr) - { // Make sure the meta class is already registered before registering this one - metaclasses[MetaClassNum]->RegisterClass(); - } - cls = static_cast(metaclasses[MetaClassNum]->MyClass->CreateNew()); + PClass *cls = new PClass; SetupClass(cls); cls->InsertIntoHash(); @@ -3034,7 +363,7 @@ void ClassReg::SetupClass(PClass *cls) cls->Size = SizeOf; cls->Pointers = Pointers; cls->ConstructNative = ConstructNative; - cls->mDescriptiveName.Format("Class<%s>", cls->TypeName.GetChars()); + //cls->mDescriptiveName.Format("Class<%s>", cls->TypeName.GetChars()); } //========================================================================== @@ -3047,17 +376,18 @@ void ClassReg::SetupClass(PClass *cls) void PClass::InsertIntoHash () { - size_t bucket; - PType *found; - - found = TypeTable.FindType(RUNTIME_CLASS(PClass), 0, TypeName, &bucket); - if (found != nullptr) + 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 { - TypeTable.AddType(this, RUNTIME_CLASS(PClass), 0, TypeName, bucket); + ClassMap[TypeName] = this; + } + if (IsDescendantOf(RUNTIME_CLASS(AActor))) + { + PClassActor::AllActorClasses.Push(static_cast(this)); } } @@ -3095,7 +425,8 @@ PClass *PClass::FindClass (FName zaname) { return nullptr; } - return static_cast(TypeTable.FindType(RUNTIME_CLASS(PClass), 0, zaname, nullptr)); + auto k = ClassMap.CheckKey(zaname); + return k ? *k : nullptr; } //========================================================================== @@ -3162,8 +493,6 @@ void PClass::InitializeSpecials(void *addr, void *defaults, TArrayDestroyMeta(addr); + for (auto tao : MetaInits) + { + tao.first->DestroyValue((uint8_t *)addr + tao.second); + } +} + //========================================================================== // // PClass :: Derive @@ -3189,10 +535,7 @@ void PClass::Derive(PClass *newclass, FName name) newclass->bRuntimeClass = true; newclass->ParentClass = this; newclass->ConstructNative = ConstructNative; - newclass->Symbols.SetParentTable(&this->Symbols); newclass->TypeName = name; - newclass->mDescriptiveName.Format("Class<%s>", name.GetChars()); - newclass->mVersion = mVersion; newclass->MetaSize = MetaSize; } @@ -3205,7 +548,7 @@ void PClass::Derive(PClass *newclass, FName name) void PClass::InitializeDefaults() { - if (IsKindOf(RUNTIME_CLASS(PClassActor))) + if (IsDescendantOf(RUNTIME_CLASS(AActor))) { assert(Defaults == nullptr); Defaults = (uint8_t *)M_Malloc(Size); @@ -3260,40 +603,30 @@ void PClass::InitializeDefaults() } } - if (bRuntimeClass) + if (VMType != nullptr) // purely internal classes have no symbol table { - // Copy parent values from the parent defaults. - assert(ParentClass != nullptr); - if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults, &PClass::SpecialInits); + if (bRuntimeClass) + { + // Copy parent values from the parent defaults. + assert(ParentClass != nullptr); + if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults, &PClass::SpecialInits); + for (const PField *field : Fields) + { + if (!(field->Flags & VARF_Native) && !(field->Flags & VARF_Meta)) + { + field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits); + } + } + } if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits); for (const PField *field : Fields) { - if (!(field->Flags & VARF_Native) && !(field->Flags & VARF_Meta)) + if (!(field->Flags & VARF_Native) && (field->Flags & VARF_Meta)) { - field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits); + field->Type->SetDefaultValue(Meta, unsigned(field->Offset), &MetaInits); } } } - if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits); - for (const PField *field : Fields) - { - if (!(field->Flags & VARF_Native) && (field->Flags & VARF_Meta)) - { - field->Type->SetDefaultValue(Meta, unsigned(field->Offset), &MetaInits); - } - } -} - -//========================================================================== -// -// PClass :: DeriveData -// -// Copies inheritable data to the child class. -// -//========================================================================== - -void PClass::DeriveData(PClass *newclass) -{ } //========================================================================== @@ -3333,7 +666,7 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) } else { - type = static_cast(GetClass()->CreateNew()); + type = new PClass; notnew = false; } @@ -3343,12 +676,12 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) type->Size = size; if (size != TentativeClass) { + NewClassType(type); type->InitializeDefaults(); type->Virtuals = Virtuals; - DeriveData(type); } else - type->ObjectFlags &= OF_Transient; + type->bOptional = false; if (!notnew) { @@ -3357,39 +690,6 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size) return type; } -//========================================================================== -// -// PStruct :: AddField -// -// Appends a new metadata field to the end of a struct. Returns either the new field -// or nullptr if a symbol by that name already exists. -// -//========================================================================== - -PField *PClass::AddMetaField(FName name, PType *type, uint32_t flags) -{ - PField *field = new PField(name, type, flags); - - // The new field is added to the end of this struct, alignment permitting. - field->Offset = (MetaSize + (type->Align - 1)) & ~(type->Align - 1); - - // Enlarge this struct to enclose the new field. - MetaSize = unsigned(field->Offset + type->Size); - - // This struct's alignment is the same as the largest alignment of any of - // its fields. - Align = MAX(Align, type->Align); - - if (Symbols.AddSymbol(field) == nullptr) - { // name is already in use - field->Destroy(); - return nullptr; - } - Fields.Push(field); - - return field; -} - //========================================================================== // // PClass :: AddField @@ -3398,10 +698,11 @@ PField *PClass::AddMetaField(FName name, PType *type, uint32_t flags) PField *PClass::AddField(FName name, PType *type, uint32_t flags) { + PField *field; if (!(flags & VARF_Meta)) { unsigned oldsize = Size; - PField *field = Super::AddField(name, type, flags); + 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 @@ -3411,23 +712,21 @@ PField *PClass::AddField(FName name, PType *type, uint32_t flags) Defaults = (uint8_t *)M_Realloc(Defaults, Size); memset(Defaults + oldsize, 0, Size - oldsize); } - return field; } else { + // Same as above, but a different data storage. unsigned oldsize = MetaSize; - PField *field = AddMetaField(name, type, flags); + field = VMType->Symbols.AddField(name, type, flags, MetaSize); - // 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) && Meta != nullptr) { Meta = (uint8_t *)M_Realloc(Meta, MetaSize); memset(Meta + oldsize, 0, MetaSize - oldsize); } - return field; } + if (field != nullptr) Fields.Push(field); + return field; } //========================================================================== @@ -3445,21 +744,17 @@ PClass *PClass::FindClassTentative(FName name) { return nullptr; } - size_t bucket; - PType *found = TypeTable.FindType(RUNTIME_CLASS(PClass), - /*FIXME:Outer*/0, name, &bucket); + PClass *found = FindClass(name); + if (found != nullptr) return found; - if (found != nullptr) - { - return static_cast(found); - } - PClass *type = static_cast(GetClass()->CreateNew()); + PClass *type = new PClass; DPrintf(DMSG_SPAMMY, "Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars()); Derive(type, name); type->Size = TentativeClass; - TypeTable.AddType(type, RUNTIME_CLASS(PClass), 0, name, bucket); + + InsertIntoHash(); return type; } @@ -3510,6 +805,12 @@ int PClass::FindVirtualIndex(FName name, PPrototype *proto) return -1; } +PSymbol *PClass::FindSymbol(FName symname, bool searchparents) const +{ + if (VMType == nullptr) return nullptr; + return VMType->Symbols.FindSymbol(symname, searchparents); +} + //========================================================================== // // PClass :: BuildFlatPointers @@ -3678,7 +979,7 @@ VMFunction *PClass::FindFunction(FName clsname, FName funcname) { auto cls = PClass::FindClass(clsname); if (!cls) return nullptr; - auto func = dyn_cast(cls->Symbols.FindSymbol(funcname, true)); + auto func = dyn_cast(cls->FindSymbol(funcname, true)); if (!func) return nullptr; return func->Variants[0].Implementation; } @@ -3687,139 +988,18 @@ void PClass::FindFunction(VMFunction **pptr, FName clsname, FName funcname) { auto cls = PClass::FindClass(clsname); if (!cls) return; - auto func = dyn_cast(cls->Symbols.FindSymbol(funcname, true)); + auto func = dyn_cast(cls->FindSymbol(funcname, true)); if (!func) return; *pptr = func->Variants[0].Implementation; FunctionPtrList.Push(pptr); } - -/* FTypeTable **************************************************************/ - -//========================================================================== -// -// FTypeTable :: FindType -// -//========================================================================== - -PType *FTypeTable::FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, size_t *bucketnum) +unsigned GetVirtualIndex(PClass *cls, const char *funcname) { - size_t bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; - if (bucketnum != nullptr) - { - *bucketnum = bucket; - } - for (PType *type = TypeHash[bucket]; type != nullptr; type = type->HashNext) - { - if (type->TypeTableType == metatype && type->IsMatch(parm1, parm2)) - { - return type; - } - } - return nullptr; -} - -//========================================================================== -// -// FTypeTable :: AddType - Fully Parameterized Version -// -//========================================================================== - -void FTypeTable::AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t parm2, size_t bucket) -{ -#ifdef _DEBUG - size_t bucketcheck; - assert(FindType(metatype, parm1, parm2, &bucketcheck) == nullptr && "Type must not be inserted more than once"); - assert(bucketcheck == bucket && "Passed bucket was wrong"); -#endif - type->TypeTableType = metatype; - type->HashNext = TypeHash[bucket]; - TypeHash[bucket] = type; - type->Release(); -} - -//========================================================================== -// -// FTypeTable :: AddType - Simple Version -// -//========================================================================== - -void FTypeTable::AddType(PType *type) -{ - intptr_t parm1, parm2; - size_t bucket; - - // Type table stuff id only needed to let all classes hash to the same group. For all other types this is pointless. - type->TypeTableType = type->GetClass(); - PClass *metatype = type->TypeTableType; - type->GetTypeIDs(parm1, parm2); - bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; - assert(FindType(metatype, parm1, parm2, nullptr) == nullptr && "Type must not be inserted more than once"); - - type->HashNext = TypeHash[bucket]; - TypeHash[bucket] = type; - type->Release(); -} - -//========================================================================== -// -// FTypeTable :: Hash STATIC -// -//========================================================================== - -size_t FTypeTable::Hash(const PClass *p1, intptr_t p2, intptr_t p3) -{ - size_t i1 = (size_t)p1; - - // Swap the high and low halves of i1. The compiler should be smart enough - // to transform this into a ROR or ROL. - i1 = (i1 >> (sizeof(size_t)*4)) | (i1 << (sizeof(size_t)*4)); - - if (p1 != RUNTIME_CLASS(PPrototype)) - { - size_t i2 = (size_t)p2; - size_t i3 = (size_t)p3; - return (~i1 ^ i2) + i3 * 961748927; // i3 is multiplied by a prime - } - else - { // Prototypes need to hash the TArrays at p2 and p3 - const TArray *a2 = (const TArray *)p2; - const TArray *a3 = (const TArray *)p3; - for (unsigned i = 0; i < a2->Size(); ++i) - { - i1 = (i1 * 961748927) + (size_t)((*a2)[i]); - } - for (unsigned i = 0; i < a3->Size(); ++i) - { - i1 = (i1 * 961748927) + (size_t)((*a3)[i]); - } - return i1; - } -} - -//========================================================================== -// -// FTypeTable :: Clear -// -//========================================================================== - -void FTypeTable::Clear() -{ - for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) - { - for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr;) - { - auto next = ty->HashNext; - delete ty; - ty = next; - } - } - memset(TypeHash, 0, sizeof(TypeHash)); -} - -#include "c_dispatch.h" -CCMD(typetable) -{ - DumpTypeTable(); + // 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(cls->FindSymbol(funcname, false)); + assert(sym != nullptr); + auto VIndex = sym->Variants[0].Implementation->VirtualIndex; + return VIndex; } diff --git a/src/dobjtype.h b/src/dobjtype.h index bf6e9feb1..24cd43451 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -5,50 +5,17 @@ #error You must #include "dobject.h" to get dobjtype.h #endif -typedef std::pair FTypeAndOffset; -class PStruct; +#include "memarena.h" +typedef std::pair 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); }; - - -#include "vm.h" - -// Variable/parameter/field flags ------------------------------------------- - -// Making all these different storage types use a common set of flags seems -// like the simplest thing to do. - -enum -{ - VARF_Optional = (1<<0), // func param is optional - VARF_Method = (1<<1), // func has an implied self parameter - VARF_Action = (1<<2), // func has implied owner and state parameters - VARF_Native = (1<<3), // func is native code, field is natively defined - VARF_ReadOnly = (1<<4), // field is read only, do not write to it - VARF_Private = (1<<5), // field is private to containing class - VARF_Protected = (1<<6), // field is only accessible by containing class and children. - VARF_Deprecated = (1<<7), // Deprecated fields should output warnings when used. - VARF_Virtual = (1<<8), // function is virtual - VARF_Final = (1<<9), // Function may not be overridden in subclasses - VARF_In = (1<<10), - VARF_Out = (1<<11), - VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures) - VARF_Static = (1<<13), - VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code. - VARF_Override = (1<<15), // overrides a virtual function from the parent class. - VARF_Ref = (1<<16), // argument is passed by reference. - VARF_Transient = (1<<17), // don't auto serialize field. - VARF_Meta = (1<<18), // static class data (by necessity read only.) - VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature - VARF_UI = (1<<20), // [ZZ] ui: object is ui-scope only (can't modify playsim) - VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui) - VARF_VirtualScope = (1<<22), // [ZZ] virtualscope: object should use the scope of the particular class it's being used with (methods only) - VARF_ClearScope = (1<<23), // [ZZ] clearscope: this method ignores the member access chain that leads to it and is always plain data. -}; +#endif // An action function ------------------------------------------------------- @@ -58,567 +25,55 @@ class VMFrameStack; struct VMValue; struct VMReturn; class VMFunction; +class PClassType; struct FNamespaceManager; -// Basic information shared by all types ------------------------------------ - -// Only one copy of a type is ever instantiated at one time. -// - Enums, classes, and structs are defined by their names and outer classes. -// - Pointers are uniquely defined by the type they point at. -// - ClassPointers are also defined by their class restriction. -// - Arrays are defined by their element type and count. -// - DynArrays are defined by their element type. -// - Maps are defined by their key and value types. -// - Prototypes are defined by the argument and return types. -// - Functions are defined by their names and outer objects. -// In table form: -// Outer Name Type Type2 Count -// Enum * * -// Class * * -// Struct * * -// Function * * -// Pointer * -// ClassPointer + * -// Array * * -// DynArray * -// Map * * -// Prototype *+ *+ - -struct ZCC_ExprConstant; -class PType : public PTypeBase -{ - DECLARE_ABSTRACT_CLASS(PType, PTypeBase) -protected: - -public: - PClass *TypeTableType; // The type to use for hashing into the type table - unsigned int Size; // this type's size - unsigned int Align; // this type's preferred alignment - PType *HashNext; // next type in this type table - PSymbolTable Symbols; - bool MemberOnly = false; // type may only be used as a struct/class member but not as a local variable or function argument. - FString mDescriptiveName; - VersionInfo mVersion = { 0,0,0 }; - uint8_t loadOp, storeOp, moveOp, RegType, RegCount; - - PType(unsigned int size = 1, unsigned int align = 1); - virtual ~PType(); - virtual bool isNumeric() { return false; } - - // Writes the value of a variable of this type at (addr) to an archive, preceded by - // a tag indicating its type. The tag is there so that variable types can be changed - // without completely breaking savegames, provided that the change isn't between - // totally unrelated types. - virtual void WriteValue(FSerializer &ar, const char *key,const void *addr) const; - - // Returns true if the stored value was compatible. False otherwise. - // If the value was incompatible, then the memory at *addr is unchanged. - virtual bool ReadValue(FSerializer &ar, const char *key,void *addr) const; - - // Sets the default value for this type at (base + offset) - // If the default value is binary 0, then this function doesn't need - // to do anything, because PClass::Extend() takes care of that. - // - // The stroffs array is so that types that need special initialization - // and destruction (e.g. strings) can add their offsets to it for special - // initialization when the object is created and destruction when the - // object is destroyed. - virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const; - virtual void SetPointer(void *base, unsigned offset, TArray *ptrofs = NULL) const; - virtual void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const; - - // Initialize the value, if needed (e.g. strings) - virtual void InitializeValue(void *addr, const void *def) const; - - // Destroy the value, if needed (e.g. strings) - virtual void DestroyValue(void *addr) const; - - // Sets the value of a variable of this type at (addr) - virtual void SetValue(void *addr, int val); - virtual void SetValue(void *addr, double val); - - // Gets the value of a variable of this type at (addr) - virtual int GetValueInt(void *addr) const; - virtual double GetValueFloat(void *addr) const; - - // Gets the opcode to store from a register to memory - int GetStoreOp() const - { - return storeOp; - } - - // Gets the opcode to load from memory to a register - int GetLoadOp() const - { - return loadOp; - } - - // Gets the opcode to move from register to another register - int GetMoveOp() const - { - return moveOp; - } - - // Gets the register type for this type - int GetRegType() const - { - return RegType; - } - - int GetRegCount() const - { - return RegCount; - } - // Returns true if this type matches the two identifiers. Referring to the - // above table, any type is identified by at most two characteristics. Each - // type that implements this function will cast these to the appropriate type. - // It is up to the caller to make sure they are the correct types. There is - // only one prototype for this function in order to simplify type table - // management. - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - - // Get the type IDs used by IsMatch - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; - - const char *DescriptiveName() const; - - static void StaticInit(); -}; - -// Not-really-a-type types -------------------------------------------------- - -class PErrorType : public PType -{ - DECLARE_CLASS(PErrorType, PType); -public: - PErrorType(int which = 1) : PType(0, which) {} -}; - -class PVoidType : public PType -{ - DECLARE_CLASS(PVoidType, PType); -public: - PVoidType() : PType(0, 1) {} -}; - -// Some categorization typing ----------------------------------------------- - -class PBasicType : public PType -{ - DECLARE_ABSTRACT_CLASS(PBasicType, PType); -public: - PBasicType(); - PBasicType(unsigned int size, unsigned int align); -}; - -class PCompoundType : public PType -{ - DECLARE_ABSTRACT_CLASS(PCompoundType, PType); -}; - -class PNamedType : public PCompoundType -{ - DECLARE_ABSTRACT_CLASS(PNamedType, PCompoundType); -public: - PTypeBase *Outer; // object this type is contained within - FName TypeName; // this type's name - - PNamedType() : Outer(NULL) { - mDescriptiveName = "NamedType"; - } - PNamedType(FName name, PTypeBase *outer) : Outer(outer), TypeName(name) { - mDescriptiveName = name.GetChars(); - } - - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; -}; - -// Basic types -------------------------------------------------------------- - -class PInt : public PBasicType -{ - DECLARE_CLASS(PInt, PBasicType); -public: - PInt(unsigned int size, bool unsign, bool compatible = true); - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; - - virtual void SetValue(void *addr, int val); - virtual void SetValue(void *addr, double val); - virtual int GetValueInt(void *addr) const; - virtual double GetValueFloat(void *addr) const; - virtual bool isNumeric() override { return IntCompatible; } - - bool Unsigned; - bool IntCompatible; -protected: - PInt(); - void SetOps(); -}; - -class PBool : public PInt -{ - DECLARE_CLASS(PBool, PInt); -public: - PBool(); - virtual void SetValue(void *addr, int val); - virtual void SetValue(void *addr, double val); - virtual int GetValueInt(void *addr) const; - virtual double GetValueFloat(void *addr) const; -}; - -class PFloat : public PBasicType -{ - DECLARE_CLASS(PFloat, PBasicType); -public: - PFloat(unsigned int size); - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; - - virtual void SetValue(void *addr, int val); - virtual void SetValue(void *addr, double val); - virtual int GetValueInt(void *addr) const; - virtual double GetValueFloat(void *addr) const; - virtual bool isNumeric() override { return true; } -protected: - PFloat(); - void SetOps(); -private: - struct SymbolInitF - { - ENamedName Name; - double Value; - }; - struct SymbolInitI - { - ENamedName Name; - int Value; - }; - - void SetSingleSymbols(); - void SetDoubleSymbols(); - void SetSymbols(const SymbolInitF *syminit, size_t count); - void SetSymbols(const SymbolInitI *syminit, size_t count); -}; - -class PString : public PBasicType -{ - DECLARE_CLASS(PString, PBasicType); -public: - PString(); - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; - void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const override; - void InitializeValue(void *addr, const void *def) const override; - void DestroyValue(void *addr) const override; -}; - -// Variations of integer types ---------------------------------------------- - -class PName : public PInt -{ - DECLARE_CLASS(PName, PInt); -public: - PName(); - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; -}; - -class PSound : public PInt -{ - DECLARE_CLASS(PSound, PInt); -public: - PSound(); - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; -}; - -class PSpriteID : public PInt -{ - DECLARE_CLASS(PSpriteID, PInt); -public: - PSpriteID(); - - void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; -}; - -class PTextureID : public PInt -{ - DECLARE_CLASS(PTextureID, PInt); -public: - PTextureID(); - - void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; -}; - -class PColor : public PInt -{ - DECLARE_CLASS(PColor, PInt); -public: - PColor(); -}; - -class PStateLabel : public PInt -{ - DECLARE_CLASS(PStateLabel, PInt); -public: - PStateLabel(); -}; - -// Pointers ----------------------------------------------------------------- - -class PPointer : public PBasicType -{ - DECLARE_CLASS(PPointer, PBasicType); - -public: - typedef void(*WriteHandler)(FSerializer &ar, const char *key, const void *addr); - typedef bool(*ReadHandler)(FSerializer &ar, const char *key, void *addr); - - PPointer(); - PPointer(PType *pointsat, bool isconst = false); - - PType *PointedType; - bool IsConst; - - WriteHandler writer = nullptr; - ReadHandler reader = nullptr; - - void InstallHandlers(WriteHandler w, ReadHandler r) - { - writer = w; - reader = r; - } - - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; - void SetPointer(void *base, unsigned offset, TArray *special = NULL) const override; - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; - -protected: - void SetOps(); -}; - -class PStatePointer : public PPointer -{ - DECLARE_CLASS(PStatePointer, PPointer); -public: - PStatePointer(); - - void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; -}; - - -class PClassPointer : public PPointer -{ - DECLARE_CLASS(PClassPointer, PPointer); -public: - PClassPointer(class PClass *restrict = nullptr); - - class PClass *ClassRestriction; - - bool isCompatible(PType *type); - - void SetPointer(void *base, unsigned offset, TArray *special = NULL) const override; - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; -}; - -// Compound types ----------------------------------------------------------- - -class PEnum : public PInt -{ - DECLARE_CLASS(PEnum, PInt); -public: - PEnum(FName name, PTypeBase *outer); - - PTypeBase *Outer; - FName EnumName; -protected: - PEnum(); -}; - -class PArray : public PCompoundType -{ - DECLARE_CLASS(PArray, PCompoundType); -public: - PArray(PType *etype, unsigned int ecount); - - PType *ElementType; - unsigned int ElementCount; - unsigned int ElementSize; - - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; - - void SetDefaultValue(void *base, unsigned offset, TArray *special) const override; - void SetPointer(void *base, unsigned offset, TArray *special) const override; - -protected: - PArray(); -}; - -class PStaticArray : public PArray -{ - DECLARE_CLASS(PStaticArray, PArray); -public: - PStaticArray(PType *etype); - - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; - -protected: - PStaticArray(); -}; - -class PDynArray : public PCompoundType -{ - DECLARE_CLASS(PDynArray, PCompoundType); -public: - PDynArray(PType *etype, PStruct *backing); - - PType *ElementType; - PStruct *BackingType; - - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; - - void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; - void SetDefaultValue(void *base, unsigned offset, TArray *specials) const override; - void InitializeValue(void *addr, const void *def) const override; - void DestroyValue(void *addr) const override; - void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const override; - -protected: - PDynArray(); -}; - -class PMap : public PCompoundType -{ - DECLARE_CLASS(PMap, PCompoundType); -public: - PMap(PType *keytype, PType *valtype); - - PType *KeyType; - PType *ValueType; - - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; -protected: - PMap(); -}; - -class PStruct : public PNamedType -{ - DECLARE_CLASS(PStruct, PNamedType); - -public: - PStruct(FName name, PTypeBase *outer); - - TArray Fields; - bool HasNativeFields; - // Some internal structs require explicit construction and destruction of fields the VM cannot handle directly so use thes two functions for it. - VMFunction *mConstructor = nullptr; - VMFunction *mDestructor = nullptr; - - virtual PField *AddField(FName name, PType *type, uint32_t flags=0); - virtual PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags = 0, int bitvalue = 0); - - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; - void SetDefaultValue(void *base, unsigned offset, TArray *specials) const override; - void SetPointer(void *base, unsigned offset, TArray *specials) const override; - - static void WriteFields(FSerializer &ar, const void *addr, const TArray &fields); - bool ReadFields(FSerializer &ar, void *addr) const; -protected: - PStruct(); -}; - -// a native struct will always be abstract and cannot be instantiated. All variables are references. -// In addition, native structs can have methods (but no virtual ones.) -class PNativeStruct : public PStruct -{ - DECLARE_CLASS(PNativeStruct, PStruct); -public: - PNativeStruct(FName name = NAME_None, PTypeBase *outer = nullptr); -}; - -class PPrototype : public PCompoundType -{ - DECLARE_CLASS(PPrototype, PCompoundType); -public: - PPrototype(const TArray &rettypes, const TArray &argtypes); - - TArray ArgumentTypes; - TArray ReturnTypes; - - size_t PropagateMark(); - virtual bool IsMatch(intptr_t id1, intptr_t id2) const; - virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; -protected: - PPrototype(); -}; - - -// Meta-info for every class derived from DObject --------------------------- +extern FMemArena ClassDataAllocator; enum { TentativeClass = UINT_MAX, }; -class PClass : public PNativeStruct + +class PClass { - DECLARE_CLASS(PClass, PNativeStruct); - // We unravel _WITH_META here just as we did for PType. protected: - TArray MetaInits; void Derive(PClass *newclass, FName name); void InitializeSpecials(void *addr, void *defaults, TArray PClass::*Inits); void SetSuper(); - PField *AddMetaField(FName name, PType *type, uint32_t flags); public: - void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; void WriteAllFields(FSerializer &ar, const void *addr) const; - bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; bool ReadAllFields(FSerializer &ar, void *addr) const; void InitializeDefaults(); int FindVirtualIndex(FName name, PPrototype *proto); - virtual void DeriveData(PClass *newclass); + PSymbol *FindSymbol(FName symname, bool searchparents) const; + PField *AddField(FName name, PType *type, uint32_t flags); static void StaticInit(); static void StaticShutdown(); static void StaticBootstrap(); // Per-class information ------------------------------------- - TArray SpecialInits; - PClass *ParentClass; // the class this class derives from - const size_t *Pointers; // object pointers defined by this class *only* - const size_t *FlatPointers; // object pointers defined by this class and all its superclasses; not initialized by default - const size_t *ArrayPointers; // dynamic arrays containing object pointers. - uint8_t *Defaults; - uint8_t *Meta; // Per-class static script data - unsigned MetaSize; - bool bRuntimeClass; // class was defined at run-time, not compile-time - bool bExported; // This type has been declared in a script - bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility + 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; + FName SourceLumpName; + 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 Virtuals; // virtual function table + TArray MetaInits; + TArray SpecialInits; + TArray Fields; + PClassType *VMType = nullptr; void (*ConstructNative)(void *); @@ -628,12 +83,12 @@ public: void InsertIntoHash(); DObject *CreateNew(); PClass *CreateDerivedClass(FName name, unsigned int size); - PField *AddField(FName name, PType *type, uint32_t flags=0) override; + void InitializeActorInfo(); void BuildFlatPointers(); void BuildArrayPointers(); - void InitMeta(); 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. @@ -681,6 +136,7 @@ public: static void FindFunction(VMFunction **pptr, FName cls, FName func); PClass *FindClassTentative(FName name); + static TMap ClassMap; static TArray AllClasses; static TArray FunctionPtrList; @@ -688,106 +144,4 @@ public: static bool bVMOperational; }; -// Type tables -------------------------------------------------------------- - -struct FTypeTable -{ - enum { HASH_SIZE = 1021 }; - - PType *TypeHash[HASH_SIZE]; - - PType *FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, size_t *bucketnum); - void AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t parm2, size_t bucket); - void AddType(PType *type); - void Clear(); - - static size_t Hash(const PClass *p1, intptr_t p2, intptr_t p3); -}; - - -extern FTypeTable TypeTable; - -// Returns a type from the TypeTable. Will create one if it isn't present. -PMap *NewMap(PType *keytype, PType *valuetype); -PArray *NewArray(PType *type, unsigned int count); -PStaticArray *NewStaticArray(PType *type); -PDynArray *NewDynArray(PType *type); -PPointer *NewPointer(PType *type, bool isconst = false); -PClassPointer *NewClassPointer(PClass *restrict); -PEnum *NewEnum(FName name, PTypeBase *outer); -PStruct *NewStruct(FName name, PTypeBase *outer); -PNativeStruct *NewNativeStruct(FName name, PTypeBase *outer); -PPrototype *NewPrototype(const TArray &rettypes, const TArray &argtypes); - -// Built-in types ----------------------------------------------------------- - -extern PErrorType *TypeError; -extern PErrorType *TypeAuto; -extern PVoidType *TypeVoid; -extern PInt *TypeSInt8, *TypeUInt8; -extern PInt *TypeSInt16, *TypeUInt16; -extern PInt *TypeSInt32, *TypeUInt32; -extern PBool *TypeBool; -extern PFloat *TypeFloat32, *TypeFloat64; -extern PString *TypeString; -extern PName *TypeName; -extern PSound *TypeSound; -extern PColor *TypeColor; -extern PTextureID *TypeTextureID; -extern PSpriteID *TypeSpriteID; -extern PStruct *TypeVector2; -extern PStruct *TypeVector3; -extern PStruct *TypeColorStruct; -extern PStruct *TypeStringStruct; -extern PStatePointer *TypeState; -extern PPointer *TypeFont; -extern PStateLabel *TypeStateLabel; -extern PPointer *TypeNullPtr; -extern PPointer *TypeVoidPtr; - -// Enumerations for serializing types in an archive ------------------------- - -inline bool &DObject::BoolVar(FName field) -{ - return *(bool*)ScriptVar(field, TypeBool); -} - -inline int &DObject::IntVar(FName field) -{ - return *(int*)ScriptVar(field, TypeSInt32); -} - -inline FSoundID &DObject::SoundVar(FName field) -{ - return *(FSoundID*)ScriptVar(field, TypeSound); -} - -inline PalEntry &DObject::ColorVar(FName field) -{ - return *(PalEntry*)ScriptVar(field, TypeColor); -} - -inline FName &DObject::NameVar(FName field) -{ - return *(FName*)ScriptVar(field, TypeName); -} - -inline double &DObject::FloatVar(FName field) -{ - return *(double*)ScriptVar(field, TypeFloat64); -} - -inline FString &DObject::StringVar(FName field) -{ - return *(FString*)ScriptVar(field, TypeString); -} - -template -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. -} - -void RemoveUnusedSymbols(); - #endif diff --git a/src/doomstat.cpp b/src/doomstat.cpp index 49b4d533f..309398331 100644 --- a/src/doomstat.cpp +++ b/src/doomstat.cpp @@ -61,7 +61,6 @@ CUSTOM_CVAR (String, language, "auto", CVAR_ARCHIVE) // [RH] Network arbitrator int Net_Arbitrator = 0; -DEFINE_GLOBAL(Net_Arbitrator); int NextSkill = -1; diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp index e02af8ccf..719fbf2d7 100644 --- a/src/dsectoreffect.cpp +++ b/src/dsectoreffect.cpp @@ -31,6 +31,7 @@ #include "statnums.h" #include "serializer.h" #include "doomstat.h" +#include "vm.h" IMPLEMENT_CLASS(DSectorEffect, false, false) @@ -75,16 +76,12 @@ void DSectorEffect::Serialize(FSerializer &arc) DEFINE_FIELD(DSectorEffect, m_Sector) -IMPLEMENT_CLASS(DMover, false, true) +IMPLEMENT_CLASS(DMover, true, true) IMPLEMENT_POINTERS_START(DMover) IMPLEMENT_POINTER(interpolation) IMPLEMENT_POINTERS_END -DMover::DMover () -{ -} - DMover::DMover (sector_t *sector) : DSectorEffect (sector) { @@ -112,11 +109,8 @@ void DMover::StopInterpolation(bool force) } } -IMPLEMENT_CLASS(DMovingFloor, false, false) +IMPLEMENT_CLASS(DMovingFloor, true, false) -DMovingFloor::DMovingFloor () -{ -} DMovingFloor::DMovingFloor (sector_t *sector) : DMover (sector) @@ -125,11 +119,8 @@ DMovingFloor::DMovingFloor (sector_t *sector) interpolation = sector->SetInterpolation(sector_t::FloorMove, true); } -IMPLEMENT_CLASS(DMovingCeiling, false, false) +IMPLEMENT_CLASS(DMovingCeiling, true, false) -DMovingCeiling::DMovingCeiling () -{ -} DMovingCeiling::DMovingCeiling (sector_t *sector, bool interpolate) : DMover (sector) diff --git a/src/dsectoreffect.h b/src/dsectoreffect.h index bdd2aa713..e583a85cd 100644 --- a/src/dsectoreffect.h +++ b/src/dsectoreffect.h @@ -25,16 +25,17 @@ protected: class DMover : public DSectorEffect { - DECLARE_CLASS (DMover, DSectorEffect) + DECLARE_ABSTRACT_CLASS (DMover, DSectorEffect) HAS_OBJECT_POINTERS -public: +protected: DMover (sector_t *sector); - void StopInterpolation(bool force = false); -protected: + TObjPtr interpolation; -private: +public: + void StopInterpolation(bool force = false); + protected: - DMover (); + DMover () {} void Serialize(FSerializer &arc); void OnDestroy() override; @@ -42,20 +43,18 @@ protected: class DMovingFloor : public DMover { - DECLARE_CLASS (DMovingFloor, DMover) -public: - DMovingFloor (sector_t *sector); + DECLARE_ABSTRACT_CLASS (DMovingFloor, DMover) protected: - DMovingFloor (); + DMovingFloor (sector_t *sector); + DMovingFloor() {} }; class DMovingCeiling : public DMover { - DECLARE_CLASS (DMovingCeiling, DMover) -public: - DMovingCeiling (sector_t *sector, bool interpolate = true); + DECLARE_ABSTRACT_CLASS (DMovingCeiling, DMover) protected: - DMovingCeiling (); + DMovingCeiling (sector_t *sector, bool interpolate = true); + DMovingCeiling () {} }; #endif //__DSECTOREFFECT_H__ diff --git a/src/dthinker.cpp b/src/dthinker.cpp index af1771c5b..5144cf56e 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -40,7 +40,7 @@ #include "doomerrors.h" #include "serializer.h" #include "d_player.h" -#include "virtual.h" +#include "vm.h" static int ThinkCount; @@ -315,7 +315,7 @@ void DThinker::CallPostBeginPlay() { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } else { @@ -560,7 +560,7 @@ void DThinker::CallTick() { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } else Tick(); } diff --git a/src/events.cpp b/src/events.cpp index 970b68da5..c2ae5f0b4 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -1,5 +1,5 @@ #include "events.h" -#include "virtual.h" +#include "vm.h" #include "r_utility.h" #include "g_levellocals.h" #include "gi.h" @@ -7,6 +7,7 @@ #include "actor.h" #include "c_dispatch.h" #include "d_net.h" +#include "vm.h" DStaticEventHandler* E_FirstEventHandler = nullptr; DStaticEventHandler* E_LastEventHandler = nullptr; @@ -626,7 +627,7 @@ void DStaticEventHandler::OnRegister() if (func == DStaticEventHandler_OnRegister_VMPtr) return; VMValue params[1] = { (DStaticEventHandler*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -638,7 +639,7 @@ void DStaticEventHandler::OnUnregister() if (func == DStaticEventHandler_OnUnregister_VMPtr) return; VMValue params[1] = { (DStaticEventHandler*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -660,7 +661,7 @@ void DStaticEventHandler::WorldLoaded() return; FWorldEvent e = E_SetupWorldEvent(); VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -673,7 +674,7 @@ void DStaticEventHandler::WorldUnloaded() return; FWorldEvent e = E_SetupWorldEvent(); VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -687,7 +688,7 @@ void DStaticEventHandler::WorldThingSpawned(AActor* actor) FWorldEvent e = E_SetupWorldEvent(); e.Thing = actor; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -702,7 +703,7 @@ void DStaticEventHandler::WorldThingDied(AActor* actor, AActor* inflictor) e.Thing = actor; e.Inflictor = inflictor; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -716,7 +717,7 @@ void DStaticEventHandler::WorldThingRevived(AActor* actor) FWorldEvent e = E_SetupWorldEvent(); e.Thing = actor; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -735,7 +736,7 @@ void DStaticEventHandler::WorldThingDamaged(AActor* actor, AActor* inflictor, AA e.DamageFlags = flags; e.DamageAngle = angle; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -749,7 +750,7 @@ void DStaticEventHandler::WorldThingDestroyed(AActor* actor) FWorldEvent e = E_SetupWorldEvent(); e.Thing = actor; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -762,7 +763,7 @@ void DStaticEventHandler::WorldLightning() return; FWorldEvent e = E_SetupWorldEvent(); VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -774,7 +775,7 @@ void DStaticEventHandler::WorldTick() if (func == DStaticEventHandler_WorldTick_VMPtr) return; VMValue params[1] = { (DStaticEventHandler*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -799,7 +800,7 @@ void DStaticEventHandler::RenderFrame() return; FRenderEvent e = E_SetupRenderEvent(); VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -813,7 +814,7 @@ void DStaticEventHandler::RenderOverlay(EHudState state) FRenderEvent e = E_SetupRenderEvent(); e.HudState = int(state); VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -826,7 +827,7 @@ void DStaticEventHandler::PlayerEntered(int num, bool fromhub) return; FPlayerEvent e = { num, fromhub }; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -839,7 +840,7 @@ void DStaticEventHandler::PlayerRespawned(int num) return; FPlayerEvent e = { num, false }; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -852,7 +853,7 @@ void DStaticEventHandler::PlayerDied(int num) return; FPlayerEvent e = { num, false }; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -865,7 +866,7 @@ void DStaticEventHandler::PlayerDisconnected(int num) return; FPlayerEvent e = { num, false }; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } @@ -921,7 +922,7 @@ bool DStaticEventHandler::UiProcess(const event_t* ev) int processed; VMReturn results[1] = { &processed }; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, results, 1, nullptr); + VMCall(func, params, 2, results, 1); return !!processed; } @@ -969,7 +970,7 @@ bool DStaticEventHandler::InputProcess(const event_t* ev) int processed; VMReturn results[1] = { &processed }; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, results, 1, nullptr); + VMCall(func, params, 2, results, 1); return !!processed; } @@ -984,7 +985,7 @@ void DStaticEventHandler::UiTick() if (func == DStaticEventHandler_UiTick_VMPtr) return; VMValue params[1] = { (DStaticEventHandler*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -1008,7 +1009,7 @@ void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int e.IsManual = manual; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } else @@ -1029,7 +1030,7 @@ void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int e.IsManual = manual; VMValue params[2] = { (DStaticEventHandler*)this, &e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } } } diff --git a/src/g_game.cpp b/src/g_game.cpp index e70935cf4..26e9d5c9c 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -85,6 +85,7 @@ #include "serializer.h" #include "w_zip.h" #include "resourcefiles/resourcefile.h" +#include "vm.h" #include @@ -3012,3 +3013,5 @@ DEFINE_GLOBAL(skyflatnum) DEFINE_GLOBAL_NAMED(bglobal.freeze, globalfreeze) DEFINE_GLOBAL(gametic) DEFINE_GLOBAL(demoplayback) +DEFINE_GLOBAL(automapactive); +DEFINE_GLOBAL(Net_Arbitrator); diff --git a/src/g_inventory/a_keys.cpp b/src/g_inventory/a_keys.cpp index 701b98b00..8b2ac54c9 100644 --- a/src/g_inventory/a_keys.cpp +++ b/src/g_inventory/a_keys.cpp @@ -43,7 +43,7 @@ #include "w_wad.h" #include "doomstat.h" #include "v_font.h" - +#include "vm.h" //=========================================================================== // diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 0a44e6d14..5d2617881 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -19,9 +19,10 @@ #include "d_player.h" #include "p_spec.h" #include "serializer.h" -#include "virtual.h" +#include "vm.h" #include "c_functions.h" #include "g_levellocals.h" +#include "vm.h" EXTERN_CVAR(Bool, sv_unlimited_pickup) @@ -237,7 +238,7 @@ double AInventory::GetSpeedFactor() VMValue params[1] = { (DObject*)self }; double retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + VMCall(func, params, 1, &ret, 1); factor *= retval; } self = self->Inventory; @@ -261,7 +262,7 @@ bool AInventory::GetNoTeleportFreeze () VMValue params[1] = { (DObject*)self }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + VMCall(func, params, 1, &ret, 1); if (retval) return true; } self = self->Inventory; @@ -282,7 +283,7 @@ bool AInventory::CallUse(bool pickup) VMValue params[2] = { (DObject*)this, pickup }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); return !!retval; } return false; @@ -347,7 +348,7 @@ void AInventory::DepleteOrDestroy () IFVIRTUAL(AInventory, DepleteOrDestroy) { VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -367,7 +368,7 @@ PalEntry AInventory::CallGetBlend() VMValue params[1] = { (DObject*)this }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + VMCall(func, params, 1, &ret, 1); return retval; } else return 0; @@ -479,7 +480,7 @@ bool AInventory::CallTryPickup(AActor *toucher, AActor **toucher_return) AActor *tret; ret[0].IntAt(&res); ret[1].PointerAt((void**)&tret); - GlobalVMStack.Call(func, params, 2, ret, 2); + VMCall(func, params, 2, ret, 2); if (toucher_return) *toucher_return = tret; return !!res; } diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index af9634846..5086d7832 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -52,11 +52,7 @@ #include "g_level.h" #include "d_net.h" #include "serializer.h" -#include "thingdef.h" -#include "virtual.h" - - -extern FFlagDef WeaponFlagDefs[]; +#include "vm.h" IMPLEMENT_CLASS(AWeapon, false, true) @@ -66,7 +62,6 @@ IMPLEMENT_POINTERS_START(AWeapon) IMPLEMENT_POINTER(SisterWeapon) IMPLEMENT_POINTERS_END -DEFINE_FIELD(AWeapon, WeaponFlags) DEFINE_FIELD(AWeapon, AmmoType1) DEFINE_FIELD(AWeapon, AmmoType2) DEFINE_FIELD(AWeapon, AmmoGive1) @@ -99,6 +94,7 @@ DEFINE_FIELD(AWeapon, Crosshair) DEFINE_FIELD(AWeapon, GivenAsMorphWeapon) DEFINE_FIELD(AWeapon, bAltFire) DEFINE_FIELD(AWeapon, SlotNumber) +DEFINE_FIELD(AWeapon, WeaponFlags) DEFINE_FIELD_BIT(AWeapon, WeaponFlags, bDehAmmo, WIF_DEHAMMO) //=========================================================================== @@ -404,7 +400,7 @@ FState *AWeapon::GetUpState () VMReturn ret; FState *retval; ret.PointerAt((void**)&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + VMCall(func, params, 1, &ret, 1); return retval; } return nullptr; @@ -424,7 +420,7 @@ FState *AWeapon::GetDownState () VMReturn ret; FState *retval; ret.PointerAt((void**)&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + VMCall(func, params, 1, &ret, 1); return retval; } return nullptr; @@ -444,7 +440,7 @@ FState *AWeapon::GetReadyState () VMReturn ret; FState *retval; ret.PointerAt((void**)&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); + VMCall(func, params, 1, &ret, 1); return retval; } return nullptr; @@ -464,7 +460,7 @@ FState *AWeapon::GetAtkState (bool hold) VMReturn ret; FState *retval; ret.PointerAt((void**)&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); return retval; } return nullptr; @@ -484,7 +480,7 @@ FState *AWeapon::GetAltAtkState (bool hold) VMReturn ret; FState *retval; ret.PointerAt((void**)&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); return retval; } return nullptr; @@ -514,7 +510,7 @@ FState *AWeapon::GetStateForButtonName (FName button) bool FWeaponSlot::AddWeapon(const char *type) { - return AddWeapon(static_cast(PClass::FindClass(type))); + return AddWeapon(PClass::FindActor(type)); } bool FWeaponSlot::AddWeapon(PClassActor *type) @@ -985,8 +981,9 @@ void FWeaponSlots::AddExtraWeapons() continue; } auto weapdef = ((AWeapon*)GetDefaultByType(cls)); - if ((cls->GameFilter == GAME_Any || (cls->GameFilter & gameinfo.gametype)) && - cls->Replacement == nullptr && // Replaced weapons don't get slotted. + auto gf = cls->ActorInfo()->GameFilter; + if ((gf == GAME_Any || (gf & gameinfo.gametype)) && + cls->ActorInfo()->Replacement == nullptr && // Replaced weapons don't get slotted. !(weapdef->WeaponFlags & WIF_POWERED_UP) && !LocateWeapon(cls, nullptr, nullptr) // Don't duplicate it if it's already present. ) @@ -1268,7 +1265,7 @@ CCMD (setslot) Net_WriteByte(argv.argc()-2); for (int i = 2; i < argv.argc(); i++) { - Net_WriteWeapon(dyn_cast(PClass::FindClass(argv[i]))); + Net_WriteWeapon(PClass::FindActor(argv[i])); } } } @@ -1297,7 +1294,7 @@ CCMD (addslot) return; } - PClassActor *type= dyn_cast(PClass::FindClass(argv[2])); + PClassActor *type= PClass::FindActor(argv[2]); if (type == nullptr) { Printf("%s is not a weapon\n", argv[2]); @@ -1373,7 +1370,7 @@ CCMD (addslotdefault) return; } - type = dyn_cast(PClass::FindClass(argv[2])); + type = PClass::FindActor(argv[2]); if (type == nullptr) { Printf ("%s is not a weapon\n", argv[2]); @@ -1442,7 +1439,7 @@ void P_SetupWeapons_ntohton() if (cls->IsDescendantOf(NAME_Weapon)) { - Weapons_ntoh.Push(static_cast(cls)); + Weapons_ntoh.Push(cls); } } qsort(&Weapons_ntoh[1], Weapons_ntoh.Size() - 1, sizeof(Weapons_ntoh[0]), ntoh_cmp); @@ -1470,8 +1467,8 @@ static int ntoh_cmp(const void *a, const void *b) { PClassActor *c1 = *(PClassActor **)a; PClassActor *c2 = *(PClassActor **)b; - int g1 = c1->GameFilter == GAME_Any ? 1 : (c1->GameFilter & gameinfo.gametype) ? 0 : 2; - int g2 = c2->GameFilter == GAME_Any ? 1 : (c2->GameFilter & gameinfo.gametype) ? 0 : 2; + int g1 = c1->ActorInfo()->GameFilter == GAME_Any ? 1 : (c1->ActorInfo()->GameFilter & gameinfo.gametype) ? 0 : 2; + int g2 = c2->ActorInfo()->GameFilter == GAME_Any ? 1 : (c2->ActorInfo()->GameFilter & gameinfo.gametype) ? 0 : 2; if (g1 != g2) { return g1 - g2; @@ -1522,7 +1519,7 @@ void P_ReadDemoWeaponsChunk(uint8_t **demo) for (i = 1; i < count; ++i) { s = ReadStringConst(demo); - type = dyn_cast(PClass::FindClass(s)); + type = PClass::FindActor(s); // If a demo was recorded with a weapon that is no longer present, // should we report it? Weapons_ntoh[i] = type; diff --git a/src/g_level.cpp b/src/g_level.cpp index ecde760f3..46248deca 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -84,7 +84,7 @@ #include "r_utility.h" #include "p_spec.h" #include "serializer.h" -#include "virtual.h" +#include "vm.h" #include "events.h" #include "gi.h" @@ -92,6 +92,7 @@ #include "g_hub.h" #include "g_levellocals.h" #include "actorinlines.h" +#include "vm.h" #include @@ -1318,8 +1319,7 @@ void G_FinishTravel () IFVIRTUALPTR(inv, AInventory, Travelled) { VMValue params[1] = { inv }; - VMFrameStack stack; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } if (ib_compatflags & BCOMPATF_RESETPLAYERSPEED) diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index 575e7027c..71f796a89 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -11,6 +11,7 @@ #include "templates.h" #include "serializer.h" #include "r_data/r_translate.h" +#include "vm.h" //---------------------------------------------------------------------------- // diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index 95e87baf4..4aaa75fa5 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -60,7 +60,7 @@ #include "p_local.h" #include "c_dispatch.h" #include "g_level.h" -#include "scripting/thingdef.h" +#include "thingdef.h" #include "i_system.h" #include "templates.h" #include "doomdata.h" @@ -74,6 +74,7 @@ #include "actorinlines.h" #include "c_cvars.h" #include "gl/system//gl_interface.h" +#include "vm.h" EXTERN_CVAR(Int, vid_renderer) diff --git a/src/g_shared/a_dynlightdata.cpp b/src/g_shared/a_dynlightdata.cpp index 7db1f8f7c..757d26bf3 100644 --- a/src/g_shared/a_dynlightdata.cpp +++ b/src/g_shared/a_dynlightdata.cpp @@ -1058,7 +1058,7 @@ void InitializeActorLights() void *mem = ClassDataAllocator.Alloc(sizeof(FInternalLightAssociation)); FInternalLightAssociation * iasso = new(mem) FInternalLightAssociation(&LightAssociations[i]); if (iasso->Light() != nullptr) - ti->LightAssociations.Push(iasso); + ti->ActorInfo()->LightAssociations.Push(iasso); } } // we don't need the parser data for the light associations anymore @@ -1124,7 +1124,7 @@ void AActor::AttachLight(unsigned int count, const FLightDefaults *lightdef) void AActor::SetDynamicLights() { - TArray & LightAssociations = GetClass()->LightAssociations; + TArray & LightAssociations = GetInfo()->LightAssociations; unsigned int count = 0; if (state == NULL) return; diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 36ffd800b..3b68e8c2b 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -15,7 +15,8 @@ #include "d_player.h" #include "r_data/sprites.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" +#include "vm.h" static FRandom pr_morphmonst ("MorphMonster"); @@ -612,8 +613,7 @@ void EndAllPowerupEffects(AInventory *item) IFVIRTUALPTRNAME(item, NAME_Powerup, EndEffect) { VMValue params[1] = { item }; - VMFrameStack stack; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } item = item->Inventory; @@ -638,8 +638,7 @@ void InitAllPowerupEffects(AInventory *item) IFVIRTUALPTRNAME(item, NAME_Powerup, InitEffect) { VMValue params[1] = { item }; - VMFrameStack stack; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } item = item->Inventory; @@ -714,14 +713,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_Morph) bool res = false; if (self->player) { - if (type->IsKindOf(RUNTIME_CLASS(APlayerPawn))) + if (type->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) { res = P_MorphPlayer(self->player, self->player, type, duration, flags, enter_flash, exit_flash); } } else { - if (type->IsKindOf(RUNTIME_CLASS(AMorphedMonster))) + if (type->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) { res = P_MorphMonster(self, type, duration, flags, enter_flash, exit_flash); } diff --git a/src/g_shared/a_specialspot.cpp b/src/g_shared/a_specialspot.cpp index 2c41b2099..5c0919820 100644 --- a/src/g_shared/a_specialspot.cpp +++ b/src/g_shared/a_specialspot.cpp @@ -40,6 +40,7 @@ #include "doomstat.h" #include "serializer.h" #include "a_pickups.h" +#include "vm.h" static FRandom pr_spot ("SpecialSpot"); static FRandom pr_spawnmace ("SpawnMace"); diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 19183a437..8ac35da24 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -54,7 +54,7 @@ #include "r_utility.h" #include "cmdlib.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" #include @@ -385,21 +385,20 @@ static void SetKeyTypes() { for(unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); i++) { - PClass *ti = PClassActor::AllActorClasses[i]; + PClassActor *ti = PClassActor::AllActorClasses[i]; auto kt = PClass::FindActor(NAME_Key); if (ti->IsDescendantOf(kt)) { - PClassActor *tia = static_cast(ti); - AInventory *key = (AInventory*)(GetDefaultByType(tia)); + AInventory *key = (AInventory*)(GetDefaultByType(ti)); if (key->Icon.isValid() && key->special1 > 0) { - KeyTypes.Push(tia); + KeyTypes.Push(ti); } else { - UnassignedKeyTypes.Push(tia); + UnassignedKeyTypes.Push(ti); } } } @@ -1094,7 +1093,7 @@ static void DrawPowerups(player_t *CPlayer) VMValue param[] = { item }; int rv; VMReturn ret(&rv); - GlobalVMStack.Call(func, param, 1, &ret, 1); + VMCall(func, param, 1, &ret, 1); auto tex = FSetTextureID(rv); if (!tex.isValid()) continue; auto texture = TexMan(tex); diff --git a/src/g_skill.cpp b/src/g_skill.cpp index 04ac2f290..6e23c9523 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -44,6 +44,7 @@ #include "m_fixed.h" #include "gstrings.h" #include "g_levellocals.h" +#include "vm.h" TArray AllSkills; int DefaultSkill = -1; @@ -476,7 +477,7 @@ const char * G_SkillName() const char *name = AllSkills[gameskill].MenuName; player_t *player = &players[consoleplayer]; - const char *playerclass = player->mo->GetClass()->DisplayName; + const char *playerclass = player->mo->GetInfo()->DisplayName; if (playerclass != NULL) { diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index a46911217..6d9baea24 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -57,7 +57,7 @@ #include "gstrings.h" #include "cmdlib.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" #define ARTIFLASH_OFFSET (statusBar->invBarOffset+6) enum diff --git a/src/g_statusbar/sbarinfo_commands.cpp b/src/g_statusbar/sbarinfo_commands.cpp index 458208b96..63c9d9ed8 100644 --- a/src/g_statusbar/sbarinfo_commands.cpp +++ b/src/g_statusbar/sbarinfo_commands.cpp @@ -1441,7 +1441,7 @@ class CommandDrawNumber : public CommandDrawString VMValue params[] = { statusBar->CPlayer->mo, inventoryItem }; int retv; VMReturn ret(&retv); - GlobalVMStack.Call(func, params, 2, &ret, 1); + VMCall(func, params, 2, &ret, 1); num = retv < 0? 0 : retv / TICRATE + 1; break; } @@ -2738,7 +2738,7 @@ class CommandDrawBar : public SBarInfoCommand int ival; ret[0].IntAt(&ival); ret[1].IntAt(&max); - GlobalVMStack.Call(func, params, 2, ret, 2); + VMCall(func, params, 2, ret, 2); value = ival + 1; max++; break; @@ -2925,7 +2925,7 @@ class CommandPlayerClass : public SBarInfoCommandFlowControl bool foundClass = false; for(unsigned int c = 0;c < PlayerClasses.Size();c++) { - if(stricmp(sc.String, PlayerClasses[c].Type->DisplayName) == 0) + if(stricmp(sc.String, PlayerClasses[c].Type->GetDisplayName()) == 0) { foundClass = true; classes.Push(PlayerClasses[c].Type); diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index 2514cd8d2..1afe8d46d 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -57,7 +57,7 @@ #include "r_utility.h" #include "cmdlib.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" #include "p_acs.h" #include "r_data/r_translate.h" #include "sbarinfo.h" @@ -329,7 +329,7 @@ void ST_CreateStatusBar(bool bTitleLevel) IFVIRTUALPTR(StatusBar, DBaseStatusBar, Init) { VMValue params[] = { StatusBar }; - GlobalVMStack.Call(func, params, 1, nullptr, 0); + VMCall(func, params, 1, nullptr, 0); } GC::WriteBarrier(StatusBar); @@ -560,7 +560,7 @@ void DBaseStatusBar::AttachToPlayer(player_t *player) IFVIRTUAL(DBaseStatusBar, AttachToPlayer) { VMValue params[] = { (DObject*)this, player }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } } @@ -641,7 +641,7 @@ void DBaseStatusBar::CallTick() IFVIRTUAL(DBaseStatusBar, Tick) { VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } else Tick(); mugshot.Tick(CPlayer); @@ -973,7 +973,7 @@ void DBaseStatusBar::Draw (EHudState state) IFVIRTUAL(DBaseStatusBar, DrawMyPos) { VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } V_SetBorderNeedRefresh(); } @@ -990,7 +990,7 @@ void DBaseStatusBar::Draw (EHudState state) IFVIRTUAL(DBaseStatusBar, DrawAutomapHUD) { VMValue params[] = { (DObject*)this, r_viewpoint.TicFrac }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } } } @@ -1008,7 +1008,7 @@ void DBaseStatusBar::CallDraw(EHudState state) IFVIRTUAL(DBaseStatusBar, Draw) { VMValue params[] = { (DObject*)this, state, r_viewpoint.TicFrac }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } else Draw(state); screen->ClearClipRect(); // make sure the scripts don't leave a valid clipping rect behind. @@ -1073,7 +1073,7 @@ bool DBaseStatusBar::MustDrawLog(EHudState state) VMValue params[] = { (DObject*)this }; int rv; VMReturn ret(&rv); - GlobalVMStack.Call(func, params, countof(params), &ret, 1); + VMCall(func, params, countof(params), &ret, 1); return !!rv; } return true; @@ -1095,7 +1095,7 @@ void DBaseStatusBar::SetMugShotState(const char *stateName, bool waitTillDone, b { FString statestring = stateName; VMValue params[] = { (DObject*)this, &statestring, waitTillDone, reset }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } } @@ -1133,7 +1133,7 @@ void DBaseStatusBar::DrawTopStuff (EHudState state) IFVIRTUAL(DBaseStatusBar, DrawPowerups) { VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0); + VMCall(func, params, 1, nullptr, 0); } fullscreenOffsets = saved; } @@ -1261,7 +1261,7 @@ void DBaseStatusBar::FlashItem (const PClass *itemtype) IFVIRTUAL(DBaseStatusBar, FlashItem) { VMValue params[] = { (DObject*)this, (PClass*)itemtype }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } } @@ -1270,7 +1270,7 @@ void DBaseStatusBar::NewGame () IFVIRTUAL(DBaseStatusBar, NewGame) { VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } mugshot.Reset(); } @@ -1280,7 +1280,7 @@ void DBaseStatusBar::ShowPop(int pop) IFVIRTUAL(DBaseStatusBar, ShowPop) { VMValue params[] = { (DObject*)this, pop }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } } @@ -1319,7 +1319,7 @@ void DBaseStatusBar::CallScreenSizeChanged() IFVIRTUAL(DBaseStatusBar, ScreenSizeChanged) { VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0); + VMCall(func, params, countof(params), nullptr, 0); } else ScreenSizeChanged(); } diff --git a/src/gi.cpp b/src/gi.cpp index 467847092..6be40f701 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -42,6 +42,7 @@ #include "i_system.h" #include "v_video.h" #include "g_level.h" +#include "vm.h" gameinfo_t gameinfo; diff --git a/src/gl/scene/gl_bsp.cpp b/src/gl/scene/gl_bsp.cpp index 804311bc6..5222e46f3 100644 --- a/src/gl/scene/gl_bsp.cpp +++ b/src/gl/scene/gl_bsp.cpp @@ -360,7 +360,7 @@ void GLSceneDrawer::RenderThings(subsector_t * sub, sector_t * sector) if (thing->validcount == validcount) continue; thing->validcount = validcount; - FIntCVar *cvar = thing->GetClass()->distancecheck; + FIntCVar *cvar = thing->GetInfo()->distancecheck; if (cvar != NULL && *cvar >= 0) { double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared(); @@ -378,7 +378,7 @@ void GLSceneDrawer::RenderThings(subsector_t * sub, sector_t * sector) for (msecnode_t *node = sec->sectorportal_thinglist; node; node = node->m_snext) { AActor *thing = node->m_thing; - FIntCVar *cvar = thing->GetClass()->distancecheck; + FIntCVar *cvar = thing->GetInfo()->distancecheck; if (cvar != NULL && *cvar >= 0) { double dist = (thing->Pos() - r_viewpoint.Pos).LengthSquared(); diff --git a/src/gl/textures/gl_texture.cpp b/src/gl/textures/gl_texture.cpp index bef0da5d5..e0b065a13 100644 --- a/src/gl/textures/gl_texture.cpp +++ b/src/gl/textures/gl_texture.cpp @@ -33,6 +33,7 @@ #include "c_dispatch.h" #include "r_state.h" #include "actor.h" +#include "cmdlib.h" #ifdef _WIN32 #include "win32gliface.h" #endif @@ -841,10 +842,11 @@ void gl_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitl PClassActor *cls = pair->Key; int gltrans = GLTranslationPalette::GetInternalTranslation(GetDefaultByType(cls)->Translation); - for (int i = 0; i < cls->NumOwnedStates; i++) + for (int i = 0; i < cls->GetStateCount(); i++) { - spritelist[cls->OwnedStates[i].sprite].Insert(gltrans, true); - FSpriteModelFrame * smf = gl_FindModelFrame(cls, cls->OwnedStates[i].sprite, cls->OwnedStates[i].Frame, false); + auto &state = cls->GetStates()[i]; + spritelist[state.sprite].Insert(gltrans, true); + FSpriteModelFrame * smf = gl_FindModelFrame(cls, state.sprite, state.Frame, false); if (smf != NULL) { for (int i = 0; i < MAX_MODELS_PER_FRAME; i++) diff --git a/src/info.cpp b/src/info.cpp index b8f7db995..7dd40c1ea 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -55,6 +55,8 @@ #include "d_player.h" #include "doomerrors.h" #include "events.h" +#include "types.h" +#include "vm.h" extern void LoadActors (); extern void InitBotStuff(); @@ -65,6 +67,62 @@ FRandom FState::pr_statetics("StateTics"); cycle_t ActionCycles; + +//========================================================================== +// +// special type for the native ActorInfo. This allows to let this struct +// be handled by the generic object constructors for the VM. +// +//========================================================================== + +class PActorInfo : public PBasicType +{ + DECLARE_CLASS(PActorInfo, PBasicType); +public: + PActorInfo() + :PBasicType(sizeof(FActorInfo), alignof(FActorInfo)) + { + } + + void SetDefaultValue(void *base, unsigned offset, TArray *special) override + { + if (base != nullptr) new((uint8_t *)base + offset) FActorInfo; + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } + } + + void InitializeValue(void *addr, const void *def) const override + { + if (def == nullptr) + { + new(addr) FActorInfo; + } + else + { + new(addr) FActorInfo(*(const FActorInfo*)def); + } + } + + void DestroyValue(void *addr) const override + { + FActorInfo *self = (FActorInfo*)addr; + self->~FActorInfo(); + } + +}; + +IMPLEMENT_CLASS(PActorInfo, false, false) + +void AddActorInfo(PClass *cls) +{ + auto type = new PActorInfo; + TypeTable.AddType(type); + cls->AddField("*", type, VARF_Meta); +} + + void FState::SetAction(const char *name) { ActionFunc = FindVMFunction(RUNTIME_CLASS(AActor), name); @@ -72,49 +130,47 @@ void FState::SetAction(const char *name) bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, FState **stateret) { - if (ActionFunc != NULL) + if (ActionFunc != nullptr) { ActionCycles.Clock(); VMValue params[3] = { self, stateowner, VMValue(info) }; // If the function returns a state, store it at *stateret. - // If it doesn't return a state but stateret is non-NULL, we need - // to set *stateret to NULL. - if (stateret != NULL) + // If it doesn't return a state but stateret is non-nullptr, we need + // to set *stateret to nullptr. + if (stateret != nullptr) { - *stateret = NULL; - if (ActionFunc->Proto == NULL || + *stateret = nullptr; + if (ActionFunc->Proto == nullptr || ActionFunc->Proto->ReturnTypes.Size() == 0 || ActionFunc->Proto->ReturnTypes[0] != TypeState) { - stateret = NULL; + stateret = nullptr; } } try { - if (stateret == NULL) + if (stateret == nullptr) { - GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL); + VMCall(ActionFunc, params, ActionFunc->ImplicitArgs, nullptr, 0); } else { VMReturn ret; ret.PointerAt((void **)stateret); - GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL); + VMCall(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1); } } catch (CVMAbortException &err) { err.MaybePrintMessage(); - auto owner = FState::StaticFindStateOwner(this); - int offs = int(this - owner->OwnedStates); const char *callinfo = ""; if (info != nullptr && info->mStateType == STATE_Psprite) { if (stateowner->IsKindOf(NAME_Weapon) && stateowner != self) callinfo = "weapon "; else callinfo = "overlay "; } - err.stacktrace.AppendFormat("Called from %sstate %s.%d in %s\n", callinfo, owner->TypeName.GetChars(), offs, stateowner->GetClass()->TypeName.GetChars()); + err.stacktrace.AppendFormat("Called from %sstate %s in %s\n", callinfo, FState::StaticGetStateName(this), stateowner->GetClass()->TypeName.GetChars()); throw; throw; } @@ -177,8 +233,6 @@ DEFINE_ACTION_FUNCTION(AActor, GetSpriteIndex) ACTION_RETURN_INT(GetSpriteIndex(sprt.GetChars(), false)); } -IMPLEMENT_CLASS(PClassActor, false, false) - //========================================================================== // // PClassActor :: StaticInit STATIC @@ -228,95 +282,10 @@ void PClassActor::StaticSetActorNums() { for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { - static_cast(PClassActor::AllActorClasses[i])->RegisterIDs(); + PClassActor::AllActorClasses[i]->RegisterIDs(); } } -//========================================================================== -// -// PClassActor Constructor -// -//========================================================================== - -PClassActor::PClassActor() -{ - GameFilter = GAME_Any; - SpawnID = 0; - DoomEdNum = -1; - OwnedStates = NULL; - NumOwnedStates = 0; - Replacement = NULL; - Replacee = NULL; - StateList = NULL; - DamageFactors = NULL; - PainChances = NULL; - - DropItems = NULL; - // Record this in the master list. - AllActorClasses.Push(this); -} - -//========================================================================== -// -// PClassActor Destructor -// -//========================================================================== - -PClassActor::~PClassActor() -{ - if (OwnedStates != NULL) - { - delete[] OwnedStates; - } - if (DamageFactors != NULL) - { - delete DamageFactors; - } - if (PainChances != NULL) - { - delete PainChances; - } - if (StateList != NULL) - { - StateList->Destroy(); - M_Free(StateList); - } -} - -//========================================================================== -// -// PClassActor :: Derive -// -//========================================================================== - -void PClassActor::DeriveData(PClass *newclass) -{ - assert(newclass->IsKindOf(RUNTIME_CLASS(PClassActor))); - PClassActor *newa = static_cast(newclass); - - newa->DefaultStateUsage = DefaultStateUsage; - newa->distancecheck = distancecheck; - - newa->DropItems = DropItems; - - newa->VisibleToPlayerClass = VisibleToPlayerClass; - - if (DamageFactors != NULL) - { - // copy damage factors from parent - newa->DamageFactors = new DmgFactors; - *newa->DamageFactors = *DamageFactors; - } - if (PainChances != NULL) - { - // copy pain chances from parent - newa->PainChances = new PainChanceList; - *newa->PainChances = *PainChances; - } - - newa->DisplayName = DisplayName; -} - //========================================================================== // // PClassActor :: SetReplacement @@ -339,27 +308,13 @@ bool PClassActor::SetReplacement(FName replaceName) } if (replacee != nullptr) { - replacee->Replacement = this; - Replacee = replacee; + replacee->ActorInfo()->Replacement = this; + ActorInfo()->Replacee = replacee; } } return true; } -//========================================================================== -// -// PClassActor :: SetDropItems -// -// Sets a new drop item list -// -//========================================================================== - -void PClassActor::SetDropItems(FDropItem *drops) -{ - DropItems = drops; -} - - //========================================================================== // // PClassActor :: Finalize @@ -378,11 +333,11 @@ void AActor::Finalize(FStateDefinitions &statedef) } catch (CRecoverableError &) { - statedef.MakeStateDefines(NULL); + statedef.MakeStateDefines(nullptr); throw; } statedef.InstallStates(GetClass(), defaults); - statedef.MakeStateDefines(NULL); + statedef.MakeStateDefines(nullptr); } //========================================================================== @@ -397,23 +352,25 @@ void PClassActor::RegisterIDs() { PClassActor *cls = PClass::FindActor(TypeName); - if (cls == NULL) + if (cls == nullptr) { Printf(TEXTCOLOR_RED"The actor '%s' has been hidden by a non-actor of the same name\n", TypeName.GetChars()); return; } // Conversation IDs have never been filtered by game so we cannot start doing that. + auto ConversationID = ActorInfo()->ConversationID; if (ConversationID > 0) { StrifeTypes[ConversationID] = cls; if (cls != this) { - Printf(TEXTCOLOR_RED"Conversation ID %d refers to hidden class type '%s'\n", SpawnID, cls->TypeName.GetChars()); + Printf(TEXTCOLOR_RED"Conversation ID %d refers to hidden class type '%s'\n", ConversationID, cls->TypeName.GetChars()); } } - if (GameFilter == GAME_Any || (GameFilter & gameinfo.gametype)) + if (ActorInfo()->GameFilter == GAME_Any || (ActorInfo()->GameFilter & gameinfo.gametype)) { + auto SpawnID = ActorInfo()->SpawnID; if (SpawnID > 0) { SpawnableThings[SpawnID] = cls; @@ -422,10 +379,11 @@ void PClassActor::RegisterIDs() Printf(TEXTCOLOR_RED"Spawn ID %d refers to hidden class type '%s'\n", SpawnID, cls->TypeName.GetChars()); } } + auto DoomEdNum = ActorInfo()->DoomEdNum; if (DoomEdNum != -1) { FDoomEdEntry *oldent = DoomEdMap.CheckKey(DoomEdNum); - if (oldent != NULL && oldent->Special == -2) + if (oldent != nullptr && oldent->Special == -2) { Printf(TEXTCOLOR_RED"Editor number %d defined twice for classes '%s' and '%s'\n", DoomEdNum, cls->TypeName.GetChars(), oldent->Type->TypeName.GetChars()); } @@ -455,7 +413,7 @@ PClassActor *PClassActor::GetReplacement(bool lookskill) if (lookskill && AllSkills.Size() > (unsigned)gameskill) { skillrepname = AllSkills[gameskill].GetReplacement(TypeName); - if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == NULL) + if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == nullptr) { Printf("Warning: incorrect actor name in definition of skill %s: \n" "class %s is replaced by non-existent class %s\n" @@ -467,15 +425,15 @@ PClassActor *PClassActor::GetReplacement(bool lookskill) lookskill = false; skillrepname = NAME_None; } } - if (Replacement == NULL && (!lookskill || skillrepname == NAME_None)) + auto Replacement = ActorInfo()->Replacement; + if (Replacement == nullptr && (!lookskill || skillrepname == NAME_None)) { return this; } // The Replacement field is temporarily NULLed to prevent // potential infinite recursion. - PClassActor *savedrep = Replacement; - Replacement = NULL; - PClassActor *rep = savedrep; + ActorInfo()->Replacement = nullptr; + PClassActor *rep = Replacement; // Handle skill-based replacement here. It has precedence on DECORATE replacement // in that the skill replacement is applied first, followed by DECORATE replacement // on the actor indicated by the skill replacement. @@ -487,7 +445,7 @@ PClassActor *PClassActor::GetReplacement(bool lookskill) // Skill replacements are not recursive, contrarily to DECORATE replacements rep = rep->GetReplacement(false); // Reset the temporarily NULLed field - Replacement = savedrep; + ActorInfo()->Replacement = Replacement; return rep; } @@ -511,7 +469,7 @@ PClassActor *PClassActor::GetReplacee(bool lookskill) if (lookskill && AllSkills.Size() > (unsigned)gameskill) { skillrepname = AllSkills[gameskill].GetReplacedBy(TypeName); - if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == NULL) + if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == nullptr) { Printf("Warning: incorrect actor name in definition of skill %s: \n" "non-existent class %s is replaced by class %s\n" @@ -523,21 +481,21 @@ PClassActor *PClassActor::GetReplacee(bool lookskill) lookskill = false; } } - if (Replacee == NULL && (!lookskill || skillrepname == NAME_None)) + PClassActor *savedrep = ActorInfo()->Replacee; + if (savedrep == nullptr && (!lookskill || skillrepname == NAME_None)) { return this; } // The Replacee field is temporarily NULLed to prevent // potential infinite recursion. - PClassActor *savedrep = Replacee; - Replacee = NULL; + ActorInfo()->Replacee = nullptr; PClassActor *rep = savedrep; - if (lookskill && (skillrepname != NAME_None) && (PClass::FindClass(skillrepname) != NULL)) + if (lookskill && (skillrepname != NAME_None) && (PClass::FindClass(skillrepname) != nullptr)) { rep = PClass::FindActor(skillrepname); } rep = rep->GetReplacee(false); - Replacee = savedrep; + ActorInfo()->Replacee = savedrep; return rep; } @@ -556,11 +514,12 @@ DEFINE_ACTION_FUNCTION(AActor, GetReplacee) void PClassActor::SetDamageFactor(FName type, double factor) { - if (DamageFactors == NULL) + for (auto & p : ActorInfo()->DamageFactors) { - DamageFactors = new DmgFactors; + if (p.first == type) p.second = factor; + return; } - DamageFactors->Insert(type, factor); + ActorInfo()->DamageFactors.Push({ type, factor }); } //========================================================================== @@ -571,17 +530,15 @@ void PClassActor::SetDamageFactor(FName type, double factor) void PClassActor::SetPainChance(FName type, int chance) { + for (auto & p : ActorInfo()->PainChances) + { + if (p.first == type) p.second = chance; + return; + } + if (chance >= 0) { - if (PainChances == NULL) - { - PainChances = new PainChanceList; - } - PainChances->Insert(type, MIN(chance, 256)); - } - else if (PainChances != NULL) - { - PainChances->Remove(type); + ActorInfo()->PainChances.Push({ type, MIN(chance, 256) }); } } @@ -596,13 +553,22 @@ void PClassActor::SetPainChance(FName type, int chance) int DmgFactors::Apply(FName type, int damage) { - auto pdf = CheckKey(type); - if (pdf == NULL && type != NAME_None) + double factor = -1.; + for (auto & p : *this) { - pdf = CheckKey(NAME_None); + if (p.first == type) + { + factor = p.second; + break; + } + if (p.first == NAME_None) + { + factor = p.second; + } } - if (!pdf) return damage; - return int(damage * *pdf); + + if (factor < 0.) return damage; + return int(damage * factor); } @@ -614,7 +580,7 @@ static void SummonActor (int command, int command2, FCommandLine argv) if (argv.argc() > 1) { PClassActor *type = PClass::FindActor(argv[1]); - if (type == NULL) + if (type == nullptr) { Printf ("Unknown actor '%s'\n", argv[1]); return; @@ -709,13 +675,15 @@ FString DamageTypeDefinition::GetObituary(FName type) double DamageTypeDefinition::GetMobjDamageFactor(FName type, DmgFactors const * const factors) { + double defaultfac = -1.; if (factors) { // If the actor has named damage factors, look for a specific factor - - auto pdf = factors->CheckKey(type); - if (pdf) return *pdf; // type specific damage type - + for (auto & p : *factors) + { + if (p.first == type) return p.second; // type specific damage type + if (p.first == NAME_None) defaultfac = p.second; + } // If this was nonspecific damage, don't fall back to nonspecific search if (type == NAME_None) return 1.; } @@ -733,18 +701,17 @@ double DamageTypeDefinition::GetMobjDamageFactor(FName type, DmgFactors const * } { - auto pdf = factors->CheckKey(NAME_None); DamageTypeDefinition *dtd = Get(type); // Here we are looking for modifications to untyped damage // If the calling actor defines untyped damage factor, that is contained in "pdf". - if (pdf) // normal damage available + if (defaultfac >= 0.) // normal damage available { if (dtd) { if (dtd->ReplaceFactor) return dtd->DefaultFactor; // use default instead of untyped factor - return *pdf * dtd->DefaultFactor; // use default as modification of untyped factor + return defaultfac * dtd->DefaultFactor; // use default as modification of untyped factor } - return *pdf; // there was no default, so actor default is used + return defaultfac; // there was no default, so actor default is used } else if (dtd) { diff --git a/src/info.h b/src/info.h index 5af8ac5f5..d167631cf 100644 --- a/src/info.h +++ b/src/info.h @@ -177,6 +177,7 @@ public: bool CallAction(AActor *self, AActor *stateowner, FStateParamInfo *stateinfo, FState **stateret); static PClassActor *StaticFindStateOwner (const FState *state); static PClassActor *StaticFindStateOwner (const FState *state, PClassActor *info); + static FString StaticGetStateName(const FState *state); static FRandom pr_statetics; }; @@ -200,11 +201,11 @@ struct FStateLabels #include "gametype.h" -struct DmgFactors : public TMap +struct DmgFactors : public TArray> { int Apply(FName type, int damage); }; -typedef TMap PainChanceList; +typedef TArray> PainChanceList; struct DamageTypeDefinition { @@ -236,17 +237,64 @@ private: struct FDropItem; +struct FActorInfo +{ + TArray LightAssociations; + PClassActor *Replacement = nullptr; + PClassActor *Replacee = nullptr; + FState *OwnedStates = nullptr; + int NumOwnedStates = 0; + uint8_t GameFilter = GAME_Any; + uint16_t SpawnID = 0; + uint16_t ConversationID = 0; + int16_t DoomEdNum = -1; + + FStateLabels *StateList = nullptr; + DmgFactors DamageFactors; + PainChanceList PainChances; + + TArray VisibleToPlayerClass; + + FDropItem *DropItems; + FIntCVar *distancecheck; + + // This is from PClassPlayerPawn + FString DisplayName; + + uint8_t DefaultStateUsage = 0; // state flag defaults for blocks without a qualifier. + + FActorInfo() {} + FActorInfo(const FActorInfo & other) + { + // only copy the fields that get inherited + DefaultStateUsage = other.DefaultStateUsage; + DamageFactors = other.DamageFactors; + PainChances = other.PainChances; + VisibleToPlayerClass = other.VisibleToPlayerClass; + DropItems = other.DropItems; + distancecheck = other.distancecheck; + DisplayName = other.DisplayName; + } + + ~FActorInfo() + { + if (StateList != NULL) + { + StateList->Destroy(); + M_Free(StateList); + } + } +}; + +// This is now merely a wrapper that adds actor-specific functionality to PClass. +// No objects of this type will be created ever - its only use is to static_casr +// PClass to it. class PClassActor : public PClass { - DECLARE_CLASS(PClassActor, PClass); protected: public: static void StaticInit (); static void StaticSetActorNums (); - virtual void DeriveData(PClass *newclass); - - PClassActor(); - ~PClassActor(); void BuildDefaults(); void ApplyDefaults(uint8_t *defaults); @@ -254,7 +302,36 @@ public: void SetDamageFactor(FName type, double factor); void SetPainChance(FName type, int chance); bool SetReplacement(FName replaceName); - void SetDropItems(FDropItem *drops); + + FActorInfo *ActorInfo() const + { + return (FActorInfo*)Meta; + } + + void SetDropItems(FDropItem *drops) + { + ActorInfo()->DropItems = drops; + } + + const FString &GetDisplayName() const + { + return ActorInfo()->DisplayName; + } + + FState *GetStates() const + { + return ActorInfo()->OwnedStates; + } + + unsigned GetStateCount() const + { + return ActorInfo()->NumOwnedStates; + } + + FStateLabels *GetStateLabels() const + { + return ActorInfo()->StateList; + } FState *FindState(int numnames, FName *names, bool exact=false) const; FState *FindStateByString(const char *name, bool exact=false); @@ -265,44 +342,17 @@ public: bool OwnsState(const FState *state) { - return state >= OwnedStates && state < OwnedStates + NumOwnedStates; + auto i = ActorInfo(); + return state >= i->OwnedStates && state < i->OwnedStates + i->NumOwnedStates; } PClassActor *GetReplacement(bool lookskill=true); PClassActor *GetReplacee(bool lookskill=true); - TArray LightAssociations; - FState *OwnedStates; - PClassActor *Replacement; - PClassActor *Replacee; - int NumOwnedStates; - uint8_t GameFilter; - uint8_t DefaultStateUsage; // state flag defaults for blocks without a qualifier. - uint16_t SpawnID; - uint16_t ConversationID; - int16_t DoomEdNum; - FStateLabels *StateList; - DmgFactors *DamageFactors; - PainChanceList *PainChances; - - TArray VisibleToPlayerClass; - - FDropItem *DropItems; - FString SourceLumpName; - FIntCVar *distancecheck; - - // This is from PClassPlayerPawn - FString DisplayName; - // For those times when being able to scan every kind of actor is convenient static TArray AllActorClasses; }; -inline PClassActor *PClass::FindActor(FName name) -{ - return dyn_cast(FindClass(name)); -} - struct FDoomEdEntry { PClassActor *Type; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 437e7ff89..b667415a8 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -48,7 +48,7 @@ #include "r_utility.h" #include "a_morph.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" #include "events.h" #include "p_acs.h" @@ -479,7 +479,7 @@ void cht_DoCheat (player_t *player, int cheat) VMReturn ret; int oldpieces = 1; ret.IntAt(&oldpieces); - GlobalVMStack.Call(gsp, params, 1, &ret, 1, nullptr); + VMCall(gsp, params, 1, &ret, 1); item = player->mo->FindInventory(NAME_Sigil); if (item != NULL) @@ -604,7 +604,7 @@ void cht_Give (player_t *player, const char *name, int amount) { FString namestr = name; VMValue params[3] = { player->mo, &namestr, amount }; - GlobalVMStack.Call(func, params, 3, nullptr, 0); + VMCall(func, params, 3, nullptr, 0); } } @@ -616,7 +616,7 @@ void cht_Take (player_t *player, const char *name, int amount) { FString namestr = name; VMValue params[3] = { player->mo, &namestr, amount }; - GlobalVMStack.Call(func, params, 3, nullptr, 0); + VMCall(func, params, 3, nullptr, 0); } } diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index 59b3ad990..fb50834c0 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -50,6 +50,7 @@ #include "d_gui.h" #include "i_music.h" #include "m_joy.h" +#include "vm.h" static TArray Joysticks; diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index fdca9abc8..f76654165 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -48,6 +48,7 @@ #include "d_gui.h" #include "serializer.h" #include "resourcefiles/resourcefile.h" +#include "vm.h" // Save name length limit for old binary formats. #define OLDSAVESTRINGSIZE 24 diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 89503c42a..bdc46d8b3 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -55,7 +55,7 @@ #include "r_utility.h" #include "menu/menu.h" #include "textures/textures.h" -#include "virtual.h" +#include "vm.h" #include "events.h" #include "gl/renderer/gl_renderer.h" // for menu blur @@ -183,7 +183,7 @@ bool DMenu::CallResponder(event_t *ev) VMValue params[] = { (DObject*)this, &e }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); return !!retval; } } @@ -195,7 +195,7 @@ bool DMenu::CallResponder(event_t *ev) VMValue params[] = { (DObject*)this, &e }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); return !!retval; } } @@ -215,7 +215,7 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller) VMValue params[] = { (DObject*)this, mkey, fromcontroller }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr); + VMCall(func, params, 3, &ret, 1); return !!retval; } else return false; @@ -247,7 +247,7 @@ void DMenu::Close () IFVIRTUALPTR(CurrentMenu, DMenu, OnReturn) { VMValue params[] = { CurrentMenu }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -275,7 +275,7 @@ void DMenu::CallTicker() IFVIRTUAL(DMenu, Ticker) { VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -285,7 +285,7 @@ void DMenu::CallDrawer() IFVIRTUAL(DMenu, Drawer) { VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); screen->ClearClipRect(); // make sure the scripts don't leave a valid clipping rect behind. } } @@ -297,7 +297,7 @@ bool DMenu::TranslateKeyboardEvents() VMValue params[] = { (DObject*)this }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + VMCall(func, params, countof(params), &ret, 1); return !!retval; } return true; @@ -473,7 +473,7 @@ void M_SetMenu(FName menu, int param) IFVIRTUALPTRNAME(newmenu, "ListMenu", Init) { VMValue params[3] = { newmenu, CurrentMenu, ld }; - GlobalVMStack.Call(func, params, 3, nullptr, 0); + VMCall(func, params, 3, nullptr, 0); } M_ActivateMenu(newmenu); } @@ -489,7 +489,7 @@ void M_SetMenu(FName menu, int param) IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init) { VMValue params[3] = { newmenu, CurrentMenu, ld }; - GlobalVMStack.Call(func, params, 3, nullptr, 0); + VMCall(func, params, 3, nullptr, 0); } M_ActivateMenu(newmenu); } @@ -507,7 +507,7 @@ void M_SetMenu(FName menu, int param) IFVIRTUALPTRNAME(newmenu, "GenericMenu", Init) { VMValue params[3] = { newmenu, CurrentMenu }; - GlobalVMStack.Call(func, params, 2, nullptr, 0); + VMCall(func, params, 2, nullptr, 0); } M_ActivateMenu(newmenu); return; @@ -1061,7 +1061,7 @@ CCMD(undocolorpic) IFVIRTUALPTR(CurrentMenu, DMenu, ResetColor) { VMValue params[] = { (DObject*)CurrentMenu }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr); + VMCall(func, params, countof(params), nullptr, 0); } } } @@ -1130,8 +1130,8 @@ DMenuItemBase * CreateOptionMenuItemStaticText(const char *name, bool v) auto p = c->CreateNew(); FString namestr = name; VMValue params[] = { p, &namestr, v }; - auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); - GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + auto f = dyn_cast(c->FindSymbol("Init", false)); + VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0); return (DMenuItemBase*)p; } @@ -1141,8 +1141,8 @@ DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickCo auto p = c->CreateNew(); FString namestr = label; VMValue params[] = { p, &namestr, joy }; - auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); - GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + auto f = dyn_cast(c->FindSymbol("Init", false)); + VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0); return (DMenuItemBase*)p; } @@ -1152,8 +1152,8 @@ DMenuItemBase * CreateOptionMenuItemSubmenu(const char *label, FName cmd, int ce auto p = c->CreateNew(); FString namestr = label; VMValue params[] = { p, &namestr, cmd.GetIndex(), center }; - auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); - GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + auto f = dyn_cast(c->FindSymbol("Init", false)); + VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0); return (DMenuItemBase*)p; } @@ -1163,8 +1163,8 @@ DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBi auto p = c->CreateNew(); FString namestr = label; VMValue params[] = { p, &namestr, cmd.GetIndex(), bindings }; - auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); - GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + auto f = dyn_cast(c->FindSymbol("Init", false)); + VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0); return (DMenuItemBase*)p; } @@ -1174,8 +1174,8 @@ DMenuItemBase * CreateListMenuItemPatch(double x, double y, int height, int hotk auto p = c->CreateNew(); FString keystr = FString(char(hotkey)); VMValue params[] = { p, x, y, height, tex.GetIndex(), &keystr, command.GetIndex(), param }; - auto f = dyn_cast(c->Symbols.FindSymbol("InitDirect", false)); - GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + auto f = dyn_cast(c->FindSymbol("InitDirect", false)); + VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0); return (DMenuItemBase*)p; } @@ -1186,8 +1186,8 @@ DMenuItemBase * CreateListMenuItemText(double x, double y, int height, int hotke FString keystr = FString(char(hotkey)); FString textstr = text; VMValue params[] = { p, x, y, height, &keystr, &textstr, font, int(color1.d), int(color2.d), command.GetIndex(), param }; - auto f = dyn_cast(c->Symbols.FindSymbol("InitDirect", false)); - GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + auto f = dyn_cast(c->FindSymbol("InitDirect", false)); + VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0); return (DMenuItemBase*)p; } @@ -1198,7 +1198,7 @@ bool DMenuItemBase::Activate() VMValue params[] = { (DObject*)this }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + VMCall(func, params, countof(params), &ret, 1); return !!retval; } return false; @@ -1212,7 +1212,7 @@ bool DMenuItemBase::SetString(int i, const char *s) VMValue params[] = { (DObject*)this, i, &namestr }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + VMCall(func, params, countof(params), &ret, 1); return !!retval; } return false; @@ -1226,7 +1226,7 @@ bool DMenuItemBase::GetString(int i, char *s, int len) int retval; FString retstr; VMReturn ret[2]; ret[0].IntAt(&retval); ret[1].StringAt(&retstr); - GlobalVMStack.Call(func, params, countof(params), ret, 2, nullptr); + VMCall(func, params, countof(params), ret, 2); strncpy(s, retstr, len); return !!retval; } @@ -1241,7 +1241,7 @@ bool DMenuItemBase::SetValue(int i, int value) VMValue params[] = { (DObject*)this, i, value }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + VMCall(func, params, countof(params), &ret, 1); return !!retval; } return false; @@ -1254,7 +1254,7 @@ bool DMenuItemBase::GetValue(int i, int *pvalue) VMValue params[] = { (DObject*)this, i }; int retval[2]; VMReturn ret[2]; ret[0].IntAt(&retval[0]); ret[1].IntAt(&retval[1]); - GlobalVMStack.Call(func, params, countof(params), ret, 2, nullptr); + VMCall(func, params, countof(params), ret, 2); *pvalue = retval[1]; return !!retval[0]; } diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index ce7137293..5a2f18974 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -51,6 +51,8 @@ #include "gi.h" #include "i_sound.h" #include "cmdlib.h" +#include "vm.h" +#include "types.h" @@ -371,7 +373,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) PClass *cls = PClass::FindClass(buildname); if (cls != nullptr && cls->IsDescendantOf("ListMenuItem")) { - auto func = dyn_cast(cls->Symbols.FindSymbol("Init", true)); + auto func = dyn_cast(cls->FindSymbol("Init", true)); if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method. { auto &args = func->Variants[0].Proto->ArgumentTypes; @@ -384,7 +386,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) params.Push(desc); start = 2; } - auto TypeCVar = NewPointer(NewNativeStruct("CVar", nullptr)); + auto TypeCVar = NewPointer(NewStruct("CVar", nullptr, true)); // Note that this array may not be reallocated so its initial size must be the maximum possible elements. TArray strings(args.Size()); @@ -480,7 +482,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) } DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); params[0] = item; - GlobalVMStack.Call(func->Variants[0].Implementation, ¶ms[0], params.Size(), nullptr, 0); + VMCall(func->Variants[0].Implementation, ¶ms[0], params.Size(), nullptr, 0); desc->mItems.Push((DMenuItemBase*)item); if (cls->IsDescendantOf("ListMenuItemSelectable")) @@ -746,14 +748,14 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc) PClass *cls = PClass::FindClass(buildname); if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem")) { - auto func = dyn_cast(cls->Symbols.FindSymbol("Init", true)); + auto func = dyn_cast(cls->FindSymbol("Init", true)); if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method. { auto &args = func->Variants[0].Proto->ArgumentTypes; TArray params; params.Push(0); - auto TypeCVar = NewPointer(NewNativeStruct("CVar", nullptr)); + auto TypeCVar = NewPointer(NewStruct("CVar", nullptr, true)); // Note that this array may not be reallocated so its initial size must be the maximum possible elements. TArray strings(args.Size()); @@ -837,7 +839,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc) DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); params[0] = item; - GlobalVMStack.Call(func->Variants[0].Implementation, ¶ms[0], params.Size(), nullptr, 0); + VMCall(func->Variants[0].Implementation, ¶ms[0], params.Size(), nullptr, 0); desc->mItems.Push((DMenuItemBase*)item); success = true; diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp index 514331d7f..368420964 100644 --- a/src/menu/messagebox.cpp +++ b/src/menu/messagebox.cpp @@ -45,6 +45,7 @@ #include "st_start.h" #include "c_dispatch.h" #include "g_game.h" +#include "vm.h" EXTERN_CVAR (Bool, saveloadconfirmation) // [mxd] @@ -70,8 +71,8 @@ DMenu *CreateMessageBoxMenu(DMenu *parent, const char *message, int messagemode, FString namestr = message; VMValue params[] = { p, parent, &namestr, messagemode, playsound, action.GetIndex(), reinterpret_cast(handler) }; - auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); - GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + auto f = dyn_cast(c->FindSymbol("Init", false)); + VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0); return (DMenu*)p; } diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index fa5796a45..55f85c221 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -47,6 +47,7 @@ #include "r_state.h" #include "r_data/r_translate.h" #include "v_text.h" +#include "vm.h" EXTERN_CVAR(Int, team) EXTERN_CVAR(Float, autoaim) diff --git a/src/menu/videomenu.cpp b/src/menu/videomenu.cpp index b23798727..5f139f0af 100644 --- a/src/menu/videomenu.cpp +++ b/src/menu/videomenu.cpp @@ -53,6 +53,7 @@ #include "m_joy.h" #include "sbar.h" #include "hardware.h" +#include "vm.h" /*======================================= * diff --git a/src/p_acs.cpp b/src/p_acs.cpp index f9e0f58e7..55dfdd8e6 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -87,6 +87,8 @@ #include "g_levellocals.h" #include "actorinlines.h" #include "stats.h" +#include "types.h" +#include "vm.h" // P-codes for ACS scripts enum @@ -5012,7 +5014,7 @@ int DLevelScript::LineFromID(int id) bool GetVarAddrType(AActor *self, FName varname, int index, void *&addr, PType *&type, bool readonly) { - PField *var = dyn_cast(self->GetClass()->Symbols.FindSymbol(varname, true)); + PField *var = dyn_cast(self->GetClass()->FindSymbol(varname, true)); PArray *arraytype; if (var == NULL || (!readonly && (var->Flags & VARF_Native))) @@ -5362,7 +5364,7 @@ static int ScriptCall(AActor *activator, unsigned argc, int32_t *args) { I_Error("ACS call to unknown class in script function %s.%s", clsname, funcname); } - auto funcsym = dyn_cast(cls->Symbols.FindSymbol(funcname, true)); + auto funcsym = dyn_cast(cls->FindSymbol(funcname, true)); if (funcsym == nullptr) { I_Error("ACS call to unknown script function %s.%s", clsname, funcname); @@ -5431,7 +5433,7 @@ static int ScriptCall(AActor *activator, unsigned argc, int32_t *args) // The return value can be the same types as the parameter types, plus void if (func->Proto->ReturnTypes.Size() == 0) { - GlobalVMStack.Call(func, ¶ms[0], params.Size(), nullptr, 0); + VMCall(func, ¶ms[0], params.Size(), nullptr, 0); } else { @@ -5439,7 +5441,7 @@ static int ScriptCall(AActor *activator, unsigned argc, int32_t *args) if (rettype == TypeSInt32 || rettype == TypeBool || rettype == TypeColor || rettype == TypeName || rettype == TypeSound) { VMReturn ret(&retval); - GlobalVMStack.Call(func, ¶ms[0], params.Size(), &ret, 1); + VMCall(func, ¶ms[0], params.Size(), &ret, 1); if (rettype == TypeName) { retval = GlobalACSStrings.AddString(FName(ENamedName(retval))); @@ -5453,20 +5455,20 @@ static int ScriptCall(AActor *activator, unsigned argc, int32_t *args) { double d; VMReturn ret(&d); - GlobalVMStack.Call(func, ¶ms[0], params.Size(), &ret, 1); + VMCall(func, ¶ms[0], params.Size(), &ret, 1); retval = DoubleToACS(d); } else if (rettype == TypeString) { FString d; VMReturn ret(&d); - GlobalVMStack.Call(func, ¶ms[0], params.Size(), &ret, 1); + VMCall(func, ¶ms[0], params.Size(), &ret, 1); retval = GlobalACSStrings.AddString(d); } else { // All other return values can not be handled so ignore them. - GlobalVMStack.Call(func, ¶ms[0], params.Size(), nullptr, 0); + VMCall(func, ¶ms[0], params.Size(), nullptr, 0); } } } @@ -6910,7 +6912,7 @@ static void SetMarineWeapon(AActor *marine, int weapon) if (smw) { VMValue params[2] = { marine, weapon }; - GlobalVMStack.Call(smw, params, 2, nullptr, 0, nullptr); + VMCall(smw, params, 2, nullptr, 0); } } @@ -6921,7 +6923,7 @@ static void SetMarineSprite(AActor *marine, PClassActor *source) if (sms) { VMValue params[2] = { marine, source }; - GlobalVMStack.Call(sms, params, 2, nullptr, 0, nullptr); + VMCall(sms, params, 2, nullptr, 0); } } @@ -9777,8 +9779,7 @@ scriptwait: } else { - AInventory *item = activator->FindInventory (dyn_cast( - PClass::FindClass (FBehavior::StaticLookupString (STACK(1))))); + AInventory *item = activator->FindInventory (PClass::FindActor (FBehavior::StaticLookupString (STACK(1)))); if (item == NULL || !item->IsKindOf(NAME_Weapon)) { diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 97be1b108..8fe698a2f 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -82,6 +82,8 @@ #include "r_utility.h" #include "sbar.h" #include "actorinlines.h" +#include "vm.h" +#include "types.h" AActor *SingleActorFromTID(int tid, AActor *defactor); @@ -130,8 +132,7 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) { if (!(state->UseFlags & SUF_ITEM)) { - auto so = FState::StaticFindStateOwner(state); - Printf(TEXTCOLOR_RED "State %s.%d not flagged for use in CustomInventory state chains.\n", so->TypeName.GetChars(), int(state - so->OwnedStates)); + Printf(TEXTCOLOR_RED "State %s not flagged for use in CustomInventory state chains.\n", FState::StaticGetStateName(state)); return false; } @@ -144,8 +145,8 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) { // If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash. auto owner = FState::StaticFindStateOwner(state); - Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s which accesses user variables. The action function has been removed from this state\n", - owner->TypeName.GetChars(), int(state - owner->OwnedStates), state->ActionFunc->PrintableName.GetChars()); + Printf(TEXTCOLOR_RED "Unsafe state call in state %s to %s which accesses user variables. The action function has been removed from this state\n", + FState::StaticGetStateName(state), state->ActionFunc->PrintableName.GetChars()); state->ActionFunc = nullptr; } @@ -183,14 +184,12 @@ bool AStateProvider::CallStateChain (AActor *actor, FState *state) } try { - GlobalVMStack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); + VMCall(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); } catch (CVMAbortException &err) { err.MaybePrintMessage(); - auto owner = FState::StaticFindStateOwner(state); - int offs = int(state - owner->OwnedStates); - err.stacktrace.AppendFormat("Called from state %s.%d in inventory state chain in %s\n", owner->TypeName.GetChars(), offs, GetClass()->TypeName.GetChars()); + err.stacktrace.AppendFormat("Called from state %s in inventory state chain in %s\n", FState::StaticGetStateName(state), GetClass()->TypeName.GetChars()); throw; } @@ -3320,9 +3319,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_SpawnDebris) { mo->Translation = self->Translation; } - if (i < mo->GetClass()->NumOwnedStates) + if (i < mo->GetInfo()->NumOwnedStates) { - mo->SetState (mo->GetClass()->OwnedStates + i); + mo->SetState (mo->GetInfo()->OwnedStates + i); } mo->Vel.X = mult_h * pr_spawndebris.Random2() / 64.; mo->Vel.Y = mult_h * pr_spawndebris.Random2() / 64.; @@ -4752,7 +4751,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeVelocity) static PField *GetVar(DObject *self, FName varname) { - PField *var = dyn_cast(self->GetClass()->Symbols.FindSymbol(varname, true)); + PField *var = dyn_cast(self->GetClass()->FindSymbol(varname, true)); if (var == NULL || (var->Flags & (VARF_Native | VARF_Private | VARF_Protected | VARF_Static)) || !var->Type->IsKindOf(RUNTIME_CLASS(PBasicType))) { @@ -4801,7 +4800,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SetUserVarFloat) static PField *GetArrayVar(DObject *self, FName varname, int pos) { - PField *var = dyn_cast(self->GetClass()->Symbols.FindSymbol(varname, true)); + PField *var = dyn_cast(self->GetClass()->FindSymbol(varname, true)); if (var == NULL || (var->Flags & (VARF_Native | VARF_Private | VARF_Protected | VARF_Static)) || !var->Type->IsKindOf(RUNTIME_CLASS(PArray)) || diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 4c2c509e8..c7bc715f2 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -33,6 +33,7 @@ #include "p_spec.h" #include "g_levellocals.h" #include "textures.h" +#include "vm.h" //============================================================================ // diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index d01fb5193..bbd9281b7 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -61,7 +61,7 @@ #include "p_local.h" #include "menu/menu.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" #include "actorinlines.h" // The conversations as they exist inside a SCRIPTxx lump. @@ -367,7 +367,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, uint32_t &prevSpea node->SpeakerName = speech.Name; // The item the speaker should drop when killed. - node->DropType = dyn_cast(GetStrifeType(speech.DropType)); + node->DropType = GetStrifeType(speech.DropType); // Items you need to have to make the speaker use a different node. node->ItemCheck.Resize(3); @@ -447,7 +447,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, uint32_t &prevSpea node->SpeakerName = speech.Name; // The item the speaker should drop when killed. - node->DropType = dyn_cast(GetStrifeType (speech.DropType)); + node->DropType = GetStrifeType (speech.DropType); // Items you need to have to make the speaker use a different node. node->ItemCheck.Resize(3); @@ -512,7 +512,7 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses) reply->LogString = ""; // The item to receive when this reply is used. - reply->GiveType = dyn_cast(GetStrifeType (rsp->GiveType)); + reply->GiveType = GetStrifeType (rsp->GiveType); reply->ActionSpecial = 0; // Do you need anything special for this reply to succeed? @@ -639,7 +639,7 @@ static void TakeStrifeItem (player_t *player, PClassActor *itemtype, int amount) return; // Don't take the sigil. - if (itemtype->GetClass()->TypeName == NAME_Sigil) + if (itemtype->TypeName == NAME_Sigil) return; player->mo->TakeInventory(itemtype, amount); @@ -867,7 +867,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang { VMValue params[] = { cmenu, CurNode, pc->player, StaticLastReply }; VMReturn ret(&ConversationMenuY); - GlobalVMStack.Call(func, params, countof(params), &ret, 1); + VMCall(func, params, countof(params), &ret, 1); } if (CurNode != PrevNode) diff --git a/src/p_effect.cpp b/src/p_effect.cpp index 20579ced4..81a1bc5d6 100644 --- a/src/p_effect.cpp +++ b/src/p_effect.cpp @@ -54,6 +54,7 @@ #include "d_player.h" #include "r_utility.h" #include "g_levellocals.h" +#include "vm.h" CVAR (Int, cl_rockettrails, 1, CVAR_ARCHIVE); CVAR (Bool, r_rail_smartspiral, 0, CVAR_ARCHIVE); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index aca2d8bb0..533098abd 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -52,7 +52,7 @@ #include "p_checkposition.h" #include "math/cmath.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" #include "actorinlines.h" #include "gi.h" @@ -3332,7 +3332,7 @@ AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int c VMValue params[2] = { inv, source }; int retval; VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); if (retval) { // The special action indicates that the item should not spawn diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 8f7d935ee..918419fbe 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -33,6 +33,7 @@ #include "p_spec.h" #include "r_data/r_interpolate.h" #include "g_levellocals.h" +#include "vm.h" //========================================================================== // diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index b4db7cb0b..6de00eb37 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -58,7 +58,7 @@ #include "d_net.h" #include "d_netinf.h" #include "a_morph.h" -#include "virtual.h" +#include "vm.h" #include "g_levellocals.h" #include "events.h" #include "actorinlines.h" @@ -248,7 +248,7 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf VMValue params[] = { attacker, self, inflictor, mod.GetIndex(), !!(dmgflags & DMG_PLAYERATTACK) }; FString ret; VMReturn rett(&ret); - GlobalVMStack.Call(func, params, countof(params), &rett, 1); + VMCall(func, params, countof(params), &rett, 1); if (ret.IsNotEmpty()) message = ret; } } @@ -326,7 +326,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) IFVIRTUALPTR(item, AInventory, OwnerDied) { VMValue params[1] = { item }; - GlobalVMStack.Call(func, params, 1, nullptr, 0); + VMCall(func, params, 1, nullptr, 0); } item = next; } @@ -367,7 +367,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) { VMValue params[] = { (DObject*)this }; VMReturn ret(&Height); - GlobalVMStack.Call(func, params, 1, &ret, 1); + VMCall(func, params, 1, &ret, 1); } // [RH] If the thing has a special, execute and remove it @@ -733,7 +733,7 @@ void AActor::CallDie(AActor *source, AActor *inflictor, int dmgflags) IFVIRTUAL(AActor, Die) { VMValue params[4] = { (DObject*)this, source, inflictor, dmgflags }; - GlobalVMStack.Call(func, params, 4, nullptr, 0, nullptr); + VMCall(func, params, 4, nullptr, 0); } else return Die(source, inflictor, dmgflags); } @@ -916,7 +916,6 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da int temp; int painchance = 0; FState * woundstate = NULL; - PainChanceList * pc = NULL; bool justhit = false; bool plrDontThrust = false; bool invulpain = false; @@ -1422,7 +1421,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da { VMValue params[] = { source, target, draindmg, mod.GetIndex() }; VMReturn ret(&draindmg); - GlobalVMStack.Call(func, params, countof(params), &ret, 1); + VMCall(func, params, countof(params), &ret, 1); } if (P_GiveBody(source, draindmg)) { @@ -1500,14 +1499,13 @@ fakepain: //Needed so we can skip the rest of the above, but still obey the orig if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) && (target->player != NULL || !G_SkillProperty(SKILLP_NoPain)) && !(target->flags & MF_SKULLFLY)) { - pc = target->GetClass()->PainChances; painchance = target->PainChance; - if (pc != NULL) + for (auto & pc : target->GetInfo()->PainChances) { - int *ppc = pc->CheckKey(mod); - if (ppc != NULL) + if (pc.first == mod) { - painchance = *ppc; + painchance = pc.second; + break; } } @@ -1620,7 +1618,7 @@ int P_DamageMobj(AActor *target, AActor *inflictor, AActor *source, int damage, VMReturn ret; int retval; ret.IntAt(&retval); - GlobalVMStack.Call(func, params, 7, &ret, 1, nullptr); + VMCall(func, params, 7, &ret, 1); return retval; } else @@ -1772,7 +1770,7 @@ bool AActor::CallOkayToSwitchTarget(AActor *other) VMValue params[] = { (DObject*)this, other }; int retv; VMReturn ret(&retv); - GlobalVMStack.Call(func, params, 2, &ret, 1); + VMCall(func, params, 2, &ret, 1); return !!retv; } return OkayToSwitchTarget(other); diff --git a/src/p_linkedsectors.cpp b/src/p_linkedsectors.cpp index 7ac614f2e..11e520a0d 100644 --- a/src/p_linkedsectors.cpp +++ b/src/p_linkedsectors.cpp @@ -38,6 +38,7 @@ #include "p_lnspec.h" #include "p_spec.h" #include "g_levellocals.h" +#include "vm.h" enum { diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 39b97f1d8..12a29dd3b 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -62,6 +62,7 @@ #include "fragglescript/t_fs.h" #include "p_spec.h" #include "g_levellocals.h" +#include "vm.h" // Remaps EE sector change types to Generic_Floor values. According to the Eternity Wiki: /* diff --git a/src/p_map.cpp b/src/p_map.cpp index d2d98d237..dd00519e2 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -47,7 +47,7 @@ #include "r_utility.h" #include "p_blockmap.h" #include "p_3dmidtex.h" -#include "virtual.h" +#include "vm.h" #include "s_sound.h" #include "decallib.h" @@ -154,7 +154,7 @@ bool P_CanCollideWith(AActor *tmthing, AActor *thing) VMFunction *func = clss->Virtuals.Size() > VIndex ? clss->Virtuals[VIndex] : nullptr; if (func != nullptr) { - GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr); + VMCall(func, params, 3, &ret, 1); if (!retval) return false; } std::swap(params[0].a, params[1].a); @@ -165,7 +165,7 @@ bool P_CanCollideWith(AActor *tmthing, AActor *thing) func = clss->Virtuals.Size() > VIndex ? clss->Virtuals[VIndex] : nullptr; if (func != nullptr) { - GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr); + VMCall(func, params, 3, &ret, 1); if (!retval) return false; } return true; @@ -5302,7 +5302,7 @@ bool P_UseTraverse(AActor *usething, const DVector2 &start, const DVector2 &end, VMValue params[] = { mobj, usething }; int ret; VMReturn vret(&ret); - GlobalVMStack.Call(func, params, 2, &vret, 1); + VMCall(func, params, 2, &vret, 1); if (ret) return true; } continue; diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index fc31d60ca..21ed99c61 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -47,6 +47,7 @@ #include "templates.h" #include "po_man.h" #include "g_levellocals.h" +#include "vm.h" sector_t *P_PointInSectorBuggy(double x, double y); int P_VanillaPointOnDivlineSide(double x, double y, const divline_t* line); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 23b079426..02fd99077 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -68,7 +68,7 @@ #include "r_utility.h" #include "thingdef.h" #include "d_player.h" -#include "virtual.h" +#include "vm.h" #include "g_levellocals.h" #include "a_morph.h" #include "events.h" @@ -153,9 +153,6 @@ AActor::~AActor () // Use Destroy() instead. } -extern FFlagDef InternalActorFlagDefs[]; -extern FFlagDef ActorFlagDefs[]; - DEFINE_FIELD(AActor, snext) DEFINE_FIELD(AActor, player) DEFINE_FIELD_NAMED(AActor, __Pos, pos) @@ -632,8 +629,7 @@ bool AActor::SetState (FState *newstate, bool nofunction) } if (!(newstate->UseFlags & SUF_ACTOR)) { - auto so = FState::StaticFindStateOwner(newstate); - Printf(TEXTCOLOR_RED "State %s.%d in %s not flagged for use as an actor sprite\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates), GetClass()->TypeName.GetChars()); + Printf(TEXTCOLOR_RED "State %s in %s not flagged for use as an actor sprite\n", FState::StaticGetStateName(newstate), GetClass()->TypeName.GetChars()); state = nullptr; Destroy(); return false; @@ -842,7 +838,7 @@ void AActor::RemoveInventory(AInventory *item) IFVIRTUALPTR(item, AInventory, DetachFromOwner) { VMValue params[1] = { item }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } item->Owner = NULL; @@ -1110,7 +1106,7 @@ AInventory *AActor::DropInventory (AInventory *item, int amt) { VMValue params[] = { (DObject*)item, amt }; VMReturn ret((void**)&drop); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + VMCall(func, params, countof(params), &ret, 1); } if (drop == nullptr) return NULL; drop->SetOrigin(PosPlusZ(10.), false); @@ -1606,22 +1602,21 @@ bool AActor::IsVisibleToPlayer() const (signed)(VisibleToTeam-1) != players[consoleplayer].userinfo.GetTeam() ) return false; + auto &vis = GetInfo()->VisibleToPlayerClass; + if (vis.Size() == 0) return true; // early out for the most common case. + const player_t* pPlayer = players[consoleplayer].camera->player; - if (pPlayer && pPlayer->mo && GetClass()->VisibleToPlayerClass.Size() > 0) + if (pPlayer) { - bool visible = false; - for(unsigned int i = 0;i < GetClass()->VisibleToPlayerClass.Size();++i) + for(auto cls : vis) { - auto cls = GetClass()->VisibleToPlayerClass[i]; if (cls && pPlayer->mo->GetClass()->IsDescendantOf(cls)) { - visible = true; - break; + return true; } } - if (!visible) - return false; + return false; } // [BB] Passed all checks. @@ -1683,7 +1678,7 @@ void AActor::CallTouch(AActor *toucher) IFVIRTUAL(AActor, Touch) { VMValue params[2] = { (DObject*)this, toucher }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } else Touch(toucher); } @@ -3575,7 +3570,7 @@ int AActor::GetMissileDamage (int mask, int add) result.IntAt(&amount); - if (GlobalVMStack.Call(DamageFunc, ¶m, 1, &result, 1) < 1) + if (VMCall(DamageFunc, ¶m, 1, &result, 1) < 1) { // No results return 0; } @@ -3640,7 +3635,7 @@ bool AActor::CallSlam(AActor *thing) VMReturn ret; int retval; ret.IntAt(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); return !!retval; } @@ -3658,7 +3653,7 @@ int AActor::SpecialMissileHit (AActor *victim) VMReturn ret; int retval; ret.IntAt(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + VMCall(func, params, 2, &ret, 1); return retval; } else return -1; @@ -3718,7 +3713,7 @@ int AActor::AbsorbDamage(int damage, FName dmgtype) IFVIRTUALPTR(item, AInventory, AbsorbDamage) { VMValue params[4] = { item, damage, dmgtype.GetIndex(), &damage }; - GlobalVMStack.Call(func, params, 4, nullptr, 0, nullptr); + VMCall(func, params, 4, nullptr, 0); } } return damage; @@ -3738,7 +3733,7 @@ void AActor::AlterWeaponSprite(visstyle_t *vis) IFVIRTUALPTR(items[i], AInventory, AlterWeaponSprite) { VMValue params[3] = { items[i], vis, &changed }; - GlobalVMStack.Call(func, params, 3, nullptr, 0, nullptr); + VMCall(func, params, 3, nullptr, 0); } } } @@ -3893,7 +3888,7 @@ PClassActor *AActor::GetBloodType(int type) const VMValue params[] = { (DObject*)this, type }; PClassActor *res; VMReturn ret((void**)&res); - GlobalVMStack.Call(func, params, countof(params), &ret, 1); + VMCall(func, params, countof(params), &ret, 1); return res; } return nullptr; @@ -4053,7 +4048,7 @@ void AActor::Tick () IFVIRTUALPTR(item, AInventory, DoEffect) { VMValue params[1] = { item }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } item = item->Inventory; } @@ -5043,7 +5038,7 @@ PClassActor *ClassForSpawn(FName classname) { I_Error("Attempt to spawn actor of unknown type '%s'\n", classname.GetChars()); } - if (!cls->IsKindOf(RUNTIME_CLASS(PClassActor))) + if (!cls->IsDescendantOf(RUNTIME_CLASS(AActor))) { I_Error("Attempt to spawn non-actor of type '%s'\n", classname.GetChars()); } @@ -5141,7 +5136,7 @@ void AActor::CallBeginPlay() { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } else BeginPlay(); } @@ -5232,7 +5227,7 @@ void AActor::CallActivate(AActor *activator) { // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)this, (DObject*)activator }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } else Activate(activator); } @@ -5278,7 +5273,7 @@ void AActor::CallDeactivate(AActor *activator) { // Without the type cast this picks the 'void *' assignment... VMValue params[2] = { (DObject*)this, (DObject*)activator }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMCall(func, params, 2, nullptr, 0); } else Deactivate(activator); } @@ -5584,7 +5579,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) IFVIRTUALPTR(p->mo, APlayerPawn, OnRespawn) { VMValue param = p->mo; - GlobalVMStack.Call(func, ¶m, 1, nullptr, 0); + VMCall(func, ¶m, 1, nullptr, 0); } } @@ -5865,7 +5860,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) Printf ("%s at (%.1f, %.1f) has no frames\n", i->TypeName.GetChars(), mthing->pos.X, mthing->pos.Y); i = PClass::FindActor("Unknown"); - assert(i->IsKindOf(RUNTIME_CLASS(PClassActor))); + assert(i->IsDescendantOf(RUNTIME_CLASS(AActor))); } const AActor *info = GetDefaultByType (i); @@ -7460,7 +7455,7 @@ int AActor::CallDoSpecialDamage(AActor *target, int damage, FName damagetype) VMReturn ret; int retval; ret.IntAt(&retval); - GlobalVMStack.Call(func, params, 4, &ret, 1, nullptr); + VMCall(func, params, 4, &ret, 1); return retval; } else return DoSpecialDamage(target, damage, damagetype); @@ -7525,7 +7520,7 @@ int AActor::CallTakeSpecialDamage(AActor *inflictor, AActor *source, int damage, VMReturn ret; int retval; ret.IntAt(&retval); - GlobalVMStack.Call(func, params, 5, &ret, 1, nullptr); + VMCall(func, params, 5, &ret, 1); return retval; } else return TakeSpecialDamage(inflictor, source, damage, damagetype); @@ -7669,7 +7664,7 @@ int AActor::GetGibHealth() const VMValue params[] = { (DObject*)this }; int h; VMReturn ret(&h); - GlobalVMStack.Call(func, params, 1, &ret, 1); + VMCall(func, params, 1, &ret, 1); return h; } return -SpawnHealth(); @@ -7689,7 +7684,7 @@ DEFINE_ACTION_FUNCTION(AActor, GetCameraHeight) FDropItem *AActor::GetDropItems() const { - return GetClass()->DropItems; + return GetInfo()->DropItems; } DEFINE_ACTION_FUNCTION(AActor, GetDropItems) @@ -7805,7 +7800,7 @@ int AActor::GetModifiedDamage(FName damagetype, int damage, bool passive) IFVIRTUALPTR(inv, AInventory, ModifyDamage) { VMValue params[5] = { (DObject*)inv, damage, int(damagetype), &damage, passive }; - GlobalVMStack.Call(func, params, 5, nullptr, 0, nullptr); + VMCall(func, params, 5, nullptr, 0); } inv = inv->Inventory; } @@ -7826,7 +7821,7 @@ int AActor::ApplyDamageFactor(FName damagetype, int damage) const damage = int(damage * DamageFactor); if (damage > 0) { - damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, damagetype, GetClass()->DamageFactors); + damage = DamageTypeDefinition::ApplyMobjDamageFactor(damage, damagetype, &GetInfo()->DamageFactors); } return damage; } @@ -8277,10 +8272,10 @@ DEFINE_ACTION_FUNCTION(AActor, ApplyDamageFactors) PARAM_INT(damage); PARAM_INT(defdamage); - DmgFactors *df = itemcls->DamageFactors; - if (df != nullptr && df->CountUsed() != 0) + DmgFactors &df = itemcls->ActorInfo()->DamageFactors; + if (df.Size() != 0) { - ACTION_RETURN_INT(df->Apply(damagetype, damage)); + ACTION_RETURN_INT(df.Apply(damagetype, damage)); } else { diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index c0e8a3a4b..12c158c94 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -31,6 +31,7 @@ #include "v_text.h" #include "cmdlib.h" #include "g_levellocals.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ @@ -350,8 +351,7 @@ void DPSprite::SetState(FState *newstate, bool pending) if (!(newstate->UseFlags & (SUF_OVERLAY|SUF_WEAPON))) // Weapon and overlay are mostly the same, the main difference is that weapon states restrict the self pointer to class Actor. { - auto so = FState::StaticFindStateOwner(newstate); - Printf(TEXTCOLOR_RED "State %s.%d not flagged for use in overlays or weapons\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates)); + Printf(TEXTCOLOR_RED "State %s not flagged for use in overlays or weapons\n", FState::StaticGetStateName(newstate)); State = nullptr; Destroy(); return; @@ -360,8 +360,7 @@ void DPSprite::SetState(FState *newstate, bool pending) { if (Caller->IsKindOf(NAME_Weapon)) { - auto so = FState::StaticFindStateOwner(newstate); - Printf(TEXTCOLOR_RED "State %s.%d not flagged for use in weapons\n", so->TypeName.GetChars(), int(newstate - so->OwnedStates)); + Printf(TEXTCOLOR_RED "State %s.%d not flagged for use in weapons\n", FState::StaticGetStateName(newstate)); State = nullptr; Destroy(); return; @@ -414,9 +413,8 @@ void DPSprite::SetState(FState *newstate, bool pending) if (newstate->ActionFunc != nullptr && newstate->ActionFunc->Unsafe) { // If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash. - auto owner = FState::StaticFindStateOwner(newstate); - Printf(TEXTCOLOR_RED "Unsafe state call in state %s.%d to %s which accesses user variables. The action function has been removed from this state\n", - owner->TypeName.GetChars(), int(newstate - owner->OwnedStates), newstate->ActionFunc->PrintableName.GetChars()); + Printf(TEXTCOLOR_RED "Unsafe state call in state %sd to %s which accesses user variables. The action function has been removed from this state\n", + FState::StaticGetStateName(newstate), newstate->ActionFunc->PrintableName.GetChars()); newstate->ActionFunc = nullptr; } if (newstate->CallAction(Owner->mo, Caller, &stp, &nextstate)) @@ -1512,11 +1510,11 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i PClassActor *cls = weapon->GetClass(); while (cls != RUNTIME_CLASS(AWeapon)) { - if (flashstate >= cls->OwnedStates && flashstate < cls->OwnedStates + cls->NumOwnedStates) + if (cls->OwnsState(flashstate)) { // The flash state belongs to this class. // Now let's check if the actually wanted state does also - if (flashstate + index < cls->OwnedStates + cls->NumOwnedStates) + if (cls->OwnsState(flashstate + index)) { // we're ok so set the state P_SetPsprite(player, PSP_FLASH, flashstate + index, true); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index f191afe5d..b20e9af09 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -852,7 +852,7 @@ void CopyPlayer(player_t *dst, player_t *src, const char *name) { dst->userinfo.TransferFrom(uibackup); // The player class must come from the save, so that the menu reflects the currently playing one. - dst->userinfo.PlayerClassChanged(src->mo->GetClass()->DisplayName); + dst->userinfo.PlayerClassChanged(src->mo->GetInfo()->DisplayName); } // Validate the skin diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index b64478c77..f84afb1ff 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -36,7 +36,7 @@ #include "r_sky.h" #include "r_data/colormaps.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" // [RH] @@ -1514,7 +1514,7 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt) VMReturn ret; int didit; ret.IntAt(&didit); - GlobalVMStack.Call(func, params, 3, &ret, 1, nullptr); + VMCall(func, params, 3, &ret, 1); if (didit) { diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 064a92a1f..9ee249cc1 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -79,6 +79,7 @@ #include "edata.h" #endif #include "events.h" +#include "types.h" #include "fragglescript/t_fs.h" @@ -1616,7 +1617,7 @@ static void SetMapThingUserData(AActor *actor, unsigned udi) { FName varname = MapThingsUserData[udi].Property; int value = MapThingsUserData[udi].Value; - PField *var = dyn_cast(actor->GetClass()->Symbols.FindSymbol(varname, true)); + PField *var = dyn_cast(actor->GetClass()->FindSymbol(varname, true)); udi++; diff --git a/src/p_sight.cpp b/src/p_sight.cpp index 04cb9c9ea..b4c69d3fe 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -24,6 +24,7 @@ #include "r_utility.h" #include "b_bot.h" #include "p_spec.h" +#include "vm.h" // State. #include "r_state.h" diff --git a/src/p_spec.cpp b/src/p_spec.cpp index fcfd0ddf4..43c094e25 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -76,6 +76,7 @@ #ifndef NO_EDATA #include "edata.h" #endif +#include "vm.h" // State. #include "r_state.h" diff --git a/src/p_states.cpp b/src/p_states.cpp index 762a76896..c358ceed5 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -41,6 +41,7 @@ #include "v_text.h" #include "thingdef.h" #include "r_state.h" +#include "vm.h" // stores indices for symbolic state labels for some old-style DECORATE functions. @@ -96,8 +97,7 @@ PClassActor *FState::StaticFindStateOwner (const FState *state) for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i) { PClassActor *info = PClassActor::AllActorClasses[i]; - if (state >= info->OwnedStates && - state < info->OwnedStates + info->NumOwnedStates) + if (info->OwnsState(state)) { return info; } @@ -117,16 +117,25 @@ PClassActor *FState::StaticFindStateOwner (const FState *state, PClassActor *inf { while (info != NULL) { - if (state >= info->OwnedStates && - state < info->OwnedStates + info->NumOwnedStates) + if (info->OwnsState(state)) { return info; } - info = dyn_cast(info->ParentClass); + info = ValidateActor(info->ParentClass); } return NULL; } +//========================================================================== +// +// +//========================================================================== + +FString FState::StaticGetStateName(const FState *state) +{ + auto so = FState::StaticFindStateOwner(state); + return FStringf("%s.%d", so->TypeName.GetChars(), int(state - so->GetStates())); +} //========================================================================== // @@ -164,9 +173,9 @@ bool AActor::HasSpecialDeathStates () const { const PClassActor *info = static_cast(GetClass()); - if (info->StateList != NULL) + if (info->GetStateLabels() != NULL) { - FStateLabel *slabel = info->StateList->FindLabel (NAME_Death); + FStateLabel *slabel = info->GetStateLabels()->FindLabel (NAME_Death); if (slabel != NULL && slabel->Children != NULL) { for(int i = 0; i < slabel->Children->NumLabels; i++) @@ -249,7 +258,7 @@ TArray &MakeStateNameList(const char * fname) //=========================================================================== FState *PClassActor::FindState(int numnames, FName *names, bool exact) const { - FStateLabels *labels = StateList; + FStateLabels *labels = GetStateLabels(); FState *best = NULL; if (labels != NULL) @@ -573,12 +582,13 @@ void FStateDefinitions::InstallStates(PClassActor *info, AActor *defaults) SetStateLabel("Spawn", GetDefault()->SpawnState); } - if (info->StateList != NULL) + auto &sl = info->ActorInfo()->StateList; + if (sl != NULL) { - info->StateList->Destroy(); - M_Free(info->StateList); + sl->Destroy(); + M_Free(sl); } - info->StateList = CreateStateLabelList(StateLabels); + sl = CreateStateLabelList(StateLabels); // Cache these states as member veriables. defaults->SpawnState = info->FindState(NAME_Spawn); @@ -623,9 +633,9 @@ void FStateDefinitions::MakeStateDefines(const PClassActor *cls) laststatebeforelabel = NULL; lastlabel = -1; - if (cls != NULL && cls->StateList != NULL) + if (cls != NULL && cls->GetStateLabels() != NULL) { - MakeStateList(cls->StateList, StateLabels); + MakeStateList(cls->GetStateLabels(), StateLabels); } else { @@ -730,7 +740,7 @@ FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, PClassActor *mytype, // superclass, or it may be the name of any class that this one derives from. if (stricmp (classname, "Super") == 0) { - type = dyn_cast(type->ParentClass); + type = ValidateActor(type->ParentClass); actor = GetDefaultByType(type); } else @@ -808,7 +818,7 @@ void FStateDefinitions::FixStatePointers (PClassActor *actor, TArrayOwnedStates + v - 1; + list[i].State = actor->GetStates() + v - 1; list[i].DefineFlags = SDF_STATE; } if (list[i].Children.Size() > 0) @@ -1000,12 +1010,12 @@ int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults) if (count > 0) { - FState *realstates = new FState[count]; + FState *realstates = (FState*)ClassDataAllocator.Alloc(count * sizeof(FState)); int i; memcpy(realstates, &StateArray[0], count*sizeof(FState)); - actor->OwnedStates = realstates; - actor->NumOwnedStates = count; + actor->ActorInfo()->OwnedStates = realstates; + actor->ActorInfo()->NumOwnedStates = count; SaveStateSourceLines(realstates, SourceLines); // adjust the state pointers @@ -1071,8 +1081,7 @@ void DumpStateHelper(FStateLabels *StateList, const FString &prefix) } else { - Printf(PRINT_LOG, "%s%s: %s.%d\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(), - owner->TypeName.GetChars(), int(StateList->Labels[i].State - owner->OwnedStates)); + Printf(PRINT_LOG, "%s%s: %s\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(), FState::StaticGetStateName(StateList->Labels[i].State)); } } if (StateList->Labels[i].Children != NULL) @@ -1088,7 +1097,7 @@ CCMD(dumpstates) { PClassActor *info = PClassActor::AllActorClasses[i]; Printf(PRINT_LOG, "State labels for %s\n", info->TypeName.GetChars()); - DumpStateHelper(info->StateList, ""); + DumpStateHelper(info->GetStateLabels(), ""); Printf(PRINT_LOG, "----------------------------\n"); } } @@ -1124,7 +1133,7 @@ DEFINE_ACTION_FUNCTION(FState, DistanceTo) { // Safely calculate the distance between two states. auto o1 = FState::StaticFindStateOwner(self); - if (other >= o1->OwnedStates && other < o1->OwnedStates + o1->NumOwnedStates) retv = int(other - self); + if (o1->OwnsState(other)) retv = int(other - self); } ACTION_RETURN_INT(retv); } diff --git a/src/p_tags.cpp b/src/p_tags.cpp index 748e25ca3..6fe06ed0f 100644 --- a/src/p_tags.cpp +++ b/src/p_tags.cpp @@ -37,6 +37,7 @@ #include "p_tags.h" #include "c_dispatch.h" #include "g_levellocals.h" +#include "vm.h" FTagManager tagManager; diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index b34b4de1d..a56e4e790 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -39,6 +39,7 @@ #include "r_utility.h" #include "p_spec.h" #include "g_levellocals.h" +#include "vm.h" #define FUDGEFACTOR 10 diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index b044b3066..717747a8a 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -47,6 +47,7 @@ #include "p_local.h" #include "templates.h" #include "actor.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ diff --git a/src/p_things.cpp b/src/p_things.cpp index da6f56cac..94dc1df1d 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -53,6 +53,7 @@ #include "actorptrselect.h" #include "g_levellocals.h" #include "actorinlines.h" +#include "vm.h" // Set of spawnable things for the Thing_Spawn and Thing_Projectile specials. FClassMap SpawnableThings; @@ -526,7 +527,7 @@ DEFINE_ACTION_FUNCTION(AActor, GetSpawnableType) { PARAM_PROLOGUE; PARAM_INT(num); - ACTION_RETURN_OBJECT(P_GetSpawnableType(num)); + ACTION_RETURN_POINTER(P_GetSpawnableType(num)); } struct MapinfoSpawnItem diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 1506059ea..81b1d0a90 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -50,6 +50,7 @@ #include "p_terrain.h" #include "g_levellocals.h" #include "info.h" +#include "vm.h" //=========================================================================== // diff --git a/src/p_user.cpp b/src/p_user.cpp index 932c5fd72..b0cce58ab 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -58,7 +58,7 @@ #include "p_blockmap.h" #include "a_morph.h" #include "p_spec.h" -#include "virtual.h" +#include "vm.h" #include "g_levellocals.h" #include "actorinlines.h" #include "r_data/r_translate.h" @@ -170,14 +170,14 @@ FString GetPrintableDisplayName(PClassActor *cls) { // Fixme; This needs a decent way to access the string table without creating a mess. // [RH] ???? - return cls->DisplayName; + return cls->GetDisplayName(); } DEFINE_ACTION_FUNCTION(APlayerPawn, GetPrintableDisplayName) { PARAM_PROLOGUE; PARAM_CLASS(type, AActor); - ACTION_RETURN_STRING(type->DisplayName); + ACTION_RETURN_STRING(type->GetDisplayName()); } bool ValidatePlayerClass(PClassActor *ti, const char *name) @@ -192,7 +192,7 @@ bool ValidatePlayerClass(PClassActor *ti, const char *name) Printf("Invalid player class '%s'\n", name); return false; } - else if (ti->DisplayName.IsEmpty()) + else if (ti->GetDisplayName().IsEmpty()) { Printf ("Missing displayname for player class '%s'\n", name); return false; @@ -267,7 +267,7 @@ CCMD (playerclasses) { Printf ("%3d: Class = %s, Name = %s\n", i, PlayerClasses[i].Type->TypeName.GetChars(), - PlayerClasses[i].Type->DisplayName.GetChars()); + PlayerClasses[i].Type->GetDisplayName().GetChars()); } } @@ -1242,7 +1242,7 @@ void APlayerPawn::CheckWeaponSwitch(PClassActor *ammotype) DEFINE_ACTION_FUNCTION(APlayerPawn, CheckWeaponSwitch) { PARAM_SELF_PROLOGUE(APlayerPawn); - PARAM_OBJECT(ammotype, PClassActor); + PARAM_POINTER(ammotype, PClassActor); self->CheckWeaponSwitch(ammotype); return 0; } @@ -1505,7 +1505,7 @@ void APlayerPawn::PlayIdle () IFVIRTUAL(APlayerPawn, PlayIdle) { VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -1514,7 +1514,7 @@ void APlayerPawn::PlayRunning () IFVIRTUAL(APlayerPawn, PlayRunning) { VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -1523,7 +1523,7 @@ void APlayerPawn::PlayAttacking () IFVIRTUAL(APlayerPawn, PlayAttacking) { VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -1532,7 +1532,7 @@ void APlayerPawn::PlayAttacking2 () IFVIRTUAL(APlayerPawn, PlayAttacking2) { VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } @@ -1630,7 +1630,7 @@ void APlayerPawn::MorphPlayerThink () IFVIRTUAL(APlayerPawn, MorphPlayerThink) { VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } } diff --git a/src/polyrenderer/scene/poly_sprite.cpp b/src/polyrenderer/scene/poly_sprite.cpp index ae7295f1e..2ddf46df3 100644 --- a/src/polyrenderer/scene/poly_sprite.cpp +++ b/src/polyrenderer/scene/poly_sprite.cpp @@ -154,7 +154,7 @@ void RenderPolySprite::Render(const TriMatrix &worldToClip, const PolyClipPlane bool RenderPolySprite::IsThingCulled(AActor *thing) { - FIntCVar *cvar = thing->GetClass()->distancecheck; + FIntCVar *cvar = thing->GetInfo()->distancecheck; if (cvar != nullptr && *cvar >= 0) { double dist = (thing->Pos() - PolyRenderer::Instance()->Viewpoint.Pos).LengthSquared(); diff --git a/src/portal.cpp b/src/portal.cpp index acf6e022d..9926f289e 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -52,6 +52,7 @@ #include "p_checkposition.h" #include "math/cmath.h" #include "g_levellocals.h" +#include "vm.h" // simulation recurions maximum CVAR(Int, sv_portal_recursions, 4, CVAR_ARCHIVE|CVAR_SERVERINFO) diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index adcbc93fd..3c61ec4ac 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -50,6 +50,7 @@ #include "d_player.h" #include "r_data/sprites.h" #include "r_state.h" +#include "vm.h" #include "gi.h" #include "stats.h" diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index 7a5927be6..17abea01e 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -14,6 +14,7 @@ #include "r_data/sprites.h" #include "r_data/voxels.h" #include "textures/textures.h" +#include "vm.h" void gl_InitModels(); diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index 71cb943f8..8d4652d2b 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -30,6 +30,7 @@ #include "serializer.h" #include "d_player.h" #include "g_levellocals.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ diff --git a/src/s_sound.cpp b/src/s_sound.cpp index a767fdcaa..75f6a68a8 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -54,7 +54,7 @@ #include "d_player.h" #include "r_state.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ @@ -487,7 +487,7 @@ void S_PrecacheLevel () { // Without the type cast this picks the 'void *' assignment... VMValue params[1] = { actor }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + VMCall(func, params, 1, nullptr, 0); } else { diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 4327bde9d..0c47493ea 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -56,6 +56,8 @@ #include "w_wad.h" #include "math/cmath.h" +inline PClass *PObjectPointer::PointedClass() const { return static_cast(PointedType)->Descriptor; } + extern FRandom pr_exrandom; FMemArena FxAlloc(65536); int utf8_decode(const char *src, int *size); @@ -112,7 +114,7 @@ FCompileContext::FCompileContext(PNamespace *cg, PFunction *fnc, PPrototype *ret if (fnc != nullptr) Class = fnc->OwningClass; } -FCompileContext::FCompileContext(PNamespace *cg, PStruct *cls, bool fromdecorate) +FCompileContext::FCompileContext(PNamespace *cg, PContainerType *cls, bool fromdecorate) : ReturnProto(nullptr), Function(nullptr), Class(cls), FromDecorate(fromdecorate), StateIndex(-1), StateCount(0), Lump(-1), CurGlobals(cg) { } @@ -198,14 +200,14 @@ FxLocalVariableDeclaration *FCompileContext::FindLocalVariable(FName name) } } -static PStruct *FindStructType(FName name, FCompileContext &ctx) +static PContainerType *FindContainerType(FName name, FCompileContext &ctx) { auto sym = ctx.Class->Symbols.FindSymbol(name, true); if (sym == nullptr) sym = ctx.CurGlobals->Symbols.FindSymbol(name, true); if (sym && sym->IsKindOf(RUNTIME_CLASS(PSymbolType))) { auto type = static_cast(sym); - return dyn_cast(type->Type); + return dyn_cast(type->Type); } return nullptr; } @@ -217,11 +219,18 @@ static PClass *FindClassType(FName name, FCompileContext &ctx) if (sym && sym->IsKindOf(RUNTIME_CLASS(PSymbolType))) { auto type = static_cast(sym); - return dyn_cast(type->Type); + auto ctype = dyn_cast(type->Type); + if (ctype) return ctype->Descriptor; } return nullptr; } +bool isActor(PContainerType *type) +{ + auto cls = dyn_cast(type); + return cls ? cls->Descriptor->IsDescendantOf(RUNTIME_CLASS(AActor)) : false; +} + //========================================================================== // // ExpEmit @@ -294,10 +303,18 @@ static bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompar // A type is always compatible to itself. if (fromtype == totype) return true; // Pointers to different types are only compatible if both point to an object and the source type is a child of the destination type. - if (fromtype->PointedType->IsKindOf(RUNTIME_CLASS(PClass)) && totype->PointedType->IsKindOf(RUNTIME_CLASS(PClass))) + if (source->IsA(RUNTIME_CLASS(PObjectPointer)) && dest->IsA(RUNTIME_CLASS(PObjectPointer))) { - auto fromcls = static_cast(fromtype->PointedType); - auto tocls = static_cast(totype->PointedType); + auto fromcls = static_cast(source)->PointedClass(); + auto tocls = static_cast(dest)->PointedClass(); + if (forcompare && tocls->IsDescendantOf(fromcls)) return true; + return (fromcls->IsDescendantOf(tocls)); + } + // The same rules apply to class pointers. A child type can be assigned to a variable of a parent type. + if (source->IsA(RUNTIME_CLASS(PClassPointer)) && dest->IsA(RUNTIME_CLASS(PClassPointer))) + { + auto fromcls = static_cast(source)->ClassRestriction; + auto tocls = static_cast(dest)->ClassRestriction; if (forcompare && tocls->IsDescendantOf(fromcls)) return true; return (fromcls->IsDescendantOf(tocls)); } @@ -1746,17 +1763,17 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) // this is not yet ready and does not get assigned to actual values. } */ - else if (ValueType->IsKindOf(RUNTIME_CLASS(PClass))) // this should never happen because the VM doesn't handle plain class types - just pointers + else if (ValueType->IsKindOf(RUNTIME_CLASS(PClassType))) // this should never happen because the VM doesn't handle plain class types - just pointers { - if (basex->ValueType->IsKindOf(RUNTIME_CLASS(PClass))) + if (basex->ValueType->IsKindOf(RUNTIME_CLASS(PClassType))) { // class types are only compatible if the base type is a descendant of the result type. - auto fromtype = static_cast(basex->ValueType); - auto totype = static_cast(ValueType); + auto fromtype = static_cast(basex->ValueType)->Descriptor; + auto totype = static_cast(ValueType)->Descriptor; if (fromtype->IsDescendantOf(totype)) goto basereturn; } } - else if (basex->ValueType->IsA(RUNTIME_CLASS(PNativeStruct)) && ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(ValueType)->PointedType == basex->ValueType) + else if (basex->IsNativeStruct() && ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(ValueType)->PointedType == basex->ValueType) { bool writable; basex->RequestAddress(ctx, &writable); @@ -2477,7 +2494,7 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) } // Both types are the same so this is ok. } - else if (Right->ValueType->IsA(RUNTIME_CLASS(PNativeStruct)) && Base->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(Base->ValueType)->PointedType == Right->ValueType) + else if (Right->IsNativeStruct() && Base->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(Base->ValueType)->PointedType == Right->ValueType) { // allow conversion of native structs to pointers of the same type. This is necessary to assign elements from global arrays like players, sectors, etc. to local pointers. // For all other types this is not needed. Structs are not assignable and classes can only exist as references. @@ -5132,7 +5149,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) if (val->isConstant()) { auto cls = static_cast(static_cast(val)->GetValue().GetPointer()); - if (cls->ObjectFlags & OF_Abstract) + if (cls->bAbstract) { ScriptPosition.Message(MSG_ERROR, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); delete this; @@ -5143,7 +5160,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) int outerside = ctx.Function && ctx.Function->Variants.Size() ? FScopeBarrier::SideFromFlags(ctx.Function->Variants[0].Flags) : FScopeBarrier::Side_Virtual; if (outerside == FScopeBarrier::Side_Virtual) outerside = FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags); - int innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); + int innerside = FScopeBarrier::SideFromObjectFlags(cls->VMType->ObjectFlags); if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context" { ScriptPosition.Message(MSG_ERROR, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside)); @@ -5953,7 +5970,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) delete this; return nullptr; } - if (!ctx.Function->Variants[0].SelfClass->IsKindOf(RUNTIME_CLASS(PClassActor))) + if (!isActor(ctx.Function->Variants[0].SelfClass)) { ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type."); delete this; @@ -6105,15 +6122,15 @@ foundit: // //========================================================================== -FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classctx, FxExpression *&object, PStruct *objtype) +FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PContainerType *classctx, FxExpression *&object, PContainerType *objtype) { PSymbol *sym; PSymbolTable *symtbl; - bool isclass = objtype->IsKindOf(RUNTIME_CLASS(PClass)); + bool isclass = objtype->IsKindOf(RUNTIME_CLASS(PClassType)); if (Identifier == NAME_Default) { - if (!objtype->IsKindOf(RUNTIME_CLASS(PClassActor))) + if (!isActor(objtype)) { ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type."); delete object; @@ -6163,8 +6180,8 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct object = nullptr; return nullptr; } - PClass* cls_ctx = dyn_cast(classctx); - PClass* cls_target = dyn_cast(objtype); + auto cls_ctx = dyn_cast(classctx); + auto cls_target = dyn_cast(objtype); // [ZZ] neither PSymbol, PField or PSymbolTable have the necessary information. so we need to do the more complex check here. if (vsym->Flags & VARF_Protected) { @@ -6178,7 +6195,7 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct } // find the class that declared this field. - PClass* p = cls_target; + auto p = cls_target; while (p) { if (&p->Symbols == symtbl) @@ -6187,10 +6204,10 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct break; } - p = p->ParentClass; + p = p->ParentType; } - if (!cls_ctx->IsDescendantOf(cls_target)) + if (!cls_ctx->Descriptor->IsDescendantOf(cls_target->Descriptor)) { ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); delete object; @@ -6253,7 +6270,7 @@ FxMemberIdentifier::~FxMemberIdentifier() FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) { - PStruct *ccls = nullptr; + PContainerType *ccls = nullptr; CHECKRESOLVED(); if (Object->ExprType == EFX_Identifier) @@ -6261,7 +6278,7 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) auto id = static_cast(Object)->Identifier; // If the left side is a class name for a static member function call it needs to be resolved manually // because the resulting value type would cause problems in nearly every other place where identifiers are being used. - ccls = FindStructType(id, ctx); + ccls = FindContainerType(id, ctx); if (ccls != nullptr) { static_cast(Object)->noglobal = true; @@ -6342,32 +6359,29 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) { if (ccls != nullptr) { - if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast(ccls)->bExported) + PSymbol *sym; + if ((sym = ccls->Symbols.FindSymbol(Identifier, true)) != nullptr) { - PSymbol *sym; - if ((sym = ccls->Symbols.FindSymbol(Identifier, true)) != nullptr) + if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) { - if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) + ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s.%s' as constant\n", ccls->TypeName.GetChars(), Identifier.GetChars()); + delete this; + return FxConstant::MakeConstant(sym, ScriptPosition); + } + else + { + auto f = dyn_cast(sym); + if (f != nullptr && (f->Flags & (VARF_Static | VARF_ReadOnly | VARF_Meta)) == (VARF_Static | VARF_ReadOnly)) { - ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s.%s' as constant\n", ccls->TypeName.GetChars(), Identifier.GetChars()); + auto x = new FxGlobalVariable(f, ScriptPosition); delete this; - return FxConstant::MakeConstant(sym, ScriptPosition); + return x->Resolve(ctx); } else { - auto f = dyn_cast(sym); - if (f != nullptr && (f->Flags & (VARF_Static | VARF_ReadOnly | VARF_Meta)) == (VARF_Static | VARF_ReadOnly)) - { - auto x = new FxGlobalVariable(f, ScriptPosition); - delete this; - return x->Resolve(ctx); - } - else - { - ScriptPosition.Message(MSG_ERROR, "Unable to access '%s.%s' in a static context\n", ccls->TypeName.GetChars(), Identifier.GetChars()); - delete this; - return nullptr; - } + ScriptPosition.Message(MSG_ERROR, "Unable to access '%s.%s' in a static context\n", ccls->TypeName.GetChars(), Identifier.GetChars()); + delete this; + return nullptr; } } } @@ -6382,9 +6396,9 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) if (Object->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) { auto ptype = static_cast(Object->ValueType)->PointedType; - if (ptype->IsKindOf(RUNTIME_CLASS(PStruct))) + if (ptype->IsKindOf(RUNTIME_CLASS(PContainerType))) { - auto ret = ResolveMember(ctx, ctx.Class, Object, static_cast(ptype)); + auto ret = ResolveMember(ctx, ctx.Class, Object, static_cast(ptype)); delete this; return ret; } @@ -6606,7 +6620,7 @@ ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build) ob.Free(build); ExpEmit meta(build, REGT_POINTER); build->Emit(OP_CLSS, meta.RegNum, ob.RegNum); - build->Emit(OP_LOS, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); + build->Emit(OP_LP, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); return meta; } @@ -6864,7 +6878,7 @@ ExpEmit FxStackVariable::Emit(VMFunctionBuilder *build) if (offsetreg == -1) offsetreg = build->GetConstantInt(0); auto op = membervar->Type->GetLoadOp(); if (op == OP_LO) - op = OP_LOS; + op = OP_LP; build->Emit(op, loc.RegNum, build->FramePointer.RegNum, offsetreg); } else @@ -6958,8 +6972,8 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) if (membervar->SymbolName == NAME_Default) { - if (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) - || !static_cast(classx->ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(AActor))) + if (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PObjectPointer)) + || !static_cast(classx->ValueType)->PointedClass()->IsDescendantOf(RUNTIME_CLASS(AActor))) { ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type"); delete this; @@ -7007,7 +7021,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) if (classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) { PPointer *ptrtype = dyn_cast(classx->ValueType); - if (ptrtype == nullptr || !ptrtype->PointedType->IsKindOf(RUNTIME_CLASS(PStruct))) + if (ptrtype == nullptr || !ptrtype->PointedType->IsKindOf(RUNTIME_CLASS(PContainerType))) { ScriptPosition.Message(MSG_ERROR, "Member variable requires a struct or class object"); delete this; @@ -7484,9 +7498,9 @@ static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction bool match = (callingself == calledself); if (!match) { - auto callingselfcls = dyn_cast(caller->Variants[0].SelfClass); - auto calledselfcls = dyn_cast(callee->Variants[0].SelfClass); - match = callingselfcls != nullptr && calledselfcls != nullptr && callingselfcls->IsDescendantOf(calledselfcls); + auto callingselfcls = dyn_cast(caller->Variants[0].SelfClass); + auto calledselfcls = dyn_cast(callee->Variants[0].SelfClass); + match = callingselfcls != nullptr && calledselfcls != nullptr && callingselfcls->Descriptor->IsDescendantOf(calledselfcls->Descriptor); } if (!match) @@ -7597,7 +7611,6 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) } // [ZZ] validate call - PClass* cls = (PClass*)ctx.Class; int outerflags = 0; if (ctx.Function) { @@ -7610,7 +7623,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) // [ZZ] check this at compile time. this would work for most legit cases. if (innerside == FScopeBarrier::Side_Virtual) { - innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); + innerside = FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags); innerflags = FScopeBarrier::FlagsFromSide(innerside); } FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); @@ -7679,14 +7692,14 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) delete this; return nullptr; } - FxExpression *self = (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_Method) && ctx.Class->IsKindOf(RUNTIME_CLASS(PClassActor))) ? new FxSelf(ScriptPosition) : (FxExpression*)new FxConstant(ScriptPosition); + FxExpression *self = (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_Method) && isActor(ctx.Class)) ? new FxSelf(ScriptPosition) : (FxExpression*)new FxConstant(ScriptPosition); FxExpression *x = new FxActionSpecialCall(self, special, ArgList, ScriptPosition); delete this; return x->Resolve(ctx); } PClass *cls = FindClassType(MethodName, ctx); - if (cls != nullptr && cls->bExported) + if (cls != nullptr) { if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition)) { @@ -7847,14 +7860,17 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) if (CheckArgSize(MethodName, ArgList, 0, 1, ScriptPosition)) { // [ZZ] allow implicit new() call to mean "create current class instance" - if (!ArgList.Size() && !ctx.Class->IsKindOf(RUNTIME_CLASS(PClass))) + if (!ArgList.Size() && !ctx.Class->IsKindOf(RUNTIME_CLASS(PClassType))) { ScriptPosition.Message(MSG_ERROR, "Cannot use implicit new() in a struct"); delete this; return nullptr; } else if (!ArgList.Size()) - ArgList.Push(new FxConstant((PClass*)ctx.Class, NewClassPointer((PClass*)ctx.Class), ScriptPosition)); + { + auto cls = static_cast(ctx.Class)->Descriptor; + ArgList.Push(new FxConstant(cls, NewClassPointer(cls), ScriptPosition)); + } func = new FxNew(ArgList[0]); ArgList[0] = nullptr; @@ -7909,12 +7925,12 @@ FxMemberFunctionCall::~FxMemberFunctionCall() FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { - PStruct *cls; + PContainerType *cls = nullptr; bool staticonly = false; bool novirtual = false; bool isreadonly = false; - PStruct *ccls = nullptr; + PContainerType *ccls = nullptr; if (ctx.Class == nullptr) { @@ -7941,7 +7957,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) // because the resulting value type would cause problems in nearly every other place where identifiers are being used. // [ZZ] substitute ccls for String internal type. if (id == NAME_String) ccls = TypeStringStruct; - else ccls = FindStructType(id, ctx); + else ccls = FindContainerType(id, ctx); if (ccls != nullptr) static_cast(Self)->noglobal = true; } @@ -7951,40 +7967,37 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (ccls != nullptr) { - if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast(ccls)->bExported) + cls = ccls; + staticonly = true; + if (ccls->IsKindOf(RUNTIME_CLASS(PClassType))) { - cls = ccls; - staticonly = true; - if (ccls->IsKindOf(RUNTIME_CLASS(PClass))) + if (ctx.Function == nullptr) { - if (ctx.Function == nullptr) + ScriptPosition.Message(MSG_ERROR, "Unable to call %s from constant declaration", MethodName.GetChars()); + delete this; + return nullptr; + } + auto clstype = dyn_cast(ctx.Function->Variants[0].SelfClass); + if (clstype != nullptr) + { + novirtual = clstype->Descriptor->IsDescendantOf(static_cast(ccls)->Descriptor); + if (novirtual) { - ScriptPosition.Message(MSG_ERROR, "Unable to call %s from constant declaration", MethodName.GetChars()); - delete this; - return nullptr; - } - auto clstype = dyn_cast(ctx.Function->Variants[0].SelfClass); - if (clstype != nullptr) - { - novirtual = clstype->IsDescendantOf(static_cast(ccls)); - if (novirtual) + bool error; + PFunction *afd = FindClassMemberFunction(ccls, ctx.Class, MethodName, ScriptPosition, &error); + if ((afd->Variants[0].Flags & VARF_Method) && (afd->Variants[0].Flags & VARF_Virtual)) { - bool error; - PFunction *afd = FindClassMemberFunction(ccls, ctx.Class, MethodName, ScriptPosition, &error); - if ((afd->Variants[0].Flags & VARF_Method) && (afd->Variants[0].Flags & VARF_Virtual)) - { - staticonly = false; - novirtual = true; - delete Self; - Self = new FxSelf(ScriptPosition); - Self->ValueType = NewPointer(cls); - } - else novirtual = false; + staticonly = false; + novirtual = true; + delete Self; + Self = new FxSelf(ScriptPosition); + Self->ValueType = NewPointer(cls); } + else novirtual = false; } } - if (!novirtual) goto isresolved; } + if (!novirtual) goto isresolved; } } @@ -7996,11 +8009,11 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) delete this; return nullptr; } - auto clstype = dyn_cast(ctx.Function->Variants[0].SelfClass); + auto clstype = dyn_cast(ctx.Function->Variants[0].SelfClass); if (clstype != nullptr) { // give the node the proper value type now that we know it's properly used. - cls = clstype->ParentClass; + cls = clstype->ParentType; Self->ValueType = NewPointer(cls); Self->ExprType = EFX_Self; novirtual = true; // super calls are always non-virtual @@ -8212,9 +8225,9 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !Self->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { auto ptype = static_cast(Self->ValueType)->PointedType; - if (ptype->IsKindOf(RUNTIME_CLASS(PStruct))) + if (ptype->IsKindOf(RUNTIME_CLASS(PContainerType))) { - if (ptype->IsKindOf(RUNTIME_CLASS(PClass)) && MethodName == NAME_GetClass) + if (ptype->IsKindOf(RUNTIME_CLASS(PClassType)) && MethodName == NAME_GetClass) { if (ArgList.Size() > 0) { @@ -8227,7 +8240,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } - cls = static_cast(ptype); + cls = static_cast(ptype); } else { @@ -8317,9 +8330,9 @@ isresolved: { if (!novirtual || !(afd->Variants[0].Flags & VARF_Virtual)) { - auto clstype = dyn_cast(ctx.Class); - auto ccls = dyn_cast(cls); - if (clstype == nullptr || ccls == nullptr || !clstype->IsDescendantOf(ccls)) + auto clstype = dyn_cast(ctx.Class); + auto ccls = dyn_cast(cls); + if (clstype == nullptr || ccls == nullptr || !clstype->Descriptor->IsDescendantOf(ccls->Descriptor)) { ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here", cls->TypeName.GetChars(), MethodName.GetChars()); delete this; @@ -8809,9 +8822,9 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) ArgList[i] = ArgList[i]->Resolve(ctx); // nust be resolved before the address is requested. if (ArgList[i] != nullptr && ArgList[i]->ValueType != TypeNullPtr) { - if (type == ArgList[i]->ValueType && type->IsA(RUNTIME_CLASS(PPointer)) && static_cast(type)->IsA(RUNTIME_CLASS(PStruct))) + if (type == ArgList[i]->ValueType && type->IsA(RUNTIME_CLASS(PPointer)) && static_cast(type)->PointedType->IsA(RUNTIME_CLASS(PStruct))) { - // trying to pass a struct reference as a struct refg + // trying to pass a struct reference as a struct reference. This must preserve the type. } else { @@ -9287,7 +9300,7 @@ FxExpression *FxGetClass::Resolve(FCompileContext &ctx) delete this; return nullptr; } - ValueType = NewClassPointer(static_cast(static_cast(Self->ValueType)->PointedType)); + ValueType = NewClassPointer(static_cast(static_cast(Self->ValueType)->PointedType)->Descriptor); return this; } @@ -9342,7 +9355,7 @@ ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build) op.Free(build); } ExpEmit to(build, REGT_POINTER); - build->Emit(OP_LOS, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, ParentClass))); + build->Emit(OP_LP, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, ParentClass))); return to; } @@ -9414,7 +9427,7 @@ ExpEmit FxGetDefaultByType::Emit(VMFunctionBuilder *build) build->Emit(OP_LKP, to.RegNum, op.RegNum); op = to; } - build->Emit(OP_LOS, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); + build->Emit(OP_LP, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); return to; } @@ -10623,7 +10636,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) if (Explicit) cls = FindClassType(clsname, ctx); else cls = PClass::FindClass(clsname); - if (cls == nullptr) + if (cls == nullptr || cls->VMType == nullptr) { /* lax */ // Since this happens in released WADs it must pass without a terminal error... :( @@ -10678,14 +10691,14 @@ int BuiltinNameToClass(VMValue *param, TArray &defaultparam, int numpar const PClass *cls = PClass::FindClass(clsname); const PClass *desttype = reinterpret_cast(param[1].a); - if (!cls->IsDescendantOf(desttype)) + if (cls->VMType == nullptr || !cls->IsDescendantOf(desttype)) { // Let the caller check this. Making this an error with a message is only taking away options from the user. cls = nullptr; } - ret->SetObject(const_cast(cls)); + ret->SetPointer(const_cast(cls)); } - else ret->SetObject(nullptr); + else ret->SetPointer(nullptr); return 1; } @@ -10800,7 +10813,7 @@ int BuiltinClassCast(VMValue *param, TArray &defaultparam, int numparam PARAM_PROLOGUE; PARAM_CLASS(from, DObject); PARAM_CLASS(to, DObject); - ACTION_RETURN_OBJECT(from && to && from->IsDescendantOf(to) ? from : nullptr); + ACTION_RETURN_POINTER(from && to && from->IsDescendantOf(to) ? from : nullptr); } ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) @@ -10835,19 +10848,21 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); ABORT(ctx.Class); - auto aclass = dyn_cast(ctx.Class); + auto vclass = dyn_cast(ctx.Class); + assert(vclass != nullptr); + auto aclass = ValidateActor(vclass->Descriptor); // This expression type can only be used from actors, for everything else it has already produced a compile error. - assert(aclass != nullptr && aclass->NumOwnedStates > 0); + assert(aclass != nullptr && aclass->GetStateCount() > 0); - if (aclass->NumOwnedStates <= index) + if (aclass->GetStateCount() <= index) { ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d", ctx.Class->TypeName.GetChars(), index); delete this; return nullptr; } - int symlabel = StateLabels.AddPointer(aclass->OwnedStates + index); + int symlabel = StateLabels.AddPointer(aclass->GetStates() + index); FxExpression *x = new FxConstant(symlabel, ScriptPosition); x->ValueType = TypeStateLabel; delete this; @@ -10911,9 +10926,13 @@ FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx) Index = new FxIntCast(Index, ctx.FromDecorate); SAFE_RESOLVE(Index, ctx); } - auto aclass = dyn_cast(ctx.Class); - assert(aclass != nullptr && aclass->NumOwnedStates > 0); - symlabel = StateLabels.AddPointer(aclass->OwnedStates + ctx.StateIndex); + + auto vclass = dyn_cast(ctx.Class); + assert(vclass != nullptr); + auto aclass = ValidateActor(vclass->Descriptor); + assert(aclass != nullptr && aclass->GetStateCount() > 0); + + symlabel = StateLabels.AddPointer(aclass->GetStates() + ctx.StateIndex); ValueType = TypeStateLabel; return this; } @@ -10967,7 +10986,10 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx) CHECKRESOLVED(); ABORT(ctx.Class); int symlabel; - auto clstype = dyn_cast(ctx.Class); + + auto vclass = dyn_cast(ctx.Class); + assert(vclass != nullptr); + auto clstype = ValidateActor(vclass->Descriptor); if (names[0] == NAME_None) { @@ -10982,7 +11004,7 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx) } else if (names[0] == NAME_Super) { - scope = dyn_cast(clstype->ParentClass); + scope = ValidateActor(clstype->ParentClass); } else { diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index f90a03a53..1f6c8baca 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -46,6 +46,8 @@ #include "actor.h" #include "vmbuilder.h" #include "scopebarrier.h" +#include "types.h" +#include "vmintern.h" #define CHECKRESOLVED() if (isresolved) return this; isresolved=true; @@ -78,7 +80,7 @@ struct FCompileContext FxCompoundStatement *Block = nullptr; PPrototype *ReturnProto; PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.) - PStruct *Class; // The type of the owning class. + PContainerType *Class; // The type of the owning class. bool FromDecorate; // DECORATE must silence some warnings and demote some errors. int StateIndex; // index in actor's state table for anonymous functions, otherwise -1 (not used by DECORATE which pre-resolves state indices) int StateCount; // amount of states an anoymous function is being used on (must be 1 for state indices to be allowed.) @@ -90,7 +92,7 @@ struct FCompileContext FString VersionString; FCompileContext(PNamespace *spc, PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump, const VersionInfo &ver); - FCompileContext(PNamespace *spc, PStruct *cls, bool fromdecorate); // only to be used to resolve constants! + FCompileContext(PNamespace *spc, PContainerType *cls, bool fromdecorate); // only to be used to resolve constants! PSymbol *FindInClass(FName identifier, PSymbolTable *&symt); PSymbol *FindInSelfClass(FName identifier, PSymbolTable *&symt); @@ -333,10 +335,11 @@ public: bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; } bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3; }; bool IsBoolCompat() const { return ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT || ValueType->GetRegType() == REGT_POINTER); } - bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); } + bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PObjectPointer)); } bool IsArray() const { return ValueType->IsKindOf(RUNTIME_CLASS(PArray)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PArray))); } bool IsResizableArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PStaticArray))); } // can only exist in pointer form. bool IsDynamicArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PDynArray))); } + bool IsNativeStruct() const { return (ValueType->IsA(RUNTIME_CLASS(PStruct)) && static_cast(ValueType)->isNative); } virtual ExpEmit Emit(VMFunctionBuilder *build); void EmitStatement(VMFunctionBuilder *build); @@ -372,7 +375,7 @@ public: FxIdentifier(FName i, const FScriptPosition &p); FxExpression *Resolve(FCompileContext&); - FxExpression *ResolveMember(FCompileContext&, PStruct*, FxExpression*&, PStruct*); + FxExpression *ResolveMember(FCompileContext&, PContainerType*, FxExpression*&, PContainerType*); }; @@ -2004,7 +2007,7 @@ public: class FxStateByIndex : public FxExpression { - int index; + unsigned index; public: diff --git a/src/scripting/backend/dynarrays.cpp b/src/scripting/backend/dynarrays.cpp index 0152c6520..c12e602a0 100644 --- a/src/scripting/backend/dynarrays.cpp +++ b/src/scripting/backend/dynarrays.cpp @@ -39,7 +39,9 @@ #include "tarray.h" #include "dobject.h" -#include "thingdef.h" +#include "vm.h" +#include "types.h" + // We need one specific type for each of the 7 integral VM types and instantiate the needed functions for each of them. // Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible. @@ -97,7 +99,7 @@ DEFINE_ACTION_FUNCTION(FDynArray_I8, Delete) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8); PARAM_INT(index); - PARAM_INT(count); + PARAM_INT_DEF(count); self->Delete(index, count); return 0; } @@ -200,7 +202,7 @@ DEFINE_ACTION_FUNCTION(FDynArray_I16, Delete) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16); PARAM_INT(index); - PARAM_INT(count); + PARAM_INT_DEF(count); self->Delete(index, count); return 0; } @@ -303,7 +305,7 @@ DEFINE_ACTION_FUNCTION(FDynArray_I32, Delete) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32); PARAM_INT(index); - PARAM_INT(count); + PARAM_INT_DEF(count); self->Delete(index, count); return 0; } @@ -406,7 +408,7 @@ DEFINE_ACTION_FUNCTION(FDynArray_F32, Delete) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32); PARAM_INT(index); - PARAM_INT(count); + PARAM_INT_DEF(count); self->Delete(index, count); return 0; } @@ -509,7 +511,7 @@ DEFINE_ACTION_FUNCTION(FDynArray_F64, Delete) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64); PARAM_INT(index); - PARAM_INT(count); + PARAM_INT_DEF(count); self->Delete(index, count); return 0; } @@ -612,7 +614,7 @@ DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Delete) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr); PARAM_INT(index); - PARAM_INT(count); + PARAM_INT_DEF(count); self->Delete(index, count); return 0; } @@ -716,7 +718,7 @@ DEFINE_ACTION_FUNCTION(FDynArray_String, Delete) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String); PARAM_INT(index); - PARAM_INT(count); + PARAM_INT_DEF(count); self->Delete(index, count); return 0; } diff --git a/src/scripting/backend/scopebarrier.cpp b/src/scripting/backend/scopebarrier.cpp index 29caa2457..c42632318 100644 --- a/src/scripting/backend/scopebarrier.cpp +++ b/src/scripting/backend/scopebarrier.cpp @@ -1,5 +1,7 @@ #include "dobject.h" #include "scopebarrier.h" +#include "types.h" +#include "vmintern.h" // Note: the same object can't be both UI and Play. This is checked explicitly in the field construction and will cause esoteric errors here if found. @@ -175,14 +177,14 @@ void FScopeBarrier::AddFlags(int flags1, int flags2, const char* name) // these are for vmexec.h void FScopeBarrier::ValidateNew(PClass* cls, int outerside) { - int innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); + int innerside = FScopeBarrier::SideFromObjectFlags(cls->VMType->ObjectFlags); if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context" ThrowAbortException(X_OTHER, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside)); } void FScopeBarrier::ValidateCall(PClass* selftype, VMFunction *calledfunc, int outerside) { - int innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags); + int innerside = FScopeBarrier::SideFromObjectFlags(selftype->VMType->ObjectFlags); if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->PrintableName.GetChars(), FScopeBarrier::StringFromSide(outerside)); } \ No newline at end of file diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index d8af3204d..13f9ecb27 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -35,8 +35,9 @@ #include "codegen.h" #include "info.h" #include "m_argv.h" -#include "thingdef.h" +//#include "thingdef.h" #include "doomerrors.h" +#include "vmintern.h" struct VMRemap { diff --git a/src/scripting/backend/vmbuilder.h b/src/scripting/backend/vmbuilder.h index ea5213b66..d33688276 100644 --- a/src/scripting/backend/vmbuilder.h +++ b/src/scripting/backend/vmbuilder.h @@ -2,6 +2,7 @@ #define VMUTIL_H #include "dobject.h" +#include "vmintern.h" class VMFunctionBuilder; class FxExpression; diff --git a/src/scripting/backend/vmdisasm.cpp b/src/scripting/backend/vmdisasm.cpp index 3a37da8e3..be5390bba 100644 --- a/src/scripting/backend/vmdisasm.cpp +++ b/src/scripting/backend/vmdisasm.cpp @@ -34,6 +34,7 @@ #include "dobject.h" #include "c_console.h" #include "templates.h" +#include "vmintern.h" #define NOP MODE_AUNUSED | MODE_BUNUSED | MODE_CUNUSED diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp index 9ecf8c2a0..7dc12d017 100644 --- a/src/scripting/decorate/olddecorations.cpp +++ b/src/scripting/decorate/olddecorations.cpp @@ -86,7 +86,7 @@ static const char *RenderStyles[] = "STYLE_Translucent", "STYLE_Add", //"STYLE_Shaded", - NULL + nullptr }; // CODE -------------------------------------------------------------------- @@ -124,14 +124,12 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) bag.ClassName = type->TypeName; #endif - type->GameFilter = GAME_Any; sc.MustGetStringName("{"); memset (&extra, 0, sizeof(extra)); ParseInsideDecoration (bag, (AActor *)(type->Defaults), extra, def, sc, StateArray, SourceLines); - bag.Info->NumOwnedStates = StateArray.Size(); - if (bag.Info->NumOwnedStates == 0) + if (StateArray.Size() == 0) { sc.ScriptError ("%s does not define any animation frames", typeName.GetChars() ); } @@ -155,17 +153,17 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) FScriptPosition icepos = SourceLines[extra.IceDeathEnd - 1]; StateArray.Push (icecopy); SourceLines.Push(icepos); - type->NumOwnedStates += 1; } - type->OwnedStates = new FState[type->NumOwnedStates]; - SaveStateSourceLines(type->OwnedStates, SourceLines); - memcpy (type->OwnedStates, &StateArray[0], type->NumOwnedStates * sizeof(type->OwnedStates[0])); - if (type->NumOwnedStates == 1) + FState *states; + states = (FState*)ClassDataAllocator.Alloc(StateArray.Size() * sizeof(FState)); + SaveStateSourceLines(states, SourceLines); + memcpy (states, &StateArray[0], StateArray.Size() * sizeof(states[0])); + if (StateArray.Size() == 1) { - type->OwnedStates->Tics = -1; - type->OwnedStates->TicRange = 0; - type->OwnedStates->Misc1 = 0; + states->Tics = -1; + states->TicRange = 0; + states->Misc1 = 0; } else { @@ -174,50 +172,51 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) // Spawn states loop endlessly for (i = extra.SpawnStart; i < extra.SpawnEnd-1; ++i) { - type->OwnedStates[i].NextState = &type->OwnedStates[i+1]; + states[i].NextState = &states[i+1]; } - type->OwnedStates[i].NextState = &type->OwnedStates[extra.SpawnStart]; + states[i].NextState = &states[extra.SpawnStart]; // Death states are one-shot and freeze on the final state if (extra.DeathEnd != 0) { for (i = extra.DeathStart; i < extra.DeathEnd-1; ++i) { - type->OwnedStates[i].NextState = &type->OwnedStates[i+1]; + states[i].NextState = &states[i+1]; } + FState *state = &states[i]; if (extra.bDiesAway || def == DEF_Projectile) { - type->OwnedStates[i].NextState = NULL; + state->NextState = nullptr; } else { - type->OwnedStates[i].Tics = -1; - type->OwnedStates[i].TicRange = 0; - type->OwnedStates[i].Misc1 = 0; + state->Tics = -1; + state->TicRange = 0; + state->Misc1 = 0; } if (def == DEF_Projectile) { if (extra.bExplosive) { - type->OwnedStates[extra.DeathStart].SetAction("A_Explode"); + states[extra.DeathStart].SetAction("A_Explode"); } } else { // The first frame plays the death sound and // the second frame makes it nonsolid. - type->OwnedStates[extra.DeathStart].SetAction("A_Scream"); + states[extra.DeathStart].SetAction("A_Scream"); if (extra.bSolidOnDeath) { } else if (extra.DeathStart + 1 < extra.DeathEnd) { - type->OwnedStates[extra.DeathStart+1].SetAction("A_NoBlocking"); + states[extra.DeathStart+1].SetAction("A_NoBlocking"); } else { - type->OwnedStates[extra.DeathStart].SetAction("A_ScreamAndUnblock"); + states[extra.DeathStart].SetAction("A_ScreamAndUnblock"); } if (extra.DeathHeight == 0) @@ -226,7 +225,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) } ((AActor*)(type->Defaults))->FloatVar("DeathHeight") = extra.DeathHeight; } - bag.statedef.SetStateLabel("Death", &type->OwnedStates[extra.DeathStart]); + bag.statedef.SetStateLabel("Death", &states[extra.DeathStart]); } // Burn states are the same as death states, except they can optionally terminate @@ -234,38 +233,39 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) { for (i = extra.FireDeathStart; i < extra.FireDeathEnd-1; ++i) { - type->OwnedStates[i].NextState = &type->OwnedStates[i+1]; + states[i].NextState = &states[i+1]; } + FState *state = &states[i]; if (extra.bBurnAway) { - type->OwnedStates[i].NextState = NULL; + state->NextState = nullptr; } else { - type->OwnedStates[i].Tics = -1; - type->OwnedStates[i].TicRange = 0; - type->OwnedStates[i].Misc1 = 0; + state->Tics = -1; + state->TicRange = 0; + state->Misc1 = 0; } // The first frame plays the burn sound and // the second frame makes it nonsolid. - type->OwnedStates[extra.FireDeathStart].SetAction("A_ActiveSound"); + states[extra.FireDeathStart].SetAction("A_ActiveSound"); if (extra.bSolidOnBurn) { } else if (extra.FireDeathStart + 1 < extra.FireDeathEnd) { - type->OwnedStates[extra.FireDeathStart+1].SetAction("A_NoBlocking"); + states[extra.FireDeathStart+1].SetAction("A_NoBlocking"); } else { - type->OwnedStates[extra.FireDeathStart].SetAction("A_ActiveAndUnblock"); + states[extra.FireDeathStart].SetAction("A_ActiveAndUnblock"); } if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->Height; ((AActor*)(type->Defaults))->FloatVar("BurnHeight") = extra.BurnHeight; - bag.statedef.SetStateLabel("Burn", &type->OwnedStates[extra.FireDeathStart]); + bag.statedef.SetStateLabel("Burn", &states[extra.FireDeathStart]); } // Ice states are similar to burn and death, except their final frame enters @@ -274,21 +274,22 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) { for (i = extra.IceDeathStart; i < extra.IceDeathEnd-1; ++i) { - type->OwnedStates[i].NextState = &type->OwnedStates[i+1]; + states[i].NextState = &states[i+1]; } - type->OwnedStates[i].NextState = &type->OwnedStates[type->NumOwnedStates-1]; - type->OwnedStates[i].Tics = 5; - type->OwnedStates[i].TicRange = 0; - type->OwnedStates[i].Misc1 = 0; - type->OwnedStates[i].SetAction("A_FreezeDeath"); + FState *state = &states[i]; + state->NextState = &states[StateArray.Size() - 1]; + state->Tics = 5; + state->TicRange = 0; + state->Misc1 = 0; + state->SetAction("A_FreezeDeath"); - i = type->NumOwnedStates - 1; - type->OwnedStates[i].NextState = &type->OwnedStates[i]; - type->OwnedStates[i].Tics = 1; - type->OwnedStates[i].TicRange = 0; - type->OwnedStates[i].Misc1 = 0; - type->OwnedStates[i].SetAction("A_FreezeDeathChunks"); - bag.statedef.SetStateLabel("Ice", &type->OwnedStates[extra.IceDeathStart]); + i = StateArray.Size() - 1; + state->NextState = &states[i]; + state->Tics = 1; + state->TicRange = 0; + state->Misc1 = 0; + state->SetAction("A_FreezeDeathChunks"); + bag.statedef.SetStateLabel("Ice", &states[extra.IceDeathStart]); } else if (extra.bGenericIceDeath) { @@ -303,8 +304,10 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns) { ((AActor *)(type->Defaults))->flags |= MF_DROPOFF|MF_MISSILE; } - bag.statedef.SetStateLabel("Spawn", &type->OwnedStates[extra.SpawnStart]); + bag.statedef.SetStateLabel("Spawn", &states[extra.SpawnStart]); bag.statedef.InstallStates (type, ((AActor *)(type->Defaults))); + bag.Info->ActorInfo()->OwnedStates = states; + bag.Info->ActorInfo()->NumOwnedStates = StateArray.Size(); } //========================================================================== @@ -331,7 +334,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, { sc.ScriptError ("DoomEdNum must be in the range [-1,32767]"); } - bag.Info->DoomEdNum = (int16_t)sc.Number; + bag.Info->ActorInfo()->DoomEdNum = (int16_t)sc.Number; } else if (sc.Compare ("SpawnNum")) { @@ -340,7 +343,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, { sc.ScriptError ("SpawnNum must be in the range [0,255]"); } - bag.Info->SpawnID = (uint8_t)sc.Number; + bag.Info->ActorInfo()->SpawnID = (uint8_t)sc.Number; } else if (sc.Compare ("Sprite") || ( (def == DEF_BreakableDecoration || def == DEF_Projectile) && @@ -558,11 +561,11 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, } else if (sc.String[0] != '*') { - HandleActorFlag(sc, bag, sc.String, NULL, '+'); + HandleActorFlag(sc, bag, sc.String, nullptr, '+'); } else { - sc.ScriptError (NULL); + sc.ScriptError (nullptr); } sc.MustGetString (); } @@ -619,9 +622,9 @@ static void ParseSpriteFrames (PClassActor *info, TArray &states, TArray char *token = strtok (sc.String, ",\t\n\r"); memset (&state, 0, sizeof(state)); - state.UseFlags = info->DefaultStateUsage; + state.UseFlags = info->ActorInfo()->DefaultStateUsage; - while (token != NULL) + while (token != nullptr) { // Skip leading white space while (*token == ' ') @@ -631,7 +634,7 @@ static void ParseSpriteFrames (PClassActor *info, TArray &states, TArray bool firstState = true; char *colon = strchr (token, ':'); - if (colon != NULL) + if (colon != nullptr) { char *stop; @@ -682,7 +685,7 @@ static void ParseSpriteFrames (PClassActor *info, TArray &states, TArray SourceLines.Push(sc); } - token = strtok (NULL, ",\t\n\r"); + token = strtok (nullptr, ",\t\n\r"); } } diff --git a/src/scripting/decorate/thingdef_exp.cpp b/src/scripting/decorate/thingdef_exp.cpp index 6757a2382..d3acae632 100644 --- a/src/scripting/decorate/thingdef_exp.cpp +++ b/src/scripting/decorate/thingdef_exp.cpp @@ -88,7 +88,7 @@ FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, PNamespace *spc) if (spc) { - FCompileContext ctx(spc, cls, true); + FCompileContext ctx(spc, cls->VMType, true); data = data->Resolve(ctx); } @@ -505,7 +505,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) default: if (cls != nullptr) { - func = dyn_cast(cls->Symbols.FindSymbol(identifier, true)); + func = dyn_cast(cls->FindSymbol(identifier, true)); // There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work. if (func != nullptr && identifier != NAME_ACS_NamedExecuteWithResult) diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 0bf96e441..4758aaa23 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -69,9 +69,9 @@ EXTERN_CVAR(Bool, strictdecorate); PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName) { - if (parent->mVersion > MakeVersion(2, 0)) + if (parent->VMType->mVersion > MakeVersion(2, 0)) { - sc.Message(MSG_ERROR, "Parent class %s of %s not accessible to DECORATE", parent->GetClass()->TypeName.GetChars(), typeName.GetChars()); + sc.Message(MSG_ERROR, "Parent class %s of %s not accessible to DECORATE", parent->TypeName.GetChars(), typeName.GetChars()); } PClassActor *type = static_cast(parent->CreateDerivedClass(typeName, parent->Size)); if (type == nullptr) @@ -101,7 +101,8 @@ PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FN if (type != nullptr) { // [ZZ] DECORATE classes are always play - type->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(type->ObjectFlags, FScopeBarrier::Side_Play); + auto vmtype = type->VMType; + vmtype->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(vmtype->ObjectFlags, FScopeBarrier::Side_Play); } return type; @@ -582,7 +583,7 @@ static FState *CheckState(FScanner &sc, PClass *type) FState *state = NULL; sc.MustGetString(); - PClassActor *info = dyn_cast(type->ParentClass); + PClassActor *info = ValidateActor(type->ParentClass); if (info != NULL) { @@ -964,7 +965,7 @@ static void ParseActorProperty(FScanner &sc, Baggage &bag) FName name(propname, true); if (name != NAME_None) { - auto propp = dyn_cast(bag.Info->Symbols.FindSymbol(name, true)); + auto propp = dyn_cast(bag.Info->FindSymbol(name, true)); if (propp != nullptr) { DispatchScriptProperty(sc, propp, (AActor *)bag.Info->Defaults, bag); @@ -999,7 +1000,7 @@ PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName par sc.Message(MSG_ERROR, "'%s' inherits from a class with the same name", typeName.GetChars()); break; } - p = dyn_cast(p->ParentClass); + p = ValidateActor(p->ParentClass); } if (parent == NULL) @@ -1016,8 +1017,7 @@ PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName par ti = DecoDerivedClass(sc, parent, typeName); ti->bDecorateClass = true; // we only set this for 'modern' DECORATE. The original stuff is so limited that it cannot do anything that may require flagging. - ti->Replacee = ti->Replacement = NULL; - ti->DoomEdNum = -1; + ti->ActorInfo()->DoomEdNum = -1; return ti; } @@ -1109,7 +1109,7 @@ static PClassActor *ParseActorHeader(FScanner &sc, Baggage *bag) try { PClassActor *info = CreateNewActor(sc, typeName, parentName); - info->DoomEdNum = DoomEdNum > 0 ? DoomEdNum : -1; + info->ActorInfo()->DoomEdNum = DoomEdNum > 0 ? DoomEdNum : -1; info->SourceLumpName = Wads.GetLumpFullPath(sc.LumpNum); if (!info->SetReplacement(replaceName)) @@ -1117,7 +1117,7 @@ static PClassActor *ParseActorHeader(FScanner &sc, Baggage *bag) sc.ScriptMessage("Replaced type '%s' not found for %s", replaceName.GetChars(), info->TypeName.GetChars()); } - ResetBaggage (bag, info == RUNTIME_CLASS(AActor) ? NULL : static_cast(info->ParentClass)); + ResetBaggage (bag, ValidateActor(info->ParentClass)); bag->Info = info; bag->Lumpnum = sc.LumpNum; #ifdef _DEBUG @@ -1152,15 +1152,15 @@ static void ParseActor(FScanner &sc, PNamespace *ns) switch (sc.TokenType) { case TK_Const: - ParseConstant (sc, &info->Symbols, info, ns); + ParseConstant (sc, &info->VMType->Symbols, info, ns); break; case TK_Enum: - ParseEnum (sc, &info->Symbols, info, ns); + ParseEnum (sc, &info->VMType->Symbols, info, ns); break; case TK_Var: - ParseUserVariable (sc, &info->Symbols, info, ns); + ParseUserVariable (sc, &info->VMType->Symbols, info, ns); break; case TK_Identifier: diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index 4355941be..a5f48aa6c 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -97,7 +97,7 @@ FxVMFunctionCall *DoActionSpecials(FScanner &sc, FState & state, Baggage &bag) { sc.ScriptError ("Too many arguments to %s", specname.GetChars()); } - auto f = dyn_cast(RUNTIME_CLASS(AActor)->Symbols.FindSymbol("A_CallSpecial", false)); + auto f = dyn_cast(RUNTIME_CLASS(AActor)->FindSymbol("A_CallSpecial", false)); assert(f != nullptr); return new FxVMFunctionCall(new FxSelf(sc), f, args, sc, false); } @@ -142,7 +142,7 @@ void ParseStates(FScanner &sc, PClassActor * actor, AActor * defaults, Baggage & char lastsprite[5] = ""; FxExpression *ScriptCode; FArgumentList *args = nullptr; - int flagdef = actor->DefaultStateUsage; + int flagdef = actor->ActorInfo()->DefaultStateUsage; FScriptPosition scp; if (sc.CheckString("(")) @@ -339,7 +339,7 @@ do_stop: endofstate: if (ScriptCode != nullptr) { - auto funcsym = CreateAnonymousFunction(actor, nullptr, state.UseFlags); + auto funcsym = CreateAnonymousFunction(actor->VMType, nullptr, state.UseFlags); state.ActionFunc = FunctionBuildList.AddFunction(bag.Namespace, bag.Version, funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true, bag.statedef.GetStateCount(), int(statestring.Len()), sc.LumpNum); } int count = bag.statedef.AddStates(&state, statestring, scp); @@ -583,7 +583,7 @@ FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, B FName symname = FName(sc.String, true); symname = CheckCastKludges(symname); - PFunction *afd = dyn_cast(bag.Info->Symbols.FindSymbol(symname, true)); + PFunction *afd = dyn_cast(bag.Info->FindSymbol(symname, true)); if (afd != NULL) { FArgumentList args; diff --git a/src/scripting/symbols.cpp b/src/scripting/symbols.cpp index 41ca4a1a8..098ecaaa5 100644 --- a/src/scripting/symbols.cpp +++ b/src/scripting/symbols.cpp @@ -36,6 +36,10 @@ #include #include "dobject.h" #include "i_system.h" +#include "templates.h" +#include "serializer.h" +#include "types.h" +#include "vm.h" // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -102,7 +106,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TA assert(proto->ArgumentTypes.Size() > 0); auto selftypeptr = dyn_cast(proto->ArgumentTypes[0]); assert(selftypeptr != nullptr); - variant.SelfClass = dyn_cast(selftypeptr->PointedType); + variant.SelfClass = dyn_cast(selftypeptr->PointedType); assert(variant.SelfClass != nullptr); } else @@ -126,6 +130,80 @@ int PFunction::GetImplicitArgs() 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->IsA(RUNTIME_CLASS(PInt)) && 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 &fields) + : PSymbol(name) +{ + Variables = std::move(fields); +} + //========================================================================== // // @@ -238,6 +316,123 @@ PSymbol *PSymbolTable::AddSymbol (PSymbol *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(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(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(sym)->Type->ReadValue(ar, nullptr, + (uint8_t *)addr + static_cast(sym)->Offset); + } + } + return readsomething || !foundsomething; +} + +//========================================================================== +// +// +// +//========================================================================== + void PSymbolTable::RemoveSymbol(PSymbol *sym) { auto mysym = Symbols.CheckKey(sym->SymbolName); @@ -387,7 +582,7 @@ void RemoveUnusedSymbols() { for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext) { - if (ty->IsKindOf(RUNTIME_CLASS(PStruct))) + if (ty->IsKindOf(RUNTIME_CLASS(PContainerType))) { auto it = ty->Symbols.GetIterator(); PSymbolTable::MapType::Pair *pair; diff --git a/src/scripting/symbols.h b/src/scripting/symbols.h index 648462168..2268bd5ea 100644 --- a/src/scripting/symbols.h +++ b/src/scripting/symbols.h @@ -10,7 +10,7 @@ class VMFunction; class PType; class PPrototype; struct ZCC_TreeNode; -class PStruct; +class PContainerType; // Symbol information ------------------------------------------------------- @@ -175,15 +175,15 @@ public: TArray ArgNames; // we need the names to access them later when the function gets compiled. uint32_t Flags; int UseFlags; - PStruct *SelfClass; + PContainerType *SelfClass; }; TArray Variants; - PStruct *OwningClass = nullptr; + PContainerType *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) {} + PFunction(PContainerType *owner = nullptr, FName name = NAME_None) : PSymbol(name), OwningClass(owner) {} }; // A symbol table ----------------------------------------------------------- @@ -215,6 +215,10 @@ struct PSymbolTable // 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); + PField *AddField(FName name, PType *type, uint32_t flags, unsigned &Size, unsigned *Align = nullptr); + PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue); + bool ReadFields(FSerializer &ar, void *addr, const char *TypeName) const; + void WriteFields(FSerializer &ar, const void *addr, const void *def = nullptr) const; // Similar to AddSymbol but always succeeds. Returns the symbol that used // to be in the table with this name, if any. @@ -270,5 +274,4 @@ struct FNamespaceManager }; extern FNamespaceManager Namespaces; - - +void RemoveUnusedSymbols(); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index a8503e64e..bf585077f 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -59,7 +59,7 @@ #include "a_weapons.h" #include "p_conversation.h" #include "v_text.h" -#include "thingdef.h" +//#include "thingdef.h" #include "backend/codegen.h" #include "a_sharedglobal.h" #include "backend/vmbuilder.h" @@ -101,7 +101,7 @@ FScriptPosition & GetStateSource(FState *state) // //========================================================================== -void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PStruct *cls, uint32_t funcflags, int useflags) +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PContainerType *cls, uint32_t funcflags, int useflags) { // Must be called before adding any other arguments. assert(args == nullptr || args->Size() == 0); @@ -154,7 +154,7 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArray rets(1); TArray args; @@ -184,7 +184,7 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i // //========================================================================== -PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error) +PFunction *FindClassMemberFunction(PContainerType *selfcls, PContainerType *funccls, FName name, FScriptPosition &sc, bool *error) { // Skip ACS_NamedExecuteWithResult. Anything calling this should use the builtin instead. if (name == NAME_ACS_NamedExecuteWithResult) return nullptr; @@ -195,8 +195,8 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam if (symbol != nullptr) { - PClass* cls_ctx = dyn_cast(funccls); - PClass* cls_target = funcsym?dyn_cast(funcsym->OwningClass):nullptr; + auto cls_ctx = dyn_cast(funccls); + auto cls_target = funcsym ? dyn_cast(funcsym->OwningClass) : nullptr; if (funcsym == nullptr) { sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars()); @@ -206,7 +206,7 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam // private access is only allowed if the symbol table belongs to the class in which the current function is being defined. sc.Message(MSG_ERROR, "%s is declared private and not accessible", symbol->SymbolName.GetChars()); } - else if ((funcsym->Variants[0].Flags & VARF_Protected) && symtable != &funccls->Symbols && (!cls_ctx || !cls_target || !cls_ctx->IsDescendantOf((PClass*)cls_target))) + else if ((funcsym->Variants[0].Flags & VARF_Protected) && symtable != &funccls->Symbols && (!cls_ctx || !cls_target || !cls_ctx->Descriptor->IsDescendantOf(cls_target->Descriptor))) { sc.Message(MSG_ERROR, "%s is declared protected and not accessible", symbol->SymbolName.GetChars()); } @@ -236,7 +236,7 @@ void CreateDamageFunction(PNamespace *OutNamespace, const VersionInfo &ver, PCla else { auto dmg = new FxReturnStatement(new FxIntCast(id, true), id->ScriptPosition); - auto funcsym = CreateAnonymousFunction(info, TypeSInt32, 0); + auto funcsym = CreateAnonymousFunction(info->VMType, TypeSInt32, 0); defaults->DamageFunc = FunctionBuildList.AddFunction(OutNamespace, ver, funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate, -1, 0, lumpnum); } } @@ -282,9 +282,8 @@ static void CheckForUnsafeStates(PClassActor *obj) if (state->ActionFunc && state->ActionFunc->Unsafe) { // If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash. - auto owner = FState::StaticFindStateOwner(state); - GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "Unsafe state call in state %s.%d which accesses user variables, reached by %s.%s.\n", - owner->TypeName.GetChars(), int(state - owner->OwnedStates), obj->TypeName.GetChars(), FName(*test).GetChars()); + GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "Unsafe state call in state %s which accesses user variables, reached by %s.%s.\n", + FState::StaticGetStateName(state), obj->TypeName.GetChars(), FName(*test).GetChars()); } state = state->NextState; } @@ -308,9 +307,8 @@ static void CheckLabel(PClassActor *obj, FStateLabel *slb, int useflag, FName st { if (!(state->UseFlags & useflag)) { - auto owner = FState::StaticFindStateOwner(state); - GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "%s references state %s.%d as %s state, but this state is not flagged for use as %s.\n", - obj->TypeName.GetChars(), owner->TypeName.GetChars(), int(state - owner->OwnedStates), statename.GetChars(), descript); + GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "%s references state %s as %s state, but this state is not flagged for use as %s.\n", + obj->TypeName.GetChars(), FState::StaticGetStateName(state), statename.GetChars(), descript); } } if (slb->Children != nullptr) @@ -325,7 +323,7 @@ static void CheckLabel(PClassActor *obj, FStateLabel *slb, int useflag, FName st static void CheckStateLabels(PClassActor *obj, ENamedName *test, int useflag, const char *descript) { - FStateLabels *labels = obj->StateList; + FStateLabels *labels = obj->GetStateLabels(); for (; *test != NAME_None; test++) { @@ -355,13 +353,13 @@ static void CheckStates(PClassActor *obj) { CheckStateLabels(obj, pickupstates, SUF_ITEM, "CustomInventory state chain"); } - for (int i = 0; i < obj->NumOwnedStates; i++) + for (unsigned i = 0; i < obj->GetStateCount(); i++) { - auto state = obj->OwnedStates + i; + auto state = obj->GetStates() + i; if (state->NextState && (state->UseFlags & state->NextState->UseFlags) != state->UseFlags) { - GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "State %s.%d links to a state with incompatible restrictions.\n", - obj->TypeName.GetChars(), int(state - obj->OwnedStates)); + GetStateSource(state).Message(MSG_ERROR, TEXTCOLOR_RED "State %s links to a state with incompatible restrictions.\n", + FState::StaticGetStateName(state)); } } } @@ -375,6 +373,7 @@ static void CheckStates(PClassActor *obj) //========================================================================== void ParseScripts(); void ParseAllDecorate(); +void SynthesizeFlagFields(); void LoadActors() { @@ -389,6 +388,7 @@ void LoadActors() FScriptPosition::StrictErrors = false; ParseAllDecorate(); + SynthesizeFlagFields(); FunctionBuildList.Build(); @@ -403,7 +403,7 @@ void LoadActors() auto ti = PClassActor::AllActorClasses[i]; if (ti->Size == TentativeClass) { - if (ti->ObjectFlags & OF_Transient) + if (ti->bOptional) { Printf(TEXTCOLOR_ORANGE "Class %s referenced but not defined\n", ti->TypeName.GetChars()); FScriptPosition::WarnCounter++; diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 7517283cf..8019a5a41 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -6,6 +6,7 @@ #include "s_sound.h" #include "sc_man.h" #include "cmdlib.h" +#include "vm.h" class FScanner; @@ -148,8 +149,8 @@ inline void ResetBaggage (Baggage *bag, PClassActor *stateclass) // //========================================================================== -AFuncDesc *FindFunction(PStruct *cls, const char * string); -FieldDesc *FindField(PStruct *cls, const char * string); +AFuncDesc *FindFunction(PContainerType *cls, const char * string); +FieldDesc *FindField(PContainerType *cls, const char * string); FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, PNamespace *resolvenspc = nullptr); @@ -159,9 +160,9 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray *args, TArray *argflags, TArray *argnames, PStruct *cls, uint32_t funcflags, int useflags); -PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags); -PFunction *FindClassMemberFunction(PStruct *cls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error); +void SetImplicitArgs(TArray *args, TArray *argflags, TArray *argnames, PContainerType *cls, uint32_t funcflags, int useflags); +PFunction *CreateAnonymousFunction(PContainerType *containingclass, PType *returntype, int flags); +PFunction *FindClassMemberFunction(PContainerType *cls, PContainerType *funccls, FName name, FScriptPosition &sc, bool *error); void CreateDamageFunction(PNamespace *ns, const VersionInfo &ver, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum); //========================================================================== diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 01a79dfc1..3c2b4e3f1 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -61,13 +61,22 @@ #include "serializer.h" #include "wi_stuff.h" #include "a_dynlight.h" +#include "vm.h" +#include "types.h" static TArray properties; static TArray AFTable; static TArray FieldTable; extern int BackbuttonTime; extern float BackbuttonAlpha; -static AWeapon *wpnochg; + +// Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but is used everywhere as a special flag. +// It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution' +// is to create a static variable from it that points to an otherwise unused object and reference that in the script. Yuck!!! +// This must point to a valid DObject derived object so that the garbage collector can deal with it. +// The global VM types are the most convenient options here because they get created before the compiler is started and they +// are not exposed in other ways to scripts - and they do not change unless the engine is shut down. +DEFINE_GLOBAL_NAMED(TypeSInt32, wp_nochange); //========================================================================== // @@ -505,6 +514,7 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int { &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs), 3 }, { &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs), 3 }, { &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs), 3 }, + { &RUNTIME_CLASS_CASTLESS(ADynamicLight),DynLightFlagDefs, countof(DynLightFlagDefs), 3 }, }; #define NUM_FLAG_LISTS (countof(FlagLists)) @@ -667,7 +677,7 @@ static int CompareClassNames(const Desc& a, const Desc& b) // //========================================================================== -AFuncDesc *FindFunction(PStruct *cls, const char * string) +AFuncDesc *FindFunction(PContainerType *cls, const char * string) { int min = 0, max = AFTable.Size() - 1; @@ -698,7 +708,7 @@ AFuncDesc *FindFunction(PStruct *cls, const char * string) // //========================================================================== -FieldDesc *FindField(PStruct *cls, const char * string) +FieldDesc *FindField(PContainerType *cls, const char * string) { int min = 0, max = FieldTable.Size() - 1; const char * cname = cls ? cls->TypeName.GetChars() : ""; @@ -733,7 +743,7 @@ FieldDesc *FindField(PStruct *cls, const char * string) VMFunction *FindVMFunction(PClass *cls, const char *name) { - auto f = dyn_cast(cls->Symbols.FindSymbol(name, true)); + auto f = dyn_cast(cls->FindSymbol(name, true)); return f == nullptr ? nullptr : f->Variants[0].Implementation; } @@ -777,11 +787,11 @@ static int fieldcmp(const void * a, const void * b) void InitThingdef() { // Some native types need size and serialization information added before the scripts get compiled. - auto secplanestruct = NewNativeStruct("Secplane", nullptr); + auto secplanestruct = NewStruct("Secplane", nullptr, true); secplanestruct->Size = sizeof(secplane_t); secplanestruct->Align = alignof(secplane_t); - auto sectorstruct = NewNativeStruct("Sector", nullptr); + auto sectorstruct = NewStruct("Sector", nullptr, true); sectorstruct->Size = sizeof(sector_t); sectorstruct->Align = alignof(sector_t); NewPointer(sectorstruct, false)->InstallHandlers( @@ -796,7 +806,7 @@ void InitThingdef() } ); - auto linestruct = NewNativeStruct("Line", nullptr); + auto linestruct = NewStruct("Line", nullptr, true); linestruct->Size = sizeof(line_t); linestruct->Align = alignof(line_t); NewPointer(linestruct, false)->InstallHandlers( @@ -811,7 +821,7 @@ void InitThingdef() } ); - auto sidestruct = NewNativeStruct("Side", nullptr); + auto sidestruct = NewStruct("Side", nullptr, true); sidestruct->Size = sizeof(side_t); sidestruct->Align = alignof(side_t); NewPointer(sidestruct, false)->InstallHandlers( @@ -826,7 +836,7 @@ void InitThingdef() } ); - auto vertstruct = NewNativeStruct("Vertex", nullptr); + auto vertstruct = NewStruct("Vertex", nullptr, true); vertstruct->Size = sizeof(vertex_t); vertstruct->Align = alignof(vertex_t); NewPointer(vertstruct, false)->InstallHandlers( @@ -841,23 +851,23 @@ void InitThingdef() } ); - auto sectorportalstruct = NewNativeStruct("SectorPortal", nullptr); + auto sectorportalstruct = NewStruct("SectorPortal", nullptr, true); sectorportalstruct->Size = sizeof(FSectorPortal); sectorportalstruct->Align = alignof(FSectorPortal); - auto playerclassstruct = NewNativeStruct("PlayerClass", nullptr); + auto playerclassstruct = NewStruct("PlayerClass", nullptr, true); playerclassstruct->Size = sizeof(FPlayerClass); playerclassstruct->Align = alignof(FPlayerClass); - auto playerskinstruct = NewNativeStruct("PlayerSkin", nullptr); + auto playerskinstruct = NewStruct("PlayerSkin", nullptr, true); playerskinstruct->Size = sizeof(FPlayerSkin); playerskinstruct->Align = alignof(FPlayerSkin); - auto teamstruct = NewNativeStruct("Team", nullptr); + auto teamstruct = NewStruct("Team", nullptr, true); teamstruct->Size = sizeof(FTeam); teamstruct->Align = alignof(FTeam); - PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); + PStruct *pstruct = NewStruct("PlayerInfo", nullptr, true); pstruct->Size = sizeof(player_t); pstruct->Align = alignof(player_t); NewPointer(pstruct, false)->InstallHandlers( @@ -872,7 +882,7 @@ void InitThingdef() } ); - auto fontstruct = NewNativeStruct("FFont", nullptr); + auto fontstruct = NewStruct("FFont", nullptr, true); fontstruct->Size = sizeof(FFont); fontstruct->Align = alignof(FFont); NewPointer(fontstruct, false)->InstallHandlers( @@ -887,32 +897,9 @@ void InitThingdef() } ); - auto wbplayerstruct = NewNativeStruct("WBPlayerStruct", nullptr); + auto wbplayerstruct = NewStruct("WBPlayerStruct", nullptr, true); wbplayerstruct->Size = sizeof(wbplayerstruct_t); wbplayerstruct->Align = alignof(wbplayerstruct_t); - - - // Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag. - // It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution' - // is to create a static variable from it and reference that in the script. Yuck!!! - wpnochg = WP_NOCHANGE; - PField *fieldptr = new PField("WP_NOCHANGE", NewPointer(RUNTIME_CLASS(AWeapon), false), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&wpnochg); - Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); - - // synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them. - for (auto &fl : FlagLists) - { - if (fl.Use & 2) - { - for(int i=0;i 0) // skip the deprecated entries in this list - { - const_cast(*fl.Type)->AddNativeField(FStringf("b%s", fl.Defs[i].name), (fl.Defs[i].fieldsize == 4 ? TypeSInt32 : TypeSInt16), fl.Defs[i].structoffset, fl.Defs[i].varflags, fl.Defs[i].flagbit); - } - } - } - } FAutoSegIterator probe(CRegHead, CRegTail); @@ -980,9 +967,37 @@ void InitThingdef() FieldTable.ShrinkToFit(); qsort(&FieldTable[0], FieldTable.Size(), sizeof(FieldTable[0]), fieldcmp); } - } +void SynthesizeFlagFields() +{ + // These are needed for inserting the flag symbols + /* + NewClassType(RUNTIME_CLASS(DObject)); + NewClassType(RUNTIME_CLASS(DThinker)); + NewClassType(RUNTIME_CLASS(AActor)); + NewClassType(RUNTIME_CLASS(AInventory)); + NewClassType(RUNTIME_CLASS(AStateProvider)); + NewClassType(RUNTIME_CLASS(AWeapon)); + NewClassType(RUNTIME_CLASS(APlayerPawn)); + NewClassType(RUNTIME_CLASS(ADynamicLight)); + */ + // synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them. + for (auto &fl : FlagLists) + { + auto cls = const_cast(*fl.Type); + if (fl.Use & 2) + { + for (int i = 0; i < fl.NumDefs; i++) + { + if (fl.Defs[i].structoffset > 0) // skip the deprecated entries in this list + { + cls->VMType->AddNativeField(FStringf("b%s", fl.Defs[i].name), (fl.Defs[i].fieldsize == 4 ? TypeSInt32 : TypeSInt16), fl.Defs[i].structoffset, fl.Defs[i].varflags, fl.Defs[i].flagbit); + } + } + } + } +} DEFINE_ACTION_FUNCTION(DObject, GameType) { PARAM_PROLOGUE; diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 50994f1e7..374d8a524 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -70,6 +70,7 @@ #include "a_keys.h" #include "g_levellocals.h" #include "d_player.h" +#include "types.h" //========================================================================== // @@ -92,7 +93,7 @@ static PClassActor *FindClassTentative(const char *name, PClass *ancestor, bool } if (cls->Size == TentativeClass && optional) { - cls->ObjectFlags |= OF_Transient; // since this flag has no meaning in class types, let's use it for marking the type optional. + cls->bOptional = true; } return static_cast(cls); } @@ -342,7 +343,7 @@ void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int in FName name(propname, true); if (name != NAME_None) { - auto propp = dyn_cast(info->Symbols.FindSymbol(name, true)); + auto propp = dyn_cast(info->FindSymbol(name, true)); if (propp != nullptr) { *((char*)defaults + propp->Variables[0]->Offset) = set ? 1 : 0; @@ -391,10 +392,9 @@ bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index) case DEPF_QUARTERGRAVITY: return actor->Gravity == 1./4; case DEPF_FIRERESIST: - if (info->DamageFactors) + for (auto &df : info->ActorInfo()->DamageFactors) { - double *df = info->DamageFactors->CheckKey(NAME_Fire); - return df && (*df) == 0.5; + if (df.first == NAME_Fire) return df.second == 0.5; } return false; @@ -464,33 +464,34 @@ static bool PointerCheck(PType *symtype, PType *checktype) DEFINE_INFO_PROPERTY(game, S, Actor) { PROP_STRING_PARM(str, 0); + auto & GameFilter = info->ActorInfo()->GameFilter; if (!stricmp(str, "Doom")) { - info->GameFilter |= GAME_Doom; + GameFilter |= GAME_Doom; } else if (!stricmp(str, "Heretic")) { - info->GameFilter |= GAME_Heretic; + GameFilter |= GAME_Heretic; } else if (!stricmp(str, "Hexen")) { - info->GameFilter |= GAME_Hexen; + GameFilter |= GAME_Hexen; } else if (!stricmp(str, "Raven")) { - info->GameFilter |= GAME_Raven; + GameFilter |= GAME_Raven; } else if (!stricmp(str, "Strife")) { - info->GameFilter |= GAME_Strife; + GameFilter |= GAME_Strife; } else if (!stricmp(str, "Chex")) { - info->GameFilter |= GAME_Chex; + GameFilter |= GAME_Chex; } else if (!stricmp(str, "Any")) { - info->GameFilter = GAME_Any; + GameFilter = GAME_Any; } else { @@ -508,7 +509,7 @@ DEFINE_INFO_PROPERTY(spawnid, I, Actor) { I_Error ("SpawnID must be in the range [0,65535]"); } - else info->SpawnID=(uint16_t)id; + else info->ActorInfo()->SpawnID=(uint16_t)id; } //========================================================================== @@ -521,7 +522,7 @@ DEFINE_INFO_PROPERTY(conversationid, IiI, Actor) PROP_INT_PARM(id2, 2); if (convid <= 0 || convid > 65535) return; // 0 is not usable because the dialogue scripts use it as 'no object'. - else info->ConversationID=(uint16_t)convid; + else info->ActorInfo()->ConversationID=(uint16_t)convid; } //========================================================================== @@ -559,7 +560,7 @@ DEFINE_PROPERTY(skip_super, 0, Actor) DEFINE_PROPERTY(defaultstateusage, I, Actor) { PROP_INT_PARM(use, 0); - static_cast(bag.Info)->DefaultStateUsage = use; + static_cast(bag.Info)->ActorInfo()->DefaultStateUsage = use; } //========================================================================== @@ -740,7 +741,7 @@ DEFINE_PROPERTY(translation, L, Actor) if (type == 0) { PROP_INT_PARM(trans, 1); - int max = 6;// (gameinfo.gametype == GAME_Strife || (info->GameFilter&GAME_Strife)) ? 6 : 2; + int max = 6; if (trans < 0 || trans > max) { I_Error ("Translation must be in the range [0,%d]", max); @@ -1048,12 +1049,12 @@ DEFINE_PROPERTY(visibletoteam, I, Actor) //========================================================================== DEFINE_PROPERTY(visibletoplayerclass, Ssssssssssssssssssss, Actor) { - info->VisibleToPlayerClass.Clear(); + info->ActorInfo()->VisibleToPlayerClass.Clear(); for(int i = 0;i < PROP_PARM_COUNT;++i) { PROP_STRING_PARM(n, i); if (*n != 0) - info->VisibleToPlayerClass.Push(FindClassTentative(n, RUNTIME_CLASS(APlayerPawn))); + info->ActorInfo()->VisibleToPlayerClass.Push(FindClassTentative(n, RUNTIME_CLASS(APlayerPawn))); } } @@ -1071,7 +1072,7 @@ DEFINE_PROPERTY(distancecheck, S, Actor) } else if (cv->GetRealType() == CVAR_Int) { - static_cast(info)->distancecheck = static_cast(cv); + info->ActorInfo()->distancecheck = static_cast(cv); } else { @@ -1133,7 +1134,7 @@ static void SetIcon(FTextureID &icon, Baggage &bag, const char *i) { // Don't print warnings if the item is for another game or if this is a shareware IWAD. // Strife's teaser doesn't contain all the icon graphics of the full game. - if ((bag.Info->GameFilter == GAME_Any || bag.Info->GameFilter & gameinfo.gametype) && + if ((bag.Info->ActorInfo()->GameFilter == GAME_Any || bag.Info->ActorInfo()->GameFilter & gameinfo.gametype) && !(gameinfo.flags&GI_SHAREWARE) && Wads.GetLumpFile(bag.Lumpnum) != 0) { bag.ScriptPosition.Message(MSG_WARNING, @@ -1367,7 +1368,7 @@ DEFINE_SCRIPTED_PROPERTY_PREFIX(powerup, type, S, PowerupGiver) DEFINE_CLASS_PROPERTY_PREFIX(player, displayname, S, PlayerPawn) { PROP_STRING_PARM(str, 0); - info->DisplayName = str; + info->ActorInfo()->DisplayName = str; } //========================================================================== diff --git a/src/scripting/types.cpp b/src/scripting/types.cpp new file mode 100644 index 000000000..b445ad884 --- /dev/null +++ b/src/scripting/types.cpp @@ -0,0 +1,2689 @@ +/* +** types.cpp +** Implements the VM type hierarchy +** +**--------------------------------------------------------------------------- +** Copyright 2008-2016 Randy Heit +** Copyright 2016-2017 Cheistoph 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 "vmintern.h" +#include "s_sound.h" +#include "dthinker.h" +#include "types.h" + + +FTypeTable TypeTable; + +PErrorType *TypeError; +PErrorType *TypeAuto; +PVoidType *TypeVoid; +PInt *TypeSInt8, *TypeUInt8; +PInt *TypeSInt16, *TypeUInt16; +PInt *TypeSInt32, *TypeUInt32; +PBool *TypeBool; +PFloat *TypeFloat32, *TypeFloat64; +PString *TypeString; +PName *TypeName; +PSound *TypeSound; +PColor *TypeColor; +PTextureID *TypeTextureID; +PSpriteID *TypeSpriteID; +PStatePointer *TypeState; +PPointer *TypeFont; +PStateLabel *TypeStateLabel; +PStruct *TypeVector2; +PStruct *TypeVector3; +PStruct *TypeColorStruct; +PStruct *TypeStringStruct; +PPointer *TypeNullPtr; +PPointer *TypeVoidPtr; + + +// CODE -------------------------------------------------------------------- + +IMPLEMENT_CLASS(PErrorType, false, false) +IMPLEMENT_CLASS(PVoidType, false, false) + +void DumpTypeTable() +{ + int used = 0; + int min = INT_MAX; + int max = 0; + int all = 0; + int lens[10] = {0}; + for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) + { + int len = 0; + Printf("%4zu:", i); + for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext) + { + Printf(" -> %s", ty->DescriptiveName()); + len++; + all++; + } + if (len != 0) + { + used++; + if (len < min) + min = len; + if (len > max) + max = len; + } + if (len < (int)countof(lens)) + { + lens[len]++; + } + Printf("\n"); + } + Printf("Used buckets: %d/%lu (%.2f%%) for %d entries\n", used, countof(TypeTable.TypeHash), double(used)/countof(TypeTable.TypeHash)*100, all); + Printf("Min bucket size: %d\n", min); + Printf("Max bucket size: %d\n", max); + Printf("Avg bucket size: %.2f\n", double(all) / used); + int j,k; + for (k = countof(lens)-1; k > 0; --k) + if (lens[k]) + break; + for (j = 0; j <= k; ++j) + Printf("Buckets of len %d: %d (%.2f%%)\n", j, lens[j], j!=0?double(lens[j])/used*100:-1.0); +} + +/* PType ******************************************************************/ + +IMPLEMENT_CLASS(PType, true, false) + +//========================================================================== +// +// PType Parameterized Constructor +// +//========================================================================== + +PType::PType(unsigned int size, unsigned int align) +: Size(size), Align(align), HashNext(nullptr) +{ + mDescriptiveName = "Type"; + loadOp = OP_NOP; + storeOp = OP_NOP; + moveOp = OP_NOP; + RegType = REGT_NIL; + RegCount = 1; +} + +//========================================================================== +// +// PType Destructor +// +//========================================================================== + +PType::~PType() +{ +} + +//========================================================================== +// +// PType :: WriteValue +// +//========================================================================== + +void PType::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + assert(0 && "Cannot write value for this type"); +} + +//========================================================================== +// +// PType :: ReadValue +// +//========================================================================== + +bool PType::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + assert(0 && "Cannot read value for this type"); + return false; +} + +//========================================================================== +// +// PType :: SetDefaultValue +// +//========================================================================== + +void PType::SetDefaultValue(void *base, unsigned offset, TArray *stroffs) +{ +} + +//========================================================================== +// +// PType :: SetDefaultValue +// +//========================================================================== + +void PType::SetPointer(void *base, unsigned offset, TArray *stroffs) +{ +} + +void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs) const +{ +} + +//========================================================================== +// +// PType :: InitializeValue +// +//========================================================================== + +void PType::InitializeValue(void *addr, const void *def) const +{ +} + +//========================================================================== +// +// PType :: DestroyValue +// +//========================================================================== + +void PType::DestroyValue(void *addr) const +{ +} + +//========================================================================== +// +// PType :: SetValue +// +//========================================================================== + +void PType::SetValue(void *addr, int val) +{ + assert(0 && "Cannot set int value for this type"); +} + +void PType::SetValue(void *addr, double val) +{ + assert(0 && "Cannot set float value for this type"); +} + +//========================================================================== +// +// PType :: GetValue +// +//========================================================================== + +int PType::GetValueInt(void *addr) const +{ + assert(0 && "Cannot get value for this type"); + return 0; +} + +double PType::GetValueFloat(void *addr) const +{ + assert(0 && "Cannot get value for this type"); + return 0; +} + +//========================================================================== +// +// PType :: IsMatch +// +//========================================================================== + +bool PType::IsMatch(intptr_t id1, intptr_t id2) const +{ + return false; +} + +//========================================================================== +// +// PType :: GetTypeIDs +// +//========================================================================== + +void PType::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = 0; + id2 = 0; +} + +//========================================================================== +// +// PType :: GetTypeIDs +// +//========================================================================== + +const char *PType::DescriptiveName() const +{ + return mDescriptiveName.GetChars(); +} + +//========================================================================== +// +// PType :: StaticInit STATIC +// +//========================================================================== + +void PType::StaticInit() +{ + // Create types and add them type the type table. + TypeTable.AddType(TypeError = new PErrorType); + TypeTable.AddType(TypeAuto = new PErrorType(2)); + TypeTable.AddType(TypeVoid = new PVoidType); + TypeTable.AddType(TypeSInt8 = new PInt(1, false)); + TypeTable.AddType(TypeUInt8 = new PInt(1, true)); + TypeTable.AddType(TypeSInt16 = new PInt(2, false)); + TypeTable.AddType(TypeUInt16 = new PInt(2, true)); + TypeTable.AddType(TypeSInt32 = new PInt(4, false)); + TypeTable.AddType(TypeUInt32 = new PInt(4, true)); + TypeTable.AddType(TypeBool = new PBool); + TypeTable.AddType(TypeFloat32 = new PFloat(4)); + TypeTable.AddType(TypeFloat64 = new PFloat(8)); + TypeTable.AddType(TypeString = new PString); + TypeTable.AddType(TypeName = new PName); + TypeTable.AddType(TypeSound = new PSound); + TypeTable.AddType(TypeColor = new PColor); + TypeTable.AddType(TypeState = new PStatePointer); + TypeTable.AddType(TypeStateLabel = new PStateLabel); + TypeTable.AddType(TypeNullPtr = new PPointer); + TypeTable.AddType(TypeSpriteID = new PSpriteID); + TypeTable.AddType(TypeTextureID = new PTextureID); + + TypeVoidPtr = NewPointer(TypeVoid, false); + TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value. + TypeStringStruct = NewStruct("Stringstruct", nullptr, true); + TypeFont = NewPointer(NewStruct("Font", nullptr, true)); +#ifdef __BIG_ENDIAN__ + TypeColorStruct->AddField(NAME_a, TypeUInt8); + TypeColorStruct->AddField(NAME_r, TypeUInt8); + TypeColorStruct->AddField(NAME_g, TypeUInt8); + TypeColorStruct->AddField(NAME_b, TypeUInt8); +#else + TypeColorStruct->AddField(NAME_b, TypeUInt8); + TypeColorStruct->AddField(NAME_g, TypeUInt8); + TypeColorStruct->AddField(NAME_r, TypeUInt8); + TypeColorStruct->AddField(NAME_a, TypeUInt8); +#endif + + TypeVector2 = new PStruct(NAME_Vector2, nullptr); + TypeVector2->AddField(NAME_X, TypeFloat64); + TypeVector2->AddField(NAME_Y, TypeFloat64); + TypeTable.AddType(TypeVector2); + TypeVector2->loadOp = OP_LV2; + TypeVector2->storeOp = OP_SV2; + TypeVector2->moveOp = OP_MOVEV2; + TypeVector2->RegType = REGT_FLOAT; + TypeVector2->RegCount = 2; + + TypeVector3 = new PStruct(NAME_Vector3, nullptr); + TypeVector3->AddField(NAME_X, TypeFloat64); + TypeVector3->AddField(NAME_Y, TypeFloat64); + TypeVector3->AddField(NAME_Z, TypeFloat64); + // allow accessing xy as a vector2. This is not supposed to be serialized so it's marked transient + TypeVector3->Symbols.AddSymbol(new PField(NAME_XY, TypeVector2, VARF_Transient, 0)); + TypeTable.AddType(TypeVector3); + TypeVector3->loadOp = OP_LV3; + TypeVector3->storeOp = OP_SV3; + TypeVector3->moveOp = OP_MOVEV3; + TypeVector3->RegType = REGT_FLOAT; + TypeVector3->RegCount = 3; + + + + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_sByte, TypeSInt8)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Byte, TypeUInt8)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Short, TypeSInt16)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uShort, TypeUInt16)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Int, TypeSInt32)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uInt, TypeUInt32)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Bool, TypeBool)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float, TypeFloat64)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Double, TypeFloat64)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float32, TypeFloat32)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float64, TypeFloat64)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_String, TypeString)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Name, TypeName)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Sound, TypeSound)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Color, TypeColor)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_State, TypeState)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector2, TypeVector2)); + Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector3, TypeVector3)); +} + + +/* PBasicType *************************************************************/ + +IMPLEMENT_CLASS(PBasicType, true, false) + +//========================================================================== +// +// PBasicType Default Constructor +// +//========================================================================== + +PBasicType::PBasicType() +{ +} + +//========================================================================== +// +// PBasicType Parameterized Constructor +// +//========================================================================== + +PBasicType::PBasicType(unsigned int size, unsigned int align) +: PType(size, align) +{ + mDescriptiveName = "BasicType"; +} + +/* PCompoundType **********************************************************/ + +IMPLEMENT_CLASS(PCompoundType, true, false) + +/* PContainerType *************************************************************/ + +IMPLEMENT_CLASS(PContainerType, true, false) + +//========================================================================== +// +// PContainerType :: IsMatch +// +//========================================================================== + +bool PContainerType::IsMatch(intptr_t id1, intptr_t id2) const +{ + const DObject *outer = (const DObject *)id1; + FName name = (ENamedName)(intptr_t)id2; + + return Outer == outer && TypeName == name; +} + +//========================================================================== +// +// PContainerType :: GetTypeIDs +// +//========================================================================== + +void PContainerType::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)Outer; + id2 = TypeName; +} + +/* PInt *******************************************************************/ + +IMPLEMENT_CLASS(PInt, false, false) + +//========================================================================== +// +// PInt Default Constructor +// +//========================================================================== + +PInt::PInt() +: PBasicType(4, 4), Unsigned(false), IntCompatible(true) +{ + mDescriptiveName = "SInt32"; + Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, -0x7FFFFFFF - 1)); + Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, 0x7FFFFFFF)); + SetOps(); +} + +//========================================================================== +// +// PInt Parameterized Constructor +// +//========================================================================== + +PInt::PInt(unsigned int size, bool unsign, bool compatible) +: PBasicType(size, size), Unsigned(unsign), IntCompatible(compatible) +{ + mDescriptiveName.Format("%cInt%d", unsign? 'U':'S', size); + + MemberOnly = (size < 4); + if (!unsign) + { + int maxval = (1u << ((8 * size) - 1)) - 1; // compute as unsigned to prevent overflow before -1 + int minval = -maxval - 1; + Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, minval)); + Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, maxval)); + } + else + { + Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, 0u)); + Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, (1u << ((8 * size) - 1)))); + } + SetOps(); +} + +void PInt::SetOps() +{ + moveOp = OP_MOVE; + RegType = REGT_INT; + if (Size == 4) + { + storeOp = OP_SW; + loadOp = OP_LW; + } + else if (Size == 1) + { + storeOp = OP_SB; + loadOp = Unsigned ? OP_LBU : OP_LB; + } + else if (Size == 2) + { + storeOp = OP_SH; + loadOp = Unsigned ? OP_LHU : OP_LH; + } + else + { + assert(0 && "Unhandled integer size"); + storeOp = OP_NOP; + } +} + +//========================================================================== +// +// PInt :: WriteValue +// +//========================================================================== + +void PInt::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + if (Size == 8 && Unsigned) + { + // this is a special case that cannot be represented by an int64_t. + uint64_t val = *(uint64_t*)addr; + ar(key, val); + } + else + { + int64_t val; + switch (Size) + { + case 1: + val = Unsigned ? *(uint8_t*)addr : *(int8_t*)addr; + break; + + case 2: + val = Unsigned ? *(uint16_t*)addr : *(int16_t*)addr; + break; + + case 4: + val = Unsigned ? *(uint32_t*)addr : *(int32_t*)addr; + break; + + case 8: + val = *(int64_t*)addr; + break; + + default: + return; // something invalid + } + ar(key, val); + } +} + +//========================================================================== +// +// PInt :: ReadValue +// +//========================================================================== + +bool PInt::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + NumericValue val; + + ar(key, val); + if (val.type == NumericValue::NM_invalid) return false; // not found or usable + if (val.type == NumericValue::NM_float) val.signedval = (int64_t)val.floatval; + + // No need to check the unsigned state here. Downcasting to smaller types will yield the same result for both. + switch (Size) + { + case 1: + *(uint8_t*)addr = (uint8_t)val.signedval; + break; + + case 2: + *(uint16_t*)addr = (uint16_t)val.signedval; + break; + + case 4: + *(uint32_t*)addr = (uint32_t)val.signedval; + break; + + case 8: + *(uint64_t*)addr = (uint64_t)val.signedval; + break; + + default: + return false; // something invalid + } + + return true; +} + +//========================================================================== +// +// PInt :: SetValue +// +//========================================================================== + +void PInt::SetValue(void *addr, int val) +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + if (Size == 4) + { + *(int *)addr = val; + } + else if (Size == 1) + { + *(uint8_t *)addr = val; + } + else if (Size == 2) + { + *(uint16_t *)addr = val; + } + else if (Size == 8) + { + *(uint64_t *)addr = val; + } + else + { + assert(0 && "Unhandled integer size"); + } +} + +void PInt::SetValue(void *addr, double val) +{ + SetValue(addr, (int)val); +} + +//========================================================================== +// +// PInt :: GetValueInt +// +//========================================================================== + +int PInt::GetValueInt(void *addr) const +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + if (Size == 4) + { + return *(int *)addr; + } + else if (Size == 1) + { + return Unsigned ? *(uint8_t *)addr : *(int8_t *)addr; + } + else if (Size == 2) + { + return Unsigned ? *(uint16_t *)addr : *(int16_t *)addr; + } + else if (Size == 8) + { // truncated output + return (int)*(uint64_t *)addr; + } + else + { + assert(0 && "Unhandled integer size"); + return 0; + } +} + +//========================================================================== +// +// PInt :: GetValueFloat +// +//========================================================================== + +double PInt::GetValueFloat(void *addr) const +{ + return GetValueInt(addr); +} + +//========================================================================== +// +// PInt :: GetStoreOp +// +//========================================================================== + +/* PBool ******************************************************************/ + +IMPLEMENT_CLASS(PBool, false, false) + +//========================================================================== +// +// PInt :: SetValue +// +//========================================================================== + +void PBool::SetValue(void *addr, int val) +{ + *(bool*)addr = !!val; +} + +void PBool::SetValue(void *addr, double val) +{ + *(bool*)addr = val != 0.; +} + +int PBool::GetValueInt(void *addr) const +{ + return *(bool *)addr; +} + +double PBool::GetValueFloat(void *addr) const +{ + return *(bool *)addr; +} + +//========================================================================== +// +// PBool Default Constructor +// +//========================================================================== + +PBool::PBool() +: PInt(sizeof(bool), true) +{ + mDescriptiveName = "Bool"; + MemberOnly = false; +} + +/* PFloat *****************************************************************/ + +IMPLEMENT_CLASS(PFloat, false, false) + +//========================================================================== +// +// PFloat Default Constructor +// +//========================================================================== + +PFloat::PFloat() +: PBasicType(8, 8) +{ + mDescriptiveName = "Float"; + SetDoubleSymbols(); + SetOps(); +} + +//========================================================================== +// +// PFloat Parameterized Constructor +// +//========================================================================== + +PFloat::PFloat(unsigned int size) +: PBasicType(size, size) +{ + mDescriptiveName.Format("Float%d", size); + if (size == 8) + { +#ifdef __i386__ + // According to System V i386 ABI alignment of double type is 4 + // GCC and Clang for 32-bit Intel targets follow this requirement + // However GCC has -malign-double option to enable 8-byte alignment + // So calculation of the actual alignment is needed + struct AlignmentCheck { uint8_t i; double d; }; + Align = static_cast(offsetof(AlignmentCheck, d)); +#endif // __i386__ + + SetDoubleSymbols(); + } + else + { + assert(size == 4); + MemberOnly = true; + SetSingleSymbols(); + } + SetOps(); +} + +//========================================================================== +// +// PFloat :: SetDoubleSymbols +// +// Setup constant values for 64-bit floats. +// +//========================================================================== + +void PFloat::SetDoubleSymbols() +{ + static const SymbolInitF symf[] = + { + { NAME_Min_Normal, DBL_MIN }, + { NAME_Max, DBL_MAX }, + { NAME_Epsilon, DBL_EPSILON }, + { NAME_NaN, std::numeric_limits::quiet_NaN() }, + { NAME_Infinity, std::numeric_limits::infinity() }, + { NAME_Min_Denormal, std::numeric_limits::denorm_min() } + }; + static const SymbolInitI symi[] = + { + { NAME_Dig, DBL_DIG }, + { NAME_Min_Exp, DBL_MIN_EXP }, + { NAME_Max_Exp, DBL_MAX_EXP }, + { NAME_Mant_Dig, DBL_MANT_DIG }, + { NAME_Min_10_Exp, DBL_MIN_10_EXP }, + { NAME_Max_10_Exp, DBL_MAX_10_EXP } + }; + SetSymbols(symf, countof(symf)); + SetSymbols(symi, countof(symi)); +} + +//========================================================================== +// +// PFloat :: SetSingleSymbols +// +// Setup constant values for 32-bit floats. +// +//========================================================================== + +void PFloat::SetSingleSymbols() +{ + static const SymbolInitF symf[] = + { + { NAME_Min_Normal, FLT_MIN }, + { NAME_Max, FLT_MAX }, + { NAME_Epsilon, FLT_EPSILON }, + { NAME_NaN, std::numeric_limits::quiet_NaN() }, + { NAME_Infinity, std::numeric_limits::infinity() }, + { NAME_Min_Denormal, std::numeric_limits::denorm_min() } + }; + static const SymbolInitI symi[] = + { + { NAME_Dig, FLT_DIG }, + { NAME_Min_Exp, FLT_MIN_EXP }, + { NAME_Max_Exp, FLT_MAX_EXP }, + { NAME_Mant_Dig, FLT_MANT_DIG }, + { NAME_Min_10_Exp, FLT_MIN_10_EXP }, + { NAME_Max_10_Exp, FLT_MAX_10_EXP } + }; + SetSymbols(symf, countof(symf)); + SetSymbols(symi, countof(symi)); +} + +//========================================================================== +// +// PFloat :: SetSymbols +// +//========================================================================== + +void PFloat::SetSymbols(const PFloat::SymbolInitF *sym, size_t count) +{ + for (size_t i = 0; i < count; ++i) + { + Symbols.AddSymbol(new PSymbolConstNumeric(sym[i].Name, this, sym[i].Value)); + } +} + +void PFloat::SetSymbols(const PFloat::SymbolInitI *sym, size_t count) +{ + for (size_t i = 0; i < count; ++i) + { + Symbols.AddSymbol(new PSymbolConstNumeric(sym[i].Name, this, sym[i].Value)); + } +} + +//========================================================================== +// +// PFloat :: WriteValue +// +//========================================================================== + +void PFloat::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + if (Size == 8) + { + ar(key, *(double*)addr); + } + else + { + ar(key, *(float*)addr); + } +} + +//========================================================================== +// +// PFloat :: ReadValue +// +//========================================================================== + +bool PFloat::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + NumericValue val; + + ar(key, val); + if (val.type == NumericValue::NM_invalid) return false; // not found or usable + else if (val.type == NumericValue::NM_signed) val.floatval = (double)val.signedval; + else if (val.type == NumericValue::NM_unsigned) val.floatval = (double)val.unsignedval; + + if (Size == 8) + { + *(double*)addr = val.floatval; + } + else + { + *(float*)addr = (float)val.floatval; + } + return true; +} + +//========================================================================== +// +// PFloat :: SetValue +// +//========================================================================== + +void PFloat::SetValue(void *addr, int val) +{ + return SetValue(addr, (double)val); +} + +void PFloat::SetValue(void *addr, double val) +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + if (Size == 4) + { + *(float *)addr = (float)val; + } + else + { + assert(Size == 8); + *(double *)addr = val; + } +} + +//========================================================================== +// +// PFloat :: GetValueInt +// +//========================================================================== + +int PFloat::GetValueInt(void *addr) const +{ + return xs_ToInt(GetValueFloat(addr)); +} + +//========================================================================== +// +// PFloat :: GetValueFloat +// +//========================================================================== + +double PFloat::GetValueFloat(void *addr) const +{ + assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); + if (Size == 4) + { + return *(float *)addr; + } + else + { + assert(Size == 8); + return *(double *)addr; + } +} + +//========================================================================== +// +// PFloat :: GetStoreOp +// +//========================================================================== + +void PFloat::SetOps() +{ + if (Size == 4) + { + storeOp = OP_SSP; + loadOp = OP_LSP; + } + else + { + assert(Size == 8); + storeOp = OP_SDP; + loadOp = OP_LDP; + } + moveOp = OP_MOVEF; + RegType = REGT_FLOAT; +} + +/* PString ****************************************************************/ + +IMPLEMENT_CLASS(PString, false, false) + +//========================================================================== +// +// PString Default Constructor +// +//========================================================================== + +PString::PString() +: PBasicType(sizeof(FString), alignof(FString)) +{ + mDescriptiveName = "String"; + storeOp = OP_SS; + loadOp = OP_LS; + moveOp = OP_MOVES; + RegType = REGT_STRING; + +} + +//========================================================================== +// +// PString :: WriteValue +// +//========================================================================== + +void PString::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + ar(key, *(FString*)addr); +} + +//========================================================================== +// +// PString :: ReadValue +// +//========================================================================== + +bool PString::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + const char *cptr; + ar.StringPtr(key, cptr); + if (cptr == nullptr) + { + return false; + } + else + { + *(FString*)addr = cptr; + return true; + } +} + +//========================================================================== +// +// PString :: SetDefaultValue +// +//========================================================================== + +void PString::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + if (base != nullptr) new((uint8_t *)base + offset) FString; + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } +} + +//========================================================================== +// +// PString :: InitializeValue +// +//========================================================================== + +void PString::InitializeValue(void *addr, const void *def) const +{ + if (def != nullptr) + { + new(addr) FString(*(FString *)def); + } + else + { + new(addr) FString; + } +} + +//========================================================================== +// +// PString :: DestroyValue +// +//========================================================================== + +void PString::DestroyValue(void *addr) const +{ + ((FString *)addr)->~FString(); +} + +/* PName ******************************************************************/ + +IMPLEMENT_CLASS(PName, false, false) + +//========================================================================== +// +// PName Default Constructor +// +//========================================================================== + +PName::PName() +: PInt(sizeof(FName), true, false) +{ + mDescriptiveName = "Name"; + assert(sizeof(FName) == alignof(FName)); +} + +//========================================================================== +// +// PName :: WriteValue +// +//========================================================================== + +void PName::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + const char *cptr = ((const FName*)addr)->GetChars(); + ar.StringPtr(key, cptr); +} + +//========================================================================== +// +// PName :: ReadValue +// +//========================================================================== + +bool PName::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + const char *cptr; + ar.StringPtr(key, cptr); + if (cptr == nullptr) + { + return false; + } + else + { + *(FName*)addr = FName(cptr); + return true; + } +} + +/* PSpriteID ******************************************************************/ + +IMPLEMENT_CLASS(PSpriteID, false, false) + +//========================================================================== +// +// PName Default Constructor +// +//========================================================================== + +PSpriteID::PSpriteID() + : PInt(sizeof(int), true, true) +{ + mDescriptiveName = "SpriteID"; +} + +//========================================================================== +// +// PName :: WriteValue +// +//========================================================================== + +void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + int32_t val = *(int*)addr; + ar.Sprite(key, val, nullptr); +} + +//========================================================================== +// +// PName :: ReadValue +// +//========================================================================== + +bool PSpriteID::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + int32_t val; + ar.Sprite(key, val, nullptr); + *(int*)addr = val; + return true; +} + +/* PTextureID ******************************************************************/ + +IMPLEMENT_CLASS(PTextureID, false, false) + +//========================================================================== +// +// PTextureID Default Constructor +// +//========================================================================== + +PTextureID::PTextureID() + : PInt(sizeof(FTextureID), true, false) +{ + mDescriptiveName = "TextureID"; + assert(sizeof(FTextureID) == alignof(FTextureID)); +} + +//========================================================================== +// +// PTextureID :: WriteValue +// +//========================================================================== + +void PTextureID::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + FTextureID val = *(FTextureID*)addr; + ar(key, val); +} + +//========================================================================== +// +// PTextureID :: ReadValue +// +//========================================================================== + +bool PTextureID::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + FTextureID val; + ar(key, val); + *(FTextureID*)addr = val; + return true; +} + +/* PSound *****************************************************************/ + +IMPLEMENT_CLASS(PSound, false, false) + +//========================================================================== +// +// PSound Default Constructor +// +//========================================================================== + +PSound::PSound() +: PInt(sizeof(FSoundID), true) +{ + mDescriptiveName = "Sound"; + assert(sizeof(FSoundID) == alignof(FSoundID)); +} + +//========================================================================== +// +// PSound :: WriteValue +// +//========================================================================== + +void PSound::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + const char *cptr = *(const FSoundID *)addr; + ar.StringPtr(key, cptr); +} + +//========================================================================== +// +// PSound :: ReadValue +// +//========================================================================== + +bool PSound::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + const char *cptr; + ar.StringPtr(key, cptr); + if (cptr == nullptr) + { + return false; + } + else + { + *(FSoundID *)addr = FSoundID(cptr); + return true; + } +} + +/* PColor *****************************************************************/ + +IMPLEMENT_CLASS(PColor, false, false) + +//========================================================================== +// +// PColor Default Constructor +// +//========================================================================== + +PColor::PColor() +: PInt(sizeof(PalEntry), true) +{ + mDescriptiveName = "Color"; + assert(sizeof(PalEntry) == alignof(PalEntry)); +} + +/* PStateLabel *****************************************************************/ + +IMPLEMENT_CLASS(PStateLabel, false, false) + +//========================================================================== +// +// PStateLabel Default Constructor +// +//========================================================================== + +PStateLabel::PStateLabel() + : PInt(sizeof(int), false, false) +{ + mDescriptiveName = "StateLabel"; +} + +/* PPointer ***************************************************************/ + +IMPLEMENT_CLASS(PPointer, false, false) + +//========================================================================== +// +// PPointer - Default Constructor +// +//========================================================================== + +PPointer::PPointer() +: PBasicType(sizeof(void *), alignof(void *)), PointedType(nullptr), IsConst(false) +{ + mDescriptiveName = "NullPointer"; + loadOp = OP_LP; + storeOp = OP_SP; + moveOp = OP_MOVEA; + RegType = REGT_POINTER; +} + +//========================================================================== +// +// PPointer - Parameterized Constructor +// +//========================================================================== + +PPointer::PPointer(PType *pointsat, bool isconst) +: PBasicType(sizeof(void *), alignof(void *)), PointedType(pointsat), IsConst(isconst) +{ + if (pointsat != nullptr) + { + mDescriptiveName.Format("Pointer<%s%s>", pointsat->DescriptiveName(), isconst ? "readonly " : ""); + mVersion = pointsat->mVersion; + } + else + { + mDescriptiveName = "Pointer"; + mVersion = 0; + } + loadOp = OP_LP; + storeOp = OP_SP; + moveOp = OP_MOVEA; + RegType = REGT_POINTER; +} + +//========================================================================== +// +// PPointer :: IsMatch +// +//========================================================================== + +bool PPointer::IsMatch(intptr_t id1, intptr_t id2) const +{ + assert(id2 == 0 || id2 == 1); + PType *pointat = (PType *)id1; + + return pointat == PointedType && (!!id2) == IsConst; +} + +//========================================================================== +// +// PPointer :: GetTypeIDs +// +//========================================================================== + +void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)PointedType; + id2 = 0; +} + +//========================================================================== +// +// PPointer :: WriteValue +// +//========================================================================== + +void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + if (writer != nullptr) + { + writer(ar, key, addr); + } + else + { + I_Error("Attempt to save pointer to unhandled type %s", PointedType->DescriptiveName()); + } +} + +//========================================================================== +// +// PPointer :: ReadValue +// +//========================================================================== + +bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + if (reader != nullptr) + { + return reader(ar, key, addr); + } + return false; +} + +/* PObjectPointer **********************************************************/ + +IMPLEMENT_CLASS(PObjectPointer, false, false) + +//========================================================================== +// +// PPointer :: GetStoreOp +// +//========================================================================== + +PObjectPointer::PObjectPointer(PClass *cls, bool isconst) + : PPointer(cls->VMType, isconst) +{ + loadOp = OP_LO; + // Non-destroyed thinkers are always guaranteed to be linked into the thinker chain so we don't need the write barrier for them. + if (cls && !cls->IsDescendantOf(RUNTIME_CLASS(DThinker))) storeOp = OP_SO; +} + +//========================================================================== +// +// PPointer :: SetPointer +// +//========================================================================== + +void PObjectPointer::SetPointer(void *base, unsigned offset, TArray *special) +{ + // Add to the list of pointers for this class. + special->Push(offset); +} + +//========================================================================== +// +// PPointer :: WriteValue +// +//========================================================================== + +void PObjectPointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + ar(key, *(DObject **)addr); +} + +//========================================================================== +// +// PPointer :: ReadValue +// +//========================================================================== + +bool PObjectPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + bool res; + ::Serialize(ar, key, *(DObject **)addr, nullptr, &res); + return res; +} + +//========================================================================== +// +// NewPointer +// +// Returns a PPointer to an object of the specified type +// +//========================================================================== + +PPointer *NewPointer(PType *type, bool isconst) +{ + auto cp = dyn_cast(type); + if (cp) return NewPointer(cp->Descriptor, isconst); + + size_t bucket; + PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &bucket); + if (ptype == nullptr) + { + ptype = new PPointer(type, isconst); + TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket); + } + return static_cast(ptype); +} + +PPointer *NewPointer(PClass *cls, bool isconst) +{ + assert(cls->VMType != nullptr); + + auto type = cls->VMType; + size_t bucket; + PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PObjectPointer), (intptr_t)type, isconst ? 1 : 0, &bucket); + if (ptype == nullptr) + { + ptype = new PObjectPointer(cls, isconst); + TypeTable.AddType(ptype, RUNTIME_CLASS(PObjectPointer), (intptr_t)type, isconst ? 1 : 0, bucket); + } + return static_cast(ptype); +} + +/* PStatePointer **********************************************************/ + +IMPLEMENT_CLASS(PStatePointer, false, false) + +//========================================================================== +// +// PStatePointer Default Constructor +// +//========================================================================== + +PStatePointer::PStatePointer() +{ + mDescriptiveName = "Pointer"; + PointedType = NewStruct(NAME_State, nullptr, true); + IsConst = true; +} + +//========================================================================== +// +// PStatePointer :: WriteValue +// +//========================================================================== + +void PStatePointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + ar(key, *(FState **)addr); +} + +//========================================================================== +// +// PStatePointer :: ReadValue +// +//========================================================================== + +bool PStatePointer::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + bool res = false; + ::Serialize(ar, key, *(FState **)addr, nullptr, &res); + return res; +} + + + +/* PClassPointer **********************************************************/ + +IMPLEMENT_CLASS(PClassPointer,false, false) + +//========================================================================== +// +// PClassPointer - Parameterized Constructor +// +//========================================================================== + +PClassPointer::PClassPointer(PClass *restrict) +: PPointer(restrict->VMType), ClassRestriction(restrict) +{ + if (restrict) mDescriptiveName.Format("ClassPointer<%s>", restrict->TypeName.GetChars()); + else mDescriptiveName = "ClassPointer"; + loadOp = OP_LP; + storeOp = OP_SP; + mVersion = restrict->VMType->mVersion; +} + +//========================================================================== +// +// PPointer :: WriteValue +// +//========================================================================== + +void PClassPointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + ar(key, *(PClass **)addr); +} + +//========================================================================== +// +// PPointer :: ReadValue +// +//========================================================================== + +bool PClassPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + ::Serialize(ar, key, *(PClass **)addr, (PClass**)nullptr); + return false; +} + +//========================================================================== +// +// PClassPointer - isCompatible +// +//========================================================================== + +bool PClassPointer::isCompatible(PType *type) +{ + auto other = dyn_cast(type); + return (other != nullptr && other->ClassRestriction->IsDescendantOf(ClassRestriction)); +} + +//========================================================================== +// +// PClassPointer :: SetPointer +// +//========================================================================== + +void PClassPointer::SetPointer(void *base, unsigned offset, TArray *special) +{ +} + +//========================================================================== +// +// PClassPointer :: IsMatch +// +//========================================================================== + +bool PClassPointer::IsMatch(intptr_t id1, intptr_t id2) const +{ + const PClass *classat = (const PClass *)id2; + return classat == ClassRestriction; +} + +//========================================================================== +// +// PClassPointer :: GetTypeIDs +// +//========================================================================== + +void PClassPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = 0; + id2 = (intptr_t)ClassRestriction; +} + +//========================================================================== +// +// NewClassPointer +// +// Returns a PClassPointer for the restricted type. +// +//========================================================================== + +PClassPointer *NewClassPointer(PClass *restrict) +{ + size_t bucket; + PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PClassPointer), 0, (intptr_t)restrict, &bucket); + if (ptype == nullptr) + { + ptype = new PClassPointer(restrict); + TypeTable.AddType(ptype, RUNTIME_CLASS(PClassPointer), 0, (intptr_t)restrict, bucket); + } + return static_cast(ptype); +} + +/* PEnum ******************************************************************/ + +IMPLEMENT_CLASS(PEnum, false, false) + +//========================================================================== +// +// PEnum - Default Constructor +// +//========================================================================== + +PEnum::PEnum() +: PInt(4, false) +{ + mDescriptiveName = "Enum"; +} + +//========================================================================== +// +// PEnum - Parameterized Constructor +// +//========================================================================== + +PEnum::PEnum(FName name, PTypeBase *outer) +: PInt(4, false) +{ + EnumName = name; + Outer = outer; + mDescriptiveName.Format("Enum<%s>", name.GetChars()); +} + +//========================================================================== +// +// NewEnum +// +// Returns a PEnum for the given name and container, making sure not to +// create duplicates. +// +//========================================================================== + +PEnum *NewEnum(FName name, PTypeBase *outer) +{ + size_t bucket; + if (outer == nullptr) outer = Namespaces.GlobalNamespace; + PType *etype = TypeTable.FindType(RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, &bucket); + if (etype == nullptr) + { + etype = new PEnum(name, outer); + TypeTable.AddType(etype, RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, bucket); + } + return static_cast(etype); +} + +/* PArray *****************************************************************/ + +IMPLEMENT_CLASS(PArray, false, false) + +//========================================================================== +// +// PArray - Default Constructor +// +//========================================================================== + +PArray::PArray() +: ElementType(nullptr), ElementCount(0) +{ + mDescriptiveName = "Array"; +} + +//========================================================================== +// +// PArray - Parameterized Constructor +// +//========================================================================== + +PArray::PArray(PType *etype, unsigned int ecount) +: ElementType(etype), ElementCount(ecount) +{ + mDescriptiveName.Format("Array<%s>[%d]", etype->DescriptiveName(), ecount); + + Align = etype->Align; + // Since we are concatenating elements together, the element size should + // also be padded to the nearest alignment. + ElementSize = (etype->Size + (etype->Align - 1)) & ~(etype->Align - 1); + Size = ElementSize * ecount; +} + +//========================================================================== +// +// PArray :: IsMatch +// +//========================================================================== + +bool PArray::IsMatch(intptr_t id1, intptr_t id2) const +{ + const PType *elemtype = (const PType *)id1; + unsigned int count = (unsigned int)(intptr_t)id2; + + return elemtype == ElementType && count == ElementCount; +} + +//========================================================================== +// +// PArray :: GetTypeIDs +// +//========================================================================== + +void PArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)ElementType; + id2 = ElementCount; +} + +//========================================================================== +// +// PArray :: WriteValue +// +//========================================================================== + +void PArray::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + if (ar.BeginArray(key)) + { + const uint8_t *addrb = (const uint8_t *)addr; + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->WriteValue(ar, nullptr, addrb); + addrb += ElementSize; + } + ar.EndArray(); + } +} + +//========================================================================== +// +// PArray :: ReadValue +// +//========================================================================== + +bool PArray::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + if (ar.BeginArray(key)) + { + bool readsomething = false; + unsigned count = ar.ArraySize(); + unsigned loop = MIN(count, ElementCount); + uint8_t *addrb = (uint8_t *)addr; + for(unsigned i=0;iReadValue(ar, nullptr, addrb); + addrb += ElementSize; + } + if (loop < count) + { + DPrintf(DMSG_WARNING, "Array on disk (%u) is bigger than in memory (%u)\n", + count, ElementCount); + } + ar.EndArray(); + return readsomething; + } + return false; +} + +//========================================================================== +// +// PArray :: SetDefaultValue +// +//========================================================================== + +void PArray::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->SetDefaultValue(base, offset + i*ElementSize, special); + } +} + +//========================================================================== +// +// PArray :: SetDefaultValue +// +//========================================================================== + +void PArray::SetPointer(void *base, unsigned offset, TArray *special) +{ + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->SetPointer(base, offset + i*ElementSize, special); + } +} + +//========================================================================== +// +// NewArray +// +// Returns a PArray for the given type and size, making sure not to create +// duplicates. +// +//========================================================================== + +PArray *NewArray(PType *type, unsigned int count) +{ + size_t bucket; + PType *atype = TypeTable.FindType(RUNTIME_CLASS(PArray), (intptr_t)type, count, &bucket); + if (atype == nullptr) + { + atype = new PArray(type, count); + TypeTable.AddType(atype, RUNTIME_CLASS(PArray), (intptr_t)type, count, bucket); + } + return (PArray *)atype; +} + +/* PArray *****************************************************************/ + +IMPLEMENT_CLASS(PStaticArray, false, false) + +//========================================================================== +// +// PArray - Default Constructor +// +//========================================================================== + +PStaticArray::PStaticArray() +{ + mDescriptiveName = "ResizableArray"; +} + +//========================================================================== +// +// PArray - Parameterized Constructor +// +//========================================================================== + +PStaticArray::PStaticArray(PType *etype) + : PArray(etype, 0) +{ + mDescriptiveName.Format("ResizableArray<%s>", etype->DescriptiveName()); +} + +//========================================================================== +// +// PArray :: IsMatch +// +//========================================================================== + +bool PStaticArray::IsMatch(intptr_t id1, intptr_t id2) const +{ + const PType *elemtype = (const PType *)id1; + unsigned int count = (unsigned int)(intptr_t)id2; + + return elemtype == ElementType && count == 0; +} + +//========================================================================== +// +// PArray :: GetTypeIDs +// +//========================================================================== + +void PStaticArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)ElementType; + id2 = 0; +} + +//========================================================================== +// +// NewStaticArray +// +// Returns a PArray for the given type and size, making sure not to create +// duplicates. +// +//========================================================================== + +PStaticArray *NewStaticArray(PType *type) +{ + size_t bucket; + PType *atype = TypeTable.FindType(RUNTIME_CLASS(PStaticArray), (intptr_t)type, 0, &bucket); + if (atype == nullptr) + { + atype = new PStaticArray(type); + TypeTable.AddType(atype, RUNTIME_CLASS(PStaticArray), (intptr_t)type, 0, bucket); + } + return (PStaticArray *)atype; +} + +/* PDynArray **************************************************************/ + +IMPLEMENT_CLASS(PDynArray, false, false) + +//========================================================================== +// +// PDynArray - Default Constructor +// +//========================================================================== + +PDynArray::PDynArray() +: ElementType(nullptr) +{ + mDescriptiveName = "DynArray"; + Size = sizeof(FArray); + Align = alignof(FArray); +} + +//========================================================================== +// +// PDynArray - Parameterized Constructor +// +//========================================================================== + +PDynArray::PDynArray(PType *etype,PStruct *backing) +: ElementType(etype), BackingType(backing) +{ + mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName()); + Size = sizeof(FArray); + Align = alignof(FArray); +} + +//========================================================================== +// +// PDynArray :: IsMatch +// +//========================================================================== + +bool PDynArray::IsMatch(intptr_t id1, intptr_t id2) const +{ + assert(id2 == 0); + const PType *elemtype = (const PType *)id1; + + return elemtype == ElementType; +} + +//========================================================================== +// +// PDynArray :: GetTypeIDs +// +//========================================================================== + +void PDynArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)ElementType; + id2 = 0; +} + +//========================================================================== +// +// PDynArray :: InitializeValue +// +//========================================================================== + +void PDynArray::InitializeValue(void *addr, const void *deff) const +{ + const FArray *def = (const FArray*)deff; + FArray *aray = (FArray*)addr; + + if (def == nullptr || def->Count == 0) + { + // Empty arrays do not need construction. + *aray = { nullptr, 0, 0 }; + } + else if (ElementType->GetRegType() != REGT_STRING) + { + // These are just integral values which can be done without any constructor hackery. + size_t blocksize = ElementType->Size * def->Count; + aray->Array = M_Malloc(blocksize); + memcpy(aray->Array, def->Array, blocksize); + aray->Most = aray->Count = def->Count; + } + else + { + // non-empty string arrays require explicit construction. + new(addr) TArray(*(TArray*)def); + } +} + +//========================================================================== +// +// PDynArray :: DestroyValue +// +//========================================================================== + +void PDynArray::DestroyValue(void *addr) const +{ + FArray *aray = (FArray*)addr; + + if (aray->Array != nullptr) + { + if (ElementType->GetRegType() != REGT_STRING) + { + M_Free(aray->Array); + } + else + { + // Damn those cursed strings again. :( + ((TArray*)addr)->~TArray(); + } + } + aray->Count = aray->Most = 0; + aray->Array = nullptr; +} + +//========================================================================== +// +// PDynArray :: SetDefaultValue +// +//========================================================================== + +void PDynArray::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + if (base != nullptr) memset((char*)base + offset, 0, sizeof(FArray)); // same as constructing an empty array. + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } +} + +//========================================================================== +// +// PDynArray :: SetPointer +// +//========================================================================== + +void PDynArray::SetPointerArray(void *base, unsigned offset, TArray *special) const +{ + if (ElementType->IsKindOf(RUNTIME_CLASS(PObjectPointer))) + { + // Add to the list of pointer arrays for this class. + special->Push(offset); + } +} + +//========================================================================== +// +// PDynArray :: WriteValue +// +//========================================================================== + +void PDynArray::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + FArray *aray = (FArray*)addr; + if (aray->Count > 0) + { + if (ar.BeginArray(key)) + { + const uint8_t *addrb = (const uint8_t *)aray->Array; + for (unsigned i = 0; i < aray->Count; ++i) + { + ElementType->WriteValue(ar, nullptr, addrb); + addrb += ElementType->Size; + } + ar.EndArray(); + } + } +} + +//========================================================================== +// +// PDynArray :: ReadValue +// +//========================================================================== + +bool PDynArray::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + FArray *aray = (FArray*)addr; + DestroyValue(addr); // note that even after calling this we still got a validly constructed empty array. + + if (ar.BeginArray(key)) + { + bool readsomething = false; + unsigned count = ar.ArraySize(); + + size_t blocksize = ElementType->Size * count; + aray->Array = M_Malloc(blocksize); + memset(aray->Array, 0, blocksize); + aray->Most = aray->Count = count; + + uint8_t *addrb = (uint8_t *)aray->Array; + for (unsigned i = 0; iGetRegType() == REGT_STRING) new(addrb) FString; + readsomething |= ElementType->ReadValue(ar, nullptr, addrb); + addrb += ElementType->Size; + } + ar.EndArray(); + return readsomething; + } + return false; +} + +//========================================================================== +// +// NewDynArray +// +// Creates a new DynArray of the given type, making sure not to create a +// duplicate. +// +//========================================================================== + +PDynArray *NewDynArray(PType *type) +{ + size_t bucket; + PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket); + if (atype == nullptr) + { + FString backingname; + + switch (type->GetRegType()) + { + case REGT_INT: + backingname.Format("DynArray_I%d", type->Size * 8); + break; + + case REGT_FLOAT: + backingname.Format("DynArray_F%d", type->Size * 8); + break; + + case REGT_STRING: + backingname = "DynArray_String"; + break; + + case REGT_POINTER: + backingname = "DynArray_Ptr"; + break; + + default: + I_Error("Unsupported dynamic array requested"); + break; + } + + auto backing = NewStruct(backingname, nullptr, true); + atype = new PDynArray(type, backing); + TypeTable.AddType(atype, RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, bucket); + } + return (PDynArray *)atype; +} + +/* PMap *******************************************************************/ + +IMPLEMENT_CLASS(PMap, false, false) + +//========================================================================== +// +// PMap - Default Constructor +// +//========================================================================== + +PMap::PMap() +: KeyType(nullptr), ValueType(nullptr) +{ + mDescriptiveName = "Map"; + Size = sizeof(FMap); + Align = alignof(FMap); +} + +//========================================================================== +// +// PMap - Parameterized Constructor +// +//========================================================================== + +PMap::PMap(PType *keytype, PType *valtype) +: KeyType(keytype), ValueType(valtype) +{ + mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName()); + Size = sizeof(FMap); + Align = alignof(FMap); +} + +//========================================================================== +// +// PMap :: IsMatch +// +//========================================================================== + +bool PMap::IsMatch(intptr_t id1, intptr_t id2) const +{ + const PType *keyty = (const PType *)id1; + const PType *valty = (const PType *)id2; + + return keyty == KeyType && valty == ValueType; +} + +//========================================================================== +// +// PMap :: GetTypeIDs +// +//========================================================================== + +void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)KeyType; + id2 = (intptr_t)ValueType; +} + +//========================================================================== +// +// NewMap +// +// Returns a PMap for the given key and value types, ensuring not to create +// duplicates. +// +//========================================================================== + +PMap *NewMap(PType *keytype, PType *valuetype) +{ + size_t bucket; + PType *maptype = TypeTable.FindType(RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, &bucket); + if (maptype == nullptr) + { + maptype = new PMap(keytype, valuetype); + TypeTable.AddType(maptype, RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, bucket); + } + return (PMap *)maptype; +} + +/* PStruct ****************************************************************/ + +IMPLEMENT_CLASS(PStruct, false, false) + +//========================================================================== +// +// PStruct - Default Constructor +// +//========================================================================== + +PStruct::PStruct() +{ + mDescriptiveName = "Struct"; + Size = 0; +} + +//========================================================================== +// +// PStruct - Parameterized Constructor +// +//========================================================================== + +PStruct::PStruct(FName name, PTypeBase *outer, bool isnative) +: PContainerType(name, outer) +{ + mDescriptiveName.Format("%sStruct<%s>", isnative? "Native" : "", name.GetChars()); + Size = 0; + isNative = isnative; +} + +//========================================================================== +// +// PStruct :: SetDefaultValue +// +//========================================================================== + +void PStruct::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + auto it = Symbols.GetIterator(); + PSymbolTable::MapType::Pair *pair; + while (it.NextPair(pair)) + { + auto field = dyn_cast(pair->Value); + if (field && !(field->Flags & VARF_Transient)) + { + field->Type->SetDefaultValue(base, unsigned(offset + field->Offset), special); + } + } +} + +//========================================================================== +// +// PStruct :: SetPointer +// +//========================================================================== + +void PStruct::SetPointer(void *base, unsigned offset, TArray *special) +{ + auto it = Symbols.GetIterator(); + PSymbolTable::MapType::Pair *pair; + while (it.NextPair(pair)) + { + auto field = dyn_cast(pair->Value); + if (field && !(field->Flags & VARF_Transient)) + { + field->Type->SetPointer(base, unsigned(offset + field->Offset), special); + } + } +} + +//========================================================================== +// +// PStruct :: WriteValue +// +//========================================================================== + +void PStruct::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + if (ar.BeginObject(key)) + { + Symbols.WriteFields(ar, addr); + ar.EndObject(); + } +} + +//========================================================================== +// +// PStruct :: ReadValue +// +//========================================================================== + +bool PStruct::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + if (ar.BeginObject(key)) + { + bool ret = Symbols.ReadFields(ar, addr, DescriptiveName()); + ar.EndObject(); + return ret; + } + return false; +} + +//========================================================================== +// +// PStruct :: AddField +// +// Appends a new field to the end of a struct. Returns either the new field +// or nullptr if a symbol by that name already exists. +// +//========================================================================== + +PField *PStruct::AddField(FName name, PType *type, uint32_t flags) +{ + return Symbols.AddField(name, type, flags, Size, &Align); +} + +//========================================================================== +// +// 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 *PStruct::AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue) +{ + return Symbols.AddNativeField(name, type, address, flags, bitvalue); +} + +//========================================================================== +// +// NewStruct +// Returns a PStruct for the given name and container, making sure not to +// create duplicates. +// +//========================================================================== + +PStruct *NewStruct(FName name, PTypeBase *outer, bool native) +{ + size_t bucket; + if (outer == nullptr) outer = Namespaces.GlobalNamespace; + PType *stype = TypeTable.FindType(RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, &bucket); + if (stype == nullptr) + { + stype = new PStruct(name, outer, native); + TypeTable.AddType(stype, RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, bucket); + } + return static_cast(stype); +} + + +/* PPrototype *************************************************************/ + +IMPLEMENT_CLASS(PPrototype, false, false) + +//========================================================================== +// +// PPrototype - Default Constructor +// +//========================================================================== + +PPrototype::PPrototype() +{ +} + +//========================================================================== +// +// PPrototype - Parameterized Constructor +// +//========================================================================== + +PPrototype::PPrototype(const TArray &rettypes, const TArray &argtypes) +: ArgumentTypes(argtypes), ReturnTypes(rettypes) +{ +} + +//========================================================================== +// +// PPrototype :: IsMatch +// +//========================================================================== + +bool PPrototype::IsMatch(intptr_t id1, intptr_t id2) const +{ + const TArray *args = (const TArray *)id1; + const TArray *rets = (const TArray *)id2; + + return *args == ArgumentTypes && *rets == ReturnTypes; +} + +//========================================================================== +// +// PPrototype :: GetTypeIDs +// +//========================================================================== + +void PPrototype::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)&ArgumentTypes; + id2 = (intptr_t)&ReturnTypes; +} + +//========================================================================== +// +// PPrototype :: PropagateMark +// +//========================================================================== + +size_t PPrototype::PropagateMark() +{ + GC::MarkArray(ArgumentTypes); + GC::MarkArray(ReturnTypes); + return (ArgumentTypes.Size() + ReturnTypes.Size()) * sizeof(void*) + + Super::PropagateMark(); +} + +//========================================================================== +// +// NewPrototype +// +// Returns a PPrototype for the given return and argument types, making sure +// not to create duplicates. +// +//========================================================================== + +PPrototype *NewPrototype(const TArray &rettypes, const TArray &argtypes) +{ + size_t bucket; + PType *proto = TypeTable.FindType(RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, &bucket); + if (proto == nullptr) + { + proto = new PPrototype(rettypes, argtypes); + TypeTable.AddType(proto, RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, bucket); + } + return static_cast(proto); +} + +/* PClass *****************************************************************/ + +IMPLEMENT_CLASS(PClassType, false, false) + +//========================================================================== +// +// +// +//========================================================================== + +PClassType::PClassType(PClass *cls) +{ + assert(cls->VMType == nullptr); + Descriptor = cls; + TypeName = cls->TypeName; + if (cls->ParentClass != nullptr) + { + ParentType = cls->ParentClass->VMType; + assert(ParentType != nullptr); + Symbols.SetParentTable(&ParentType->Symbols); + } + cls->VMType = this; + mDescriptiveName.Format("Class<%s>", cls->TypeName.GetChars()); +} + +//========================================================================== +// +// PClass :: AddField +// +//========================================================================== + +PField *PClassType::AddField(FName name, PType *type, uint32_t flags) +{ + return Descriptor->AddField(name, type, flags); +} + +//========================================================================== +// +// PClass :: AddNativeField +// +//========================================================================== + +PField *PClassType::AddNativeField(FName name, PType *type, size_t address, uint32_t flags, int bitvalue) +{ + auto field = Symbols.AddNativeField(name, type, address, flags, bitvalue); + if (field != nullptr) Descriptor->Fields.Push(field); + return field; +} + +//========================================================================== +// +// +// +//========================================================================== + +PClassType *NewClassType(PClass *cls) +{ + size_t bucket; + PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PClassType), 0, (intptr_t)cls->TypeName, &bucket); + if (ptype == nullptr) + { + ptype = new PClassType(cls); + TypeTable.AddType(ptype, RUNTIME_CLASS(PClassType), 0, (intptr_t)cls->TypeName, bucket); + } + return static_cast(ptype); +} + + +/* FTypeTable **************************************************************/ + +//========================================================================== +// +// FTypeTable :: FindType +// +//========================================================================== + +PType *FTypeTable::FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, size_t *bucketnum) +{ + size_t bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; + if (bucketnum != nullptr) + { + *bucketnum = bucket; + } + for (PType *type = TypeHash[bucket]; type != nullptr; type = type->HashNext) + { + if (type->TypeTableType == metatype && type->IsMatch(parm1, parm2)) + { + return type; + } + } + return nullptr; +} + +//========================================================================== +// +// FTypeTable :: AddType - Fully Parameterized Version +// +//========================================================================== + +void FTypeTable::AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t parm2, size_t bucket) +{ +#ifdef _DEBUG + size_t bucketcheck; + assert(FindType(metatype, parm1, parm2, &bucketcheck) == nullptr && "Type must not be inserted more than once"); + assert(bucketcheck == bucket && "Passed bucket was wrong"); +#endif + type->TypeTableType = metatype; + type->HashNext = TypeHash[bucket]; + TypeHash[bucket] = type; + type->Release(); +} + +//========================================================================== +// +// FTypeTable :: AddType - Simple Version +// +//========================================================================== + +void FTypeTable::AddType(PType *type) +{ + intptr_t parm1, parm2; + size_t bucket; + + // Type table stuff id only needed to let all classes hash to the same group. For all other types this is pointless. + type->TypeTableType = type->GetClass(); + PClass *metatype = type->TypeTableType; + type->GetTypeIDs(parm1, parm2); + bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; + assert(FindType(metatype, parm1, parm2, nullptr) == nullptr && "Type must not be inserted more than once"); + + type->HashNext = TypeHash[bucket]; + TypeHash[bucket] = type; + type->Release(); +} + +//========================================================================== +// +// FTypeTable :: Hash STATIC +// +//========================================================================== + +size_t FTypeTable::Hash(const PClass *p1, intptr_t p2, intptr_t p3) +{ + size_t i1 = (size_t)p1; + + // Swap the high and low halves of i1. The compiler should be smart enough + // to transform this into a ROR or ROL. + i1 = (i1 >> (sizeof(size_t)*4)) | (i1 << (sizeof(size_t)*4)); + + if (p1 != RUNTIME_CLASS(PPrototype)) + { + size_t i2 = (size_t)p2; + size_t i3 = (size_t)p3; + return (~i1 ^ i2) + i3 * 961748927; // i3 is multiplied by a prime + } + else + { // Prototypes need to hash the TArrays at p2 and p3 + const TArray *a2 = (const TArray *)p2; + const TArray *a3 = (const TArray *)p3; + for (unsigned i = 0; i < a2->Size(); ++i) + { + i1 = (i1 * 961748927) + (size_t)((*a2)[i]); + } + for (unsigned i = 0; i < a3->Size(); ++i) + { + i1 = (i1 * 961748927) + (size_t)((*a3)[i]); + } + return i1; + } +} + +//========================================================================== +// +// FTypeTable :: Clear +// +//========================================================================== + +void FTypeTable::Clear() +{ + for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) + { + for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr;) + { + auto next = ty->HashNext; + delete ty; + ty = next; + } + } + memset(TypeHash, 0, sizeof(TypeHash)); +} + +#include "c_dispatch.h" +CCMD(typetable) +{ + DumpTypeTable(); +} + diff --git a/src/scripting/types.h b/src/scripting/types.h new file mode 100644 index 000000000..3c233c617 --- /dev/null +++ b/src/scripting/types.h @@ -0,0 +1,641 @@ +#pragma once + +#include "dobject.h" +#include "serializer.h" + +// Variable/parameter/field flags ------------------------------------------- +class PStruct; + +// Making all these different storage types use a common set of flags seems +// like the simplest thing to do. + +enum +{ + VARF_Optional = (1<<0), // func param is optional + VARF_Method = (1<<1), // func has an implied self parameter + VARF_Action = (1<<2), // func has implied owner and state parameters + VARF_Native = (1<<3), // func is native code, field is natively defined + VARF_ReadOnly = (1<<4), // field is read only, do not write to it + VARF_Private = (1<<5), // field is private to containing class + VARF_Protected = (1<<6), // field is only accessible by containing class and children. + VARF_Deprecated = (1<<7), // Deprecated fields should output warnings when used. + VARF_Virtual = (1<<8), // function is virtual + VARF_Final = (1<<9), // Function may not be overridden in subclasses + VARF_In = (1<<10), + VARF_Out = (1<<11), + VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures) + VARF_Static = (1<<13), + VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code. + VARF_Override = (1<<15), // overrides a virtual function from the parent class. + VARF_Ref = (1<<16), // argument is passed by reference. + VARF_Transient = (1<<17), // don't auto serialize field. + VARF_Meta = (1<<18), // static class data (by necessity read only.) + VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature + VARF_UI = (1<<20), // [ZZ] ui: object is ui-scope only (can't modify playsim) + VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui) + VARF_VirtualScope = (1<<22), // [ZZ] virtualscope: object should use the scope of the particular class it's being used with (methods only) + VARF_ClearScope = (1<<23), // [ZZ] clearscope: this method ignores the member access chain that leads to it and is always plain data. +}; + +// Basic information shared by all types ------------------------------------ + +// Only one copy of a type is ever instantiated at one time. +// - Enums, classes, and structs are defined by their names and outer classes. +// - Pointers are uniquely defined by the type they point at. +// - ClassPointers are also defined by their class restriction. +// - Arrays are defined by their element type and count. +// - DynArrays are defined by their element type. +// - Maps are defined by their key and value types. +// - Prototypes are defined by the argument and return types. +// - Functions are defined by their names and outer objects. +// In table form: +// Outer Name Type Type2 Count +// Enum * * +// Class * * +// Struct * * +// Function * * +// Pointer * +// ClassPointer + * +// Array * * +// DynArray * +// Map * * +// Prototype *+ *+ + +struct ZCC_ExprConstant; +class PType : public PTypeBase +{ + DECLARE_ABSTRACT_CLASS(PType, PTypeBase) +protected: + +public: + PClass *TypeTableType; // The type to use for hashing into the type table + unsigned int Size; // this type's size + unsigned int Align; // this type's preferred alignment + PType *HashNext; // next type in this type table + PSymbolTable Symbols; + bool MemberOnly = false; // type may only be used as a struct/class member but not as a local variable or function argument. + FString mDescriptiveName; + VersionInfo mVersion = { 0,0,0 }; + uint8_t loadOp, storeOp, moveOp, RegType, RegCount; + + PType(unsigned int size = 1, unsigned int align = 1); + virtual ~PType(); + virtual bool isNumeric() { return false; } + + // Writes the value of a variable of this type at (addr) to an archive, preceded by + // a tag indicating its type. The tag is there so that variable types can be changed + // without completely breaking savegames, provided that the change isn't between + // totally unrelated types. + virtual void WriteValue(FSerializer &ar, const char *key,const void *addr) const; + + // Returns true if the stored value was compatible. False otherwise. + // If the value was incompatible, then the memory at *addr is unchanged. + virtual bool ReadValue(FSerializer &ar, const char *key,void *addr) const; + + // Sets the default value for this type at (base + offset) + // If the default value is binary 0, then this function doesn't need + // to do anything, because PClass::Extend() takes care of that. + // + // The stroffs array is so that types that need special initialization + // and destruction (e.g. strings) can add their offsets to it for special + // initialization when the object is created and destruction when the + // object is destroyed. + virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL); + virtual void SetPointer(void *base, unsigned offset, TArray *ptrofs = NULL); + virtual void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const; + + // Initialize the value, if needed (e.g. strings) + virtual void InitializeValue(void *addr, const void *def) const; + + // Destroy the value, if needed (e.g. strings) + virtual void DestroyValue(void *addr) const; + + // Sets the value of a variable of this type at (addr) + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); + + // Gets the value of a variable of this type at (addr) + virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; + + // Gets the opcode to store from a register to memory + int GetStoreOp() const + { + return storeOp; + } + + // Gets the opcode to load from memory to a register + int GetLoadOp() const + { + return loadOp; + } + + // Gets the opcode to move from register to another register + int GetMoveOp() const + { + return moveOp; + } + + // Gets the register type for this type + int GetRegType() const + { + return RegType; + } + + int GetRegCount() const + { + return RegCount; + } + // Returns true if this type matches the two identifiers. Referring to the + // above table, any type is identified by at most two characteristics. Each + // type that implements this function will cast these to the appropriate type. + // It is up to the caller to make sure they are the correct types. There is + // only one prototype for this function in order to simplify type table + // management. + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + + // Get the type IDs used by IsMatch + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + const char *DescriptiveName() const; + + static void StaticInit(); +}; + +// Not-really-a-type types -------------------------------------------------- + +class PErrorType : public PType +{ + DECLARE_CLASS(PErrorType, PType); +public: + PErrorType(int which = 1) : PType(0, which) {} +}; + +class PVoidType : public PType +{ + DECLARE_CLASS(PVoidType, PType); +public: + PVoidType() : PType(0, 1) {} +}; + +// Some categorization typing ----------------------------------------------- + +class PBasicType : public PType +{ + DECLARE_ABSTRACT_CLASS(PBasicType, PType); +public: + PBasicType(); + PBasicType(unsigned int size, unsigned int align); +}; + +class PCompoundType : public PType +{ + DECLARE_ABSTRACT_CLASS(PCompoundType, PType); +}; + +class PContainerType : public PCompoundType +{ + DECLARE_ABSTRACT_CLASS(PContainerType, PCompoundType); +public: + PTypeBase *Outer; // object this type is contained within + FName TypeName; // this type's name + + PContainerType() : Outer(NULL) { + mDescriptiveName = "NamedType"; + } + PContainerType(FName name, PTypeBase *outer) : Outer(outer), TypeName(name) { + mDescriptiveName = name.GetChars(); + } + + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + virtual PField *AddField(FName name, PType *type, uint32_t flags = 0) = 0; + virtual PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags = 0, int bitvalue = 0) = 0; +}; + +// Basic types -------------------------------------------------------------- + +class PInt : public PBasicType +{ + DECLARE_CLASS(PInt, PBasicType); +public: + PInt(unsigned int size, bool unsign, bool compatible = true); + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; + + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); + virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; + virtual bool isNumeric() override { return IntCompatible; } + + bool Unsigned; + bool IntCompatible; +protected: + PInt(); + void SetOps(); +}; + +class PBool : public PInt +{ + DECLARE_CLASS(PBool, PInt); +public: + PBool(); + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); + virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; +}; + +class PFloat : public PBasicType +{ + DECLARE_CLASS(PFloat, PBasicType); +public: + PFloat(unsigned int size); + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; + + virtual void SetValue(void *addr, int val); + virtual void SetValue(void *addr, double val); + virtual int GetValueInt(void *addr) const; + virtual double GetValueFloat(void *addr) const; + virtual bool isNumeric() override { return true; } +protected: + PFloat(); + void SetOps(); +private: + struct SymbolInitF + { + ENamedName Name; + double Value; + }; + struct SymbolInitI + { + ENamedName Name; + int Value; + }; + + void SetSingleSymbols(); + void SetDoubleSymbols(); + void SetSymbols(const SymbolInitF *syminit, size_t count); + void SetSymbols(const SymbolInitI *syminit, size_t count); +}; + +class PString : public PBasicType +{ + DECLARE_CLASS(PString, PBasicType); +public: + PString(); + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) override; + void InitializeValue(void *addr, const void *def) const override; + void DestroyValue(void *addr) const override; +}; + +// Variations of integer types ---------------------------------------------- + +class PName : public PInt +{ + DECLARE_CLASS(PName, PInt); +public: + PName(); + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; +}; + +class PSound : public PInt +{ + DECLARE_CLASS(PSound, PInt); +public: + PSound(); + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; +}; + +class PSpriteID : public PInt +{ + DECLARE_CLASS(PSpriteID, PInt); +public: + PSpriteID(); + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; +}; + +class PTextureID : public PInt +{ + DECLARE_CLASS(PTextureID, PInt); +public: + PTextureID(); + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; +}; + +class PColor : public PInt +{ + DECLARE_CLASS(PColor, PInt); +public: + PColor(); +}; + +class PStateLabel : public PInt +{ + DECLARE_CLASS(PStateLabel, PInt); +public: + PStateLabel(); +}; + +// Pointers ----------------------------------------------------------------- + +class PPointer : public PBasicType +{ + DECLARE_CLASS(PPointer, PBasicType); + +public: + typedef void(*WriteHandler)(FSerializer &ar, const char *key, const void *addr); + typedef bool(*ReadHandler)(FSerializer &ar, const char *key, void *addr); + + PPointer(); + PPointer(PType *pointsat, bool isconst = false); + + PType *PointedType; + bool IsConst; + + WriteHandler writer = nullptr; + ReadHandler reader = nullptr; + + void InstallHandlers(WriteHandler w, ReadHandler r) + { + writer = w; + reader = r; + } + + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; + +protected: + void SetOps(); +}; + +class PStatePointer : public PPointer +{ + DECLARE_CLASS(PStatePointer, PPointer); +public: + PStatePointer(); + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; +}; + + +class PObjectPointer : public PPointer +{ + DECLARE_CLASS(PObjectPointer, PPointer); +public: + PObjectPointer(PClass *pointedtype = nullptr, bool isconst = false); + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; + void SetPointer(void *base, unsigned offset, TArray *special = NULL) override; + PClass *PointedClass() const; +}; + + +class PClassPointer : public PPointer +{ + DECLARE_CLASS(PClassPointer, PPointer); +public: + PClassPointer(class PClass *restrict = nullptr); + + class PClass *ClassRestriction; + + bool isCompatible(PType *type); + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; + + void SetPointer(void *base, unsigned offset, TArray *special = NULL) override; + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; +}; + +// Compound types ----------------------------------------------------------- + +class PEnum : public PInt +{ + DECLARE_CLASS(PEnum, PInt); +public: + PEnum(FName name, PTypeBase *outer); + + PTypeBase *Outer; + FName EnumName; +protected: + PEnum(); +}; + +class PArray : public PCompoundType +{ + DECLARE_CLASS(PArray, PCompoundType); +public: + PArray(PType *etype, unsigned int ecount); + + PType *ElementType; + unsigned int ElementCount; + unsigned int ElementSize; + + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; + + void SetDefaultValue(void *base, unsigned offset, TArray *special) override; + void SetPointer(void *base, unsigned offset, TArray *special) override; + +protected: + PArray(); +}; + +class PStaticArray : public PArray +{ + DECLARE_CLASS(PStaticArray, PArray); +public: + PStaticArray(PType *etype); + + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + +protected: + PStaticArray(); +}; + +class PDynArray : public PCompoundType +{ + DECLARE_CLASS(PDynArray, PCompoundType); +public: + PDynArray(PType *etype, PStruct *backing); + + PType *ElementType; + PStruct *BackingType; + + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; + void InitializeValue(void *addr, const void *def) const override; + void DestroyValue(void *addr) const override; + void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const override; + +protected: + PDynArray(); +}; + +class PMap : public PCompoundType +{ + DECLARE_CLASS(PMap, PCompoundType); +public: + PMap(PType *keytype, PType *valtype); + + PType *KeyType; + PType *ValueType; + + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; +protected: + PMap(); +}; + +class PStruct : public PContainerType +{ + DECLARE_CLASS(PStruct, PContainerType); + +public: + PStruct(FName name, PTypeBase *outer, bool isnative = false); + + bool isNative; + // Some internal structs require explicit construction and destruction of fields the VM cannot handle directly so use these two functions for it. + VMFunction *mConstructor = nullptr; + VMFunction *mDestructor = nullptr; + + virtual PField *AddField(FName name, PType *type, uint32_t flags=0); + virtual PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags = 0, int bitvalue = 0); + + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; + void SetPointer(void *base, unsigned offset, TArray *specials) override; + +protected: + PStruct(); +}; + +class PPrototype : public PCompoundType +{ + DECLARE_CLASS(PPrototype, PCompoundType); +public: + PPrototype(const TArray &rettypes, const TArray &argtypes); + + TArray ArgumentTypes; + TArray ReturnTypes; + + size_t PropagateMark(); + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; +protected: + PPrototype(); +}; + + +// Meta-info for every class derived from DObject --------------------------- + +class PClassType : public PContainerType +{ + DECLARE_CLASS(PClassType, PContainerType); + +private: + +public: + PClass *Descriptor; + PClassType *ParentType; + + PClassType(PClass *cls = nullptr); + PField *AddField(FName name, PType *type, uint32_t flags = 0) override; + PField *AddNativeField(FName name, PType *type, size_t address, uint32_t flags = 0, int bitvalue = 0) override; +}; + + + +// Returns a type from the TypeTable. Will create one if it isn't present. +PMap *NewMap(PType *keytype, PType *valuetype); +PArray *NewArray(PType *type, unsigned int count); +PStaticArray *NewStaticArray(PType *type); +PDynArray *NewDynArray(PType *type); +PPointer *NewPointer(PType *type, bool isconst = false); +PPointer *NewPointer(PClass *type, bool isconst = false); +PClassPointer *NewClassPointer(PClass *restrict); +PEnum *NewEnum(FName name, PTypeBase *outer); +PStruct *NewStruct(FName name, PTypeBase *outer, bool native = false); +PPrototype *NewPrototype(const TArray &rettypes, const TArray &argtypes); +PClassType *NewClassType(PClass *cls); + +// Built-in types ----------------------------------------------------------- + +extern PErrorType *TypeError; +extern PErrorType *TypeAuto; +extern PVoidType *TypeVoid; +extern PInt *TypeSInt8, *TypeUInt8; +extern PInt *TypeSInt16, *TypeUInt16; +extern PInt *TypeSInt32, *TypeUInt32; +extern PBool *TypeBool; +extern PFloat *TypeFloat32, *TypeFloat64; +extern PString *TypeString; +extern PName *TypeName; +extern PSound *TypeSound; +extern PColor *TypeColor; +extern PTextureID *TypeTextureID; +extern PSpriteID *TypeSpriteID; +extern PStruct *TypeVector2; +extern PStruct *TypeVector3; +extern PStruct *TypeColorStruct; +extern PStruct *TypeStringStruct; +extern PStatePointer *TypeState; +extern PPointer *TypeFont; +extern PStateLabel *TypeStateLabel; +extern PPointer *TypeNullPtr; +extern PPointer *TypeVoidPtr; + +inline FString &DObject::StringVar(FName field) +{ + return *(FString*)ScriptVar(field, TypeString); +} + +// Type tables -------------------------------------------------------------- + +struct FTypeTable +{ + enum { HASH_SIZE = 1021 }; + + PType *TypeHash[HASH_SIZE]; + + PType *FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, size_t *bucketnum); + void AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t parm2, size_t bucket); + void AddType(PType *type); + void Clear(); + + static size_t Hash(const PClass *p1, intptr_t p2, intptr_t p3); +}; + + +extern FTypeTable TypeTable; + diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index a0103c83d..b7b7aa1c5 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -26,134 +26,6 @@ typedef signed int VM_SWORD; #define VM_EPSILON (1/65536.0) -#ifdef __BIG_ENDIAN__ -#define VM_DEFINE_OP2(TYPE, ARG1, ARG2) TYPE ARG2, ARG1 -#define VM_DEFINE_OP4(TYPE, ARG1, ARG2, ARG3, ARG4) TYPE ARG4, ARG3, ARG2, ARG1 -#else // little endian -#define VM_DEFINE_OP2(TYPE, ARG1, ARG2) TYPE ARG1, ARG2 -#define VM_DEFINE_OP4(TYPE, ARG1, ARG2, ARG3, ARG4) TYPE ARG1, ARG2, ARG3, ARG4 -#endif // __BIG_ENDIAN__ - -union VMOP -{ - struct - { - VM_DEFINE_OP4(VM_UBYTE, op, a, b, c); - }; - struct - { - VM_DEFINE_OP4(VM_SBYTE, pad0, as, bs, cs); - }; - struct - { - VM_DEFINE_OP2(VM_SWORD, pad1:8, i24:24); - }; - struct - { - VM_DEFINE_OP2(VM_SWORD, pad2:16, i16:16); - }; - struct - { - VM_DEFINE_OP2(VM_UHALF, pad3, i16u); - }; - VM_UWORD word; - - // Interesting fact: VC++ produces better code for i16 when it's defined - // as a bitfield than when it's defined as two discrete units. - // Compare: - // mov eax,dword ptr [op] ; As two discrete units - // shr eax,10h - // movsx eax,ax - // versus: - // mov eax,dword ptr [op] ; As a bitfield - // sar eax,10h -}; - -#undef VM_DEFINE_OP4 -#undef VM_DEFINE_OP2 - -enum -{ -#include "vmops.h" -NUM_OPS -}; - -// Flags for A field of CMPS -enum -{ - CMP_CHECK = 1, - - CMP_EQ = 0, - CMP_LT = 2, - CMP_LE = 4, - CMP_METHOD_MASK = 6, - - CMP_BK = 8, - CMP_CK = 16, - CMP_APPROX = 32, -}; - -// Floating point operations for FLOP -enum -{ - FLOP_ABS, - FLOP_NEG, - FLOP_EXP, - FLOP_LOG, - FLOP_LOG10, - FLOP_SQRT, - FLOP_CEIL, - FLOP_FLOOR, - - FLOP_ACOS, // This group works with radians - FLOP_ASIN, - FLOP_ATAN, - FLOP_COS, - FLOP_SIN, - FLOP_TAN, - - FLOP_ACOS_DEG, // This group works with degrees - FLOP_ASIN_DEG, - FLOP_ATAN_DEG, - FLOP_COS_DEG, - FLOP_SIN_DEG, - FLOP_TAN_DEG, - - FLOP_COSH, - FLOP_SINH, - FLOP_TANH, -}; - -// Cast operations -enum -{ - CAST_I2F, - CAST_I2S, - CAST_U2F, - CAST_U2S, - CAST_F2I, - CAST_F2U, - CAST_F2S, - CAST_P2S, - CAST_S2I, - CAST_S2F, - CAST_S2N, - CAST_N2S, - CAST_S2Co, - CAST_S2So, - CAST_Co2S, - CAST_So2S, - CAST_V22S, - CAST_V32S, - CAST_SID2S, - CAST_TID2S, - - CASTB_I, - CASTB_F, - CASTB_A, - CASTB_S -}; - // Register types for VMParam enum { @@ -198,105 +70,6 @@ public: // This must be a separate function because the VC compiler would otherwise allocate memory on the stack for every separate instance of the exception object that may get thrown. void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...); -enum EVMOpMode -{ - MODE_ASHIFT = 0, - MODE_BSHIFT = 4, - MODE_CSHIFT = 8, - MODE_BCSHIFT = 12, - - MODE_ATYPE = 15 << MODE_ASHIFT, - MODE_BTYPE = 15 << MODE_BSHIFT, - MODE_CTYPE = 15 << MODE_CSHIFT, - MODE_BCTYPE = 31 << MODE_BCSHIFT, - - MODE_I = 0, - MODE_F, - MODE_S, - MODE_P, - MODE_V, - MODE_X, - MODE_KI, - MODE_KF, - MODE_KS, - MODE_KP, - MODE_KV, - MODE_UNUSED, - MODE_IMMS, - MODE_IMMZ, - MODE_JOINT, - MODE_CMP, - - MODE_PARAM, - MODE_THROW, - MODE_CATCH, - MODE_CAST, - - MODE_AI = MODE_I << MODE_ASHIFT, - MODE_AF = MODE_F << MODE_ASHIFT, - MODE_AS = MODE_S << MODE_ASHIFT, - MODE_AP = MODE_P << MODE_ASHIFT, - MODE_AV = MODE_V << MODE_ASHIFT, - MODE_AX = MODE_X << MODE_ASHIFT, - MODE_AKP = MODE_KP << MODE_ASHIFT, - MODE_AUNUSED = MODE_UNUSED << MODE_ASHIFT, - MODE_AIMMS = MODE_IMMS << MODE_ASHIFT, - MODE_AIMMZ = MODE_IMMZ << MODE_ASHIFT, - MODE_ACMP = MODE_CMP << MODE_ASHIFT, - - MODE_BI = MODE_I << MODE_BSHIFT, - MODE_BF = MODE_F << MODE_BSHIFT, - MODE_BS = MODE_S << MODE_BSHIFT, - MODE_BP = MODE_P << MODE_BSHIFT, - MODE_BV = MODE_V << MODE_BSHIFT, - MODE_BX = MODE_X << MODE_BSHIFT, - MODE_BKI = MODE_KI << MODE_BSHIFT, - MODE_BKF = MODE_KF << MODE_BSHIFT, - MODE_BKS = MODE_KS << MODE_BSHIFT, - MODE_BKP = MODE_KP << MODE_BSHIFT, - MODE_BKV = MODE_KV << MODE_BSHIFT, - MODE_BUNUSED = MODE_UNUSED << MODE_BSHIFT, - MODE_BIMMS = MODE_IMMS << MODE_BSHIFT, - MODE_BIMMZ = MODE_IMMZ << MODE_BSHIFT, - - MODE_CI = MODE_I << MODE_CSHIFT, - MODE_CF = MODE_F << MODE_CSHIFT, - MODE_CS = MODE_S << MODE_CSHIFT, - MODE_CP = MODE_P << MODE_CSHIFT, - MODE_CV = MODE_V << MODE_CSHIFT, - MODE_CX = MODE_X << MODE_CSHIFT, - MODE_CKI = MODE_KI << MODE_CSHIFT, - MODE_CKF = MODE_KF << MODE_CSHIFT, - MODE_CKS = MODE_KS << MODE_CSHIFT, - MODE_CKP = MODE_KP << MODE_CSHIFT, - MODE_CKV = MODE_KV << MODE_CSHIFT, - MODE_CUNUSED = MODE_UNUSED << MODE_CSHIFT, - MODE_CIMMS = MODE_IMMS << MODE_CSHIFT, - MODE_CIMMZ = MODE_IMMZ << MODE_CSHIFT, - - MODE_BCJOINT = (MODE_JOINT << MODE_BSHIFT) | (MODE_JOINT << MODE_CSHIFT), - MODE_BCKI = MODE_KI << MODE_BCSHIFT, - MODE_BCKF = MODE_KF << MODE_BCSHIFT, - MODE_BCKS = MODE_KS << MODE_BCSHIFT, - MODE_BCKP = MODE_KP << MODE_BCSHIFT, - MODE_BCIMMS = MODE_IMMS << MODE_BCSHIFT, - MODE_BCIMMZ = MODE_IMMZ << MODE_BCSHIFT, - MODE_BCPARAM = MODE_PARAM << MODE_BCSHIFT, - MODE_BCTHROW = MODE_THROW << MODE_BCSHIFT, - MODE_BCCATCH = MODE_CATCH << MODE_BCSHIFT, - MODE_BCCAST = MODE_CAST << MODE_BCSHIFT, - - MODE_ABCJOINT = (MODE_JOINT << MODE_ASHIFT) | MODE_BCJOINT, -}; - -struct VMOpInfo -{ - const char *Name; - int Mode; -}; - -extern const VMOpInfo OpInfo[NUM_OPS]; - struct VMReturn { void *Location; @@ -544,186 +317,6 @@ public: protected: }; -// VM frame layout: -// VMFrame header -// parameter stack - 16 byte boundary, 16 bytes each -// double registers - 8 bytes each -// string registers - 4 or 8 bytes each -// address registers - 4 or 8 bytes each -// data registers - 4 bytes each -// address register tags-1 byte each -// extra space - 16 byte boundary -struct VMFrame -{ - VMFrame *ParentFrame; - VMFunction *Func; - VM_UBYTE NumRegD; - VM_UBYTE NumRegF; - VM_UBYTE NumRegS; - VM_UBYTE NumRegA; - VM_UHALF MaxParam; - VM_UHALF NumParam; // current number of parameters - - static int FrameSize(int numregd, int numregf, int numregs, int numrega, int numparam, int numextra) - { - int size = (sizeof(VMFrame) + 15) & ~15; - size += numparam * sizeof(VMValue); - size += numregf * sizeof(double); - size += numrega * sizeof(void *); - size += numregs * sizeof(FString); - size += numregd * sizeof(int); - if (numextra != 0) - { - size = (size + 15) & ~15; - size += numextra; - } - return size; - } - - VMValue *GetParam() const - { - assert(((size_t)this & 15) == 0 && "VM frame is unaligned"); - return (VMValue *)(((size_t)(this + 1) + 15) & ~15); - } - - double *GetRegF() const - { - return (double *)(GetParam() + MaxParam); - } - - FString *GetRegS() const - { - return (FString *)(GetRegF() + NumRegF); - } - - void **GetRegA() const - { - return (void **)(GetRegS() + NumRegS); - } - - int *GetRegD() const - { - return (int *)(GetRegA() + NumRegA); - } - - void *GetExtra() const - { - uint8_t *pbeg = (uint8_t*)(GetRegD() + NumRegD); - ptrdiff_t ofs = pbeg - (uint8_t *)this; - return (VM_UBYTE *)this + ((ofs + 15) & ~15); - } - - void GetAllRegs(int *&d, double *&f, FString *&s, void **&a, VMValue *¶m) const - { - // Calling the individual functions produces suboptimal code. :( - param = GetParam(); - f = (double *)(param + MaxParam); - s = (FString *)(f + NumRegF); - a = (void **)(s + NumRegS); - d = (int *)(a + NumRegA); - } - - void InitRegS(); -}; - -struct VMRegisters -{ - VMRegisters(const VMFrame *frame) - { - frame->GetAllRegs(d, f, s, a, param); - } - - VMRegisters(const VMRegisters &o) - : d(o.d), f(o.f), s(o.s), a(o.a), param(o.param) - { } - - int *d; - double *f; - FString *s; - void **a; - VMValue *param; -}; - -union FVoidObj -{ - DObject *o; - void *v; -}; - -struct FStatementInfo -{ - uint16_t InstructionIndex; - uint16_t LineNumber; -}; - -class VMScriptFunction : public VMFunction -{ -public: - VMScriptFunction(FName name=NAME_None); - ~VMScriptFunction(); - void Alloc(int numops, int numkonstd, int numkonstf, int numkonsts, int numkonsta, int numlinenumbers); - - VMOP *Code; - FStatementInfo *LineInfo; - FString SourceFileName; - int *KonstD; - double *KonstF; - FString *KonstS; - FVoidObj *KonstA; - int ExtraSpace; - int CodeSize; // Size of code in instructions (not bytes) - unsigned LineInfoCount; - unsigned StackSize; - VM_UBYTE NumRegD; - VM_UBYTE NumRegF; - VM_UBYTE NumRegS; - VM_UBYTE NumRegA; - VM_UHALF NumKonstD; - VM_UHALF NumKonstF; - VM_UHALF NumKonstS; - VM_UHALF NumKonstA; - VM_UHALF MaxParam; // Maximum number of parameters this function has on the stack at once - VM_UBYTE NumArgs; // Number of arguments this function takes - TArray SpecialInits; // list of all contents on the extra stack which require construction and destruction - - void InitExtra(void *addr); - void DestroyExtra(void *addr); - int AllocExtraStack(PType *type); - int PCToLine(const VMOP *pc); -}; - -class VMFrameStack -{ -public: - VMFrameStack(); - ~VMFrameStack(); - VMFrame *AllocFrame(VMScriptFunction *func); - VMFrame *PopFrame(); - VMFrame *TopFrame() - { - assert(Blocks != NULL && Blocks->LastFrame != NULL); - return Blocks->LastFrame; - } - int Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap=NULL); -private: - enum { BLOCK_SIZE = 4096 }; // Default block size - struct BlockHeader - { - BlockHeader *NextBlock; - VMFrame *LastFrame; - VM_UBYTE *FreeSpace; - int BlockSize; - - void InitFreeSpace() - { - FreeSpace = (VM_UBYTE *)(((size_t)(this + 1) + 15) & ~15); - } - }; - BlockHeader *Blocks; - BlockHeader *UnusedBlocks; - VMFrame *Alloc(int size); -}; - class VMNativeFunction : public VMFunction { public: @@ -738,66 +331,7 @@ public: NativeCallType NativeCall; }; -class VMParamFiller -{ -public: - VMParamFiller(const VMFrame *frame) : Reg(frame), RegD(0), RegF(0), RegS(0), RegA(0) {} - VMParamFiller(const VMRegisters *reg) : Reg(*reg), RegD(0), RegF(0), RegS(0), RegA(0) {} - - void ParamInt(int val) - { - Reg.d[RegD++] = val; - } - - void ParamFloat(double val) - { - Reg.f[RegF++] = val; - } - - void ParamString(FString &val) - { - Reg.s[RegS++] = val; - } - - void ParamString(const char *val) - { - Reg.s[RegS++] = val; - } - - void ParamObject(DObject *obj) - { - Reg.a[RegA] = obj; - RegA++; - } - - void ParamPointer(void *ptr) - { - Reg.a[RegA] = ptr; - RegA++; - } - -private: - const VMRegisters Reg; - int RegD, RegF, RegS, RegA; -}; - - -enum EVMEngine -{ - VMEngine_Default, - VMEngine_Unchecked, - VMEngine_Checked -}; - -extern thread_local VMFrameStack GlobalVMStack; - - -void VMSelectEngine(EVMEngine engine); -extern int (*VMExec)(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret); -void VMFillParams(VMValue *params, VMFrame *callee, int numparam); - -void VMDumpConstants(FILE *out, const VMScriptFunction *func); -void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func); +int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults/*, VMException **trap = NULL*/); // Use this in the prototype for a native function. #define VM_ARGS VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret @@ -834,10 +368,10 @@ bool AssertObject(void * ob); #define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)param[p].a; #define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type x = (type )param[p].a; #define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && AssertObject(param[p].a)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); -#define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && AssertObject(param[p].a)); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); +#define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); #define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); #define PARAM_OBJECT_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (AssertObject(param[p].a))); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); -#define PARAM_CLASS_NOT_NULL_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (AssertObject(param[p].a))); base::MetaClass *x = (base::MetaClass *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); +#define PARAM_CLASS_NOT_NULL_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); base::MetaClass *x = (base::MetaClass *)PARAM_NULLCHECK(param[p].a, #x); assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); #define PARAM_EXISTS(p) ((p) < numparam) @@ -853,8 +387,8 @@ bool AssertObject(void * ob); #define PARAM_STATE_ACTION_DEF_AT(p,x) FState *x; if (PARAM_EXISTS(p)) { ASSERTINT(param[p]); x = (FState*)StateLabels.GetState(param[p].i, stateowner->GetClass()); } else { ASSERTINT(defaultparam[p]); x = (FState*)StateLabels.GetState(defaultparam[p].i, stateowner->GetClass()); } #define PARAM_POINTER_DEF_AT(p,x,t) t *x; if (PARAM_EXISTS(p)) { ASSERTPOINTER(param[p]); x = (t*)param[p].a; } else { ASSERTPOINTER(defaultparam[p]); x = (t*)defaultparam[p].a; } #define PARAM_OBJECT_DEF_AT(p,x,t) t *x; if (PARAM_EXISTS(p)) { ASSERTOBJECT(param[p]); x = (t*)param[p].a; } else { ASSERTOBJECT(defaultparam[p]); x = (t*)defaultparam[p].a; } -#define PARAM_CLASS_DEF_AT(p,x,t) t::MetaClass *x; if (PARAM_EXISTS(p)) { ASSERTOBJECT(param[p]); x = (t::MetaClass*)param[p].a; } else { ASSERTOBJECT(defaultparam[p]); x = (t::MetaClass*)defaultparam[p].a; } -#define PARAM_CLASS_DEF_NOT_NULL_AT(p,x,t) t::MetaClass *x; if (PARAM_EXISTS(p)) { ASSERTOBJECT(param[p]); x = (t::MetaClass*)PARAM_NULLCHECK(param[p].a, #x); } else { ASSERTOBJECT(defaultparam[p]); x = (t::MetaClass*)PARAM_NULLCHECK(defaultparam[p].a, #x); } +#define PARAM_CLASS_DEF_AT(p,x,t) t::MetaClass *x; if (PARAM_EXISTS(p)) { x = (t::MetaClass*)param[p].a; } else { ASSERTPOINTER(defaultparam[p]); x = (t::MetaClass*)defaultparam[p].a; } +#define PARAM_CLASS_DEF_NOT_NULL_AT(p,x,t) t::MetaClass *x; if (PARAM_EXISTS(p)) { x = (t::MetaClass*)PARAM_NULLCHECK(param[p].a, #x); } else { x = (t::MetaClass*)PARAM_NULLCHECK(defaultparam[p].a, #x); } // The above, but with an automatically increasing position index. #define PARAM_PROLOGUE int paramnum = -1; @@ -1004,7 +538,7 @@ class AActor; #define ACTION_CALL_FROM_PSPRITE() (self->player && stateinfo != nullptr && stateinfo->mStateType == STATE_Psprite) #define ACTION_CALL_FROM_INVENTORY() (stateinfo != nullptr && stateinfo->mStateType == STATE_StateChain) -// Standard parameters for all action functons +// Standard parameters for all action functions // self - Actor this action is to operate on (player if a weapon) // stateowner - Actor this action really belongs to (may be an item) // callingstate - State this action was called from @@ -1033,4 +567,29 @@ VMFunction *FindVMFunction(PClass *cls, const char *name); FString FStringFormat(VM_ARGS); + +unsigned GetVirtualIndex(PClass *cls, const char *funcname); + +#define IFVIRTUALPTR(self, cls, funcname) \ + static unsigned VIndex = ~0u; \ + if (VIndex == ~0u) { \ + VIndex = GetVirtualIndex(RUNTIME_CLASS(cls), #funcname); \ + assert(VIndex != ~0u); \ + } \ + auto clss = self->GetClass(); \ + VMFunction *func = clss->Virtuals.Size() > VIndex? clss->Virtuals[VIndex] : nullptr; \ + if (func != nullptr) + +#define IFVIRTUAL(cls, funcname) IFVIRTUALPTR(this, cls, funcname) + +#define IFVIRTUALPTRNAME(self, cls, funcname) \ + static unsigned VIndex = ~0u; \ + if (VIndex == ~0u) { \ + VIndex = GetVirtualIndex(PClass::FindClass(cls), #funcname); \ + assert(VIndex != ~0u); \ + } \ + auto clss = self->GetClass(); \ + VMFunction *func = clss->Virtuals.Size() > VIndex? clss->Virtuals[VIndex] : nullptr; \ + if (func != nullptr) + #endif diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 1e3afd709..0bad8c055 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -40,12 +40,16 @@ #include "textures/textures.h" #include "math/cmath.h" #include "stats.h" +#include "vmintern.h" +#include "types.h" extern cycle_t VMCycles[10]; extern int VMCalls[10]; // intentionally implemented in a different source file to prevent inlining. +#if 0 void ThrowVMException(VMException *x); +#endif #define IMPLEMENT_VMEXEC diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index adb0480de..b573c0670 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -215,16 +215,6 @@ static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret) GETADDR(PB,RC,X_READ_NIL); reg.a[a] = GC::ReadBarrier(*(DObject **)ptr); NEXTOP; - OP(LOS): - ASSERTA(a); ASSERTA(B); ASSERTKD(C); - GETADDR(PB,KC,X_READ_NIL); - reg.a[a] = *(DObject **)ptr; - NEXTOP; - OP(LOS_R): - ASSERTA(a); ASSERTA(B); ASSERTD(C); - GETADDR(PB,RC,X_READ_NIL); - reg.a[a] = *(DObject **)ptr; - NEXTOP; OP(LP): ASSERTA(a); ASSERTA(B); ASSERTKD(C); GETADDR(PB,KC,X_READ_NIL); @@ -791,7 +781,7 @@ static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret) { ThrowAbortException(X_OTHER, "Class %s requires native construction", cls->TypeName.GetChars()); } - if (cls->ObjectFlags & OF_Abstract) + if (cls->bAbstract) { ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); } diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index c631dea74..69a6febbf 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -36,12 +36,17 @@ #include "dobject.h" #include "v_text.h" #include "stats.h" +#include "c_dispatch.h" #include "templates.h" +#include "vmintern.h" +#include "types.h" cycle_t VMCycles[10]; int VMCalls[10]; +#if 0 IMPLEMENT_CLASS(VMException, false, false) +#endif TArray VMFunction::AllFunctions; @@ -423,9 +428,8 @@ VMFrame *VMFrameStack::PopFrame() // //=========================================================================== -int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap) +int VMCall(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults/*, VMException **trap*/) { - assert(this == &GlobalVMStack); // why would anyone even want to create a local stack? bool allocated = false; try { @@ -452,16 +456,18 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur { VMCycles[0].Clock(); VMCalls[0]++; - AllocFrame(static_cast(func)); + auto &stack = GlobalVMStack; + stack.AllocFrame(static_cast(func)); allocated = true; - VMFillParams(params, TopFrame(), numparams); - int numret = VMExec(this, code, results, numresults); - PopFrame(); + VMFillParams(params, stack.TopFrame(), numparams); + int numret = VMExec(&stack, code, results, numresults); + stack.PopFrame(); VMCycles[0].Unclock(); return numret; } } } +#if 0 catch (VMException *exception) { if (allocated) @@ -475,11 +481,12 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur } throw; } +#endif catch (...) { if (allocated) { - PopFrame(); + GlobalVMStack.PopFrame(); } throw; } @@ -576,10 +583,12 @@ void NullParam(const char *varname) ThrowAbortException(X_READ_NIL, "In function parameter %s", varname); } +#if 0 void ThrowVMException(VMException *x) { throw x; } +#endif ADD_STAT(VM) @@ -599,3 +608,32 @@ ADD_STAT(VM) VMCalls[0] = 0; return FStringf("VM time in last 10 tics: %f ms, %d calls, peak = %f ms", added, addedc, peak); } + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- +CCMD(vmengine) +{ + if (argv.argc() == 2) + { + if (stricmp(argv[1], "default") == 0) + { + VMSelectEngine(VMEngine_Default); + return; + } + else if (stricmp(argv[1], "checked") == 0) + { + VMSelectEngine(VMEngine_Checked); + return; + } + else if (stricmp(argv[1], "unchecked") == 0) + { + VMSelectEngine(VMEngine_Unchecked); + return; + } + } + Printf("Usage: vmengine \n"); +} + diff --git a/src/scripting/vm/vmintern.h b/src/scripting/vm/vmintern.h new file mode 100644 index 000000000..e79a2360d --- /dev/null +++ b/src/scripting/vm/vmintern.h @@ -0,0 +1,474 @@ +#pragma once + +#include "vm.h" + +class VMScriptFunction; + +#ifdef __BIG_ENDIAN__ +#define VM_DEFINE_OP2(TYPE, ARG1, ARG2) TYPE ARG2, ARG1 +#define VM_DEFINE_OP4(TYPE, ARG1, ARG2, ARG3, ARG4) TYPE ARG4, ARG3, ARG2, ARG1 +#else // little endian +#define VM_DEFINE_OP2(TYPE, ARG1, ARG2) TYPE ARG1, ARG2 +#define VM_DEFINE_OP4(TYPE, ARG1, ARG2, ARG3, ARG4) TYPE ARG1, ARG2, ARG3, ARG4 +#endif // __BIG_ENDIAN__ + +union VMOP +{ + struct + { + VM_DEFINE_OP4(VM_UBYTE, op, a, b, c); + }; + struct + { + VM_DEFINE_OP4(VM_SBYTE, pad0, as, bs, cs); + }; + struct + { + VM_DEFINE_OP2(VM_SWORD, pad1:8, i24:24); + }; + struct + { + VM_DEFINE_OP2(VM_SWORD, pad2:16, i16:16); + }; + struct + { + VM_DEFINE_OP2(VM_UHALF, pad3, i16u); + }; + VM_UWORD word; + + // Interesting fact: VC++ produces better code for i16 when it's defined + // as a bitfield than when it's defined as two discrete units. + // Compare: + // mov eax,dword ptr [op] ; As two discrete units + // shr eax,10h + // movsx eax,ax + // versus: + // mov eax,dword ptr [op] ; As a bitfield + // sar eax,10h +}; + +#undef VM_DEFINE_OP4 +#undef VM_DEFINE_OP2 + +enum +{ +#include "vmops.h" +NUM_OPS +}; + +// Flags for A field of CMPS +enum +{ + CMP_CHECK = 1, + + CMP_EQ = 0, + CMP_LT = 2, + CMP_LE = 4, + CMP_METHOD_MASK = 6, + + CMP_BK = 8, + CMP_CK = 16, + CMP_APPROX = 32, +}; + +// Floating point operations for FLOP +enum +{ + FLOP_ABS, + FLOP_NEG, + FLOP_EXP, + FLOP_LOG, + FLOP_LOG10, + FLOP_SQRT, + FLOP_CEIL, + FLOP_FLOOR, + + FLOP_ACOS, // This group works with radians + FLOP_ASIN, + FLOP_ATAN, + FLOP_COS, + FLOP_SIN, + FLOP_TAN, + + FLOP_ACOS_DEG, // This group works with degrees + FLOP_ASIN_DEG, + FLOP_ATAN_DEG, + FLOP_COS_DEG, + FLOP_SIN_DEG, + FLOP_TAN_DEG, + + FLOP_COSH, + FLOP_SINH, + FLOP_TANH, +}; + +// Cast operations +enum +{ + CAST_I2F, + CAST_I2S, + CAST_U2F, + CAST_U2S, + CAST_F2I, + CAST_F2U, + CAST_F2S, + CAST_P2S, + CAST_S2I, + CAST_S2F, + CAST_S2N, + CAST_N2S, + CAST_S2Co, + CAST_S2So, + CAST_Co2S, + CAST_So2S, + CAST_V22S, + CAST_V32S, + CAST_SID2S, + CAST_TID2S, + + CASTB_I, + CASTB_F, + CASTB_A, + CASTB_S +}; + +enum EVMOpMode +{ + MODE_ASHIFT = 0, + MODE_BSHIFT = 4, + MODE_CSHIFT = 8, + MODE_BCSHIFT = 12, + + MODE_ATYPE = 15 << MODE_ASHIFT, + MODE_BTYPE = 15 << MODE_BSHIFT, + MODE_CTYPE = 15 << MODE_CSHIFT, + MODE_BCTYPE = 31 << MODE_BCSHIFT, + + MODE_I = 0, + MODE_F, + MODE_S, + MODE_P, + MODE_V, + MODE_X, + MODE_KI, + MODE_KF, + MODE_KS, + MODE_KP, + MODE_KV, + MODE_UNUSED, + MODE_IMMS, + MODE_IMMZ, + MODE_JOINT, + MODE_CMP, + + MODE_PARAM, + MODE_THROW, + MODE_CATCH, + MODE_CAST, + + MODE_AI = MODE_I << MODE_ASHIFT, + MODE_AF = MODE_F << MODE_ASHIFT, + MODE_AS = MODE_S << MODE_ASHIFT, + MODE_AP = MODE_P << MODE_ASHIFT, + MODE_AV = MODE_V << MODE_ASHIFT, + MODE_AX = MODE_X << MODE_ASHIFT, + MODE_AKP = MODE_KP << MODE_ASHIFT, + MODE_AUNUSED = MODE_UNUSED << MODE_ASHIFT, + MODE_AIMMS = MODE_IMMS << MODE_ASHIFT, + MODE_AIMMZ = MODE_IMMZ << MODE_ASHIFT, + MODE_ACMP = MODE_CMP << MODE_ASHIFT, + + MODE_BI = MODE_I << MODE_BSHIFT, + MODE_BF = MODE_F << MODE_BSHIFT, + MODE_BS = MODE_S << MODE_BSHIFT, + MODE_BP = MODE_P << MODE_BSHIFT, + MODE_BV = MODE_V << MODE_BSHIFT, + MODE_BX = MODE_X << MODE_BSHIFT, + MODE_BKI = MODE_KI << MODE_BSHIFT, + MODE_BKF = MODE_KF << MODE_BSHIFT, + MODE_BKS = MODE_KS << MODE_BSHIFT, + MODE_BKP = MODE_KP << MODE_BSHIFT, + MODE_BKV = MODE_KV << MODE_BSHIFT, + MODE_BUNUSED = MODE_UNUSED << MODE_BSHIFT, + MODE_BIMMS = MODE_IMMS << MODE_BSHIFT, + MODE_BIMMZ = MODE_IMMZ << MODE_BSHIFT, + + MODE_CI = MODE_I << MODE_CSHIFT, + MODE_CF = MODE_F << MODE_CSHIFT, + MODE_CS = MODE_S << MODE_CSHIFT, + MODE_CP = MODE_P << MODE_CSHIFT, + MODE_CV = MODE_V << MODE_CSHIFT, + MODE_CX = MODE_X << MODE_CSHIFT, + MODE_CKI = MODE_KI << MODE_CSHIFT, + MODE_CKF = MODE_KF << MODE_CSHIFT, + MODE_CKS = MODE_KS << MODE_CSHIFT, + MODE_CKP = MODE_KP << MODE_CSHIFT, + MODE_CKV = MODE_KV << MODE_CSHIFT, + MODE_CUNUSED = MODE_UNUSED << MODE_CSHIFT, + MODE_CIMMS = MODE_IMMS << MODE_CSHIFT, + MODE_CIMMZ = MODE_IMMZ << MODE_CSHIFT, + + MODE_BCJOINT = (MODE_JOINT << MODE_BSHIFT) | (MODE_JOINT << MODE_CSHIFT), + MODE_BCKI = MODE_KI << MODE_BCSHIFT, + MODE_BCKF = MODE_KF << MODE_BCSHIFT, + MODE_BCKS = MODE_KS << MODE_BCSHIFT, + MODE_BCKP = MODE_KP << MODE_BCSHIFT, + MODE_BCIMMS = MODE_IMMS << MODE_BCSHIFT, + MODE_BCIMMZ = MODE_IMMZ << MODE_BCSHIFT, + MODE_BCPARAM = MODE_PARAM << MODE_BCSHIFT, + MODE_BCTHROW = MODE_THROW << MODE_BCSHIFT, + MODE_BCCATCH = MODE_CATCH << MODE_BCSHIFT, + MODE_BCCAST = MODE_CAST << MODE_BCSHIFT, + + MODE_ABCJOINT = (MODE_JOINT << MODE_ASHIFT) | MODE_BCJOINT, +}; + +struct VMOpInfo +{ + const char *Name; + int Mode; +}; + +extern const VMOpInfo OpInfo[NUM_OPS]; + + +// VM frame layout: +// VMFrame header +// parameter stack - 16 byte boundary, 16 bytes each +// double registers - 8 bytes each +// string registers - 4 or 8 bytes each +// address registers - 4 or 8 bytes each +// data registers - 4 bytes each +// address register tags-1 byte each +// extra space - 16 byte boundary +struct VMFrame +{ + VMFrame *ParentFrame; + VMFunction *Func; + VM_UBYTE NumRegD; + VM_UBYTE NumRegF; + VM_UBYTE NumRegS; + VM_UBYTE NumRegA; + VM_UHALF MaxParam; + VM_UHALF NumParam; // current number of parameters + + static int FrameSize(int numregd, int numregf, int numregs, int numrega, int numparam, int numextra) + { + int size = (sizeof(VMFrame) + 15) & ~15; + size += numparam * sizeof(VMValue); + size += numregf * sizeof(double); + size += numrega * sizeof(void *); + size += numregs * sizeof(FString); + size += numregd * sizeof(int); + if (numextra != 0) + { + size = (size + 15) & ~15; + size += numextra; + } + return size; + } + + VMValue *GetParam() const + { + assert(((size_t)this & 15) == 0 && "VM frame is unaligned"); + return (VMValue *)(((size_t)(this + 1) + 15) & ~15); + } + + double *GetRegF() const + { + return (double *)(GetParam() + MaxParam); + } + + FString *GetRegS() const + { + return (FString *)(GetRegF() + NumRegF); + } + + void **GetRegA() const + { + return (void **)(GetRegS() + NumRegS); + } + + int *GetRegD() const + { + return (int *)(GetRegA() + NumRegA); + } + + void *GetExtra() const + { + uint8_t *pbeg = (uint8_t*)(GetRegD() + NumRegD); + ptrdiff_t ofs = pbeg - (uint8_t *)this; + return (VM_UBYTE *)this + ((ofs + 15) & ~15); + } + + void GetAllRegs(int *&d, double *&f, FString *&s, void **&a, VMValue *¶m) const + { + // Calling the individual functions produces suboptimal code. :( + param = GetParam(); + f = (double *)(param + MaxParam); + s = (FString *)(f + NumRegF); + a = (void **)(s + NumRegS); + d = (int *)(a + NumRegA); + } + + void InitRegS(); +}; + +struct VMRegisters +{ + VMRegisters(const VMFrame *frame) + { + frame->GetAllRegs(d, f, s, a, param); + } + + VMRegisters(const VMRegisters &o) + : d(o.d), f(o.f), s(o.s), a(o.a), param(o.param) + { } + + int *d; + double *f; + FString *s; + void **a; + VMValue *param; +}; + +union FVoidObj +{ + DObject *o; + void *v; +}; + +struct FStatementInfo +{ + uint16_t InstructionIndex; + uint16_t LineNumber; +}; + +class VMFrameStack +{ +public: + VMFrameStack(); + ~VMFrameStack(); + VMFrame *AllocFrame(VMScriptFunction *func); + VMFrame *PopFrame(); + VMFrame *TopFrame() + { + assert(Blocks != NULL && Blocks->LastFrame != NULL); + return Blocks->LastFrame; + } +private: + enum { BLOCK_SIZE = 4096 }; // Default block size + struct BlockHeader + { + BlockHeader *NextBlock; + VMFrame *LastFrame; + VM_UBYTE *FreeSpace; + int BlockSize; + + void InitFreeSpace() + { + FreeSpace = (VM_UBYTE *)(((size_t)(this + 1) + 15) & ~15); + } + }; + BlockHeader *Blocks; + BlockHeader *UnusedBlocks; + VMFrame *Alloc(int size); +}; + +class VMParamFiller +{ +public: + VMParamFiller(const VMFrame *frame) : Reg(frame), RegD(0), RegF(0), RegS(0), RegA(0) {} + VMParamFiller(const VMRegisters *reg) : Reg(*reg), RegD(0), RegF(0), RegS(0), RegA(0) {} + + void ParamInt(int val) + { + Reg.d[RegD++] = val; + } + + void ParamFloat(double val) + { + Reg.f[RegF++] = val; + } + + void ParamString(FString &val) + { + Reg.s[RegS++] = val; + } + + void ParamString(const char *val) + { + Reg.s[RegS++] = val; + } + + void ParamObject(DObject *obj) + { + Reg.a[RegA] = obj; + RegA++; + } + + void ParamPointer(void *ptr) + { + Reg.a[RegA] = ptr; + RegA++; + } + +private: + const VMRegisters Reg; + int RegD, RegF, RegS, RegA; +}; + + +enum EVMEngine +{ + VMEngine_Default, + VMEngine_Unchecked, + VMEngine_Checked +}; + +void VMSelectEngine(EVMEngine engine); +extern int (*VMExec)(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret); +void VMFillParams(VMValue *params, VMFrame *callee, int numparam); + +void VMDumpConstants(FILE *out, const VMScriptFunction *func); +void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func); + +extern thread_local VMFrameStack GlobalVMStack; + +typedef std::pair FTypeAndOffset; + +class VMScriptFunction : public VMFunction +{ +public: + VMScriptFunction(FName name = NAME_None); + ~VMScriptFunction(); + void Alloc(int numops, int numkonstd, int numkonstf, int numkonsts, int numkonsta, int numlinenumbers); + + VMOP *Code; + FStatementInfo *LineInfo; + FString SourceFileName; + int *KonstD; + double *KonstF; + FString *KonstS; + FVoidObj *KonstA; + int ExtraSpace; + int CodeSize; // Size of code in instructions (not bytes) + unsigned LineInfoCount; + unsigned StackSize; + VM_UBYTE NumRegD; + VM_UBYTE NumRegF; + VM_UBYTE NumRegS; + VM_UBYTE NumRegA; + VM_UHALF NumKonstD; + VM_UHALF NumKonstF; + VM_UHALF NumKonstS; + VM_UHALF NumKonstA; + VM_UHALF MaxParam; // Maximum number of parameters this function has on the stack at once + VM_UBYTE NumArgs; // Number of arguments this function takes + TArray SpecialInits; // list of all contents on the extra stack which require construction and destruction + + void InitExtra(void *addr); + void DestroyExtra(void *addr); + int AllocExtraStack(PType *type); + int PCToLine(const VMOP *pc); +}; diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 50f6568cb..dac907580 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -45,8 +45,6 @@ xx(LS, ls, RSRPKI, LS_R, 4, REGT_INT), // load string xx(LS_R, ls, RSRPRI, NOP, 0, 0), xx(LO, lo, RPRPKI, LO_R, 4, REGT_INT), // load object xx(LO_R, lo, RPRPRI, NOP, 0, 0), -xx(LOS, los, RPRPKI, LOS_R, 4, REGT_INT), // load object (stack version without read barrier) -xx(LOS_R, lo, RPRPRI, NOP, 0, 0), xx(LP, lp, RPRPKI, LP_R, 4, REGT_INT), // load pointer xx(LP_R, lp, RPRPRI, NOP, 0, 0), xx(LV2, lv2, RVRPKI, LV2_R, 4, REGT_INT), // load vector2 diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index 69b8af3bf..d886c8db2 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -32,6 +32,8 @@ */ #include "dobject.h" +#include "vmintern.h" +#include "types.h" #include "sc_man.h" #include "memarena.h" #include "zcc_parser.h" diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index fe93be7c9..1e4acc9b1 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -49,8 +49,11 @@ #include "i_system.h" #include "gdtoa.h" #include "backend/vmbuilder.h" +#include "types.h" FSharedStringArena VMStringConstants; +bool isActor(PContainerType *type); + static int GetIntConst(FxExpression *ex, FCompileContext &ctx) { @@ -75,7 +78,7 @@ const char * ZCCCompiler::GetStringConst(FxExpression *ex, FCompileContext &ctx) return AST.Strings.Alloc(static_cast(ex)->GetValue().GetString())->GetChars(); } -int ZCCCompiler::IntConstFromNode(ZCC_TreeNode *node, PStruct *cls) +int ZCCCompiler::IntConstFromNode(ZCC_TreeNode *node, PContainerType *cls) { FCompileContext ctx(OutNamespace, cls, false); FxExpression *ex = new FxIntCast(ConvertNode(node), false); @@ -89,7 +92,7 @@ int ZCCCompiler::IntConstFromNode(ZCC_TreeNode *node, PStruct *cls) return static_cast(ex)->GetValue().GetInt(); } -FString ZCCCompiler::StringConstFromNode(ZCC_TreeNode *node, PStruct *cls) +FString ZCCCompiler::StringConstFromNode(ZCC_TreeNode *node, PContainerType *cls) { FCompileContext ctx(OutNamespace, cls, false); FxExpression *ex = new FxStringCast(ConvertNode(node)); @@ -496,8 +499,8 @@ void ZCCCompiler::CreateStructTypes() s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->CType(); if (s->Outer) { - outer = s->Outer; - syms = &s->Outer->Symbols; + outer = s->Outer->VMType; + syms = &s->Outer->VMType->Symbols; } else { @@ -513,7 +516,7 @@ void ZCCCompiler::CreateStructTypes() } else if (s->strct->Flags & ZCC_Native) { - s->strct->Type = NewNativeStruct(s->NodeName(), outer); + s->strct->Type = NewStruct(s->NodeName(), outer, true); } else { @@ -603,7 +606,7 @@ void ZCCCompiler::CreateClassTypes() parent = RUNTIME_CLASS(DObject); } - if (parent != nullptr) + if (parent != nullptr && (parent->VMType != nullptr || c->NodeName() == NAME_Object)) { // The parent exists, we may create a type for this class if (c->cls->Flags & ZCC_Native) @@ -624,34 +627,41 @@ void ZCCCompiler::CreateClassTypes() { DPrintf(DMSG_SPAMMY, "Registered %s as native with parent %s\n", me->TypeName.GetChars(), parent->TypeName.GetChars()); } - c->cls->Type = me; - auto ac = dyn_cast(me); - if (ac != nullptr) ac->SourceLumpName = *c->cls->SourceName; + c->cls->Type = NewClassType(me); + me->SourceLumpName = *c->cls->SourceName; } else { // We will never get here if the name is a duplicate, so we can just do the assignment. try { - if (parent->mVersion > mVersion) + if (parent->VMType->mVersion > mVersion) { Error(c->cls, "Parent class %s of %s not accessible to ZScript version %d.%d.%d", parent->TypeName.GetChars(), c->NodeName().GetChars(), mVersion.major, mVersion.minor, mVersion.revision); } - c->cls->Type = parent->CreateDerivedClass(c->NodeName(), TentativeClass); - if (c->Type() == nullptr) + auto newclass = parent->CreateDerivedClass(c->NodeName(), TentativeClass); + if (newclass == nullptr) { Error(c->cls, "Class name %s already exists", c->NodeName().GetChars()); } - else DPrintf(DMSG_SPAMMY, "Created class %s with parent %s\n", c->Type()->TypeName.GetChars(), c->Type()->ParentClass->TypeName.GetChars()); + else + { + c->cls->Type = NewClassType(newclass); + DPrintf(DMSG_SPAMMY, "Created class %s with parent %s\n", c->Type()->TypeName.GetChars(), c->ClassType()->ParentClass->TypeName.GetChars()); + } } catch (CRecoverableError &err) { Error(c->cls, "%s", err.GetMessage()); - // create a placeholder so that the compiler can continue looking for errors. c->cls->Type = nullptr; } } - if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName()); + if (c->Type() == nullptr) + { + // create a placeholder so that the compiler can continue looking for errors. + c->cls->Type = NewClassType(parent->FindClassTentative(c->NodeName())); + } + if (c->cls->Flags & ZCC_Abstract) c->Type()->ObjectFlags |= OF_Abstract; @@ -676,13 +686,13 @@ void ZCCCompiler::CreateClassTypes() c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI; if (c->cls->Flags & ZCC_Play) c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play; - if (parent->ObjectFlags & (OF_UI | OF_Play)) // parent is either ui or play + if (parent->VMType->ObjectFlags & (OF_UI | OF_Play)) // parent is either ui or play { if (c->cls->Flags & (ZCC_UIFlag | ZCC_Play)) { Error(c->cls, "Can't change class scope in class %s", c->NodeName().GetChars()); } - c->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(c->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(parent->ObjectFlags)); + c->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(c->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(parent->VMType->ObjectFlags)); } } else @@ -690,14 +700,13 @@ void ZCCCompiler::CreateClassTypes() c->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(c->Type()->ObjectFlags, FScopeBarrier::Side_Play); } - c->Type()->bExported = true; // this class is accessible to script side type casts. (The reason for this flag is that types like PInt need to be skipped.) c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); OutNamespace->Symbols.AddSymbol(c->cls->Symbol); Classes.Push(c); OrigClasses.Delete(i--); donesomething = true; } - else + else if (c->cls->ParentName != nullptr) { // No base class found. Now check if something in the unprocessed classes matches. // If not, print an error. If something is found let's retry again in the next iteration. @@ -714,7 +723,7 @@ void ZCCCompiler::CreateClassTypes() { Error(c->cls, "Class %s has unknown base class %s", c->NodeName().GetChars(), FName(c->cls->ParentName->Id).GetChars()); // create a placeholder so that the compiler can continue looking for errors. - c->cls->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName()); + c->cls->Type = NewClassType(RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName())); c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); OutNamespace->Symbols.AddSymbol(c->cls->Symbol); Classes.Push(c); @@ -730,13 +739,13 @@ void ZCCCompiler::CreateClassTypes() for (auto c : OrigClasses) { Error(c->cls, "Class %s has circular inheritance", FName(c->NodeName()).GetChars()); - c->cls->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName()); + c->cls->Type = NewClassType(RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName())); c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); OutNamespace->Symbols.AddSymbol(c->cls->Symbol); Classes.Push(c); } - // Last but not least: Now that all classes have been created, we can create the symbols for the internal enums and link the treenode symbol tables and set up replacements + // Last but not least: Now that all classes have been created, we can create the symbols for the internal enums and link the treenode symbol tables. for (auto cd : Classes) { for (auto e : cd->Enums) @@ -747,18 +756,12 @@ void ZCCCompiler::CreateClassTypes() // Link the tree node tables. We only can do this after we know the class relations. for (auto cc : Classes) { - if (cc->Type() == cd->Type()->ParentClass) + if (cc->ClassType() == cd->ClassType()->ParentClass) { cd->TreeNodes.SetParentTable(&cc->TreeNodes); break; } } - - if (cd->cls->Replaces != nullptr && !static_cast(cd->Type())->SetReplacement(cd->cls->Replaces->Id)) - { - Warn(cd->cls, "Replaced type '%s' not found for %s", FName(cd->cls->Replaces->Id).GetChars(), cd->Type()->TypeName.GetChars()); - } - } } @@ -770,7 +773,7 @@ void ZCCCompiler::CreateClassTypes() // //========================================================================== -void ZCCCompiler::CopyConstants(TArray &dest, TArray &Constants, PStruct *cls, PSymbolTable *ot) +void ZCCCompiler::CopyConstants(TArray &dest, TArray &Constants, PContainerType *cls, PSymbolTable *ot) { for (auto c : Constants) { @@ -1112,6 +1115,7 @@ ZCC_ExprTypeRef *ZCCCompiler::NodeFromSymbolType(PSymbolType *sym, ZCC_Expressio // builds the internal structure of all classes and structs // //========================================================================== +void AddActorInfo(PClass *cls); void ZCCCompiler::CompileAllFields() { @@ -1131,7 +1135,7 @@ void ZCCCompiler::CompileAllFields() // We need to search the global class table here because not all children may have a scripted definition attached. for (auto ac : PClass::AllClasses) { - if (ac->ParentClass == c->Type() && ac->Size != TentativeClass) + if (ac->ParentClass != nullptr && ac->ParentClass->VMType == c->Type() && ac->Size != TentativeClass) { // Only set a marker here, so that we can print a better message when the actual fields get added. HasNativeChildren.Insert(ac, true); @@ -1155,7 +1159,8 @@ void ZCCCompiler::CompileAllFields() } for (unsigned i = 0; i < Classes.Size(); i++) { - auto type = Classes[i]->Type(); + auto type = Classes[i]->ClassType(); + if (type->Size == TentativeClass) { if (type->ParentClass->Size == TentativeClass) @@ -1166,15 +1171,20 @@ void ZCCCompiler::CompileAllFields() else { // Inherit the size of the parent class - type->Size = Classes[i]->Type()->ParentClass->Size; + type->Size = Classes[i]->ClassType()->ParentClass->Size; } } - if (Classes[i]->Type()->ParentClass) - type->MetaSize = Classes[i]->Type()->ParentClass->MetaSize; + if (type->TypeName == NAME_Actor) + { + assert(type->MetaSize == 0); + AddActorInfo(type); // AActor needs the actor info manually added to its meta data before adding any scripted fields. + } + else if (Classes[i]->ClassType()->ParentClass) + type->MetaSize = Classes[i]->ClassType()->ParentClass->MetaSize; else type->MetaSize = 0; - if (CompileFields(type, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false, !!HasNativeChildren.CheckKey(type))) + if (CompileFields(type->VMType, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false, !!HasNativeChildren.CheckKey(type))) { // Remove from the list if all fields got compiled. Classes.Delete(i--); @@ -1201,7 +1211,7 @@ void ZCCCompiler::CompileAllFields() // //========================================================================== -bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren) +bool ZCCCompiler::CompileFields(PContainerType *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren) { while (Fields.Size() > 0) { @@ -1309,7 +1319,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel Error(field, "The member variable '%s.%s' has not been exported from the executable.", type == nullptr? "" : type->TypeName.GetChars(), FName(name->Name).GetChars()); } // For native structs a size check cannot be done because they normally have no size. But for a native reference they are still fine. - else if (thisfieldtype->Size != ~0u && fd->FieldSize != ~0u && thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0 && !thisfieldtype->IsA(RUNTIME_CLASS(PNativeStruct))) + else if (thisfieldtype->Size != ~0u && fd->FieldSize != ~0u && thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0 && + (!thisfieldtype->IsA(RUNTIME_CLASS(PStruct)) || !static_cast(thisfieldtype)->isNative)) { Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type == nullptr ? "" : type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size); } @@ -1365,7 +1376,7 @@ void ZCCCompiler::CompileAllProperties() for (auto c : Classes) { if (c->Properties.Size() > 0) - CompileProperties(c->Type(), c->Properties, c->Type()->TypeName); + CompileProperties(c->ClassType(), c->Properties, c->Type()->TypeName); } } @@ -1379,7 +1390,7 @@ void ZCCCompiler::CompileAllProperties() bool ZCCCompiler::CompileProperties(PClass *type, TArray &Properties, FName prefix) { - if (!type->IsKindOf(RUNTIME_CLASS(PClassActor))) + if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) { Error(Properties[0], "Properties can only be defined for actors"); return false; @@ -1398,7 +1409,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper { do { - auto f = dyn_cast(type->Symbols.FindSymbol(id->Id, true)); + auto f = dyn_cast(type->FindSymbol(id->Id, true)); if (f == nullptr) { Error(id, "Variable %s not found in %s", FName(id->Id).GetChars(), type->TypeName.GetChars()); @@ -1416,7 +1427,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); fields.ShrinkToFit(); - if (!type->Symbols.AddSymbol(new PProperty(qualifiedname, fields))) + if (!type->VMType->Symbols.AddSymbol(new PProperty(qualifiedname, fields))) { Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); } @@ -1641,7 +1652,7 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n return TypeError; } auto typesym = dyn_cast(sym); - if (typesym == nullptr || !typesym->Type->IsKindOf(RUNTIME_CLASS(PClass))) + if (typesym == nullptr || !typesym->Type->IsKindOf(RUNTIME_CLASS(PClassType))) { Error(field, "%s does not represent a class type", FName(ctype->Restriction->Id).GetChars()); return TypeError; @@ -1651,7 +1662,7 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n Error(field, "Class %s not accessible to ZScript version %d.%d.%d", FName(ctype->Restriction->Id).GetChars(), mVersion.major, mVersion.minor, mVersion.revision); return TypeError; } - retval = NewClassPointer(static_cast(typesym->Type)); + retval = NewClassPointer(static_cast(typesym->Type)->Descriptor); } break; } @@ -1696,10 +1707,14 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt, boo { if (!nativetype) return TypeSInt32; // hack this to an integer until we can resolve the enum mess. } - if (ptype->IsKindOf(RUNTIME_CLASS(PNativeStruct))) // native structs and classes cannot be instantiated, they always get used as reference. + else if (ptype->IsKindOf(RUNTIME_CLASS(PClassType))) // classes cannot be instantiated at all, they always get used as references. + { + return NewPointer(ptype, type->isconst); + } + else if (ptype->IsKindOf(RUNTIME_CLASS(PStruct)) && static_cast(ptype)->isNative) // native structs and classes cannot be instantiated, they always get used as reference. { if (!nativetype) return NewPointer(ptype, type->isconst); - if (!ptype->IsKindOf(RUNTIME_CLASS(PClass))) return ptype; // instantiation of native structs. Only for internal use. + return ptype; // instantiation of native structs. Only for internal use. } if (!nativetype) return ptype; } @@ -1716,7 +1731,7 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt, boo // //========================================================================== -PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PStruct *cls) +PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PContainerType *cls) { TArray indices; @@ -1778,7 +1793,7 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper const char * p = prop->params; auto exp = property->Values; - FCompileContext ctx(OutNamespace, bag.Info, false); + FCompileContext ctx(OutNamespace, bag.Info->VMType, false); while (true) { FPropParam conv; @@ -1960,7 +1975,7 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop } auto exp = property->Values; - FCompileContext ctx(OutNamespace, bag.Info, false); + FCompileContext ctx(OutNamespace, bag.Info->VMType, false); for (auto f : prop->Variables) { void *addr; @@ -2107,7 +2122,7 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro FName name(propname, true); if (name != NAME_None) { - auto propp = dyn_cast(cls->Symbols.FindSymbol(name, true)); + auto propp = dyn_cast(cls->FindSymbol(name, true)); if (propp != nullptr) { DispatchScriptProperty(propp, prop, (AActor *)bag.Info->Defaults, bag); @@ -2178,37 +2193,40 @@ void ZCCCompiler::InitDefaults() for (auto c : Classes) { // This may be removed if the conditions change, but right now only subclasses of Actor can define a Default block. - if (!c->Type()->IsDescendantOf(RUNTIME_CLASS(AActor))) + if (!c->ClassType()->IsDescendantOf(RUNTIME_CLASS(AActor))) { - if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->Type()->TypeName.GetChars()); - if (c->Type()->ParentClass) + if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->ClassType()->TypeName.GetChars()); + if (c->ClassType()->ParentClass) { - auto ti = static_cast(c->Type()); - FString mename = ti->TypeName.GetChars(); - + auto ti = static_cast(c->ClassType()); ti->InitializeDefaults(); - ti->ParentClass->DeriveData(ti); } } else { + auto cls = c->ClassType(); // This should never happen. - if (c->Type()->Defaults != nullptr) + if (cls->Defaults != nullptr) { - Error(c->cls, "%s already has defaults", c->Type()->TypeName.GetChars()); + Error(c->cls, "%s already has defaults", cls->TypeName.GetChars()); } // This can only occur if a native parent is not initialized. In all other cases the sorting of the class list should prevent this from ever happening. - else if (c->Type()->ParentClass->Defaults == nullptr && c->Type() != RUNTIME_CLASS(AActor)) + else if (cls->ParentClass->Defaults == nullptr && cls != RUNTIME_CLASS(AActor)) { - Error(c->cls, "Parent class %s of %s is not initialized", c->Type()->ParentClass->TypeName.GetChars(), c->Type()->TypeName.GetChars()); + Error(c->cls, "Parent class %s of %s is not initialized", cls->ParentClass->TypeName.GetChars(), cls->TypeName.GetChars()); } else { // Copy the parent's defaults and meta data. - auto ti = static_cast(c->Type()); + auto ti = static_cast(cls); ti->InitializeDefaults(); - ti->ParentClass->DeriveData(ti); + + // Replacements require that the meta data has been allocated by InitializeDefaults. + if (c->cls->Replaces != nullptr && !ti->SetReplacement(c->cls->Replaces->Id)) + { + Warn(c->cls, "Replaced type '%s' not found for %s", FName(c->cls->Replaces->Id).GetChars(), ti->TypeName.GetChars()); + } // We need special treatment for this one field in AActor's defaults which cannot be made visible to DECORATE as a property. // It's better to do this here under controlled conditions than deeper down in the class type classes. @@ -2219,7 +2237,7 @@ void ZCCCompiler::InitDefaults() Baggage bag; #ifdef _DEBUG - bag.ClassName = c->Type()->TypeName; + bag.ClassName = cls->TypeName; #endif bag.Version = mVersion; bag.Namespace = OutNamespace; @@ -2286,7 +2304,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool do { auto type = DetermineType(c->Type(), f, f->Name, t, false, false); - if (type->IsKindOf(RUNTIME_CLASS(PStruct)) && type != TypeVector2 && type != TypeVector3) + if (type->IsKindOf(RUNTIME_CLASS(PContainerType)) && type != TypeVector2 && type != TypeVector3) { // structs and classes only get passed by pointer. type = NewPointer(type); @@ -2383,7 +2401,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Action functions cannot be declared UI"); } // Non-Actors cannot have action functions. - if (!c->Type()->IsKindOf(RUNTIME_CLASS(PClassActor))) + if (!isActor(c->Type())) { Error(f, "'Action' can only be used in child classes of Actor"); } @@ -2591,9 +2609,10 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr ? nullptr : *(afd->VMPointer), varflags, useflags); c->Type()->Symbols.ReplaceSymbol(sym); - auto cls = dyn_cast(c->Type()); + auto vcls = dyn_cast(c->Type()); + auto cls = vcls ? vcls->Descriptor : nullptr; PFunction *virtsym = nullptr; - if (cls != nullptr && cls->ParentClass != nullptr) virtsym = dyn_cast(cls->ParentClass->Symbols.FindSymbol(FName(f->Name), true)); + if (cls != nullptr && cls->ParentClass != nullptr) virtsym = dyn_cast(cls->ParentClass->VMType->Symbols.FindSymbol(FName(f->Name), true)); unsigned vindex = ~0u; if (virtsym != nullptr) { @@ -2637,7 +2656,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool sym->Variants[0].Implementation->VarFlags = sym->Variants[0].Flags; } - PClass *clstype = static_cast(c->Type()); + PClass *clstype = static_cast(c->Type())->Descriptor; if (varflags & VARF_Virtual) { if (sym->Variants[0].Implementation == nullptr) @@ -2659,7 +2678,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool else { auto oldfunc = clstype->Virtuals[vindex]; - auto parentfunc = dyn_cast(clstype->ParentClass->Symbols.FindSymbol(sym->SymbolName, true)); + auto parentfunc = dyn_cast(clstype->ParentClass->VMType->Symbols.FindSymbol(sym->SymbolName, true)); if (parentfunc && parentfunc->mVersion > mVersion) { Error(f, "Attempt to override function %s which is incompatible with version %d.%d.%d", FName(f->Name).GetChars(), mVersion.major, mVersion.minor, mVersion.revision); @@ -2740,9 +2759,9 @@ void ZCCCompiler::InitFunctions() for (auto c : Classes) { // cannot be done earlier because it requires the parent class to be processed by this code, too. - if (c->Type()->ParentClass != nullptr) + if (c->ClassType()->ParentClass != nullptr) { - c->Type()->Virtuals = c->Type()->ParentClass->Virtuals; + c->ClassType()->Virtuals = c->ClassType()->ParentClass->Virtuals; } for (auto f : c->Functions) { @@ -2791,15 +2810,14 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af, in // The actual action function is still needed by DECORATE: if (id->Identifier != NAME_ACS_NamedExecuteWithResult) { - PFunction *afd = dyn_cast(cls->Symbols.FindSymbol(id->Identifier, true)); + PFunction *afd = dyn_cast(cls->VMType->Symbols.FindSymbol(id->Identifier, true)); if (afd != nullptr) { if (fc->Parameters == nullptr && !(afd->Variants[0].Flags & VARF_Virtual)) { FArgumentList argumentlist; // We can use this function directly without wrapping it in a caller. - auto selfclass = dyn_cast(afd->Variants[0].SelfClass); - assert(selfclass != nullptr); // non classes are not supposed to get here. + assert(dyn_cast(afd->Variants[0].SelfClass) != nullptr); // non classes are not supposed to get here. int comboflags = afd->Variants[0].UseFlags & StateFlags; if (comboflags == StateFlags) // the function must satisfy all the flags the state requires @@ -2824,7 +2842,7 @@ FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af, in } } } - return ConvertAST(cls, af); + return ConvertAST(cls->VMType, af); } //========================================================================== @@ -2838,7 +2856,7 @@ void ZCCCompiler::CompileStates() for (auto c : Classes) { - if (!c->Type()->IsDescendantOf(RUNTIME_CLASS(AActor))) + if (!c->ClassType()->IsDescendantOf(RUNTIME_CLASS(AActor))) { if (c->States.Size()) Error(c->cls, "%s: States can only be defined for actors.", c->Type()->TypeName.GetChars()); continue; @@ -2846,7 +2864,7 @@ void ZCCCompiler::CompileStates() FString statename; // The state builder wants the label as one complete string, not separated into tokens. FStateDefinitions statedef; - statedef.MakeStateDefines(dyn_cast(c->Type()->ParentClass)); + statedef.MakeStateDefines(ValidateActor(c->ClassType()->ParentClass)); int numframes = 0; for (auto s : c->States) @@ -2882,7 +2900,7 @@ void ZCCCompiler::CompileStates() } else { - flags = static_cast(c->Type())->DefaultStateUsage; + flags = static_cast(c->ClassType())->ActorInfo()->DefaultStateUsage; } auto st = s->Body; if (st != nullptr) do @@ -2959,7 +2977,7 @@ void ZCCCompiler::CompileStates() if (sl->Action != nullptr) { - auto code = SetupActionFunction(static_cast(c->Type()), sl->Action, state.UseFlags); + auto code = SetupActionFunction(static_cast(c->ClassType()), sl->Action, state.UseFlags); if (code != nullptr) { auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags); @@ -3041,7 +3059,7 @@ void ZCCCompiler::CompileStates() } try { - GetDefaultByType(c->Type())->Finalize(statedef); + GetDefaultByType(c->ClassType())->Finalize(statedef); } catch (CRecoverableError &err) { @@ -3056,7 +3074,7 @@ void ZCCCompiler::CompileStates() // //========================================================================== -FxExpression *ZCCCompiler::ConvertAST(PStruct *cls, ZCC_TreeNode *ast) +FxExpression *ZCCCompiler::ConvertAST(PContainerType *cls, ZCC_TreeNode *ast) { ConvertClass = cls; // there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return. @@ -3169,7 +3187,7 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) return new FxNop(*ast); // return something so that the compiler can continue. } auto cls = PClass::FindClass(cc->ClassName); - if (cls == nullptr) + if (cls == nullptr || cls->VMType == nullptr) { Error(cc, "Unknown class %s", FName(cc->ClassName).GetChars()); return new FxNop(*ast); // return something so that the compiler can continue. diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index 9d54f925b..baa5c89f6 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -41,7 +41,7 @@ struct ZCC_StructWork return strct->NodeName; } - PStruct *Type() + PContainerType *Type() { return strct->Type; } @@ -64,9 +64,9 @@ struct ZCC_ClassWork : public ZCC_StructWork Outer = nullptr; } - PClass *Type() + PClass *ClassType() { - return static_cast(strct->Type); + return static_cast(strct->Type)->Descriptor; } }; @@ -79,7 +79,7 @@ struct ZCC_PropertyWork struct ZCC_ConstantWork { ZCC_ConstantDef *node; - PStruct *cls; + PContainerType *cls; PSymbolTable *Outputtable; ExpVal constval; }; @@ -94,25 +94,25 @@ public: private: const char * GetStringConst(FxExpression *ex, FCompileContext &ctx); - int IntConstFromNode(ZCC_TreeNode *node, PStruct *cls); - FString StringConstFromNode(ZCC_TreeNode *node, PStruct *cls); + int IntConstFromNode(ZCC_TreeNode *node, PContainerType *cls); + FString StringConstFromNode(ZCC_TreeNode *node, PContainerType *cls); void ProcessClass(ZCC_Class *node, PSymbolTreeNode *tnode); void ProcessStruct(ZCC_Struct *node, PSymbolTreeNode *tnode, ZCC_Class *outer); void CreateStructTypes(); void CreateClassTypes(); - void CopyConstants(TArray &dest, TArray &Constants, PStruct *cls, PSymbolTable *ot); + void CopyConstants(TArray &dest, TArray &Constants, PContainerType *cls, PSymbolTable *ot); void CompileAllConstants(); void AddConstant(ZCC_ConstantWork &constant); bool CompileConstant(ZCC_ConstantWork *def); void CompileArrays(ZCC_StructWork *work); void CompileAllFields(); - bool CompileFields(PStruct *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false); + bool CompileFields(PContainerType *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false); void CompileAllProperties(); bool CompileProperties(PClass *type, TArray &Properties, FName prefix); FString FlagsToString(uint32_t flags); PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember); - PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PStruct *cls); + PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PContainerType *cls); PType *ResolveUserType(ZCC_BasicType *type, PSymbolTable *sym, bool nativetype); void InitDefaults(); @@ -143,12 +143,12 @@ private: void Error(ZCC_TreeNode *node, const char *msg, ...) GCCPRINTF(3,4); void MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr); - FxExpression *ConvertAST(PStruct *cclass, ZCC_TreeNode *ast); + FxExpression *ConvertAST(PContainerType *cclass, ZCC_TreeNode *ast); FxExpression *ConvertNode(ZCC_TreeNode *node); FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head); DObject *Outer; - PStruct *ConvertClass; // class type to be used when resoving symbols while converting an AST + PContainerType *ConvertClass; // class type to be used when resoving symbols while converting an AST PSymbolTable *GlobalTreeNodes; PNamespace *OutNamespace; ZCC_AST &AST; diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 29c6ec20c..3c1e55805 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -3,6 +3,7 @@ #include "memarena.h" #include "sc_man.h" +#include "types.h" struct ZCCToken { @@ -193,9 +194,9 @@ struct ZCC_NamedNode : ZCC_TreeNode struct ZCC_Struct : ZCC_NamedNode { - VM_UWORD Flags; + uint32_t Flags; ZCC_TreeNode *Body; - PStruct *Type; + PContainerType *Type; VersionInfo Version; }; @@ -209,7 +210,7 @@ struct ZCC_Class : ZCC_Struct ZCC_Identifier *ParentName; ZCC_Identifier *Replaces; - PClass *CType() { return static_cast(Type); } + PClass *CType() { return static_cast(Type)->Descriptor; } }; struct ZCC_Enum : ZCC_NamedNode diff --git a/src/serializer.cpp b/src/serializer.cpp index f514449ac..c41fec1f3 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -1877,7 +1877,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState { arc.w->StartArray(); arc.w->String(info->TypeName.GetChars()); - arc.w->Uint((uint32_t)(state - info->OwnedStates)); + arc.w->Uint((uint32_t)(state - info->GetStates())); arc.w->EndArray(); } else @@ -1908,9 +1908,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState if (cls.IsString() && ndx.IsUint()) { PClassActor *clas = PClass::FindActor(UnicodeToString(cls.GetString())); - if (clas && ndx.GetUint() < (unsigned)clas->NumOwnedStates) + if (clas && ndx.GetUint() < (unsigned)clas->GetStateCount()) { - state = clas->OwnedStates + ndx.GetUint(); + state = clas->GetStates() + ndx.GetUint(); } else { diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index e00fdae4a..af848cbeb 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -64,6 +64,7 @@ extern void ChildSigHandler (int signum); #include "templates.h" #include "stats.h" #include "timidity/timidity.h" +#include "vm.h" #define GZIP_ID1 31 #define GZIP_ID2 139 diff --git a/src/swrenderer/r_swrenderer.cpp b/src/swrenderer/r_swrenderer.cpp index 0564dcfcf..9f6da2a14 100644 --- a/src/swrenderer/r_swrenderer.cpp +++ b/src/swrenderer/r_swrenderer.cpp @@ -132,9 +132,9 @@ void FSoftwareRenderer::Precache(uint8_t *texhitlist, TMap & { PClassActor *cls = pair->Key; - for (int i = 0; i < cls->NumOwnedStates; i++) + for (unsigned i = 0; i < cls->GetStateCount(); i++) { - spritelist[cls->OwnedStates[i].sprite] = true; + spritelist[cls->GetStates()[i].sprite] = true; } } diff --git a/src/swrenderer/scene/r_opaque_pass.cpp b/src/swrenderer/scene/r_opaque_pass.cpp index 23876235f..12bb95513 100644 --- a/src/swrenderer/scene/r_opaque_pass.cpp +++ b/src/swrenderer/scene/r_opaque_pass.cpp @@ -844,7 +844,7 @@ namespace swrenderer //if (thing->validcount == validcount) continue; //thing->validcount = validcount; - FIntCVar *cvar = thing->GetClass()->distancecheck; + FIntCVar *cvar = thing->GetInfo()->distancecheck; if (cvar != nullptr && *cvar >= 0) { double dist = (thing->Pos() - Thread->Viewport->viewpoint.Pos).LengthSquared(); diff --git a/src/teaminfo.cpp b/src/teaminfo.cpp index 0658545b3..bde80a431 100644 --- a/src/teaminfo.cpp +++ b/src/teaminfo.cpp @@ -41,6 +41,7 @@ #include "v_font.h" #include "v_video.h" #include "w_wad.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 2bd779865..f7d9a539b 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -53,6 +53,7 @@ #include "r_renderer.h" #include "r_sky.h" #include "textures/textures.h" +#include "vm.h" FTextureManager TexMan; diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 5def06068..314a0fb9a 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -62,6 +62,7 @@ #include "r_data/colormaps.h" #include "g_levellocals.h" #include "textures.h" +#include "vm.h" CUSTOM_CVAR(Int, uiscale, 0, CVAR_ARCHIVE | CVAR_NOINITCALL) { diff --git a/src/v_font.cpp b/src/v_font.cpp index e6aa57a50..f133b1be4 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -95,6 +95,7 @@ The FON2 header is followed by variable length data: #include "colormatcher.h" #include "v_palette.h" #include "v_text.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ diff --git a/src/v_text.cpp b/src/v_text.cpp index 454964aa3..3c31312b3 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -47,6 +47,7 @@ #include "doomstat.h" #include "templates.h" #include "gstrings.h" +#include "vm.h" int ListGetInt(VMVa_List &tags); diff --git a/src/v_video.cpp b/src/v_video.cpp index f27c0c499..96b45fad6 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -65,6 +65,7 @@ #include "r_renderer.h" #include "menu/menu.h" #include "r_data/voxels.h" +#include "vm.h" EXTERN_CVAR(Bool, r_blendmethod) diff --git a/src/v_video.h b/src/v_video.h index 1ea7c7bbf..56cb3ee0d 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -535,6 +535,7 @@ void V_Init2 (); void V_Shutdown (); class FScanner; +struct FScriptPosition; // Returns the closest color to the one desired. String // should be of the form "rr gg bb". int V_GetColorFromString (const uint32_t *palette, const char *colorstring, FScriptPosition *sc = nullptr); diff --git a/src/virtual.h b/src/virtual.h deleted file mode 100644 index a25e543ac..000000000 --- a/src/virtual.h +++ /dev/null @@ -1,30 +0,0 @@ -inline 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(cls->Symbols.FindSymbol(funcname, false)); - assert(sym != nullptr); - auto VIndex = sym->Variants[0].Implementation->VirtualIndex; - return VIndex; -} - -#define IFVIRTUALPTR(self, cls, funcname) \ - static unsigned VIndex = ~0u; \ - if (VIndex == ~0u) { \ - VIndex = GetVirtualIndex(RUNTIME_CLASS(cls), #funcname); \ - assert(VIndex != ~0u); \ - } \ - auto clss = self->GetClass(); \ - VMFunction *func = clss->Virtuals.Size() > VIndex? clss->Virtuals[VIndex] : nullptr; \ - if (func != nullptr) - -#define IFVIRTUAL(cls, funcname) IFVIRTUALPTR(this, cls, funcname) - -#define IFVIRTUALPTRNAME(self, cls, funcname) \ - static unsigned VIndex = ~0u; \ - if (VIndex == ~0u) { \ - VIndex = GetVirtualIndex(PClass::FindClass(cls), #funcname); \ - assert(VIndex != ~0u); \ - } \ - auto clss = self->GetClass(); \ - VMFunction *func = clss->Virtuals.Size() > VIndex? clss->Virtuals[VIndex] : nullptr; \ - if (func != nullptr) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 1f62b15c5..f1e633970 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -57,6 +57,7 @@ #include "resourcefiles/resourcefile.h" #include "md5.h" #include "doomstat.h" +#include "vm.h" // MACROS ------------------------------------------------------------------ diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index f9a49eb0d..dc915a011 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -62,7 +62,7 @@ #include "gstrings.h" #include "cmdlib.h" #include "g_levellocals.h" -#include "virtual.h" +#include "vm.h" CVAR(Bool, wi_percents, true, CVAR_ARCHIVE) CVAR(Bool, wi_showtotaltime, true, CVAR_ARCHIVE) @@ -710,7 +710,7 @@ void WI_Ticker() IFVIRTUALPTRNAME(WI_Screen, "StatusScreen", Ticker) { VMValue self = WI_Screen; - GlobalVMStack.Call(func, &self, 1, nullptr, 0); + VMCall(func, &self, 1, nullptr, 0); } } } @@ -729,7 +729,7 @@ void WI_Drawer() IFVIRTUALPTRNAME(WI_Screen, "StatusScreen", Drawer) { VMValue self = WI_Screen; - GlobalVMStack.Call(func, &self, 1, nullptr, 0); + VMCall(func, &self, 1, nullptr, 0); screen->ClearClipRect(); // make sure the scripts don't leave a valid clipping rect behind. // The internal handling here is somewhat poor. After being set to 'LeavingIntermission' @@ -780,7 +780,7 @@ void WI_Start(wbstartstruct_t *wbstartstruct) IFVIRTUALPTRNAME(WI_Screen, "StatusScreen", Start) { VMValue val[2] = { WI_Screen, wbstartstruct }; - GlobalVMStack.Call(func, val, 2, nullptr, 0); + VMCall(func, val, 2, nullptr, 0); } GC::AddSoftRoot(WI_Screen); } diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 0f1195cbf..9defba9de 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -41,6 +41,7 @@ struct _ native // These are the global variables, the struct is only here to av native ui float BackbuttonAlpha; native readonly int Net_Arbitrator; native ui BaseStatusBar StatusBar; + native readonly Weapon WP_NOCHANGE; } @@ -650,7 +651,21 @@ struct StringStruct native native String Filter(); } -class Floor : Thinker native +class SectorEffect : Thinker native +{ + native protected Sector m_Sector; +} + +class Mover : SectorEffect native +{} + +class MovingFloor : Mover native +{} + +class MovingCeiling : Mover native +{} + +class Floor : MovingFloor native { // only here so that some constants and functions can be added. Not directly usable yet. enum EFloor @@ -695,7 +710,7 @@ class Floor : Thinker native native static bool CreateFloor(sector sec, EFloor floortype, line ln, double speed, double height = 0, int crush = -1, int change = 0, bool crushmode = false, bool hereticlower = false); } -class Ceiling : Thinker native +class Ceiling : MovingCeiling native { enum ECeiling { @@ -749,11 +764,6 @@ struct LookExParams State seestate; }; -class SectorEffect : Thinker native -{ - native protected Sector m_Sector; -} - class Lighting : SectorEffect native { } diff --git a/wadsrc/static/zscript/menu/loadsavemenu.txt b/wadsrc/static/zscript/menu/loadsavemenu.txt index 2154e1015..7fe354317 100644 --- a/wadsrc/static/zscript/menu/loadsavemenu.txt +++ b/wadsrc/static/zscript/menu/loadsavemenu.txt @@ -387,7 +387,7 @@ class LoadSaveMenu : ListMenu { if (ev.Type == UIEvent.Type_KeyDown) { - if (Selected < manager.SavegameCount()) + if (Selected != -1 && Selected < manager.SavegameCount()) { switch (ev.KeyChar) {