From 8b6a714d4175cb2d2a48373a9fe2c42632f31bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Fri, 25 Nov 2022 20:39:12 -0300 Subject: [PATCH] Map and MapIterator for ZScript --- src/CMakeLists.txt | 1 + src/common/engine/namedef.h | 9 + src/common/engine/printf.h | 2 +- src/common/engine/sc_man_scanner.re | 1 + src/common/engine/sc_man_tokens.h | 1 + src/common/objects/dobject.cpp | 33 + src/common/objects/dobjtype.cpp | 64 ++ src/common/objects/dobjtype.h | 2 + src/common/scripting/backend/codegen.cpp | 201 ++++- src/common/scripting/backend/codegen.h | 2 + src/common/scripting/core/maps.cpp | 483 +++++++++++ src/common/scripting/core/maps.h | 110 +++ src/common/scripting/core/types.cpp | 796 +++++++++++++++++- src/common/scripting/core/types.h | 66 +- src/common/scripting/frontend/ast.cpp | 11 + src/common/scripting/frontend/zcc-parse.lemon | 12 +- src/common/scripting/frontend/zcc_compile.cpp | 94 ++- src/common/scripting/frontend/zcc_parser.cpp | 14 + src/common/scripting/frontend/zcc_parser.h | 7 + src/common/utility/engineerrors.cpp | 24 +- src/common/utility/tarray.h | 3 + wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/engine/maps.zs | 447 ++++++++++ 23 files changed, 2355 insertions(+), 29 deletions(-) create mode 100644 src/common/scripting/core/maps.cpp create mode 100644 src/common/scripting/core/maps.h create mode 100644 wadsrc/static/zscript/engine/maps.zs diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4bd5e14d4..911e35dce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1153,6 +1153,7 @@ set (PCH_SOURCES common/rendering/gl/gl_samplers.cpp common/rendering/gl/gl_shader.cpp common/rendering/gl/gl_shaderprogram.cpp + common/scripting/core/maps.cpp common/scripting/core/dictionary.cpp common/scripting/core/dynarrays.cpp common/scripting/core/symbols.cpp diff --git a/src/common/engine/namedef.h b/src/common/engine/namedef.h index ef33f4522..86543ca37 100644 --- a/src/common/engine/namedef.h +++ b/src/common/engine/namedef.h @@ -117,6 +117,7 @@ xx(Double) xx(String) xx(Vector) xx(Map) +xx(MapIterator) xx(Array) xx(Include) xx(Sound) @@ -162,6 +163,14 @@ xx(ToVector) xx(Size) xx(Push) xx(Insert) +xx(InsertNew) +xx(Remove) +xx(Get) +xx(GetValue) +xx(GetKey) +xx(SetValue) +xx(CheckKey) +xx(Value) xx(Copy) xx(Move) xx(Voidptr) diff --git a/src/common/engine/printf.h b/src/common/engine/printf.h index 964d7dc85..98af91ff8 100644 --- a/src/common/engine/printf.h +++ b/src/common/engine/printf.h @@ -88,7 +88,7 @@ int Printf (const char *format, ...) ATTRIBUTE((format(printf,1,2))); int DPrintf (int level, const char *format, ...) ATTRIBUTE((format(printf,2,3))); void I_DebugPrint(const char* cp); -void debugprintf(const char* f, ...); // Prints to the debugger's log. +void I_DebugPrintf(const char* fmt, ...); // Prints to the debugger's log. // flag to silence non-error output extern bool batchrun; diff --git a/src/common/engine/sc_man_scanner.re b/src/common/engine/sc_man_scanner.re index 1d18be71d..59c3c7712 100644 --- a/src/common/engine/sc_man_scanner.re +++ b/src/common/engine/sc_man_scanner.re @@ -167,6 +167,7 @@ std2: 'vector2' { RET(TK_Vector2); } 'vector3' { RET(TK_Vector3); } 'map' { RET(TK_Map); } + 'mapiterator' { RET(TK_MapIterator); } 'array' { RET(TK_Array); } 'in' { RET(TK_In); } 'sizeof' { RET(TK_SizeOf); } diff --git a/src/common/engine/sc_man_tokens.h b/src/common/engine/sc_man_tokens.h index 5aa6360fe..d93e5be62 100644 --- a/src/common/engine/sc_man_tokens.h +++ b/src/common/engine/sc_man_tokens.h @@ -128,6 +128,7 @@ xx(TK_Replaces, "'replaces'") xx(TK_Vector2, "'vector2'") xx(TK_Vector3, "'vector3'") xx(TK_Map, "'map'") +xx(TK_MapIterator, "'mapiterator'") xx(TK_Array, "'array'") xx(TK_In, "'in'") xx(TK_SizeOf, "'sizeof'") diff --git a/src/common/objects/dobject.cpp b/src/common/objects/dobject.cpp index f82f50580..ba4f0a0f1 100644 --- a/src/common/objects/dobject.cpp +++ b/src/common/objects/dobject.cpp @@ -42,6 +42,7 @@ #include "types.h" #include "i_time.h" #include "printf.h" +#include "maps.h" //========================================================================== // @@ -342,6 +343,17 @@ DEFINE_ACTION_FUNCTION(DObject, Destroy) // //========================================================================== +template +static void PropagateMarkMap(M *map) +{ + TMapIterator it(*map); + typename M::Pair * p; + while(it.NextPair(p)) + { + GC::Mark(&p->Value); + } +} + size_t DObject::PropagateMark() { const PClass *info = GetClass(); @@ -375,6 +387,27 @@ size_t DObject::PropagateMark() offsets++; } + { + const std::pair *maps = info->MapPointers; + if (maps == NULL) + { + const_cast(info)->BuildMapPointers(); + maps = info->MapPointers; + } + while (maps->first != ~(size_t)0) + { + if(maps->second->RegType == REGT_STRING) + { // FString,DObject* + PropagateMarkMap((ZSMap*)((uint8_t *)this + maps->first)); + } + else + { // uint32_t,DObject* + PropagateMarkMap((ZSMap*)((uint8_t *)this + maps->first)); + } + maps++; + } + + } return info->Size; } return 0; diff --git a/src/common/objects/dobjtype.cpp b/src/common/objects/dobjtype.cpp index 250dccbdd..fde9ea0e3 100644 --- a/src/common/objects/dobjtype.cpp +++ b/src/common/objects/dobjtype.cpp @@ -82,6 +82,8 @@ DEFINE_GLOBAL(WP_NOCHANGE); // A harmless non-nullptr FlatPointer for classes without pointers. static const size_t TheEnd = ~(size_t)0; +static const std::pair TheMapEnd = {~(size_t)0 , nullptr}; + //========================================================================== // // PClass :: WriteValue @@ -877,6 +879,68 @@ void PClass::BuildArrayPointers() } } +//========================================================================== +// +// PClass :: BuildMapPointers +// +// same as above, but creates a list to dynamic object arrays +// +//========================================================================== + +void PClass::BuildMapPointers() +{ + if (MapPointers != nullptr) + { // Already built: Do nothing. + return; + } + else if (ParentClass == nullptr) + { // No parent (i.e. DObject: FlatPointers is the same as Pointers. + MapPointers = &TheMapEnd; + } + else + { + ParentClass->BuildMapPointers(); + + TArray> ScriptPointers; + + // Collect all arrays to pointers in scripted fields. + for (auto field : Fields) + { + if (!(field->Flags & VARF_Native)) + { + field->Type->SetPointerMap(Defaults, unsigned(field->Offset), &ScriptPointers); + } + } + + if (ScriptPointers.Size() == 0) + { // No new pointers: Just use the same ArrayPointers as the parent. + MapPointers = ParentClass->MapPointers; + } + else + { // New pointers: Create a new FlatPointers array and add them. + int numSuperPointers; + + // Count pointers defined by superclasses. + for (numSuperPointers = 0; ParentClass->MapPointers[numSuperPointers].first != ~(size_t)0; numSuperPointers++) + { + } + + // Concatenate them into a new array + std::pair *flat = (std::pair*)ClassDataAllocator.Alloc(sizeof(std::pair) * (numSuperPointers + ScriptPointers.Size() + 1)); + if (numSuperPointers > 0) + { + memcpy(flat, ParentClass->MapPointers, sizeof(std::pair)*numSuperPointers); + } + if (ScriptPointers.Size() > 0) + { + memcpy(flat + numSuperPointers, &ScriptPointers[0], sizeof(std::pair) * ScriptPointers.Size()); + } + flat[numSuperPointers + ScriptPointers.Size()] = TheMapEnd; + MapPointers = flat; + } + } +} + //========================================================================== // // PClass :: NativeClass diff --git a/src/common/objects/dobjtype.h b/src/common/objects/dobjtype.h index f7996d89d..a21ab8c79 100644 --- a/src/common/objects/dobjtype.h +++ b/src/common/objects/dobjtype.h @@ -56,6 +56,7 @@ public: 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. + const std::pair *MapPointers = nullptr; // maps containing object pointers. uint8_t *Defaults = nullptr; uint8_t *Meta = nullptr; // Per-class static script data unsigned Size = sizeof(DObject); @@ -84,6 +85,7 @@ public: void InitializeActorInfo(); void BuildFlatPointers(); void BuildArrayPointers(); + void BuildMapPointers(); void DestroySpecials(void *addr); void DestroyMeta(void *addr); const PClass *NativeClass() const; diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 2bc262c96..6cf593fa2 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -8205,6 +8205,8 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) PContainerType *ccls = nullptr; + PFunction * afd_override = nullptr; + if (ctx.Class == nullptr) { // There's no way that a member function call can resolve to a constant so abort right away. @@ -8278,7 +8280,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (Self->ValueType->isRealPointer()) { auto pointedType = Self->ValueType->toPointer()->PointedType; - if (pointedType && pointedType->isDynArray()) + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) { Self = new FxOutVarDereference(Self, Self->ScriptPosition); SAFE_RESOLVE(Self, ctx); @@ -8454,7 +8456,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (a->ValueType->isRealPointer()) { auto pointedType = a->ValueType->toPointer()->PointedType; - if (pointedType && pointedType->isDynArray()) + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) { a = new FxOutVarDereference(a, a->ScriptPosition); SAFE_RESOLVE(a, ctx); @@ -8561,6 +8563,193 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } } } + else if(Self->IsMap()) + { + PMap * m = static_cast(Self->ValueType); + Self->ValueType = m->BackingType; + + auto mapKeyType = m->KeyType; + auto mapValueType = m->ValueType; + + bool isObjMap = mapValueType->isObjectPointer(); + + if (PFunction **Override; (Override = m->FnOverrides.CheckKey(MethodName))) + { + afd_override = *Override; + } + + // Adapted from DynArray codegen + + int idx = 0; + for (auto &a : ArgList) + { + a = a->Resolve(ctx); + if (a == nullptr) + { + delete this; + return nullptr; + } + if (a->ValueType->isRealPointer()) + { + auto pointedType = a->ValueType->toPointer()->PointedType; + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) + { + a = new FxOutVarDereference(a, a->ScriptPosition); + SAFE_RESOLVE(a, ctx); + } + } + if (isObjMap && (MethodName == NAME_Insert && idx == 1)) + { + // Null pointers are always valid. + if (!a->isConstant() || static_cast(a)->GetValue().GetPointer() != nullptr) + { + if (!a->ValueType->isObjectPointer() || + !static_cast(mapValueType)->PointedClass()->IsAncestorOf(static_cast(a->ValueType)->PointedClass())) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + } + } + + if (a->IsMap()) + { + // Copy and Move must turn their parameter into a pointer to the backing struct type. + auto o = static_cast(a->ValueType); + auto backingtype = o->BackingType; + if (mapKeyType != o->KeyType || mapValueType != o->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + bool writable; + if (!a->RequestAddress(ctx, &writable)) + { + ScriptPosition.Message(MSG_ERROR, "Unable to dereference map variable"); + delete this; + return nullptr; + } + a->ValueType = NewPointer(backingtype); + + // Also change the field's type so the code generator can work with this (actually this requires swapping out the entire field.) + if (Self->ExprType == EFX_StructMember || Self->ExprType == EFX_ClassMember || Self->ExprType == EFX_StackVariable) + { + auto member = static_cast(Self); + auto newfield = Create(NAME_None, backingtype, 0, member->membervar->Offset); + member->membervar = newfield; + } + } + else if (a->IsPointer() && Self->ValueType->isPointer()) + { + // the only case which must be checked up front is for pointer arrays receiving a new element. + // Since there is only one native backing class it uses a neutral void pointer as its argument, + // meaning that FxMemberFunctionCall is unable to do a proper check. So we have to do it here. + if (a->ValueType != mapValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument. Got %s, expected %s", a->ValueType->DescriptiveName(), mapValueType->DescriptiveName()); + delete this; + return nullptr; + } + } + idx++; + + } + } + else if(Self->IsMapIterator()) + { + PMapIterator * mi = static_cast(Self->ValueType); + Self->ValueType = mi->BackingType; + + auto mapKeyType = mi->KeyType; + auto mapValueType = mi->ValueType; + + bool isObjMap = mapValueType->isObjectPointer(); + + if (PFunction **Override; (Override = mi->FnOverrides.CheckKey(MethodName))) + { + afd_override = *Override; + } + + // Adapted from DynArray codegen + + int idx = 0; + for (auto &a : ArgList) + { + a = a->Resolve(ctx); + if (a == nullptr) + { + delete this; + return nullptr; + } + if (a->ValueType->isRealPointer()) + { + auto pointedType = a->ValueType->toPointer()->PointedType; + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) + { + a = new FxOutVarDereference(a, a->ScriptPosition); + SAFE_RESOLVE(a, ctx); + } + } + if (isObjMap && (MethodName == NAME_SetValue && idx == 0)) + { + // Null pointers are always valid. + if (!a->isConstant() || static_cast(a)->GetValue().GetPointer() != nullptr) + { + if (!a->ValueType->isObjectPointer() || + !static_cast(mapValueType)->PointedClass()->IsAncestorOf(static_cast(a->ValueType)->PointedClass())) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + } + } + + if (a->IsMap()) + { + // Copy and Move must turn their parameter into a pointer to the backing struct type. + auto o = static_cast(a->ValueType); + auto backingtype = o->BackingType; + if (mapKeyType != o->KeyType || mapValueType != o->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + bool writable; + if (!a->RequestAddress(ctx, &writable)) + { + ScriptPosition.Message(MSG_ERROR, "Unable to dereference map variable"); + delete this; + return nullptr; + } + a->ValueType = NewPointer(backingtype); + + // Also change the field's type so the code generator can work with this (actually this requires swapping out the entire field.) + if (Self->ExprType == EFX_StructMember || Self->ExprType == EFX_ClassMember || Self->ExprType == EFX_StackVariable) + { + auto member = static_cast(Self); + auto newfield = Create(NAME_None, backingtype, 0, member->membervar->Offset); + member->membervar = newfield; + } + } + else if (a->IsPointer() && Self->ValueType->isPointer()) + { + // the only case which must be checked up front is for pointer arrays receiving a new element. + // Since there is only one native backing class it uses a neutral void pointer as its argument, + // meaning that FxMemberFunctionCall is unable to do a proper check. So we have to do it here. + if (a->ValueType != mapValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument. Got %s, expected %s", a->ValueType->DescriptiveName(), mapValueType->DescriptiveName()); + delete this; + return nullptr; + } + } + idx++; + } + } if (MethodName == NAME_GetParentClass && @@ -8635,7 +8824,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) isresolved: bool error = false; - PFunction *afd = FindClassMemberFunction(cls, ctx.Class, MethodName, ScriptPosition, &error, ctx.Version, !ctx.FromDecorate); + PFunction *afd = afd_override ? afd_override : FindClassMemberFunction(cls, ctx.Class, MethodName, ScriptPosition, &error, ctx.Version, !ctx.FromDecorate); if (error) { delete this; @@ -9034,7 +9223,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) if (ArgList[i] && ArgList[i]->ValueType->isRealPointer()) { auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType; - if (pointedType && pointedType->isDynArray()) + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) { ArgList[i] = new FxOutVarDereference(ArgList[i], ArgList[i]->ScriptPosition); SAFE_RESOLVE(ArgList[i], ctx); @@ -11806,7 +11995,7 @@ FxExpression *FxOutVarDereference::Resolve(FCompileContext &ctx) SelfType = Self->ValueType->toPointer()->PointedType; ValueType = SelfType; - if (SelfType->GetRegType() == REGT_NIL && !SelfType->isRealPointer() && !SelfType->isDynArray()) + if (SelfType->GetRegType() == REGT_NIL && !SelfType->isRealPointer() && !SelfType->isDynArray() && !SelfType->isMap() && !SelfType->isMapIterator()) { ScriptPosition.Message(MSG_ERROR, "Cannot dereference pointer"); delete this; @@ -11835,7 +12024,7 @@ ExpEmit FxOutVarDereference::Emit(VMFunctionBuilder *build) regType = REGT_POINTER; loadOp = OP_LP; } - else if (SelfType->isDynArray()) + else if (SelfType->isDynArray() || SelfType->isMap() || SelfType->isMapIterator()) { regType = REGT_POINTER; loadOp = OP_MOVEA; diff --git a/src/common/scripting/backend/codegen.h b/src/common/scripting/backend/codegen.h index f31b4ea1e..e26cea84d 100644 --- a/src/common/scripting/backend/codegen.h +++ b/src/common/scripting/backend/codegen.h @@ -350,6 +350,8 @@ public: bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); } bool isStaticArray() const { return (ValueType->isPointer() && ValueType->toPointer()->PointedType->isStaticArray()); } // can only exist in pointer form. bool IsDynamicArray() const { return (ValueType->isDynArray()); } + bool IsMap() const { return ValueType->isMap(); } + bool IsMapIterator() const { return ValueType->isMapIterator(); } bool IsStruct() const { return ValueType->isStruct(); } bool IsNativeStruct() const { return (ValueType->isStruct() && static_cast(ValueType)->isNative); } diff --git a/src/common/scripting/core/maps.cpp b/src/common/scripting/core/maps.cpp new file mode 100644 index 000000000..bb7cd369b --- /dev/null +++ b/src/common/scripting/core/maps.cpp @@ -0,0 +1,483 @@ +#include "tarray.h" +#include "dobject.h" +#include "zstring.h" +#include "vm.h" +#include "types.h" +#include "v_draw.h" +#include "maps.h" + + +//========================================================================== +// +// MAP_GC_WRITE_BARRIER +// +//========================================================================== + + +#define MAP_GC_WRITE_BARRIER(x) { \ + TMapIterator it(*x);\ + typename M::Pair * p;\ + while(it.NextPair(p)){\ + GC::WriteBarrier(p->Value);\ + }\ +} + + +//========================================================================== +// +// MapCopy +// +//========================================================================== + + +template void MapCopy(M * self, M * other) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(other); + } + *self = *other; // how does this handle self->info? TODO double check. + self->info->rev++; +} + + +//========================================================================== +// +// MapMove +// +//========================================================================== + + +template void MapMove(M * self, M * other) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(other); + } + self->TransferFrom(*other); + self->info->rev++; + other->info->rev++; +} + + +//========================================================================== +// +// MapSwap +// +//========================================================================== + + +template void MapSwap(M * self, M * other) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(other); + } + self->Swap(*other); + self->info->rev++; + other->info->rev++; +} + + +//========================================================================== +// +// MapClear +// +//========================================================================== + + +template void MapClear(M * self) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(self); + } + self->Clear(); + self->info->rev++; +} + + +//========================================================================== +// +// +// +//========================================================================== + + +template +using expand_types_vm = +std::conditional_t || std::is_same_v, uint32_t , /* expand 8/16-bit to 32-bit */ + std::conditional_t , double , /* expand float to double */ + std::conditional_t , const FString & , T>>>; /* change String to String ref */ + +template unsigned int MapCountUsed(M * self) +{ + return self->CountUsed(); +} + +template expand_types_vm MapGet(M * self,expand_types_vm key) +{ + typename M::ValueType * v = self->CheckKey(key); + if (v) { + return *v; + } + else + { + typename M::ValueType n {}; + self->Insert(key,n); + return n; + } +} + +template void MapGetString(M * self,expand_types_vm key, FString &out) +{ + FString * v = self->CheckKey(key); + if (v) { + out = *v; + } + else + { + out = FString(); + self->Insert(key,out); + } +} + +template int MapCheckKey(M * self, expand_types_vm key) +{ + return self->CheckKey(key) != nullptr; +} + + +//========================================================================== +// +// MapInsert +// +//========================================================================== + + +template void MapInsert(M * self, expand_types_vm key, expand_types_vm value) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(self); + GC::WriteBarrier(value); + } + self->Insert(key, value); + self->info->rev++; // invalidate iterators +} + + +//========================================================================== +// +// +// +//========================================================================== + + +template void MapInsertNew(M * self, expand_types_vm key) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(self); + } + self->Insert(key,{}); + self->info->rev++; // invalidate iterators +} + +template void MapRemove(M * self, expand_types_vm key) +{ + self->Remove(key); + self->info->rev++; // invalidate iterators +} + + +//========================================================================== +// +// +// +//========================================================================== + + +template int MapIteratorInit(I * self, M * other) +{ + return self->Init(*other); +} + +template int MapIteratorReInit(I * self) +{ + return self->ReInit(); +} + +template int MapIteratorValid(I * self) +{ + return self->Valid(); +} + +template int MapIteratorNext(I * self) +{ + return self->Next(); +} + +template expand_types_vm MapIteratorGetKey(I * self) +{ + return self->GetKey(); +} + +template void MapIteratorGetKeyString(I * self, FString &out) +{ + out = self->GetKey(); +} + +template expand_types_vm MapIteratorGetValue(I * self) +{ + return self->GetValue(); +} + +template void MapIteratorGetValueString(I * self, FString &out) +{ + out = self->GetValue(); +} + +template void MapIteratorSetValue(I * self, expand_types_vm value) +{ + auto & val = self->GetValue(); + if constexpr(std::is_same_v) + { + GC::WriteBarrier(val); + GC::WriteBarrier(value); + } + val = value; +} + + +//========================================================================== +// +// +// +//========================================================================== + +#define PARAM_VOIDPOINTER(X) PARAM_POINTER( X , void ) +#define PARAM_OBJPOINTER(X) PARAM_OBJECT( X , DObject ) + +#define _DEF_MAP( name, key_type, value_type, PARAM_KEY, PARAM_VALUE ) \ + typedef ZSMap name;\ + DEFINE_ACTION_FUNCTION_NATIVE( name , Copy , MapCopy< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, name ); \ + MapCopy(self , other); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Move , MapMove< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, name ); \ + MapMove(self , other); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Swap , MapSwap< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, name ); \ + MapSwap(self , other); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Clear , MapClear< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + MapClear(self); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, CountUsed, MapCountUsed< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_INT( MapCountUsed(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, CheckKey, MapCheckKey< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + ACTION_RETURN_BOOL( MapCheckKey(self, key) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Insert, MapInsert< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + PARAM_VALUE( value ); \ + MapInsert(self, key, value); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, InsertNew, MapInsertNew< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + MapInsertNew(self, key); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Remove, MapRemove< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + MapRemove(self, key); \ + return 0; \ + } + +#define DEF_MAP_X_X( name, key_type, value_type, PARAM_KEY, PARAM_VALUE , ACTION_RETURN_VALUE ) \ + _DEF_MAP( name , key_type , value_type , PARAM_KEY , PARAM_VALUE ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Get, MapGet< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + ACTION_RETURN_VALUE( MapGet(self, key) ); \ + } + +#define DEF_MAP_X_S( name, key_type, PARAM_KEY ) \ + _DEF_MAP( name , key_type , FString , PARAM_KEY , PARAM_STRING ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Get, MapGetString< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + FString out; \ + MapGetString(self, key, out); \ + ACTION_RETURN_STRING( out ); \ + } + +#define COMMA , + +#define _DEF_MAP_IT( map_name , name, key_type, value_type, PARAM_VALUE ) \ + typedef ZSMapIterator name; \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Init , MapIteratorInit< name COMMA map_name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, map_name ); \ + ACTION_RETURN_BOOL( MapIteratorInit(self , other) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , ReInit , MapIteratorReInit< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_BOOL( MapIteratorReInit(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Valid , MapIteratorValid< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_BOOL( MapIteratorValid(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Next , MapIteratorNext< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_BOOL( MapIteratorNext(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , SetValue , MapIteratorSetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_VALUE( value ); \ + MapIteratorSetValue(self, value); \ + return 0; \ + } +/* + DEFINE_ACTION_FUNCTION_NATIVE( name , GetKey , MapIteratorGetKey< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_KEY( MapIteratorGetKey(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetValue , MapIteratorGetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_VALUE( MapIteratorGetValue(self) ); \ + } \ +*/ + +#define DEF_MAP_IT_I_X( map_name , name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + _DEF_MAP_IT( map_name , name , uint32_t , value_type , PARAM_VALUE ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetKey , MapIteratorGetKey< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_INT( MapIteratorGetKey(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetValue , MapIteratorGetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_VALUE( MapIteratorGetValue(self) ); \ + } + +#define DEF_MAP_IT_S_X( map_name , name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + _DEF_MAP_IT( map_name , name , FString , value_type , PARAM_VALUE ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetKey , MapIteratorGetKeyString< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + FString out; \ + MapIteratorGetKeyString(self , out); \ + ACTION_RETURN_STRING( out ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetValue , MapIteratorGetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_VALUE( MapIteratorGetValue(self) ); \ + } + +#define DEF_MAP_IT_I_S() \ + _DEF_MAP_IT( FMap_I32_Str , FMapIterator_I32_Str , uint32_t , FString , PARAM_STRING ) \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_I32_Str , GetKey , MapIteratorGetKey< FMapIterator_I32_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_I32_Str ); \ + ACTION_RETURN_INT( MapIteratorGetKey(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_I32_Str , GetValue , MapIteratorGetValue< FMapIterator_I32_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_I32_Str ); \ + ACTION_RETURN_STRING( MapIteratorGetValue(self) ); \ + } + +#define DEF_MAP_IT_S_S() \ + _DEF_MAP_IT( FMap_Str_Str , FMapIterator_Str_Str , FString , FString , PARAM_STRING ) \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_Str_Str , GetKey , MapIteratorGetKeyString< FMapIterator_Str_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_Str_Str ); \ + FString out; \ + MapIteratorGetKeyString(self , out); \ + ACTION_RETURN_STRING( out ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_Str_Str , GetValue , MapIteratorGetValueString< FMapIterator_Str_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_Str_Str ); \ + FString out; \ + MapIteratorGetValueString(self , out); \ + ACTION_RETURN_STRING( out ); \ + } + + + +#define DEFINE_MAP_AND_IT_I_X( name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_X_X( FMap_ ## name , uint32_t , value_type , PARAM_INT , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_IT_I_X( FMap_ ## name , FMapIterator_ ## name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) + + +#define DEFINE_MAP_AND_IT_S_X( name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_X_X( FMap_ ## name , FString , value_type , PARAM_STRING , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_IT_S_X( FMap_ ## name , FMapIterator_ ## name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) + +#define DEFINE_MAP_AND_IT_I_S() \ + DEF_MAP_X_S( FMap_I32_Str , uint32_t , PARAM_INT ) \ + DEF_MAP_IT_I_S() + +#define DEFINE_MAP_AND_IT_S_S() \ + DEF_MAP_X_S( FMap_Str_Str , FString , PARAM_STRING ) \ + DEF_MAP_IT_S_S() + +DEFINE_MAP_AND_IT_I_X(I32_I8 , uint8_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_I_X(I32_I16 , uint16_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_I_X(I32_I32 , uint32_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_I_X(I32_F32 , float , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_I_X(I32_F64 , double , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_I_X(I32_Obj , DObject* , PARAM_OBJPOINTER , ACTION_RETURN_OBJECT); +DEFINE_MAP_AND_IT_I_X(I32_Ptr , void* , PARAM_VOIDPOINTER , ACTION_RETURN_POINTER); +DEFINE_MAP_AND_IT_I_S(); + +DEFINE_MAP_AND_IT_S_X(Str_I8 , uint8_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_S_X(Str_I16 , uint16_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_S_X(Str_I32 , uint32_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_S_X(Str_F32 , float , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_S_X(Str_F64 , double , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_S_X(Str_Obj , DObject* , PARAM_OBJPOINTER , ACTION_RETURN_OBJECT); +DEFINE_MAP_AND_IT_S_X(Str_Ptr , void* , PARAM_VOIDPOINTER , ACTION_RETURN_POINTER); +DEFINE_MAP_AND_IT_S_S(); \ No newline at end of file diff --git a/src/common/scripting/core/maps.h b/src/common/scripting/core/maps.h new file mode 100644 index 000000000..ed303537b --- /dev/null +++ b/src/common/scripting/core/maps.h @@ -0,0 +1,110 @@ +#pragma once + +#include +#include "tarray.h" +#include "refcounted.h" + +class ZSMapInfo : public RefCountedBase +{ +public: + void * self = nullptr; + int rev = 0; +}; + +struct ZSFMap : FMap { + RefCountedPtr info; +}; + +template +class ZSMap : public TMap +{ +public: + RefCountedPtr info; + ZSMap() : + TMap(), info(new ZSMapInfo) + { + info->self = this; + } + ~ZSMap() + { + info->self = nullptr; + } +}; + +template +struct ZSMapIterator +{ + RefCountedPtr info; + TMapIterator *it = nullptr; + typename ZSMap::Pair *p = nullptr; + + typedef KT KeyType; + typedef VT ValueType; + + int rev = 0; + + ~ZSMapIterator() + { + if(it) delete it; + } + + bool Valid() + { + return it && p && info.get() && info->self && info->rev == rev; + } + + bool ReInit() + { + if(info.get() && info->self) { + if(it) delete it; + it = new TMapIterator(*static_cast*>(info->self)); + rev = info->rev; + p = nullptr; + return true; + } + return false; + } + + bool Init(ZSMap &m) + { + info = m.info; + return ReInit(); + } + + bool Next() + { + if(it && info.get() && info->self && info->rev == rev) + { + p = nullptr; + return it->NextPair(p); + } + else + { + ThrowAbortException(X_FORMAT_ERROR,"MapIterator::Next called from invalid iterator"); + } + } + + VT& GetValue() + { + if(p && info.get() && info->self && info->rev == rev) + { + return p->Value; + } + else + { + ThrowAbortException(X_FORMAT_ERROR,p ? "MapIterator::GetValue called from invalid iterator" : "MapIterator::GetValue called from invalid position"); + } + } + + const KT& GetKey() + { + if(p && info.get() && info->self && info->rev == rev) + { + return p->Key; + } + else + { + ThrowAbortException(X_FORMAT_ERROR,p ? "MapIterator::GetKey called from invalid iterator" : "MapIterator::GetKey called from invalid position"); + } + } +}; \ No newline at end of file diff --git a/src/common/scripting/core/types.cpp b/src/common/scripting/core/types.cpp index 0b3589302..702277e89 100644 --- a/src/common/scripting/core/types.cpp +++ b/src/common/scripting/core/types.cpp @@ -33,11 +33,14 @@ ** */ +#include + #include "vmintern.h" #include "s_soundinternal.h" #include "types.h" #include "printf.h" #include "textureid.h" +#include "maps.h" FTypeTable TypeTable; @@ -183,7 +186,7 @@ void PType::SetDefaultValue(void *base, unsigned offset, TArray //========================================================================== // -// PType :: SetDefaultValue +// PType :: SetPointer* // //========================================================================== @@ -195,6 +198,10 @@ void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs { } +void PType::SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) +{ +} + //========================================================================== // // PType :: InitializeValue @@ -1871,6 +1878,23 @@ void PArray::SetPointerArray(void *base, unsigned offset, TArray *specia } } +//========================================================================== +// +// PArray :: SetPointerMap +// +//========================================================================== + +void PArray::SetPointerMap(void *base, unsigned offset, TArray> *special) +{ + if(ElementType->isStruct() || ElementType->isMap()) + { + for (unsigned int i = 0; i < ElementCount; ++i) + { + ElementType->SetPointerMap(base, offset + ElementSize * i, special); + } + } +} + //========================================================================== // // NewArray @@ -2202,12 +2226,89 @@ PDynArray *NewDynArray(PType *type) // //========================================================================== -PMap::PMap(PType *keytype, PType *valtype) -: KeyType(keytype), ValueType(valtype) +enum OverrideFunctionRetType { + OFN_RET_VOID, + OFN_RET_VAL, + OFN_RET_KEY, + OFN_RET_BOOL, +}; +enum OverrideFunctionArgType { + OFN_ARG_VOID, + OFN_ARG_KEY, + OFN_ARG_VAL, + OFN_ARG_KEY_VAL, +}; + +template +void CreateOverrideFunction(MT *self, FName name) +{ + auto Fn = Create(self->BackingType, name); + auto NativeFn = FindFunction(self->BackingType, name.GetChars()); + + assert(NativeFn); + assert(NativeFn->VMPointer); + + + TArray ret; + TArray args; + TArray argflags; + TArray argnames; + + if constexpr(RetType == OFN_RET_VAL) + { + ret.Push(self->ValueType); + } + else if constexpr(RetType == OFN_RET_KEY) + { + ret.Push(self->KeyType); + } + else if constexpr(RetType == OFN_RET_BOOL) + { + ret.Push(TypeBool); + } + + args.Push(NewPointer(self->BackingType)); + argnames.Push(NAME_self); + argflags.Push(VARF_Implicit | VARF_ReadOnly); + + if constexpr(ArgType == OFN_ARG_KEY) + { + args.Push(self->KeyType); + argflags.Push(0); + argnames.Push(NAME_Key); + } + else if constexpr(ArgType == OFN_ARG_VAL) + { + + args.Push(self->ValueType); + argflags.Push(0); + argnames.Push(NAME_Value); + } + else if constexpr(ArgType == OFN_ARG_KEY_VAL) + { + args.Push(self->KeyType); + args.Push(self->ValueType); + argflags.Push(0); + argflags.Push(0); + argnames.Push(NAME_Key); + argnames.Push(NAME_Value); + } + + Fn->AddVariant(NewPrototype(ret, args), argflags, argnames, *NativeFn->VMPointer, VARF_Method | VARF_Native,SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM); + self->FnOverrides.Insert(name, Fn); +} + +PMap::PMap(PType *keytype, PType *valtype, PStruct *backing, int backing_class) +: KeyType(keytype), ValueType(valtype), BackingType(backing), BackingClass((decltype(BackingClass)) backing_class) { mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName()); - Size = sizeof(FMap); - Align = alignof(FMap); + Size = sizeof(ZSFMap); + Align = alignof(ZSFMap); + CreateOverrideFunction(this, NAME_Get); + CreateOverrideFunction(this, NAME_CheckKey); + CreateOverrideFunction(this, NAME_Insert); + CreateOverrideFunction(this, NAME_InsertNew); + CreateOverrideFunction(this, NAME_Remove); } //========================================================================== @@ -2236,6 +2337,345 @@ void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const id2 = (intptr_t)ValueType; } +//========================================================================== +// +// PMap :: InitializeValue +// +//========================================================================== + +void PMap::Construct(void * addr) const { + switch(BackingClass) + { + case MAP_I32_I8: + new(addr) ZSMap(); + break; + case MAP_I32_I16: + new(addr) ZSMap(); + break; + case MAP_I32_I32: + new(addr) ZSMap(); + break; + case MAP_I32_F32: + new(addr) ZSMap(); + break; + case MAP_I32_F64: + new(addr) ZSMap(); + break; + case MAP_I32_OBJ: + new(addr) ZSMap(); + break; + case MAP_I32_PTR: + new(addr) ZSMap(); + break; + case MAP_I32_STR: + new(addr) ZSMap(); + break; + case MAP_STR_I8: + new(addr) ZSMap(); + break; + case MAP_STR_I16: + new(addr) ZSMap(); + break; + case MAP_STR_I32: + new(addr) ZSMap(); + break; + case MAP_STR_F32: + new(addr) ZSMap(); + break; + case MAP_STR_F64: + new(addr) ZSMap(); + break; + case MAP_STR_OBJ: + new(addr) ZSMap(); + break; + case MAP_STR_PTR: + new(addr) ZSMap(); + break; + case MAP_STR_STR: + new(addr) ZSMap(); + break; + }; +} + +void PMap::InitializeValue(void *addr, const void *def) const +{ + if (def != nullptr) + { + I_Error("Map cannot have default values"); + } + Construct(addr); +} + +//========================================================================== +// +// PMap :: DestroyValue +// +//========================================================================== + +void PMap::DestroyValue(void *addr) const +{ + switch(BackingClass) + { + case MAP_I32_I8: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_I16: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_I32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_F32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_F64: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_OBJ: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_PTR: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_STR: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_I8: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_I16: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_I32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_F32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_F64: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_OBJ: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_PTR: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_STR: + static_cast*>(addr)->~ZSMap(); + break; + } +} + +//========================================================================== +// +// PMap :: SetDefaultValue +// +//========================================================================== + +void PMap::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + assert(!(base && special)); + if (base != nullptr) + { + Construct(((uint8_t*)base)+offset); // is this needed? string/dynarray do this initialization if base != nullptr, but their initialization doesn't need to allocate + // it might lead to double allocations (and memory leakage) for Map if both base and special != nullptr + } + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } + else + { + I_Error("null special"); + } +} + +//========================================================================== +// +// PMap :: SetPointer +// +//========================================================================== + +void PMap::SetPointerMap(void *base, unsigned offset, TArray> *special) +{ + if (ValueType->isObjectPointer()) + { + // Add to the list of pointer arrays for this class. + special->Push(std::make_pair(offset,KeyType)); + } +} + +//========================================================================== +// +// PMap :: WriteValue +// +//========================================================================== + +template +static void PMapValueWriter(FSerializer &ar, const M *map, const PMap *m) +{ + TMapConstIterator it(*map); + const typename M::Pair * p; + while(it.NextPair(p)) + { + if constexpr(std::is_same_v) + { + m->ValueType->WriteValue(ar,p->Key.GetChars(),static_cast(&p->Value)); + } + else if constexpr(std::is_same_v) + { + FString key; + key.Format("%u",p->Key); + m->ValueType->WriteValue(ar,key.GetChars(),static_cast(&p->Value)); + } + //else unknown key type + } +} + +void PMap::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + if(ar.BeginObject(key)) + { + switch(BackingClass) + { + case MAP_I32_I8: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_I16: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_I32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_F32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_F64: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_OBJ: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_PTR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_STR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_I8: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_I16: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_I32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_F32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_F64: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_OBJ: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_PTR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_STR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + } + ar.EndObject(); + } +} + +//========================================================================== +// +// PMap :: ReadValue +// +//========================================================================== + + +template +static bool PMapValueReader(FSerializer &ar, M *map, const PMap *m) +{ + const char * k; + while(k = ar.GetKey()) + { + typename M::ValueType * val; + if constexpr(std::is_same_v) + { + val = &map->InsertNew(k); + } + else if constexpr(std::is_same_v) + { + FString s(k); + if(!s.IsInt()) + { + ar.EndObject(); + return false; + } + val = &map->InsertNew(static_cast(s.ToULong())); + } + if (!m->ValueType->ReadValue(ar,nullptr,static_cast(val))) + { + ar.EndObject(); + return false; + } + } + ar.EndObject(); + return true; +} + + +bool PMap::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + DestroyValue(addr); + InitializeValue(addr, nullptr); + if(ar.BeginObject(key)) + { + switch(BackingClass) + { + case MAP_I32_I8: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_I16: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_I32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_F32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_F64: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_OBJ: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_PTR: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_STR: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_I8: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_I16: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_I32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_F32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_F64: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_OBJ: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_PTR: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_STR: + return PMapValueReader(ar, static_cast*>(addr), this); + } + } + return false; +} + //========================================================================== // // NewMap @@ -2245,16 +2685,330 @@ void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const // //========================================================================== -PMap *NewMap(PType *keytype, PType *valuetype) +int PMapBackingClass(PType *keytype, PType *valuetype, FString &backingName) { + int backingClass; + auto key_rtype = keytype->GetRegType(); + auto value_rtype = valuetype->GetRegType(); + if (key_rtype == REGT_INT) + { + if(keytype->Size != 4) + { + I_Error("Unsupported map requested"); + } + else + { + backingName += "I32_"; + backingClass = PMap::MAP_I32_I8; + } + } + else if (key_rtype == REGT_STRING) + { + backingName += "Str_"; + backingClass = PMap::MAP_STR_I8; + } + else + { + I_Error("Unsupported map requested"); + } + switch (valuetype->GetRegType()) + { + case REGT_INT: + backingName.AppendFormat("I%d", valuetype->Size * 8); + backingClass += (valuetype->Size >> 1); + break; + case REGT_FLOAT: + backingName.AppendFormat("F%d", valuetype->Size * 8); + backingClass += PMap::MAP_I32_F32 + (valuetype->Size == 8); + break; + case REGT_STRING: + backingName += "Str"; + backingClass += PMap::MAP_I32_STR; + break; + + case REGT_POINTER: + if (valuetype->isObjectPointer()) + { + backingName += "Obj"; + backingClass += PMap::MAP_I32_OBJ; + } + else + { + backingName += "Ptr"; + backingClass += PMap::MAP_I32_PTR; + } + break; + default: + I_Error("Unsupported map requested"); + break; + } + return backingClass; +} + +PMap *NewMap(PType *keyType, PType *valueType) { size_t bucket; - PType *maptype = TypeTable.FindType(NAME_Map, (intptr_t)keytype, (intptr_t)valuetype, &bucket); - if (maptype == nullptr) + PType *mapType = TypeTable.FindType(NAME_Map, (intptr_t)keyType, (intptr_t)valueType, &bucket); + if (mapType == nullptr) { - maptype = new PMap(keytype, valuetype); - TypeTable.AddType(maptype, NAME_Map, (intptr_t)keytype, (intptr_t)valuetype, bucket); + FString backingName = "Map_"; + int backingClass = PMapBackingClass(keyType, valueType, backingName); + + auto backing = NewStruct(backingName, nullptr, true); + mapType = new PMap(keyType, valueType, backing, backingClass); + TypeTable.AddType(mapType, NAME_Map, (intptr_t)keyType, (intptr_t)valueType, bucket); + } - return (PMap *)maptype; + return (PMap *)mapType; +} + +/* PMap *******************************************************************/ + +//========================================================================== +// +// PMap - Parameterized Constructor +// +//========================================================================== + +PMapIterator::PMapIterator(PType *keytype, PType *valtype, PStruct *backing, int backing_class) + : KeyType(keytype), ValueType(valtype), BackingType(backing), BackingClass((decltype(BackingClass)) backing_class) +{ + mDescriptiveName.Format("MapIterator<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName()); + Size = sizeof(ZSFMap); + Align = alignof(ZSFMap); + CreateOverrideFunction(this, NAME_GetKey); + CreateOverrideFunction(this, NAME_GetValue); + CreateOverrideFunction(this, NAME_SetValue); +} + +//========================================================================== +// +// PMapIterator :: IsMatch +// +//========================================================================== + +bool PMapIterator::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; +} + +//========================================================================== +// +// PMapIterator :: GetTypeIDs +// +//========================================================================== + +void PMapIterator::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)KeyType; + id2 = (intptr_t)ValueType; +} + +//========================================================================== +// +// PMapIterator :: InitializeValue +// +//========================================================================== + +void PMapIterator::Construct(void * addr) const { + switch(BackingClass) + { + case PMap::MAP_I32_I8: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_I16: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_I32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_F32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_F64: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_OBJ: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_PTR: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_STR: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_I8: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_I16: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_I32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_F32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_F64: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_OBJ: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_PTR: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_STR: + new(addr) ZSMapIterator(); + break; + }; +} + +void PMapIterator::InitializeValue(void *addr, const void *def) const +{ + if (def != nullptr) + { + I_Error("Map cannot have default values"); + } + Construct(addr); +} + +//========================================================================== +// +// PMapIterator :: DestroyValue +// +//========================================================================== + +void PMapIterator::DestroyValue(void *addr) const +{ + switch(BackingClass) + { + case PMap::MAP_I32_I8: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_I16: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_I32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_F32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_F64: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_OBJ: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_PTR: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_STR: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_I8: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_I16: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_I32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_F32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_F64: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_OBJ: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_PTR: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_STR: + static_cast*>(addr)->~ZSMapIterator(); + break; + } +} + +//========================================================================== +// +// PMapIterator :: SetDefaultValue +// +//========================================================================== + +void PMapIterator::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + assert(!(base && special)); + if (base != nullptr) + { + Construct(((uint8_t*)base)+offset); + } + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } + else + { + I_Error("null special"); + } +} + +//========================================================================== +// +// PMapIterator :: WriteValue +// +//========================================================================== + +void PMapIterator::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + ar.BeginObject(key); + ar.EndObject(); +} + +//========================================================================== +// +// PMapIterator :: ReadValue +// +//========================================================================== + +bool PMapIterator::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + DestroyValue(addr); + InitializeValue(addr, nullptr); + ar.BeginObject(key); + ar.EndObject(); + return true; +} + +//========================================================================== +// +// NewMapIterator +// +// Returns a PMapIterator for the given key and value types, ensuring not to create +// duplicates. +// +//========================================================================== + +PMapIterator *NewMapIterator(PType *keyType, PType *valueType) +{ + size_t bucket; + PType *mapIteratorType = TypeTable.FindType(NAME_MapIterator, (intptr_t)keyType, (intptr_t)valueType, &bucket); + if (mapIteratorType == nullptr) + { + FString backingName = "MapIterator_"; + int backingClass = PMapBackingClass(keyType, valueType, backingName); + + auto backing = NewStruct(backingName, nullptr, true); + mapIteratorType = new PMapIterator(keyType, valueType, backing, backingClass); + TypeTable.AddType(mapIteratorType, NAME_MapIterator, (intptr_t)keyType, (intptr_t)valueType, bucket); + } + return (PMapIterator *)mapIteratorType; } /* PStruct ****************************************************************/ @@ -2333,6 +3087,26 @@ void PStruct::SetPointerArray(void *base, unsigned offset, TArray *speci } } +//========================================================================== +// +// PStruct :: SetPointerMap +// +//========================================================================== + +void PStruct::SetPointerMap(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->SetPointerMap(base, unsigned(offset + field->Offset), special); + } + } +} + //========================================================================== // // PStruct :: WriteValue diff --git a/src/common/scripting/core/types.h b/src/common/scripting/core/types.h index 14597ddfd..03ac8d89e 100644 --- a/src/common/scripting/core/types.h +++ b/src/common/scripting/core/types.h @@ -129,6 +129,7 @@ public: 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); + virtual void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL); // Initialize the value, if needed (e.g. strings) virtual void InitializeValue(void *addr, const void *def) const; @@ -200,6 +201,8 @@ public: bool isArray() const { return !!(Flags & TYPE_Array); } bool isStaticArray() const { return TypeTableType == NAME_StaticArray; } bool isDynArray() const { return TypeTableType == NAME_DynArray; } + bool isMap() const { return TypeTableType == NAME_Map; } + bool isMapIterator() const { return TypeTableType == NAME_MapIterator; } bool isStruct() const { return TypeTableType == NAME_Struct; } bool isClass() const { return TypeTableType == NAME_Object; } bool isPrototype() const { return TypeTableType == NAME_Prototype; } @@ -489,6 +492,7 @@ public: void SetDefaultValue(void *base, unsigned offset, TArray *special) override; void SetPointer(void *base, unsigned offset, TArray *special) override; void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL) override; }; class PStaticArray : public PArray @@ -521,14 +525,72 @@ public: class PMap : public PCompoundType { + void Construct(void * addr) const; public: - PMap(PType *keytype, PType *valtype); + PMap(PType *keytype, PType *valtype, PStruct *backing, int backing_class); PType *KeyType; PType *ValueType; + TMap FnOverrides; + + PStruct *BackingType; + + enum EBackingClass { + MAP_I32_I8, + MAP_I32_I16, + MAP_I32_I32, + MAP_I32_F32, + MAP_I32_F64, + MAP_I32_OBJ, + MAP_I32_PTR, + MAP_I32_STR, + + MAP_STR_I8, + MAP_STR_I16, + MAP_STR_I32, + MAP_STR_F32, + MAP_STR_F64, + MAP_STR_OBJ, + MAP_STR_PTR, + MAP_STR_STR, + } BackingClass; + 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 SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; +}; + + +class PMapIterator : public PCompoundType +{ + void Construct(void * addr) const; +public: + PMapIterator(PType *keytype, PType *valtype, PStruct *backing, int backing_class); + + PType *KeyType; + PType *ValueType; + + TMap FnOverrides; + + PStruct *BackingType; + + PMap::EBackingClass BackingClass; + + 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; }; class PStruct : public PContainerType @@ -550,6 +612,7 @@ public: void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; void SetPointer(void *base, unsigned offset, TArray *specials) override; void SetPointerArray(void *base, unsigned offset, TArray *special) override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; }; class PPrototype : public PCompoundType @@ -586,6 +649,7 @@ inline PClass *PObjectPointer::PointedClass() const // Returns a type from the TypeTable. Will create one if it isn't present. PMap *NewMap(PType *keytype, PType *valuetype); +PMapIterator *NewMapIterator(PType *keytype, PType *valuetype); PArray *NewArray(PType *type, unsigned int count); PStaticArray *NewStaticArray(PType *type); PDynArray *NewDynArray(PType *type); diff --git a/src/common/scripting/frontend/ast.cpp b/src/common/scripting/frontend/ast.cpp index a491bc0a9..44cc0f516 100644 --- a/src/common/scripting/frontend/ast.cpp +++ b/src/common/scripting/frontend/ast.cpp @@ -502,6 +502,16 @@ static void PrintMapType(FLispString &out, const ZCC_TreeNode *node) out.Close(); } +static void PrintMapIteratorType(FLispString &out, const ZCC_TreeNode *node) +{ + ZCC_MapIteratorType *tnode = (ZCC_MapIteratorType *)node; + out.Open("map-iterator-type"); + PrintNodes(out, tnode->ArraySize); + PrintNodes(out, tnode->KeyType); + PrintNodes(out, tnode->ValueType); + out.Close(); +} + static void PrintDynArrayType(FLispString &out, const ZCC_TreeNode *node) { ZCC_DynArrayType *tnode = (ZCC_DynArrayType *)node; @@ -955,6 +965,7 @@ static const NodePrinterFunc TreeNodePrinter[] = PrintType, PrintBasicType, PrintMapType, + PrintMapIteratorType, PrintDynArrayType, PrintClassType, PrintExpression, diff --git a/src/common/scripting/frontend/zcc-parse.lemon b/src/common/scripting/frontend/zcc-parse.lemon index 27d74fc87..560f15ad8 100644 --- a/src/common/scripting/frontend/zcc-parse.lemon +++ b/src/common/scripting/frontend/zcc-parse.lemon @@ -932,7 +932,7 @@ type_name(X) ::= DOT dottable_id(A). /* Type names can also be used as identifiers in contexts where type names * are not normally allowed. */ %fallback IDENTIFIER - SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 VECTOR4 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY. + SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 VECTOR4 NAME MAP MAPITERATOR ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY. /* Aggregate types */ %type aggregate_type {ZCC_Type *} @@ -944,7 +944,7 @@ type_name(X) ::= DOT dottable_id(A). %type array_size{ZCC_Expression *} %type array_size_expr{ZCC_Expression *} -aggregate_type(X) ::= MAP(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* Hash table */ +aggregate_type(X) ::= MAP(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* ZSMap */ { NEW_AST_NODE(MapType,map,T); map->KeyType = A; @@ -952,6 +952,14 @@ aggregate_type(X) ::= MAP(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* H X = map; } +aggregate_type(X) ::= MAPITERATOR(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* ZSMapIterator */ +{ + NEW_AST_NODE(MapIteratorType,map_it,T); + map_it->KeyType = A; + map_it->ValueType = B; + X = map_it; +} + aggregate_type(X) ::= ARRAY(T) LT type_or_array(A) GT. /* TArray */ { NEW_AST_NODE(DynArrayType,arr,T); diff --git a/src/common/scripting/frontend/zcc_compile.cpp b/src/common/scripting/frontend/zcc_compile.cpp index b6fa2340a..7777b7327 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -1853,16 +1853,96 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n } case AST_MapType: - if (allowarraytypes) + { + if(AST.ParseVersion < MakeVersion(4, 10, 0)) { - Error(field, "%s: Map types not implemented yet", name.GetChars()); - // Todo: Decide what we allow here and if it makes sense to allow more complex constructs. - auto mtype = static_cast(ztype); - retval = NewMap(DetermineType(outertype, field, name, mtype->KeyType, false, false), DetermineType(outertype, field, name, mtype->ValueType, false, false)); + Error(field, "Map not accessible to ZScript version %d.%d.%d", AST.ParseVersion.major, AST.ParseVersion.minor, AST.ParseVersion.revision); break; } - break; + // Todo: Decide what we allow here and if it makes sense to allow more complex constructs. + auto mtype = static_cast(ztype); + + auto keytype = DetermineType(outertype, field, name, mtype->KeyType, false, false); + auto valuetype = DetermineType(outertype, field, name, mtype->ValueType, false, false); + + if (keytype->GetRegType() == REGT_INT) + { + if (keytype->Size != 4) + { + Error(field, "Map<%s , ...> not implemented yet", keytype->DescriptiveName()); + break; + } + } + else if (keytype->GetRegType() != REGT_STRING) + { + Error(field, "Map<%s , ...> not implemented yet", keytype->DescriptiveName()); + break; + } + + switch(valuetype->GetRegType()) + { + case REGT_FLOAT: + case REGT_INT: + case REGT_STRING: + case REGT_POINTER: + if (valuetype->GetRegCount() > 1) + { + Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + break; + } + + retval = NewMap(keytype, valuetype); + break; + default: + Error(field, "%s: Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + } + + break; + } + case AST_MapIteratorType: + { + if(AST.ParseVersion < MakeVersion(4, 10, 0)) + { + Error(field, "MapIterator not accessible to ZScript version %d.%d.%d", AST.ParseVersion.major, AST.ParseVersion.minor, AST.ParseVersion.revision); + break; + } + // Todo: Decide what we allow here and if it makes sense to allow more complex constructs. + auto mtype = static_cast(ztype); + + auto keytype = DetermineType(outertype, field, name, mtype->KeyType, false, false); + auto valuetype = DetermineType(outertype, field, name, mtype->ValueType, false, false); + + if (keytype->GetRegType() == REGT_INT) + { + if (keytype->Size != 4) + { + Error(field, "MapIterator<%s , ...> not implemented yet", keytype->DescriptiveName()); + } + } + else if (keytype->GetRegType() != REGT_STRING) + { + Error(field, "MapIterator<%s , ...> not implemented yet", keytype->DescriptiveName()); + } + + switch(valuetype->GetRegType()) + { + case REGT_FLOAT: + case REGT_INT: + case REGT_STRING: + case REGT_POINTER: + if (valuetype->GetRegCount() > 1) + { + Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + break; + } + retval = NewMapIterator(keytype, valuetype); + break; + default: + Error(field, "%s: Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + } + break; + } case AST_DynArrayType: { auto atype = static_cast(ztype); @@ -2357,7 +2437,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); int flags = 0; - if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray()) + if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray() || type->isMap() || type->isMapIterator()) { // Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly. type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/); diff --git a/src/common/scripting/frontend/zcc_parser.cpp b/src/common/scripting/frontend/zcc_parser.cpp index e1171400b..2343e4909 100644 --- a/src/common/scripting/frontend/zcc_parser.cpp +++ b/src/common/scripting/frontend/zcc_parser.cpp @@ -220,6 +220,7 @@ static void InitTokenMap() TOKENDEF2(TK_Vector3, ZCC_VECTOR3, NAME_Vector3); TOKENDEF2(TK_Name, ZCC_NAME, NAME_Name); TOKENDEF2(TK_Map, ZCC_MAP, NAME_Map); + TOKENDEF2(TK_MapIterator, ZCC_MAPITERATOR,NAME_MapIterator); TOKENDEF2(TK_Array, ZCC_ARRAY, NAME_Array); TOKENDEF2(TK_Include, ZCC_INCLUDE, NAME_Include); TOKENDEF (TK_Void, ZCC_VOID); @@ -896,6 +897,19 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c break; } + case AST_MapIteratorType: + { + TreeNodeDeepCopy_Start(MapIteratorType); + + // ZCC_Type + copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); + // AST_MapIteratorType + copy->KeyType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->KeyType, true, copiedNodesList)); + copy->ValueType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ValueType, true, copiedNodesList)); + + break; + } + case AST_DynArrayType: { TreeNodeDeepCopy_Start(DynArrayType); diff --git a/src/common/scripting/frontend/zcc_parser.h b/src/common/scripting/frontend/zcc_parser.h index e32a08066..7d2cce2e8 100644 --- a/src/common/scripting/frontend/zcc_parser.h +++ b/src/common/scripting/frontend/zcc_parser.h @@ -98,6 +98,7 @@ enum EZCCTreeNodeType AST_Type, AST_BasicType, AST_MapType, + AST_MapIteratorType, AST_DynArrayType, AST_ClassType, AST_Expression, @@ -367,6 +368,12 @@ struct ZCC_MapType : ZCC_Type ZCC_Type *ValueType; }; +struct ZCC_MapIteratorType : ZCC_Type +{ + ZCC_Type *KeyType; + ZCC_Type *ValueType; +}; + struct ZCC_DynArrayType : ZCC_Type { ZCC_Type *ElementType; diff --git a/src/common/utility/engineerrors.cpp b/src/common/utility/engineerrors.cpp index 1e2e2e82e..4d0125819 100644 --- a/src/common/utility/engineerrors.cpp +++ b/src/common/utility/engineerrors.cpp @@ -36,6 +36,7 @@ bool gameisdead; #ifdef _WIN32 +#include #include #include "zstring.h" void I_DebugPrint(const char *cp) @@ -46,10 +47,31 @@ void I_DebugPrint(const char *cp) OutputDebugStringW(wstr.c_str()); } } + +void I_DebugPrintf(const char *fmt,...) +{ + if (IsDebuggerPresent()) + { + va_list args; + va_start(args, fmt); + + FString s; + s.VFormat(fmt, args); + + va_end(args); + + auto wstr = WideString(s); + OutputDebugStringW(wstr.c_str()); + } +} #else void I_DebugPrint(const char *cp) { -} +} + +void I_DebugPrintf(const char *fmt,...) +{ +} #endif #include "engineerrors.h" diff --git a/src/common/utility/tarray.h b/src/common/utility/tarray.h index 1df7e5d39..a7decd4f3 100644 --- a/src/common/utility/tarray.h +++ b/src/common/utility/tarray.h @@ -916,6 +916,9 @@ public: typedef struct { const KT Key; VT Value; } Pair; typedef const Pair ConstPair; + typedef KT KeyType; + typedef VT ValueType; + TMap() { NumUsed = 0; SetNodeVector(1); } TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); } ~TMap() { ClearNodeVector(); } diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 8809734e7..409acc2e3 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -3,6 +3,7 @@ version "4.10" // Generic engine code #include "zscript/engine/base.zs" #include "zscript/engine/dynarrays.zs" +#include "zscript/engine/maps.zs" #include "zscript/engine/dictionary.zs" #include "zscript/engine/inputevents.zs" #include "zscript/engine/service.zs" diff --git a/wadsrc/static/zscript/engine/maps.zs b/wadsrc/static/zscript/engine/maps.zs new file mode 100644 index 000000000..d10ffe5d1 --- /dev/null +++ b/wadsrc/static/zscript/engine/maps.zs @@ -0,0 +1,447 @@ + +struct Map_I32_I8 native +{ + native void Copy(Map_I32_I8 other); + native void Move(Map_I32_I8 other); + native void Swap(Map_I32_I8 other); + native void Clear(); + native uint CountUsed(); + + native int Get(int key); + native bool CheckKey(int key); + native void Insert(int key,int value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_I8 native +{ + native bool Init(Map_I32_I8 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_I32_I16 native +{ + native void Copy(Map_I32_I16 other); + native void Move(Map_I32_I16 other); + native void Swap(Map_I32_I16 other); + native void Clear(); + native uint CountUsed(); + + native int Get(int key); + native bool CheckKey(int key); + native void Insert(int key,int value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_I16 native +{ + native bool Init(Map_I32_I16 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_I32_I32 native +{ + native void Copy(Map_I32_I32 other); + native void Move(Map_I32_I32 other); + native void Swap(Map_I32_I32 other); + native void Clear(); + native uint CountUsed(); + + native int Get(int key); + native bool CheckKey(int key); + native void Insert(int key,int value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_I32 native +{ + native bool Init(Map_I32_I32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_I32_F32 native +{ + native void Copy(Map_I32_F32 other); + native void Move(Map_I32_F32 other); + native void Swap(Map_I32_F32 other); + native void Clear(); + native uint CountUsed(); + + native double Get(int key); + native bool CheckKey(int key); + native void Insert(int key,double value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_F32 native +{ + native bool Init(Map_I32_F32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_I32_F64 native +{ + native void Copy(Map_I32_F64 other); + native void Move(Map_I32_F64 other); + native void Swap(Map_I32_F64 other); + native void Clear(); + native uint CountUsed(); + + native double Get(int key); + native bool CheckKey(int key); + native void Insert(int key,double value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_F64 native +{ + native bool Init(Map_I32_F64 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_I32_Obj native +{ + native void Copy(Map_I32_Obj other); + native void Move(Map_I32_Obj other); + native void Swap(Map_I32_Obj other); + native void Clear(); + native uint CountUsed(); + + native Object Get(int key); + native bool CheckKey(int key); + native void Insert(int key,Object value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_Obj native +{ + native bool Init(Map_I32_Obj other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native Object GetValue(); + native void SetValue(Object value); +} + +struct Map_I32_Ptr native +{ + native void Copy(Map_I32_Ptr other); + native void Move(Map_I32_Ptr other); + native void Swap(Map_I32_Ptr other); + native void Clear(); + native uint CountUsed(); + + native voidptr Get(int key); + native bool CheckKey(int key); + native void Insert(int key,voidptr value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_Ptr native +{ + native bool Init(Map_I32_Ptr other); + native bool Next(); + + native int GetKey(); + native voidptr GetValue(); + native void SetValue(voidptr value); +} + +struct Map_I32_Str native +{ + native void Copy(Map_I32_Str other); + native void Move(Map_I32_Str other); + native void Swap(Map_I32_Str other); + native void Clear(); + native uint CountUsed(); + + native String Get(int key); + native bool CheckKey(int key); + native void Insert(int key,String value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_Str native +{ + native bool Init(Map_I32_Str other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native String GetValue(); + native void SetValue(String value); +} + +// --------------- + +struct Map_Str_I8 native +{ + native void Copy(Map_Str_I8 other); + native void Move(Map_Str_I8 other); + native void Swap(Map_Str_I8 other); + native void Clear(); + native uint CountUsed(); + + native int Get(String key); + native bool CheckKey(String key); + native void Insert(String key,int value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_I8 native +{ + native bool Init(Map_Str_I8 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_Str_I16 native +{ + native void Copy(Map_Str_I16 other); + native void Move(Map_Str_I16 other); + native void Swap(Map_Str_I16 other); + native void Clear(); + native uint CountUsed(); + + native int Get(String key); + native bool CheckKey(String key); + native void Insert(String key,int value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_I16 native +{ + native bool Init(Map_Str_I16 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_Str_I32 native +{ + native void Copy(Map_Str_I32 other); + native void Move(Map_Str_I32 other); + native void Swap(Map_Str_I32 other); + native void Clear(); + native uint CountUsed(); + + native int Get(String key); + native bool CheckKey(String key); + native void Insert(String key,int value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_I32 native +{ + native bool Init(Map_Str_I32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_Str_F32 native +{ + native void Copy(Map_Str_F32 other); + native void Move(Map_Str_F32 other); + native void Swap(Map_Str_F32 other); + native void Clear(); + native uint CountUsed(); + + native double Get(String key); + native bool CheckKey(String key); + native void Insert(String key,double value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_F32 native +{ + native bool Init(Map_Str_F32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_Str_F64 native +{ + native void Copy(Map_Str_F64 other); + native void Move(Map_Str_F64 other); + native void Swap(Map_Str_F64 other); + native void Clear(); + native uint CountUsed(); + + native double Get(String key); + native bool CheckKey(String key); + native void Insert(String key,double value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_F64 native +{ + native bool Init(Map_Str_F64 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_Str_Obj native +{ + native void Copy(Map_Str_Obj other); + native void Move(Map_Str_Obj other); + native void Swap(Map_Str_Obj other); + native void Clear(); + native uint CountUsed(); + + native Object Get(String key); + native bool CheckKey(String key); + native void Insert(String key,Object value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_Obj native +{ + native bool Init(Map_Str_Obj other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native Object GetValue(); + native void SetValue(Object value); +} + +struct Map_Str_Ptr native +{ + native void Copy(Map_Str_Ptr other); + native void Move(Map_Str_Ptr other); + native void Swap(Map_Str_Ptr other); + native void Clear(); + native uint CountUsed(); + + native voidptr Get(String key); + native bool CheckKey(String key); + native void Insert(String key,voidptr value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_Ptr native +{ + native bool Init(Map_Str_Ptr other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native voidptr GetValue(); + native void SetValue(voidptr value); +} + +struct Map_Str_Str native +{ + native void Copy(Map_Str_Str other); + native void Move(Map_Str_Str other); + native void Swap(Map_Str_Str other); + native void Clear(); + native uint CountUsed(); + + native String Get(String key); + native bool CheckKey(String key); + native void Insert(String key,String value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_Str native +{ + native bool Init(Map_Str_Str other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native String GetValue(); + native void SetValue(String value); +}